/*
* loader.c
*
* This is the installer loader.  Its job is to somehow load the rest
* of the installer into memory and run it.  This may require setting
* up some devices and networking, etc. The main point of this code is
* to stay SMALL! Remember that, live by that, and learn to like it.
*
* Erik Troan <[email protected]>
* Matt Wilson <[email protected]>
*
* Copyright 1999 Red Hat, Inc.
*
* This software may be freely redistributed under the terms of the GNU
* public license.
*
* 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.
*
*/

#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <newt.h>
#include <popt.h>
#include <rpmio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <zlib.h>

#include "balkan/balkan.h"
#include "isys/imount.h"
#include "isys/isys.h"
#include "isys/probe.h"
#include "kudzu/kudzu.h"

#include "cdrom.h"
#include "devices.h"
#include "kickstart.h"
#include "lang.h"
#include "loader.h"
#include "log.h"
#include "misc.h"
#include "modules.h"
#include "net.h"
#include "pcmcia.h"
#include "urls.h"
#include "windows.h"

int probe_main(int argc, char ** argv);
int rmmod_main(int argc, char ** argv);
int cardmgr_main(int argc, char ** argv);
int ourInsmodCommand(int argc, char ** argv);
int kon_main(int argc, char ** argv);

struct knownDevices devices;

struct installMethod {
   char * name;
   int network;
   enum deviceClass deviceType;                        /* for pcmcia */
   char * (*mountImage)(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags);
};

#ifdef INCLUDE_LOCAL
static char * mountCdromImage(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags);
static char * mountHardDrive(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags);
#endif
#ifdef INCLUDE_NETWORK
static char * mountNfsImage(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags);
static char * mountUrlImage(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags);
#endif

static struct installMethod installMethods[] = {
#if defined(INCLUDE_LOCAL)
   { N_("Local CDROM"), 0, CLASS_CDROM, mountCdromImage },
#endif
#if defined(INCLUDE_NETWORK)
   { N_("NFS image"), 1, CLASS_NETWORK, mountNfsImage },
   { "FTP", 1, CLASS_NETWORK, mountUrlImage },
   { "HTTP", 1, CLASS_NETWORK, mountUrlImage },
#endif
#if defined(INCLUDE_LOCAL)
   { N_("Hard drive"), 0, CLASS_HD, mountHardDrive },
#endif
};
static int numMethods = sizeof(installMethods) / sizeof(struct installMethod);

static int newtRunning = 0;

void doSuspend(void) {
   newtFinished();
   exit(1);
}

static void startNewt(int flags) {
   if (!newtRunning) {
       newtInit();
       newtCls();
       newtDrawRootText(0, 0, _("Welcome to Red Hat Linux"));

       newtPushHelpLine(_("  <Tab>/<Alt-Tab> between elements  | <Space> selects | <F12> next screen "));

       newtRunning = 1;
       if (FL_TESTING(flags))
           newtSetSuspendCallback((void *) doSuspend, NULL);
   }
}

static void stopNewt(void) {
   if (newtRunning) newtFinished();
}

static void spawnShell(int flags) {
   pid_t pid;
   int fd;

   if (!FL_TESTING(flags)) {
       fd = open("/dev/tty2", O_RDWR);
       if (fd < 0) {
           logMessage("cannot open /dev/tty2 -- no shell will be provided");
           return;
       } else if (access("/bin/sh",  X_OK))  {
           logMessage("cannot open shell - /bin/sh doesn't exist");
           return;
       }

       if (!(pid = fork())) {
           dup2(fd, 0);
           dup2(fd, 1);
           dup2(fd, 2);

           close(fd);
           setsid();
           if (ioctl(0, TIOCSCTTY, NULL)) {
               perror("could not set new controlling tty");
           }

           execl("/bin/sh", "-/bin/sh", NULL);
           logMessage("exec of /bin/sh failed: %s", strerror(errno));
       }

       close(fd);
   } else {
       logMessage("not spawning a shell as we're in test mode");
   }

   return;
}

static int detectHardware(moduleInfoSet modInfo,
                         struct moduleInfo *** modules, int flags) {
   struct device ** devices, ** device;
   struct moduleInfo * mod, ** modList;
   int numMods, i;
   char *driver;

   initializeDeviceList();

   logMessage("probing buses");

   devices = probeDevices(CLASS_UNSPEC,BUS_PCI|BUS_SBUS,PROBE_ALL);

   logMessage("finished bus probing");

   if (devices == NULL) {
       *modules = NULL;
       return LOADER_OK;
   }

   modList = malloc(sizeof(*modList) * 50);    /* should be enough */
   numMods = 0;

   for (device = devices; *device; device++) {
       driver = (*device)->driver;
       if (strcmp (driver, "ignore") && strcmp (driver, "unknown")) {
           logMessage("found suggestion of %s", driver);
           if ((mod = isysFindModuleInfo(modInfo, driver))) {
               logMessage("found %s device", driver);
               for (i = 0; i < numMods; i++)
                   if (modList[i] == mod) break;
               if (i == numMods)
                   modList[numMods++] = mod;
           }
       }
       freeDevice (*device);
   }

   if (numMods) {
       *modules = modList;
       modList[numMods] = NULL;
   } else {
       free(modList);
       *modules = NULL;
   }

   free(devices);

   return LOADER_OK;
}

int addDeviceManually(moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, struct knownDevices * kd, int flags) {
   char * pristineItems[] = { N_("SCSI"), N_("Network") };
   char * items[3];
   int i, rc;
   int choice = 0;
   enum deviceClass type;

   for (i = 0; i < sizeof(pristineItems) / sizeof(*pristineItems); i++) {
       items[i] = _(pristineItems[i]);
   }

   items[i] = NULL;

   do {
       rc = newtWinMenu(_("Devices"),
                      _("What kind of device would you like to add"), 40,
                      0, 20, 2, items, &choice, _("OK"), _("Back"), NULL);
       if (rc == 2) return LOADER_BACK;

       if (choice == 1)
           type = DRIVER_NET;
       else
           type = DRIVER_SCSI;

       rc = devDeviceMenu(type, modInfo, modLoaded, modDeps, flags, NULL);
   } while (rc);

   return 0;
}

