/*
 * install.c  -  Install a network driver binary file
 *
 * Copyright (C) 2004-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: install.c,v 1.8 2007/02/01 12:09:21 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include "makerom.h"
#include "doconfig.h"



/*
 * Check if a directory is writable, and create it if it doesn't
 * exist.
 */
static char *checkrwdir __F((dirname), const char *dirname)
{
  char *retdir = NULL;
  char *dirbuf = NULL;
  char *cp;
  size_t len;

  copystr(&dirbuf, dirname);
  cp = strtok(dirbuf, ":");
  while (cp != NULL && retdir == NULL) {
	len = strlen(cp);
	if (len > 1 && cp[0] == '/' && cp[1] != '/') {
		if (cp[len - 1] == '/')
			cp[len - 1] = '\0';
		copystr(&retdir, cp);
		checkaccess(&retdir, NULL, ACCESS_DIR_EXIST);
		if (retdir != NULL)
			checkaccess(&retdir, NULL, ACCESS_DIR_RW);
#ifdef HAVE_MKDIR
		else if (mkdir(cp, 0755) == 0)
			copystr(&retdir, cp);
#endif
	}
	cp = strtok(NULL, ":");
  }
  free(dirbuf);
  return(retdir);
}



/*
 * Check if the network driver installation directory is writable
 */
static char *checkdrvdir __F((drvsubdir), const char *drvsubdir)
{
  char *retdir = NULL;
  char *subdir = NULL;
  char *tmpdir;

  /* Check for writable network driver directory */
  assert(config.netdrvdir != NULL);
  if ((retdir = checkrwdir(config.netdrvdir)) == NULL) {
#ifdef HAVE_MKDIR
	prnerr("unable to create network driver installation directory");
#else
	prnerr("unable to find writable network driver installation directory");
#endif
	nbexit(EXIT_MAKEROM_INSTDRV);
  }

  /* Check network driver subdirectory */
  tmpdir = (char *)nbmalloc(strlen(retdir) + 1 + strlen(drvsubdir) + 1);
  sprintf(tmpdir, "%s/%s", retdir, drvsubdir);
  copystr(&subdir, tmpdir);
  checkaccess(&subdir, NULL, ACCESS_DIR_EXIST);
  if (subdir != NULL) {
	checkaccess(&subdir, NULL, ACCESS_DIR_RW);
	if (subdir == NULL) {
		prnerr("directory '%s' is not writable", tmpdir);
		nbexit(EXIT_MAKEROM_INSTDRV);
	}
	free(subdir);
  }
#ifdef HAVE_MKDIR
  else if (mkdir(tmpdir, 0755) != 0) {
	prnerr("unable to create directory '%s'", tmpdir);
	nbexit(EXIT_MAKEROM_INSTDRV);
  }
#else
  else {
	prnerr("directory '%s' does not exist", tmpdir);
	nbexit(EXIT_MAKEROM_INSTDRV);
  }
#endif
  free(tmpdir);
  return(retdir);
}



/*
 * Check is MD5 checksum file is writable. This routine assumes that the
 * MD5 checksum file is located in the network driver directory, and that
 * this directory is writable. If this is not true, the MD5 checksum file
 * needs to contain absolute path names.
 * Note that the netdrvdir parameter specifies the directory which has been
 * selected for installing the network driver into. It is different from the
 * config.netdrvdir variable.
 */