int manualDeviceCheck(moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, struct knownDevices * kd, int flags) {
   int i, rc;
   char buf[2000];
   struct moduleInfo * mi;
   newtComponent done, add, text, items, form, answer;
   newtGrid grid, buttons;
   int numItems;
   int maxWidth;

   while (1) {
       numItems = 0;
       maxWidth = 0;
       for (i = 0, *buf = '\0'; i < modLoaded->numModules; i++) {
           if (!modLoaded->mods[i].weLoaded) continue;

           if (!(mi = isysFindModuleInfo(modInfo, modLoaded->mods[i].name))) {
               continue;
           }

           strcat(buf, "    ");
           strcat(buf, mi->description);

           if (maxWidth < strlen(mi->description))
               maxWidth = strlen(mi->description);

           strcat(buf, "\n");
           numItems++;
       }

       if (numItems > 0) {
           text = newtTextboxReflowed(-1, -1,
               _("I have found the following devices in your system:"),
               40, 5, 20, 0);
           buttons = newtButtonBar(_("Done"), &done, _("Add Device"), &add,
                                   NULL);
           items = newtTextbox(-1, -1, maxWidth + 8,
                               numItems < 10 ? numItems : 10,
                               (numItems < 10 ? 0 : NEWT_FLAG_SCROLL));

           newtTextboxSetText(items, buf);

           grid = newtGridSimpleWindow(text, items, buttons);
           newtGridWrappedWindow(grid, _("Devices"));

           form = newtForm(NULL, NULL, 0);
           newtGridAddComponentsToForm(grid, form, 1);

           answer = newtRunForm(form);
           newtPopWindow();

           newtGridFree(grid, 1);
           newtFormDestroy(form);

           if (answer != add)
               break;

           addDeviceManually(modInfo, modLoaded, modDeps, kd, flags);
       } else {
           rc = newtWinChoice(_("Devices"), _("Done"), _("Add Device"),
                   _("I don't have any special device drivers loaded for "
                     "your system. Would you like to load some now?"));
           if (rc != 2)
               break;

           addDeviceManually(modInfo, modLoaded, modDeps, kd, flags);
       }
   }


   return 0;
}

int busProbe(moduleInfoSet modInfo, moduleList modLoaded, moduleDeps modDeps,
            int justProbe, struct knownDevices * kd, int flags) {
   int i;
   struct moduleInfo ** modList;

   if (FL_NOPROBE(flags)) return 0;

   if (!access("/proc/bus/pci/devices", R_OK) ||
       !access("/proc/openprom", R_OK)) {
       /* autodetect whatever we can */
       if (detectHardware(modInfo, &modList, flags)) {
           logMessage("failed to scan pci bus!");
           return 0;
       } else if (modList) {
           logMessage("found devices justProbe is %d", justProbe);

           for (i = 0; modList[i]; i++) {
               if (justProbe) {
                   printf("%s\n", modList[i]->moduleName);
               } else {
                   if (modList[i]->major == DRIVER_NET) {
                       mlLoadModule(modList[i]->moduleName, modList[i]->path,
                                    modLoaded, modDeps, NULL, flags);
                   }
               }
           }

           for (i = 0; !justProbe && modList[i]; i++) {
               if (modList[i]->major == DRIVER_SCSI) {
                   startNewt(flags);

                   scsiWindow(modList[i]->moduleName);
                   mlLoadModule(modList[i]->moduleName, modList[i]->path,
                                modLoaded, modDeps, NULL, flags);
                   sleep(1);
                   newtPopWindow();
               }
           }

           kdFindScsiList(kd);
           kdFindNetList(kd);
       } else
           logMessage("found nothing");
   }

   return 0;
}

static int loadCompressedRamdisk(int fd, off_t size, char *title,
                                char *ramdisk, int flags) {
   int rc = 0, ram, i;
   gzFile stream;
   char buf[1024];
   newtComponent form = NULL, scale = NULL;
   int total;

   if (FL_TESTING(flags)) return 0;

   stream = gzdopen(dup(fd), "r");

   strcpy(buf, "/tmp/");
   strcat(buf, ramdisk);

   if (devMakeInode(ramdisk, buf)) return 1;
   ram = open(buf, O_WRONLY);
   unlink(buf);

   logMessage("created inode");

   if (title != NULL) {
       if (size > 0)
           newtCenteredWindow(70, 5, _("Loading"));
       else
           newtCenteredWindow(70, 3, _("Loading"));

       form = newtForm(NULL, NULL, 0);

       newtFormAddComponent(form, newtLabel(1, 1, title));
       if (size > 0) {
           scale = newtScale(1, 3, 68, size);
           newtFormAddComponent(form, scale);
       }
       newtDrawForm(form);
       newtRefresh();
   }

   total = 0;
   while (!gzeof(stream) && !rc) {
       if ((i = gzread(stream, buf, sizeof(buf))) != sizeof(buf)) {
           if (!gzeof(stream)) {
               logMessage("error reading from device: %s", strerror(errno));
               rc = 1;
               break;
           }
       }

       if (write(ram, buf, i) != i) {
           logMessage("error writing to device: %s", strerror(errno));
           rc = 1;
       }

       total += i;

       if (title != NULL && size > 0) {
           newtScaleSet(scale, lseek(fd, 0L, SEEK_CUR));
           newtRefresh();
       }
   }

   logMessage("done loading %d bytes", total);

   if (title != NULL) {
       newtPopWindow();
       newtFormDestroy(form);
   }

   close(ram);
   gzclose(stream);

   return rc;
}

static int loadStage2Ramdisk(int fd, off_t size, int flags) {
   int rc;

   rc = loadCompressedRamdisk(fd, size, _("Loading second stage ramdisk..."),
                              "ram3", flags);

   if (rc) {
       newtWinMessage(_("Error"), _("OK"), _("Error loading ramdisk."));
       return rc;
   }

   if (devMakeInode("ram3", "/tmp/ram3")) {
       logMessage("failed to make device ram3");
       return 1;
   }

   if (doPwMount("/tmp/ram3", "/mnt/runtime", "ext2", 0, 0, NULL, NULL)) {
       newtWinMessage(_("Error"), _("OK"),
               "Error mounting ramdisk. This shouldn't "
                   "happen, and I'm rebooting your system now.");
       exit(1);
   }

   unlink("/tmp/ram3");

   return 0;
}

#ifdef INCLUDE_LOCAL
static char * setupHardDrive(char * device, char * type, char * dir,
                            int flags) {
   int fd;
   char * path;
   int rc;
   char * url;

   logMessage("mounting device %s as %s", device, type);

   if (!FL_TESTING(flags)) {
       /* +5 skips over /dev/ */
       if (devMakeInode(device, "/tmp/hddev"))
           logMessage("devMakeInode failed!");

       if (doPwMount("/tmp/hddev", "/tmp/hdimage", type, 1, 0, NULL, NULL))
           return NULL;

       path = alloca(50 + (dir ? strlen(dir) : 2));
       sprintf(path, "/tmp/hdimage/%s/RedHat/base/stage2.img",
                   dir ? dir : "");
       if ((fd = open(path, O_RDONLY)) < 0) {
           logMessage("cannot open %s", path);
           umount("/tmp/hdimage");
           return NULL;
       }

       rc = loadStage2Ramdisk(fd, 0, flags);
       close(fd);
       umount("/tmp/hdimage");
       if (rc) return NULL;
   }

   url = malloc(50 + strlen(dir ? dir : ""));
   sprintf(url, "hd://%s:%s/%s", device, type, dir ? dir : ".");

   return url;
}

#endif

#ifdef INCLUDE_LOCAL

static char * mountHardDrive(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags) {
   int rc;
   int fd;
   int i, j;
   struct {
       char name[20];
       int type;
   } partitions[1024], * part;
   struct partitionTable table;
   newtComponent listbox, label, dirEntry, form, okay, back, text;
   struct newtExitStruct es;
   newtGrid entryGrid, grid, buttons;
   int done = 0;
   char * dir = NULL;
   char * tmpDir;
   char * type;
   char * url = NULL;
   int numPartitions;

   mlLoadModule("vfat", NULL, modLoaded, modDeps, NULL, flags);

   while (!done) {
       numPartitions = 0;
       for (i = 0; i < kd->numKnown; i++) {
           if (kd->known[i].class == CLASS_HD) {
               devMakeInode(kd->known[i].name, "/tmp/hddevice");
               if ((fd = open("/tmp/hddevice", O_RDONLY)) >= 0) {
                   if ((rc = balkanReadTable(fd, &table))) {
                       logMessage("failed to read partition table for "
                                  "device %s: %d", kd->known[i].name, rc);
                   } else {
                       for (j = 0; j < table.maxNumPartitions; j++) {
                           if (table.parts[j].type == BALKAN_PART_DOS ||
                                   table.parts[j].type == BALKAN_PART_EXT2) {
                               sprintf(partitions[numPartitions].name,
                                       "/dev/%s%d", kd->known[i].name, j + 1);
                               partitions[numPartitions].type =
                                       table.parts[j].type;
                               numPartitions++;
                           }
                       }
                   }

                   close(fd);
               } else {
                   /* XXX ignore errors on removable drives? */
               }

               unlink("/tmp/hddevice");
           }
       }

       if (!numPartitions) {
           rc = newtWinChoice(_("Hard Drives"), _("Yes"), _("Back"),
                           _("You don't seem to have any hard drives on "
                             "your system! Would you like to configure "
                             "additional devices?"));
           if (rc == 2) return NULL;

           devDeviceMenu(DRIVER_SCSI, modInfo, modLoaded, modDeps, flags,
                         NULL);
           kdFindScsiList(kd);

           continue;
       }

       text = newtTextboxReflowed(-1, -1,
               _("What partition and directory on that partition hold the "
                 "RedHat/RPMS and RedHat/base directories? If you don't "
                 "see the disk drive you're using listed here, press F2 "
                 "to configure additional devices."), 62, 5, 5, 0);

       listbox = newtListbox(-1, -1, numPartitions > 5 ? 5 : numPartitions,
                             NEWT_FLAG_RETURNEXIT |
                               (numPartitions > 5 ? NEWT_FLAG_SCROLL : 0)
                           );

       for (i = 0; i < numPartitions; i++)
           newtListboxAppendEntry(listbox, partitions[i].name,
                                  partitions + i);

       label = newtLabel(-1, -1, _("Directory holding Red Hat:"));

       dirEntry = newtEntry(28, 11, dir, 28, &tmpDir, NEWT_ENTRY_SCROLL);

       entryGrid = newtGridHStacked(NEWT_GRID_COMPONENT, label,
                                    NEWT_GRID_COMPONENT, dirEntry,
                                    NEWT_GRID_EMPTY);

       buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL);

       grid = newtCreateGrid(1, 4);
       newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
                        0, 0, 0, 1, 0, 0);
       newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, listbox,
                        0, 0, 0, 1, 0, 0);
       newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, entryGrid,
                        0, 0, 0, 1, 0, 0);
       newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons,
                        0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

       newtGridWrappedWindow(grid, _("Select Partition"));

       form = newtForm(NULL, NULL, NEWT_FLAG_NOF12);
       newtFormAddHotKey(form, NEWT_KEY_F2);

       newtGridAddComponentsToForm(grid, form, 1);
       newtGridFree(grid, 1);

       newtFormRun(form, &es);

       part = newtListboxGetCurrent(listbox);

       if (dir) free(dir);
       if (tmpDir && *tmpDir) {
           /* Protect from form free. */
           dir = strdup(tmpDir);
       } else  {
           dir = NULL;
       }

       newtFormDestroy(form);
       newtPopWindow();

       if (es.reason == NEWT_EXIT_COMPONENT && es.u.co == back) {
           return NULL;
       } else if (es.reason == NEWT_EXIT_HOTKEY && es.u.key == NEWT_KEY_F2) {
           devDeviceMenu(DRIVER_SCSI, modInfo, modLoaded, modDeps, flags,
                         NULL);
           kdFindScsiList(kd);
           continue;
       }

       logMessage("partition %s selected", part->name);

       switch (part->type) {
         case BALKAN_PART_EXT2:    type = "ext2";              break;
         case BALKAN_PART_DOS:     type = "vfat";              break;
         default:      continue;
       }

       url = setupHardDrive(part->name + 5, type, dir, flags);
       if (dir) free(dir);
       if (!url) {
           newtWinMessage(_("Error"), _("OK"),
                       _("Device %s does not appear to contain "
                         "a Red Hat installation tree."), part->name);
           continue;
       }

       done = 1;

       umount("/tmp/hdimage");
       rmdir("/tmp/hdimage");
   }

   return url;
}

static char * setupCdrom(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags, int probeQuickly) {
   int i;
   int rc;
   int hasCdrom = 0;

   do {
       for (i = 0; i < kd->numKnown; i++) {
           if (kd->known[i].class != CLASS_CDROM) continue;

           hasCdrom = 1;

           logMessage("trying to mount device %s", kd->known[i].name);
           devMakeInode(kd->known[i].name, "/tmp/cdrom");
           if (!doPwMount("/tmp/cdrom", "/mnt/source", "iso9660", 1, 0, NULL,
                         NULL)) {
               if (!access("/mnt/source/RedHat/instimage/usr/bin/anaconda",
                           X_OK)) {
                   symlink("/mnt/source/RedHat/instimage", "/mnt/runtime");
                   return "dir://mnt/source/.";
               }
               umount("/mnt/source");
           }
       }

       if (probeQuickly) return NULL;

       if (hasCdrom) {
           rc = newtWinChoice(_("Error"), _("OK"), _("Back"),
                       _("I could not find a Red Hat Linux "
                         "CDROM in any of your CDROM drives. Please insert "
                         "the Red Hat CD and press \"OK\" to retry."));
           if (rc == 2) return NULL;
       } else {
           rc = setupCDdevice(kd, modInfo, modLoaded, modDeps, flags);
           if (rc == LOADER_BACK) return NULL;
       }
   } while (1);

   return "dir://mnt/source/.";
}

static char * mountCdromImage(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags) {
   return setupCdrom(method, location, kd, modInfo, modLoaded, modDeps,
                     flags, 0);
}

#endif

#ifdef INCLUDE_NETWORK

static int ensureNetDevice(struct knownDevices * kd,
                        moduleInfoSet modInfo, moduleList modLoaded,
                        moduleDeps modDeps, int flags, char ** devNamePtr) {
   int i, rc;
   char * devName = NULL;

   /* Once we find an ethernet card, we're done. Perhaps we should
      let them specify multiple ones here?? */

   for (i = 0; i < kd->numKnown; i++) {
       if (kd->known[i].class == CLASS_NETWORK) {
           devName = kd->known[i].name;
           break;
       }
   }

   /* It seems like expert mode should do something here? */
   if (!devName) {
       rc = devDeviceMenu(DRIVER_NET, modInfo, modLoaded, modDeps, flags,
                          NULL);
       if (rc) return rc;
       kdFindNetList(kd);
   }

   if (!devName) {
       for (i = 0; i < kd->numKnown; i++) {
           if (kd->known[i].class == CLASS_NETWORK) {
               devName = kd->known[i].name;
               break;
           }
       }
   }

   if (!devName) return LOADER_ERROR;

   *devNamePtr = devName;

   return 0;
}