static char *checkmd5file __F((netdrvdir), const char *netdrvdir)
{
  char *retname = NULL;
  char *dirbuf = NULL;
  char *md5file = NULL;
  char *md5tmp = NULL;
  char *cp1, *cp2;

  /*
   * Scan through the list of all possible MD5 checksum files. If we find
   * one which is located in the network driver directory, we use that. Other-
   * wise we remember the name of the first MD5 checksum file which is in a
   * writable directory.
   */
  assert(config.md5fname != NULL);
  copystr(&dirbuf, config.md5fname);
  setpath(&dirbuf, config.netdrvdir);
  cp1 = strtok(dirbuf, ":");
  while (cp1 != NULL && retname == NULL) {
	copystr(&md5file, cp1);
	if ((cp2 = strrchr(cp1, '/')) != NULL && cp2[1] != '\0') {
		*cp2 = '\0';
		if (!strcmp(cp1, netdrvdir)) {
			copystr(&retname, md5file);
			checkaccess(&md5file, NULL, ACCESS_FILE_EXIST);
			if (md5file != NULL)
				checkaccess(&retname, NULL, ACCESS_FILE_RW);
		} else if (md5tmp == NULL) {
			copystr(&md5tmp, cp1);
			checkaccess(&md5tmp, NULL, ACCESS_DIR_RW);
			if (md5tmp != NULL) {
				copystr(&md5tmp, md5file);
				checkaccess(&md5tmp, NULL, ACCESS_FILE_EXIST);
				if (md5tmp != NULL) {
					checkaccess(&md5file, NULL,
							ACCESS_FILE_RW);
					free(md5tmp);
					md5tmp = md5file;
					md5file = NULL;
				}
			}
		}
	}
	cp1 = strtok(NULL, ":");
  }

  /*
   * If we couldn't find an MD5 checksum file in the network driver directory
   * use one which is in a writable directory.
   */
  if (retname == NULL) {
	retname = md5tmp;
	md5tmp = NULL;
  }
  if (retname == NULL) {
	prnerr("unable to find a writable MD5 checksum file");
	nbexit(EXIT_MAKEROM_INSTDRV);
  }

  /* Finally clean up */
  if (md5file != NULL)
	free(md5file);
  if (md5tmp != NULL)
	free(md5tmp);
  if (dirbuf != NULL)
	free(dirbuf);
  return(retname);
}



/*
 * Call installation program
 */
static void doexec __F((drvname, drvtype, md5file, netdrvdir),
				char *drvname AND
				char *drvtype AND
				char *md5file AND
				char *netdrvdir)
{
  char *args[11];
  int exitcode;

  /* Check if we have anything to do */
  if (config.instprog == NULL) {
	prnerr("no driver installation program specified");
	nbexit(EXIT_MAKEROM_INSTDRV);
  }

  /* Prepare command line arguments for instdrv program */
  args[0] = config.instprog;
  args[1] = "-d";
  args[2] = drvname;
  args[3] = "-t";
  args[4] = drvtype;
  args[5] = "-m";
  args[6] = md5file;
  args[7] = "-n";
  args[8] = netdrvdir;
  args[9] = (verbose >= LOGLEVEL_DEBUG ? "-v" : NULL);
  args[10] = NULL;

  /* Call instdrv program */
  exitcode = nbexec(config.instprog, args);
  if (exitcode > 0) {
	prnerr("unable to install driver file (exit code %d)", exitcode);
	nbexit(EXIT_MAKEROM_INSTDRV);
  } else if (exitcode == -1)
	nbexit(-1);
}



/*
 * Install driver file
 */
char *instdrv __F((drvname, drvtype), char *drvname AND int drvtype)
{
  char *retval, *fname;
  char *netdrvdir;
  char *subdir;
  char *md5file;
  size_t len;

  /* Check if anything to do */
  if (drvname == NULL)
	return(NULL);

  /* Determine subdirectory for driver type */
  switch (drvtype) {
	case DRVTYPE_PD:
		subdir = "pktdrvr";
		break;
	case DRVTYPE_NDIS:
		subdir = "ndis2";
		break;
	case DRVTYPE_UNDI:
		subdir = "undi";
		break;
	default:
		subdir = NULL;
		break;
  }
  assert(subdir != NULL);

  /* Determine network driver directory */
  netdrvdir = checkdrvdir(subdir);
  assert(netdrvdir != NULL);

  /* Determine MD5 checksum file */
  md5file = checkmd5file(netdrvdir);
  assert(md5file != NULL);

  /* Call installation program */
  doexec(drvname, subdir, md5file, netdrvdir);

  /* Prepare return value */
  if ((fname = strrchr(drvname, '/')) != NULL)
	fname++;
  else
	fname = drvname;
  len = strlen(netdrvdir) + 1 + strlen(subdir) + 1 + strlen(fname) + 1;
  retval = (char *)nbmalloc(len);
  sprintf(retval, "%s/%s/%s", netdrvdir, subdir, fname);
  checkaccess(&retval, NULL, ACCESS_FILE_READ);

  /* Cleanup */
  free(netdrvdir);
  free(md5file);
  return(retval);
}