#endif

#ifdef INCLUDE_NETWORK

#define NFS_STAGE_IP    1
#define NFS_STAGE_NFS   2
#define NFS_STAGE_MOUNT 3
#define NFS_STAGE_DONE  4

static char * mountNfsImage(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                        moduleInfoSet modInfo, moduleList modLoaded,
                        moduleDeps modDeps, int flags) {
   static struct networkDeviceConfig netDev;
   char * devName;
   int i, rc;
   char * host = NULL;
   char * dir = NULL;
   char * fullPath;
   int stage = NFS_STAGE_IP;

   initLoopback();

   memset(&netDev, 0, sizeof(netDev));

   i = ensureNetDevice(kd, modInfo, modLoaded, modDeps, flags, &devName);
   if (i) return NULL;

   while (stage != NFS_STAGE_DONE) {
       switch (stage) {
         case NFS_STAGE_IP:
           rc = readNetConfig(devName, &netDev, flags);
           if (rc) {
               if (!FL_TESTING(flags)) pumpDisableInterface(devName);
               return NULL;
           }
           stage = NFS_STAGE_NFS;
           break;

         case NFS_STAGE_NFS:
           if (nfsGetSetup(&host, &dir) == LOADER_BACK)
               stage = NFS_STAGE_IP;
           else
               stage = NFS_STAGE_MOUNT;
           break;

         case NFS_STAGE_MOUNT:
           if (FL_TESTING(flags)) {
               stage = NFS_STAGE_DONE;
               break;
           }

           mlLoadModule("nfs", NULL, modLoaded, modDeps, NULL, flags);
           fullPath = alloca(strlen(host) + strlen(dir) + 2);
           sprintf(fullPath, "%s:%s", host, dir);

           logMessage("mounting nfs path %s", fullPath);

           stage = NFS_STAGE_NFS;

           if (!doPwMount(fullPath, "/mnt/source", "nfs", 1, 0, NULL, NULL)) {
               if (!access("/mnt/source/RedHat/instimage/usr/bin/anaconda",
                           X_OK)) {
                   symlink("/mnt/source/RedHat/instimage", "/mnt/runtime");
                   stage = NFS_STAGE_DONE;
               } else {
                   umount("/mnt/source");
                   newtWinMessage(_("Error"), _("OK"),
                                  _("That directory does not seem to contain "
                                    "a Red Hat installation tree."));
               }
           } else {
               newtWinMessage(_("Error"), _("OK"),
                       _("I could not mount that directory from the server"));
           }

           break;
       }
   }

   writeNetInfo("/tmp/netinfo", &netDev);

   free(host);
   free(dir);

   return "dir://mnt/source/.";
}

#endif

#ifdef INCLUDE_NETWORK

#define URL_STAGE_IP                    1
#define URL_STAGE_MAIN                  2
#define URL_STAGE_SECOND                3
#define URL_STAGE_FETCH                 4
#define URL_STAGE_DONE                  20

static char * mountUrlImage(struct installMethod * method,
                     char * location, struct knownDevices * kd,
                     moduleInfoSet modInfo, moduleList modLoaded,
                     moduleDeps modDeps, int flags) {
   int i, rc;
   int stage = URL_STAGE_IP;
   char * devName;
   struct iurlinfo ui;
   char needsSecondary = ' ';
   static struct networkDeviceConfig netDev;
   FD_t fd;
   char * url;
   char buf[1024];
   enum urlprotocol_t proto =
       !strcmp(method->name, "FTP") ? URL_METHOD_FTP : URL_METHOD_HTTP;

   initLoopback();

   i = ensureNetDevice(kd, modInfo, modLoaded, modDeps, flags, &devName);
   if (i) return NULL;

   memset(&ui, 0, sizeof(ui));
   memset(&netDev, 0, sizeof(netDev));

   while (stage != URL_STAGE_DONE) {
       switch (stage) {
         case URL_STAGE_IP:
           rc = readNetConfig(devName, &netDev, flags);
           if (rc) {
               if (!FL_TESTING(flags)) pumpDisableInterface(devName);
               return NULL;
           }
           stage = NFS_STAGE_NFS;

         case URL_STAGE_MAIN:
           rc = urlMainSetupPanel(&ui, proto, &needsSecondary);
           if (rc)
               stage = URL_STAGE_IP;
           else
               stage = needsSecondary != ' ' ?
                       URL_STAGE_SECOND : URL_STAGE_FETCH;
           break;

         case URL_STAGE_SECOND:
           rc = urlSecondarySetupPanel(&ui, proto);
           stage = rc ? URL_STAGE_MAIN : URL_STAGE_FETCH;
           break;

         case URL_STAGE_FETCH:
           if (FL_TESTING(flags)) {
               stage = URL_STAGE_DONE;
               break;
           }

           fd = urlinstStartTransfer(&ui, "base/stage2.img");

           if (fd == NULL || fdFileno(fd) < 0) {
               newtPopWindow();
               snprintf(buf, sizeof(buf), "%s/RedHat/base/stage2.img",
                        ui.urlprefix);
               newtWinMessage(_("FTP"), _("OK"),
                      _("Unable to retrieve the second stage ramdisk"));
               /*XXX ufdClose(fd);*/
               stage = URL_STAGE_MAIN;
               break;
           }

           rc = loadStage2Ramdisk(fdFileno(fd), 0, flags);
           urlinstFinishTransfer(fd);
           if (!rc)
               stage = URL_STAGE_DONE;

           break;
       }
   }

   url = malloc(strlen(ui.urlprefix) + 2);
   strcpy(url, ui.urlprefix);

   writeNetInfo("/tmp/netinfo", &netDev);

   return url;
}

#endif

static char * doMountImage(char * location,
                          struct knownDevices * kd,
                          moduleInfoSet modInfo,
                          moduleList modLoaded,
                          moduleDeps modDeps,
                          char ** lang,
                          char ** keymap,
                          char ** kbdtype,
                          int flags) {
   static int defaultMethod = 0;
   int i, rc;
   int validMethods[10];
   int numValidMethods = 0;
   char * installNames[10];
   int methodNum = 0;
   int networkAvailable = 0;
   int localAvailable = 0;
   void * class;
   char * url = NULL;
   enum { STEP_LANG, STEP_KBD, STEP_METHOD, STEP_URL, STEP_DONE } step;

   if ((class = isysGetModuleList(modInfo, DRIVER_NET))) {
       networkAvailable = 1;
       free(class);
   }

   if ((class = isysGetModuleList(modInfo, DRIVER_SCSI))) {
       localAvailable = 1;
       free(class);
   }

#if defined(__alpha__)
   for (i = 0; i < numMethods; i++) {
       installNames[numValidMethods] = installMethods[i].name;
       validMethods[numValidMethods++] = i;
   }
#elif defined(INCLUDE_PCMCIA)
   for (i = 0; i < numMethods; i++) {
       int j;

       for (j = 0; j < kd->numKnown; j++)
           if (installMethods[i].deviceType == kd->known[j].class) break;

       if (j < kd->numKnown) {
           if (i == defaultMethod) methodNum = numValidMethods;

           installNames[numValidMethods] = installMethods[i].name;
           validMethods[numValidMethods++] = i;
       }
   }
#else
   for (i = 0; i < numMethods; i++) {
       if ((networkAvailable && installMethods[i].network) ||
               (localAvailable && !installMethods[i].network)) {
           if (i == defaultMethod) methodNum = numValidMethods;

           installNames[numValidMethods] = installMethods[i].name;
           validMethods[numValidMethods++] = i;
       }
   }
#endif

   installNames[numValidMethods] = NULL;

   if (!numValidMethods) {
       logMessage("no install methods have the required devices!\n");
       exit(1);
   }

#ifdef INCLUDE_LOCAL
   /* If no network is available, check any attached CDROM device for a
      Red Hat CD. If there is one there, just die happy */
   if (!networkAvailable && !FL_EXPERT(flags)) {
       url = setupCdrom(NULL, location, kd, modInfo, modLoaded, modDeps,
                        flags, 1);
       if (url) return url;
   }
#endif

   startNewt(flags);

   step = STEP_LANG;
   while (step != STEP_DONE) {
       switch (step) {
       case STEP_LANG:
           chooseLanguage(lang, flags);
           step = STEP_KBD;
           break;

       case STEP_KBD:
           rc = chooseKeyboard (keymap, kbdtype, flags);

           if (rc == LOADER_BACK)
               step = STEP_LANG;
           else
               step = STEP_METHOD;
           break;

       case STEP_METHOD:
           rc = newtWinMenu(FL_RESCUE(flags) ? _("Rescue Method") :
                            _("Installation Method"),
                            FL_RESCUE(flags) ?
                            _("What type of media contains the rescue image?")
                            :
                            _("What type of media contains the packages to be "
                              "installed?"),
                            30, 10, 20, 6, installNames,
                            &methodNum, _("OK"), _("Back"), NULL);
           if (rc && rc != 1)
               step = STEP_KBD;
           else
               step = STEP_URL;
           break;
       case STEP_URL:
           url = installMethods[validMethods[methodNum]].mountImage(
                  installMethods + validMethods[methodNum], location,
                  kd, modInfo, modLoaded, modDeps, flags);
           if (!url)
               step = STEP_METHOD;
           else
               step = STEP_DONE;
           break;
       default:
           break;
       }

   }

   return url;
}

static int kickstartDevices(struct knownDevices * kd, moduleInfoSet modInfo,
                           moduleList modLoaded, moduleDeps modDeps,
                           int flags) {
   char ** ksArgv = NULL;
   int ksArgc, rc;
   char * opts, * device, * type;
   char ** optv;
   poptContext optCon;
   int doContinue, missingOkay;        /* obsolete */
   char * fsType = "ext2";
   char * fs;
   struct poptOption diskTable[] = {
           { "type", 't', POPT_ARG_STRING, &fsType, 0 },
           { 0, 0, 0, 0, 0 }
       };
   struct poptOption table[] = {
           { "continue", '\0', POPT_ARG_STRING, &doContinue, 0 },
           { "missingok", '\0', POPT_ARG_STRING, &missingOkay, 0 },
           { "opts", '\0', POPT_ARG_STRING, &opts, 0 },
           { 0, 0, 0, 0, 0 }
       };

   if (!ksGetCommand(KS_CMD_DRIVERDISK, NULL, &ksArgc, &ksArgv)) {
       optCon = poptGetContext(NULL, ksArgc, ksArgv, diskTable, 0);

       do {
           if ((rc = poptGetNextOpt(optCon)) < -1) {
               logMessage("bad argument to kickstart driverdisk command "
                       "%s: %s",
                      poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
                      poptStrerror(rc));
               break;
           }

           fs = poptGetArg(optCon);

           if (!fs || poptGetArg(optCon)) {
               logMessage("bad arguments to kickstart driverdisk command");
               break;
           }

           if (strcmp(fsType, "nfs")) {
               devMakeInode(fs, "/tmp/disk");
               fs = "/tmp/disk";
           }

           if (!strcmp(fsType, "vfat"))
               mlLoadModule("vfat", NULL, modLoaded, modDeps, NULL, flags);

           if (doPwMount(fs, "/tmp/drivers", fsType, 1, 0, NULL, NULL)) {
               logMessage("failed to mount %s", fs);
               break;
           }

           if (devCopyDriverDisk(modInfo, modLoaded, modDeps, flags,
                                 "/tmp/drivers")) {
               logMessage("driver information missing!");
           }

           umount("/tmp/drivers");
       } while (0);
   }

   while (!ksGetCommand(KS_CMD_DEVICE, ksArgv, &ksArgc, &ksArgv)) {
       opts = NULL;

       optCon = poptGetContext(NULL, ksArgc, ksArgv, table, 0);

       if ((rc = poptGetNextOpt(optCon)) < -1) {
           logMessage("bad argument to kickstart device command %s: %s",
                      poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
                      poptStrerror(rc));
           continue;
       }

       type = poptGetArg(optCon);
       device = poptGetArg(optCon);

       if (!type || !device || poptGetArg(optCon)) {
           logMessage("bad arguments to kickstart device command");
           poptFreeContext(optCon);
           continue;
       }

       if (!isysFindModuleInfo(modInfo, device)) {
           logMessage("unknown module %s", device);
           continue;
       }

       if (opts)
           poptParseArgvString(opts, &rc, &optv);
       else
           optv = NULL;

       rc = mlLoadModule(device, NULL, modLoaded, modDeps, optv, flags);
       if (optv) free(optv);

       if (rc)
           logMessage("module %s failed to insert", device);
       else
           logMessage("module %s inserted successfully", device);
   }

   kdFindScsiList(kd);
   kdFindNetList(kd);

   return 0;
}

static char * setupKickstart(char * location, struct knownDevices * kd,
                            moduleInfoSet modInfo,
                            moduleList modLoaded,
                            moduleDeps modDeps, int * flagsPtr) {
   char ** ksArgv;
   char * device = NULL;
   int ksArgc;
   int ksType;
   int i, rc;
   int flags = *flagsPtr;
   enum deviceClass ksDeviceType;
   struct poptOption * table;
   poptContext optCon;
   char * dir = NULL;
   char * imageUrl;
#ifdef INCLUDE_NETWORK
   static struct networkDeviceConfig netDev;
   char * host = NULL, * url = NULL, * proxy = NULL, * proxyport = NULL;
   char * fullPath;

   struct poptOption ksNfsOptions[] = {
           { "server", '\0', POPT_ARG_STRING, &host, 0 },
           { "dir", '\0', POPT_ARG_STRING, &dir, 0 },
           { 0, 0, 0, 0, 0 }
       };

   struct poptOption ksUrlOptions[] = {
           { "url", '\0', POPT_ARG_STRING, &url, 0 },
           { "proxy", '\0', POPT_ARG_STRING, &proxy, 0 },
           { "proxyport", '\0', POPT_ARG_STRING, &proxyport, 0 },
           { 0, 0, 0, 0, 0 }
       };
#endif
#ifdef INCLUDE_LOCAL
   int partNum, fd;
   char * partname = NULL;
   struct partitionTable partTable;
   struct poptOption ksHDOptions[] = {
           { "dir", '\0', POPT_ARG_STRING, &dir, 0 },
           { "partition", '\0', POPT_ARG_STRING, &partname, 0 },
           { 0, 0, 0, 0, 0 }
   };
#endif

   kickstartDevices(kd, modInfo, modLoaded, modDeps, flags);

   if (0) {
#ifdef INCLUDE_NETWORK
   } else if (ksHasCommand(KS_CMD_NFS)) {
       ksDeviceType = CLASS_NETWORK;
       ksType = KS_CMD_NFS;
       table = ksNfsOptions;
   } else if (ksHasCommand(KS_CMD_URL)) {
       ksDeviceType = CLASS_NETWORK;
       ksType = KS_CMD_URL;
       table = ksUrlOptions;
#endif
#ifdef INCLUDE_LOCAL
   } else if (ksHasCommand(KS_CMD_CDROM)) {
       ksDeviceType = CLASS_CDROM;
       ksType = KS_CMD_CDROM;
       table = NULL;
   } else if (ksHasCommand(KS_CMD_HD)) {
       ksDeviceType = CLASS_UNSPEC;
       ksType = KS_CMD_HD;
       table = ksHDOptions;
#endif
   } else {
       logMessage("no install method specified for kickstart");
       return NULL;
   }

   if (ksDeviceType != CLASS_UNSPEC) {
       for (i = 0; i < kd->numKnown; i++)
           if (kd->known[i].class == ksDeviceType) break;

       if (i == kd->numKnown) {
           logMessage("no appropriate device for kickstart method is "
                      "available");
           return NULL;
       }

       device = kd->known[i].name;
       logMessage("kickstarting through device %s", device);
   }

   if (!ksGetCommand(KS_CMD_XDISPLAY, NULL, &ksArgc, &ksArgv)) {
       setenv("DISPLAY", ksArgv[1], 1);
   }

   if (!ksGetCommand(KS_CMD_TEXT, NULL, &ksArgc, &ksArgv))
       (*flagsPtr) = (*flagsPtr) | LOADER_FLAGS_TEXT;

   if (table) {
       ksGetCommand(ksType, NULL, &ksArgc, &ksArgv);

       optCon = poptGetContext(NULL, ksArgc, ksArgv, table, 0);

       if ((rc = poptGetNextOpt(optCon)) < -1) {
           logMessage("bad argument to kickstart method command %s: %s",
                      poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
                      poptStrerror(rc));
           return NULL;
       }
   }

#ifdef INCLUDE_NETWORK
   if (ksType == KS_CMD_NFS || ksType == KS_CMD_URL) {
       startNewt(flags);
       if (kickstartNetwork(device, &netDev, NULL, flags)) return NULL;
       writeNetInfo("/tmp/netinfo", &netDev);
   } else if (ksType == KS_CMD_URL) {
       abort();
   }
#endif

   imageUrl = NULL;

#ifdef INCLUDE_NETWORK
   if (ksType == KS_CMD_NFS) {
       mlLoadModule("nfs", NULL, modLoaded, modDeps, NULL, flags);
       fullPath = alloca(strlen(host) + strlen(dir) + 2);
       sprintf(fullPath, "%s:%s", host, dir);

       logMessage("mounting nfs path %s", fullPath);

       if (doPwMount(fullPath, "/mnt/source", "nfs", 1, 0, NULL, NULL))
           return NULL;

       symlink("/mnt/source/RedHat/instimage", "/mnt/runtime");

       imageUrl = "dir://mnt/source/.";
   }
#endif

#ifdef INCLUDE_LOCAL
   if (ksType == KS_CMD_CDROM) {
       imageUrl = setupCdrom(NULL, location, kd, modInfo, modLoaded, modDeps,
                         flags, 1);
   } else if (ksType == KS_CMD_HD) {
       logMessage("partname is %s", partname);

       for (i = 0; i < kd->numKnown; i++) {
           if (kd->known[i].class != CLASS_HD) continue;
           if (!strncmp(kd->known[i].name, partname, strlen(partname) - 1))
               break;
       }
       if (i == kd->numKnown) {
           logMessage("unknown partition %s", partname);
           return NULL;
       }

       devMakeInode(kd->known[i].name, "/tmp/hddevice");
       if ((fd = open("/tmp/hddevice", O_RDONLY)) < 0) {
           logMessage("failed to open device %s", kd->known[i].name);
           return NULL;
       }

       if ((rc = balkanReadTable(fd, &partTable))) {
           logMessage("failed to read partition partTable for "
                      "device %s: %d", kd->known[i].name, rc);
           return NULL;
       }

       close (fd);

       partNum = atoi(partname + 3) - 1;
       if (partTable.maxNumPartitions < partNum ||
           partTable.parts[partNum].type == -1) {
           logMessage("partition %d on device %s does not exist", partNum,
                      kd->known[i].name);
           return NULL;
       }

       /* XXX this shouldn't be hard coded to ext2 */
       imageUrl = setupHardDrive(partname,
               partTable.parts[partNum].type == BALKAN_PART_EXT2 ?
                       "ext2" : "vfat",
               dir, flags);
   }
#endif

   kickstartDevices(kd, modInfo, modLoaded, modDeps, flags);

   return imageUrl;
}

static int parseCmdLineFlags(int flags, char * cmdLine, char ** ksSource) {
   int fd;
   char buf[500];
   int len;
   char ** argv;
   int argc;
   int i;

   logMessage("here with cmdLine %s", cmdLine);

   if (!cmdLine) {
       if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) return flags;
       len = read(fd, buf, sizeof(buf) - 1);
       close(fd);
       if (len <= 0) return flags;

       buf[len] = '\0';
       cmdLine = buf;
   }

   if (poptParseArgvString(cmdLine, &argc, &argv)) return flags;

   for (i = 0; i < argc; i++) {
       if (!strcasecmp(argv[i], "expert"))
           flags |= LOADER_FLAGS_EXPERT | LOADER_FLAGS_NOPROBE |
                    LOADER_FLAGS_MODDISK;
       else if (!strcasecmp(argv[i], "text"))
           flags |= LOADER_FLAGS_TEXT;
       else if (!strcasecmp(argv[i], "isa"))
           flags |= LOADER_FLAGS_ISA;
       else if (!strcasecmp(argv[i], "dd"))
           flags |= LOADER_FLAGS_MODDISK;
       else if (!strcasecmp(argv[i], "driverdisk"))
           flags |= LOADER_FLAGS_MODDISK;
       else if (!strcasecmp(argv[i], "rescue"))
           flags |= LOADER_FLAGS_RESCUE;
       else if (!strcasecmp(argv[i], "ks"))
           flags |= LOADER_FLAGS_KICKSTART;
       else if (!strcasecmp(argv[i], "ks=floppy"))
           flags |= LOADER_FLAGS_KSFLOPPY;
       else if (!strncasecmp(argv[i], "ks=hd:", 6)) {
           flags |= LOADER_FLAGS_KSHD;
           *ksSource = argv[i] + 6;
       }
       else if (!strncasecmp(argv[i], "display=", 8))
           setenv("DISPLAY", argv[i] + 8, 1);
   }

   return flags;
}

#ifdef INCLUDE_NETWORK
int kickstartFromNfs(char * location, moduleList modLoaded, moduleDeps modDeps,
                    int flags) {
   struct networkDeviceConfig netDev;
   char * file, * fullFn;
   char * ksPath;

   if (kickstartNetwork("eth0", &netDev, "dhcp", flags)) {
       logMessage("no dhcp response received");
       return 1;
   }

   writeNetInfo("/tmp/netinfo", &netDev);

   if (!(netDev.dev.set & PUMP_INTFINFO_HAS_BOOTSERVER)) {
       logMessage("no bootserver was found");
       return 1;
   }

   if (!(netDev.dev.set & PUMP_INTFINFO_HAS_BOOTFILE)) {
       file = "/kickstart/";
       logMessage("bootp: no bootfile received");
   } else {
       file = netDev.dev.bootFile;
   }

   ksPath = alloca(strlen(file) + strlen(netDev.dev.hostname) + 70);
   strcpy(ksPath, inet_ntoa(netDev.dev.bootServer));
   strcat(ksPath, ":");
   strcat(ksPath, file);

   if (ksPath[strlen(ksPath) - 1] == '/') {
       ksPath[strlen(ksPath) - 1] = '\0';
       file = malloc(30);
       sprintf(file, "%s-kickstart", inet_ntoa(netDev.dev.ip));
   } else {
       file = strrchr(ksPath, '/');
       if (!file) {
           file = ksPath;
           ksPath = "/";
       } else {
           *file++ = '\0';
       }
   }

   logMessage("ks server: %s file: %s", ksPath, file);

   mlLoadModule("nfs", NULL, modLoaded, modDeps, NULL, flags);

   if (doPwMount(ksPath, "/tmp/nfskd", "nfs", 1, 0, NULL, NULL)) {
       logMessage("failed to mount %s", ksPath);
       return 1;
   }

   fullFn = malloc(strlen(file) + 20);
   sprintf(fullFn, "/tmp/nfskd/%s", file);
   copyFile(fullFn, location);

   umount("/tmp/nfs");

   return 0;
}
#endif

int kickstartFromHardDrive(char * location,
                          moduleList modLoaded, moduleDeps modDeps,
                          char * source, int flags) {
   char * device;
   char * fileName;
   char * fullFn;

   mlLoadModule("vfat", NULL, modLoaded, modDeps, NULL, flags);

   fileName = strchr(source, '/');
   *fileName = '\0';
   fileName++;
   device = source;

   if (devMakeInode(device, "/tmp/hddevice")) {
       logMessage("failed to make device %s", device);
       return 1;
   }

   if (doPwMount("/tmp/hddevice", "/mnt/hddrive", "ext2", 0, 0,
                 NULL, NULL) &&
       doPwMount("/tmp/hddevice", "/mnt/hddrive", "vfat", 0, 0,
                 NULL, NULL)) {
       logMessage("failed to mount %s", device);
   }

   fullFn = alloca(strlen(fileName) + 20);
   sprintf(fullFn, "/mnt/hddrive/%s", fileName);
   copyFile(fullFn, location);

   umount("/mnt/hddrive");

   return 0;
}

int kickstartFromFloppy(char * location, moduleList modLoaded,
                       moduleDeps modDeps, int flags) {
   mlLoadModule("vfat", NULL, modLoaded, modDeps, NULL, flags);

   if (devMakeInode("fd0", "/tmp/fd0"))
       return 1;

   if (doPwMount("/tmp/fd0", "/tmp/ks", "vfat", 1, 0, NULL, NULL)) {
       logMessage("failed to mount floppy: %s", strerror(errno));
       return 1;
   }

   if (access("/tmp/ks/ks.cfg", R_OK)) {
       newtWinMessage(_("Error"), _("OK"),
               _("Cannot find ks.cfg on boot floppy."));
       return 1;
   }

   copyFile("/tmp/ks/ks.cfg", location);

   umount("/tmp/ks");
   unlink("/tmp/fd0");

   logMessage("kickstart file copied to %s", location);

   return 0;
}

void readExtraModInfo(moduleInfoSet modInfo) {
   int num = 0;
   char fileName[80];
   char * dirName;

   sprintf(fileName, "/tmp/DD-%d/modinfo", num);
   while (!access(fileName, R_OK)) {
       dirName = malloc(50);
       sprintf(dirName, "/tmp/DD-%d", num);

       isysReadModuleInfo(fileName, modInfo, dirName);

       sprintf(fileName, "/tmp/DD-%d/modinfo", ++num);
   }
}

int main(int argc, char ** argv) {
   char ** argptr;
   char * anacondaArgs[40];
   char * arg, * url = NULL;
   poptContext optCon;
   int probeOnly = 0;
   moduleList modLoaded;
   char * cmdLine = NULL;
   moduleDeps modDeps;
   int i, rc;
   int flags = 0;
   int testing = 0;
   char * lang = NULL;
   char * keymap = NULL;
   char * kbdtype = NULL;
   struct knownDevices kd;
   moduleInfoSet modInfo;
   char * where;
   struct moduleInfo * mi;
   char * ksFile = NULL, * ksSource = NULL;
   struct poptOption optionTable[] = {
           { "cmdline", '\0', POPT_ARG_STRING, &cmdLine, 0 },
           { "ksfile", '\0', POPT_ARG_STRING, &ksFile, 0 },
           { "probe", '\0', POPT_ARG_NONE, &probeOnly, 0 },
           { "test", '\0', POPT_ARG_NONE, &testing, 0 },
           { 0, 0, 0, 0, 0 }
   };

   if (!strcmp(argv[0] + strlen(argv[0]) - 6, "insmod"))
       return ourInsmodCommand(argc, argv);
   else if (!strcmp(argv[0] + strlen(argv[0]) - 5, "rmmod"))
       return rmmod_main(argc, argv);
   else if (!strcmp(argv[0] + strlen(argv[0]) - 8, "modprobe"))
       return ourInsmodCommand(argc, argv);

#ifdef INCLUDE_KON
   else if (!strcmp(argv[0] + strlen(argv[0]) - 3, "kon"))
       return kon_main(argc, argv);
#endif

#ifdef INCLUDE_PCMCIA
   else if (!strcmp(argv[0] + strlen(argv[0]) - 7, "cardmgr"))
       return cardmgr_main(argc, argv);
   else if (!strcmp(argv[0] + strlen(argv[0]) - 5, "probe"))
       return probe_main(argc, argv);
#endif

   optCon = poptGetContext(NULL, argc, argv, optionTable, 0);

   if ((rc = poptGetNextOpt(optCon)) < -1) {
       fprintf(stderr, "bad option %s: %s\n",
                      poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
                      poptStrerror(rc));
       exit(1);
   }

   if ((arg = poptGetArg(optCon))) {
       fprintf(stderr, "unexpected argument: %s\n", arg);
       exit(1);
   }

   if (testing) flags |= LOADER_FLAGS_TESTING;

   flags = parseCmdLineFlags(flags, cmdLine, &ksSource);

   arg = FL_TESTING(flags) ? "./module-info" : "/modules/module-info";
   modInfo = isysNewModuleInfoSet();
   if (isysReadModuleInfo(arg, modInfo, NULL)) {
       fprintf(stderr, "failed to read %s\n", arg);
       sleep(5);
       exit(1);
   }

   openLog(FL_TESTING(flags));

   kd = kdInit();
   mlReadLoadedList(&modLoaded);
   modDeps = mlNewDeps();
   mlLoadDeps(&modDeps, "/modules/modules.dep");

#ifdef __sparc__
   /* XXX: sparc -BOOT kernels should compile openprom in. */
   if (!FL_TESTING(flags))
       insmod ("openprom", NULL, NULL);
#endif

   if (FL_KSFLOPPY(flags)) {
       ksFile = "/tmp/ks.cfg";
       kickstartFromFloppy(ksFile, modLoaded, modDeps, flags);
       flags |= LOADER_FLAGS_KICKSTART;
   }

#ifdef INCLUDE_PCMCIA
   startNewt(flags);

   winStatus(40, 3, _("PC Card"), _("Initializing PC Card Devices..."));
   startPcmcia(modLoaded, modDeps, flags);
   newtPopWindow();
#endif

   kdFindIdeList(&kd);
   kdFindScsiList(&kd);
   kdFindNetList(&kd);

   if (((access("/proc/bus/pci/devices", X_OK) &&
         access("/proc/openprom", X_OK)) || FL_MODDISK(flags))
           && !ksFile) {
       startNewt(flags);
       devLoadDriverDisk(modInfo, modLoaded, modDeps, flags, 1);
   }

   busProbe(modInfo, modLoaded, modDeps, probeOnly, &kd, flags);
   if (probeOnly) exit(0);

   if (FL_KSHD(flags)) {
       ksFile = "/tmp/ks.cfg";
       kickstartFromHardDrive(ksFile, modLoaded, modDeps, ksSource, flags);
       flags |= LOADER_FLAGS_KICKSTART;
   }

#ifdef INCLUDE_NETWORK
   if (FL_KICKSTART(flags) && !ksFile) {
       ksFile = "/tmp/ks.cfg";
       startNewt(flags);
       kickstartFromNfs(ksFile, modLoaded, modDeps, flags);
   }
#endif

   if (ksFile) {
       ksReadCommands(ksFile);
       url = setupKickstart("/mnt/source", &kd, modInfo, modLoaded, modDeps,
                            &flags);
   }

   if (!url) {
       url = doMountImage("/mnt/source", &kd, modInfo, modLoaded, modDeps,
                          &lang, &keymap, &kbdtype,
                          flags);
   }

   if (!FL_TESTING(flags)) {

       symlink("mnt/runtime/usr", "/usr");
       symlink("mnt/runtime/lib", "/lib");

       unlink("/modules/modules.dep");
       unlink("/modules/module-info");
       unlink("/modules/modules.cgz");
       unlink("/modules/pcitable");

       symlink("../mnt/runtime/modules/modules.dep",
               "/modules/modules.dep");
       symlink("../mnt/runtime/modules/module-info",
               "/modules/module-info");
       symlink("../mnt/runtime/modules/modules.cgz",
               "/modules/modules.cgz");
       symlink("../mnt/runtime/modules/pcitable",
               "/modules/pcitable");
   }

   spawnShell(flags);                  /* we can attach gdb now :-) */

   /* XXX should free old Deps */
   modDeps = mlNewDeps();
   mlLoadDeps(&modDeps, "/modules/modules.dep");

   modInfo = isysNewModuleInfoSet();
   if (isysReadModuleInfo(arg, modInfo, NULL)) {
       fprintf(stderr, "failed to read %s\n", arg);
       sleep(5);
       exit(1);
   }

   readExtraModInfo(modInfo);

   busProbe(modInfo, modLoaded, modDeps, 0, &kd, flags);

   if (((access("/proc/bus/pci/devices", X_OK) &&
         access("/proc/openprom", X_OK)) ||
         FL_ISA(flags) || FL_NOPROBE(flags)) && !ksFile) {
       manualDeviceCheck(modInfo, modLoaded, modDeps, &kd, flags);
   }

   if (!FL_TESTING(flags)) {
       int fd;

       fd = open("/tmp/conf.modules", O_WRONLY | O_CREAT, 0666);
       if (fd < 0) {
           logMessage("error creating /tmp/conf.modules: %s\n",
                      strerror(errno));
       } else {
           mlWriteConfModules(modLoaded, modInfo, fd);
           close(fd);
       }
   }

   mlLoadModule("raid0", NULL, modLoaded, modDeps, NULL, flags);
   mlLoadModule("raid1", NULL, modLoaded, modDeps, NULL, flags);
   mlLoadModule("raid5", NULL, modLoaded, modDeps, NULL, flags);

   stopNewt();
   closeLog();

#if 0
   for (i = 0; i < kd.numKnown; i++) {
       printf("%-5s ", kd.known[i].name);
       if (kd.known[i].class == CLASS_CDROM)
           printf("cdrom");
       else if (kd.known[i].class == CLASS_HD)
           printf("disk ");
       else if (kd.known[i].class == CLASS_NETWORK)
           printf("net  ");
       if (kd.known[i].model)
           printf(" %s\n", kd.known[i].model);
       else
           printf("\n");
   }
#endif

   argptr = anacondaArgs;
   if (FL_RESCUE(flags)) {
       *argptr++ = "/bin/sh";
   } else {
       *argptr++ = "/usr/bin/anaconda";
       *argptr++ = "-m";
       *argptr++ = url;

       if (FL_TEXT(flags))
           *argptr++ = "-T";
       if (FL_EXPERT(flags))
           *argptr++ = "--expert";

       if (FL_KICKSTART(flags)) {
           *argptr++ = "--kickstart";
           *argptr++ = ksFile;
       }

       if (lang) {
           *argptr++ = "--lang";
           *argptr++ = lang;
       }

       if (keymap) {
           *argptr++ = "--keymap";
           *argptr++ = keymap;
       }

       if (kbdtype) {
           *argptr++ = "--kbdtype";
           *argptr++ = kbdtype;
       }

       for (i = 0; i < modLoaded->numModules; i++) {
           if (!modLoaded->mods[i].path) continue;

           mi = isysFindModuleInfo(modInfo, modLoaded->mods[i].name);
           if (!mi) continue;
           if (mi->major == DRIVER_NET)
               where = "net";
           else if (mi->major == DRIVER_SCSI)
               where = "scsi";
           else
               continue;

           *argptr++ = "--module";
           *argptr = alloca(80);
           sprintf(*argptr, "%s:%s:%s", modLoaded->mods[i].path, where,
                   modLoaded->mods[i].name);

           argptr++;
       }
   }

   *argptr = NULL;

   if (!FL_TESTING(flags)) {
       execv(anacondaArgs[0], anacondaArgs);
       perror("exec");
   }

   return 1;
}