/* * install2.c * * This is the second half of the install. It is exec'd from the first half * once the secondary media has been mounted. It does a bunch of argv * processing to figure out what the first half did. It's a bit of a hack, but * it gives us a nice install as far as the user can see. * * Erik Troan (ewt@redhat.com) * * Copyright 1998 Red Hat Software * * 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. * */ /* * We assume the following: * * /usr/bin -> any binaries we might need * * it's up to the first stage installer to make sure this happens. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "commands.h" #include "config.h" #include "devices.h" #include "doit.h" #include "fs.h" #include "fsedit.h" #include "hd.h" #include "otherinsmod.h" #include "install.h" #include "intl.h" #include "kbd.h" #include "kernel.h" #include "kickstart.h" #include "lang.h" #include "lilo.h" #include "log.h" #include "methods.h" #include "mkswap.h" #include "net.h" #include "pkgs.h" #include "printercfg.h" #include "run.h" #include "scsi.h" #include "upgrade.h" #include "windows.h" int testing = 0; int expert = 0; int kickstart = 0; #define STEP_FIRST 0 #define STEP_PATH 0 #define STEP_SCSI 1 #define STEP_FDISKMTAB 2 #define STEP_SWAP 3 #define STEP_FINDPKGS 4 #define STEP_FORMAT 5 #define STEP_PICKPKGS 6 #define STEP_DOIT 7 #define STEP_FINISHNET 8 #define STEP_TIMECONFIG 9 #define STEP_SERVICES 10 #define STEP_PRINTER 11 #define STEP_ROOTPW 12 #define STEP_BOOTDISK 13 #define STEP_LILO 14 #define STEP_UPG_SCSI 1 #define STEP_UPG_PKGS 2 #define STEP_UPG_MTAB 3 #define STEP_UPG_FFILES 4 #define STEP_UPG_DOIT 5 #define STEP_UPG_BOOTDISK 6 #define STEP_UPG_LILO 7 #define STEP_DONE 1000 struct installState { int isUpgrade, lastChoice, direction; char * pcmcia, * kernel, * keyboard; struct partitionTable table; struct fstab fstab; struct pkgSet ps; struct componentSet cs; struct installMethod * method; struct intfInfo intf, intfFinal; struct netInfo netc, netcFinal; struct driversLoaded * dl; struct installStep * steps; } ; typedef int (*installStepFn)(struct installState * state); /* hack */ int rmmod_main(int argc, char ** argv); static int setupSCSI(struct installState * state); static int partitionDisks(struct installState * state); static int setupSwap(struct installState * state); static int findInstallFiles(struct installState * state); static int formatPartitions(struct installState * state); static int choosePackages(struct installState * state); static int doInstallStep(struct installState * state); static int setRootPassword(struct installState * state); static int createBootdisk(struct installState * state); static int configureTimezone(struct installState * state); static int configureServices(struct installState * state); static int configurePrinter(struct installState * state); static int setupBootloader(struct installState * state); static int finishNetworking(struct installState * state); static int selectPath(struct installState * state); static int upgrChoosePackages(struct installState * state); static int upgrFindInstall(struct installState * state); static void setupSerialConsole(void); struct installStep { char * name; int prev, next; installStepFn fn; int skipOnCancel; int completed; }; /* this table is translated at run time */ static struct installStep installSteps[] = { { N_("Select installation path"), -1, STEP_SCSI, selectPath, 0, 0 }, { N_("Setup SCSI"), STEP_PATH, STEP_FDISKMTAB, setupSCSI, 0, 0 }, { N_("Setup filesystems"), STEP_SCSI, STEP_SWAP, partitionDisks, 0, 0 }, { N_("Setup swap space"), STEP_FDISKMTAB, STEP_FINDPKGS, setupSwap, 0, 0 }, { N_("Find installation files"), STEP_SWAP, STEP_FORMAT, findInstallFiles, 1, 0 }, { N_("Choose partitions to format"),STEP_SWAP, STEP_PICKPKGS, formatPartitions, 0, 0 }, { N_("Choose packages to install"), STEP_FORMAT, STEP_DOIT, choosePackages, 0, 0 }, { N_("Install system"), STEP_PICKPKGS, STEP_FINISHNET, doInstallStep, 0, 0 }, { N_("Configure networking"), -1, STEP_TIMECONFIG, finishNetworking, 0, 0 }, { N_("Configure timezone"), STEP_FINISHNET, STEP_SERVICES, configureTimezone, 0, 0 }, { N_("Configure services"), STEP_TIMECONFIG,STEP_PRINTER, configureServices, 0, 0 }, { N_("Configure printer"), STEP_SERVICES, STEP_ROOTPW, configurePrinter, 0, 0 }, { N_("Set root password"), STEP_PRINTER, STEP_BOOTDISK, setRootPassword, 0, 0 }, { N_("Create bootdisk"), STEP_ROOTPW, STEP_LILO, createBootdisk, 0, 0 }, { N_("Install bootloader"), STEP_BOOTDISK, STEP_DONE, setupBootloader, 0, 0 }, }; /* this table is translated at run time */ struct installStep upgradeSteps[] = { { N_("Select installation path"), -1, STEP_UPG_SCSI, selectPath, 0, 0 }, { N_("Setup SCSI"), STEP_PATH, STEP_UPG_PKGS, setupSCSI, 0, 0 }, { N_("Find installation files"), STEP_PATH, STEP_UPG_MTAB, findInstallFiles, 1, 0 }, { N_("Find current installation"), STEP_UPG_PKGS, STEP_UPG_FFILES, upgrFindInstall, 0, 0 }, { N_("Choose packages to upgrade"), STEP_UPG_FFILES,STEP_UPG_DOIT, upgrChoosePackages, 0, 0 }, { N_("Upgrade system"), -1, STEP_UPG_BOOTDISK, doInstallStep, 0, 0 }, { N_("Create bootdisk"), -1, STEP_UPG_LILO, createBootdisk, 0, 0 }, { N_("Install bootloader"), STEP_UPG_BOOTDISK, STEP_DONE, setupBootloader, 0, 0 }, }; void spawnShell(void) { pid_t pid; int fd; if (!testing) { fd = open("/dev/tty2", O_RDWR); if (fd < 0) { logMessage("cannot open /dev/tty2 -- no shell will be provided"); return; } else if (access("/usr/bin/sh", X_OK)) { logMessage("cannot open shell - /usr/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); } } static int setupSCSI(struct installState * state) { if (state->direction < 0 && scsiDeviceAvailable()) return INST_CANCEL; return setupSCSIInterfaces(0, &state->dl); } static int useNewFdisk(int * useNew) { int rc; rc = newtWinTernary(_("Disk Setup"), _("Disk Druid"), _("fdisk"), _("Back"), _("Disk Druid is a tool for partitioning and setting up mount " "points. It is designed to be easier to use than Linux's " "traditional disk partitioning sofware, fdisk, as well " "as more powerful. However, there are some cases where fdisk " "may be preferred.\n\n" "Which tool would you like to use?")); if (rc == 3) return INST_CANCEL; if (rc == 0 || rc == 1) *useNew = 1; else *useNew = 0; return 0; } #define PART_METHOD 0 #define PART_DDRUID 1 #define PART_FDISK 2 #define PART_FSTAB 3 #define PART_DONE 10 static int partitionDisks(struct installState * state) { int rc = 0; char ** drives; static int useNew; int numDrives; int stage; if (state->isUpgrade) return findAllPartitions(NULL, &state->table); #if defined(__i386__) || defined(__alpha__) if (kickstart) { if ((rc = getDriveList(&drives, &numDrives))) return rc; return kickstartPartitioning(&state->table, &state->fstab, drives); } #endif if (state->direction > 0) stage = PART_METHOD; else stage = PART_DDRUID; while (stage != 10) { switch (stage) { case PART_METHOD: #if defined(__i386__) || defined(__alpha__) if ((rc = useNewFdisk(&useNew))) return rc; stage = useNew ? PART_DDRUID : PART_FDISK; #else useNew = 0, stage = PART_FDISK; #endif break; case PART_DDRUID: if ((rc = getDriveList(&drives, &numDrives))) return rc; rc = FSEditPartitions(&state->table, &state->fstab, drives, &state->intf, &state->netc, &state->dl, !useNew); if (rc == INST_ERROR) return rc; stage = PART_DONE; if (rc && useNew) stage = PART_METHOD; else if (rc) stage = PART_FDISK; break; case PART_FDISK: if ((rc = partitionDrives()) == INST_ERROR) return rc; stage = PART_DDRUID; if (rc) stage = PART_METHOD; break; } } return 0; } static int findInstallFiles(struct installState * state) { int rc; char * trFile; if (!state->table.parts) { rc = findAllPartitions(NULL, &state->table); if (rc) return rc; } if (state->method->prepareRoot) { rc = state->method->prepareRoot(state->method, state->table, &state->netc, &state->intf, &state->dl); if (rc) return rc; } /* We can now load the third bit of the install. FIXME: This leaks memory if we keep redoing it. I suspect much of this code does though! */ if (state->method->getFile(state->method, "install3.tr", &trFile)) { logMessage("getFile method failed for %s", "install3.tr"); } else { loadLanguageStage(3, trFile); if (state->method->rmFiles) unlink(trFile); } /* XXX Hack as we did this after freezing the translations */ if (!getenv("LANG")) winStatus(25, 3, "Scanning", _("Scanning packages...")); rc = state->method->getPackageSet(state->method, &state->ps); if (!rc) rc = state->method->getComponentSet(state->method, &state->ps, &state->cs); if (!getenv("LANG")) newtPopWindow(); return rc; } static int formatPartitions(struct installState * state) { int i; if (kickstart) { for (i = 0; i < state->fstab.numEntries; i++) { if (state->fstab.entries[i].type == PART_EXT2) state->fstab.entries[i].doFormat = 1; } return 0; } return queryFormatFilesystems(&state->fstab); } static int setupSwap(struct installState * state) { return activeSwapSpace(&state->table, &state->fstab); } static int choosePackages(struct installState * state) { return psSelectPackages(&state->ps, &state->cs, 0, 0); } static int doInstallStep(struct installState * state) { int rc; char * netSharedPath = NULL; FILE * f; int netSharedLength; int i; if (!state->isUpgrade) { if (!kickstart && !expert) { rc = newtWinChoice(_("Install log"), _("Ok"), _("Back"), _("A complete log of your installation will be in " "/tmp/install.log after rebooting your system. You " "may want to keep this file for later reference.")); if (rc == 2) return INST_CANCEL; } rc = formatFilesystems(&state->fstab); if (rc) return rc; rc = mountFilesystems(&state->fstab); if (rc) return rc; if (state->method->prepareMedia) { rc = state->method->prepareMedia(state->method, &state->fstab); if (rc) { umountFilesystems(&state->fstab); return rc; } } } else { if (!kickstart && !expert) { rc = newtWinChoice(_("Upgrade log"), _("Ok"), _("Back"), _("A complete log of your upgrade will be in " "/tmp/upgrade.log when the upgrade is finished. " "After rebooting, please read it to ensure " "configuration files are properly updated.")); if (rc == 2) return INST_CANCEL; } } /* FIXME: should this read the net shared path from /etc/rpmrc during upgrades??? Probably. */ for (i = netSharedLength = 0; i < state->fstab.numEntries; i++) if (state->fstab.entries[i].type == PART_NFS) netSharedLength += 1 + strlen(state->fstab.entries[i].mntpoint); if (netSharedLength) { netSharedPath = alloca(netSharedLength); *netSharedPath = '\0'; for (i = netSharedLength = 0; i < state->fstab.numEntries; i++) { if (state->fstab.entries[i].type == PART_NFS) { if (*netSharedPath) strcat(netSharedPath, ":"); strcat(netSharedPath, state->fstab.entries[i].mntpoint); } } logMessage("netSharedPath is: %s\n", netSharedPath); } rc = doInstall(state->method, &state->ps, netSharedPath, state->keyboard, state->isUpgrade); if (netSharedPath && access("/mnt/etc/rpmrc", X_OK)) { logMessage("creating /etc/rpmrc for netshared info (as none exists)"); f = fopen("/mnt/etc/rpmrc", "w"); if (!f) { errorWindow("error creating /mnt/etc/rpmrc: %s"); } else { fprintf(f, "netsharedpath: %s\n", netSharedPath); fclose(f); } } sync(); sync(); if (!rc) psFreeComponentSet(&state->cs); configPCMCIA(state->pcmcia); return rc; } static char mksalt(int seed) { int num = seed % 64; if (num < 26) return 'a' + num; else if (num < 52) return 'A' + (num - 26); else if (num < 62) return '0' + (num - 52); else if (num == 63) return '.'; else return '/'; } static int setRootPassword(struct installState * state) { newtComponent form = NULL, text, pw1Entry, pw2Entry, okay, cancel, answer; char * pw1 = NULL, * pw2; int done = 0; char salt[3]; char cmd[200]; struct timeval time1, time2; char * pw; pid_t pid; int status, rc; static int beenSet = 0; char ** argv; int argc; poptContext optCon; int skipCrypt = 0; char * reflowedText; int height, width; newtGrid grid, subgrid, buttons; struct poptOption ksOptions[] = { { "iscrypted", '\0', POPT_ARG_NONE, &skipCrypt, 0 }, { 0, 0, 0, 0, 0 } }; if (kickstart) { if (!ksGetCommand(KS_CMD_ROOTPW, NULL, &argc, &argv)) { optCon = poptGetContext(NULL, argc, argv, ksOptions, 0); if ((rc = poptGetNextOpt(optCon)) < -1) { newtWinMessage(_("rootpw command"), _("Ok"), _("bad argument to kickstart rootpw command %s: %s"), poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); } if (!(pw1 = poptGetArg(optCon))) { newtWinMessage(_("rootpw command"), _("Ok"), _("Missing password")); skipCrypt = 0; } if (poptGetArg(optCon)) newtWinMessage(_("rootpw command"), _("Ok"), _("Unexpected arguments")); poptFreeContext(optCon); } } if (!pw1) { gettimeofday(&time1, NULL); subgrid = newtCreateGrid(2, 2); newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT, newtLabel(-1, -1, _("Password:")), 0, 0, 1, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(subgrid, 0, 1, NEWT_GRID_COMPONENT, newtLabel(-1, -1, _("Password (again):")), 0, 0, 1, 0, NEWT_ANCHOR_LEFT, 0); pw1Entry = newtEntry(-1, -1, "", 24, &pw1, NEWT_ENTRY_HIDDEN); pw2Entry = newtEntry(-1, -1, "", 24, &pw2, NEWT_ENTRY_HIDDEN); newtGridSetField(subgrid, 1, 0, NEWT_GRID_COMPONENT, pw1Entry, 0, 0, 0, 0, 0, 0); newtGridSetField(subgrid, 1, 1, NEWT_GRID_COMPONENT, pw2Entry, 0, 0, 0, 0, 0, 0); reflowedText = newtReflowText( _("Pick a root password. You must type it twice to ensure you know " "what it is and didn't make a mistake in typing. Remember that the " "root password is a critical part of system security!"), 37, 5, 5, &width, &height); text = newtTextbox(1, 1, width, height, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, reflowedText); free(reflowedText); buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL); grid = newtGridBasicWindow(text, subgrid, buttons); form = newtForm(NULL, NULL, 0); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindow(grid, _("Root Password")); newtGridFree(grid, 1); do { newtFormSetCurrent(form, pw1Entry); answer = newtRunForm(form); if (answer == cancel) { newtPopWindow(); newtFormDestroy(form); return INST_CANCEL; } if (testing) { done = 1; } else if (strcmp(pw1, pw2)) { newtWinMessage(_("Password Mismatch"), _("Ok"), _("The passwords you entered were different. Please " "try again.")); newtEntrySet(pw1Entry, "", 0); newtEntrySet(pw2Entry, "", 0); } else if (!strlen(pw1) && beenSet) { newtFormDestroy(form); newtPopWindow(); return INST_OKAY; } else if (strlen(pw1) < 6) { newtWinMessage(_("Password Mismatch"), _("Ok"), _("The root password must be at least 6 characters " "long.")); newtEntrySet(pw1Entry, "", 0); newtEntrySet(pw2Entry, "", 0); } else done = 1; } while (!done); newtPopWindow(); pw2 = alloca(strlen(pw1) + 1); strcpy(pw2, pw1); pw1 = pw2; newtFormDestroy(form); } if (testing) return 0; if (!skipCrypt) { gettimeofday(&time2, NULL); salt[0] = mksalt(time1.tv_usec); salt[1] = mksalt(time2.tv_usec); salt[2] = '\0'; pw = crypt(pw1, salt); } else { pw = pw1; } sprintf(cmd, "/bin/sed 's&root:[^:]*:&root:%s:&' < /etc/passwd > " "/etc/passwd.new", pw); if (!(pid = fork())) { chroot("/mnt"); chdir("/mnt"); exit(system(cmd)); } waitpid(pid, &status, 0); unlink("/mnt/etc/passwd"); rename("/mnt/etc/passwd.new", "/mnt/etc/passwd"); beenSet = 1; return 0; } static int configureTimezone(struct installState * state) { return timeConfig(); } static int configureServices(struct installState * state) { return servicesConfig(); } static int setupBootloader(struct installState * state) { static int first = 1; #ifdef __alpha int rc; #else char * versionString; int rc; int append = 0; char * version, * release; int i; #endif if (!state->isUpgrade && first) { setupSerialConsole(); } #ifdef __alpha__ if (first) { first = 0; rc = kernelCopy(state->kernel); if (rc) return rc; } return INST_NOP; #else first = 0; if (state->isUpgrade && !access("/mnt/etc/conf.modules", X_OK)) { rc = readModuleConfPersist("/mnt/etc", state->dl); if (rc) return rc; append = 1; } for (i = 0; i < state->ps.numPackages; i++) { if (!strncmp(state->ps.packages[i]->name, "kernel", 6)) break; } if (i == state->ps.numPackages) { errorWindow("I couldn't find a kernel!"); return INST_ERROR; } headerGetEntry(state->ps.packages[i]->h, RPMTAG_VERSION, NULL, (void *) &version, NULL); headerGetEntry(state->ps.packages[i]->h, RPMTAG_RELEASE, NULL, (void *) &release, NULL); versionString = alloca(strlen(version) + strlen(release) + 2); sprintf(versionString, "%s-%s", version, release); logMessage("installed kernel version %s", versionString); /* installLilo installs silo on the SPARC */ return installLilo("/mnt/etc", state->table, state->fstab, versionString); #endif } static int finishNetworking(struct installState * state) { int rc; rc = checkNetConfig(&state->intf, &state->netc, &state->intfFinal, &state->netcFinal, &state->dl, state->direction); if (rc) return rc; writeNetConfig("/mnt/etc/sysconfig", &state->netcFinal, &state->intfFinal, 0); writeNetInterfaceConfig("/mnt/etc/sysconfig/network-scripts", &state->intfFinal); writeResolvConf("/mnt/etc", &state->netcFinal); /* this is a bit of a hack */ writeHosts("/mnt/etc", &state->netcFinal, &state->intfFinal, !state->isUpgrade); return 0; } static int selectPath(struct installState * state) { int result; memset(state, 0, sizeof(state)); if (kickstart) { if (!ksGetCommand(KS_CMD_UPGRADE, NULL, NULL, NULL)) { state->steps = upgradeSteps; state->isUpgrade = 1; } else { state->steps = installSteps; } return 0; } result = newtWinChoice(_("Installation Path"), _("Install"), _("Upgrade"), _("Would you like to install a new system or upgrade a system which " "already contains Red Hat Linux 2.0 or later?")); if (result == 2) { state->steps = upgradeSteps; state->isUpgrade = 1; } else state->steps = installSteps; return 0; } static int upgrFindInstall(struct installState * state) { int rc; /* this also turns on swap for us */ rc = readMountTable(state->table, &state->fstab); if (rc) return rc; if (!testing) { mountFilesystems(&state->fstab); if (state->method->prepareMedia) { rc = state->method->prepareMedia(state->method, &state->fstab); if (rc) { umountFilesystems(&state->fstab); return rc; } } } return 0; } static int upgrChoosePackages(struct installState * state) { int firstTime = 1; char * rpmconvertbin; int rc; char * path; char * argv[] = { NULL, NULL }; if (testing) path = "/"; else path = "/mnt"; if (firstTime) { if (access("/mnt/var/lib/rpm/packages.rpm", R_OK)) { if (access("/mnt/var/lib/rpm/packages", R_OK)) { errorWindow("No RPM database exists!"); return INST_ERROR; } if (state->method->getFile(state->method, "rpmconvert", &rpmconvertbin)) { return INST_ERROR; } symlink("/mnt/var", "/var"); winStatus(35, 3, _("Upgrade"), _("Converting RPM database...")); chmod(rpmconvertbin, 0755); argv[0] = rpmconvertbin; rc = runProgram(RUN_LOG, rpmconvertbin, argv); if (state->method->rmFiles) unlink(rpmconvertbin); newtPopWindow(); if (rc) return INST_ERROR; } winStatus(35, 3, "Upgrade", _("Finding packages to upgrade...")); rc = ugFindUpgradePackages(&state->ps, path); newtPopWindow(); if (rc) return rc; firstTime = 0; psVerifyDependencies(&state->ps, 1); } return psSelectPackages(&state->ps, &state->cs, 0, 1); } #define DO_RETRY 1 #define DO_NEXT 2 #define DO_PREV 3 #define DO_MENU 4 static int errcanChoices(char * name, int wasCancelled) { newtComponent form, retry, previous, menu, text, exitb, answer; char textBuf[1000]; newtGrid grid, buttons; char * reflowedText; int width, height; char * title; if (wasCancelled) { sprintf(textBuf, _("You cancelled step \"%s\".\n\n"), name); title = _("Cancelled"); } else { sprintf(textBuf, _("An error occured during step \"%s\" of the " "install.\n\n"), name); title = _("Error"); } strcat(textBuf, _("You may retry that step, return to the previous step " "in the install, or see a menu of installation steps " "which will allow you to move around in the install " "more freely. It is not recommended to use the menu " "unless you are already familiar with Red Hat Linux. " "What would you like to do?")); reflowedText = newtReflowText(textBuf, 50, 5, 5, &width, &height); text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, textBuf); if (testing) { buttons = newtButtonBar("Previous", &previous, "Retry", &retry, "Menu", &menu, "Exit", &exitb, NULL); } else { buttons = newtButtonBar("Previous", &previous, "Retry", &retry, "Menu", &menu, NULL); exitb = NULL; } grid = newtCreateGrid(1, 2); newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0); newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, buttons, 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); form = newtForm(NULL, NULL, 0); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindow(grid, title); newtGridFree(grid, 1); answer = newtRunForm(form); newtPopWindow(); newtFormDestroy(form); if (answer == previous) return DO_PREV; else if (answer == retry) return DO_RETRY; else if (answer == menu) return DO_MENU; newtFinished(); exit(0); } static int stepMenu(struct installState * state, int currStep) { int firstStep = currStep; long i; int numChoices, whichStep = 0; char ** steps; while (state->steps[firstStep].prev != -1) firstStep = state->steps[firstStep].prev; i = firstStep, numChoices = 0; do { numChoices++; i = state->steps[i].next; } while (state->steps[i].prev != -1 && state->steps[i].next != STEP_DONE); numChoices++; steps = alloca(sizeof(char *) * (numChoices + 2)); steps[0] = _(" Continue with install"); for (i = firstStep; i < (firstStep + numChoices); i++) { steps[i - firstStep + 1] = alloca(50); if (state->steps[i].completed) strcpy(steps[i - firstStep + 1], "* "); else strcpy(steps[i - firstStep + 1], " "); strcat(steps[i - firstStep + 1], state->steps[i].name); } steps[numChoices + 1] = NULL; newtWinMenu(_("Installation Steps"), _("What step would you like to run? " "Steps with a * next to them have already been completed."), 48, 5, 5, 6, steps, &whichStep, _("Ok"), NULL); if (whichStep == 0) return -1; else return (whichStep - 1) + firstStep; return i; } static int getNextStep(struct installState * state, int lastStep, int lastrc) { int choice; int nextStep; if (state->lastChoice == DO_MENU) { choice = DO_MENU; } else if (lastrc == INST_ERROR) { choice = errcanChoices(state->steps[lastStep].name, 0); kickstart = 0; } else if (lastrc == INST_CANCEL) { if (!expert) choice = DO_PREV; else choice = errcanChoices(state->steps[lastStep].name, 1); } else if (lastrc == INST_NOP) { choice = state->lastChoice; } else { choice = DO_NEXT; } switch (choice) { case DO_PREV: nextStep = state->steps[lastStep].prev; while (nextStep != -1 && state->steps[nextStep].skipOnCancel && state->steps[nextStep].prev != -1) nextStep = state->steps[nextStep].prev; if (nextStep == -1 || nextStep == lastStep) { newtWinMessage(_("Cancelled"), _("Ok"), _("I can't go to the previous step" " from here. You will have to try again.")); nextStep = lastStep; } state->direction = -1; break; case DO_RETRY: nextStep = lastStep; state->direction = -1; break; case DO_MENU: nextStep = stepMenu(state, lastStep); if (nextStep == -1) { choice = DO_NEXT; if (lastrc) nextStep = lastStep; else nextStep = state->steps[lastStep].next; } state->direction = 1; break; case DO_NEXT: default: nextStep = state->steps[lastStep].next; state->direction = 1; break; } state->lastChoice = choice; return nextStep; } void doSuspend(void) { pid_t pid; int status; if (testing) { newtFinished(); exit(1); } newtSuspend(); if (!(pid = fork())) { printf(_("\n\nType to return to the install program.\n\n")); execl("/bin/sh", "-/bin/sh", NULL); perror("error execing /bin/sh"); sleep(5); exit(1); } waitpid(pid, &status, 0); newtResume(); } static void setupSerialConsole(void) { int first = 1; struct stat sb; char * argv[10] = { "/usr/sbin/setconsole", "--speed", NULL, NULL, NULL }; struct termios tos; speed_t speed; if (!first) return; first = 0; if (fstat(0, &sb)) { logMessage("error stat'ing stdin: %s", strerror(errno)); return; } if (!S_ISCHR(sb.st_mode)) { logMessage("stdin isn't a character device!!! ack!"); return; } if (major(sb.st_rdev) != 4) { if (minor(sb.st_rdev) == 64) argv[3] = "ttya"; else argv[3] = "ttyb"; tcgetattr(0, &tos); speed = cfgetospeed(&tos); switch (speed) { case B38400: argv[2] = "38400"; break; case B19200: argv[2] = "19200"; break; default: argv[2] = "9600"; break; } if (access("/mnt/usr/sbin/setconsole", X_OK)) { logMessage("/mnt/usr/sbin/setconsole does not exist -- skipping"); return; } logMessage("setting up %s as serial console, speed is %s", argv[3], argv[2]); runProgramRoot(RUN_LOG, "/mnt", "/usr/sbin/setconsole", argv); } } static int configurePrinter(struct installState * state) { if (kickstart) return 0; else if (testing) { return doConfigurePrinters("/", state->direction); } else if (!access("/mnt/usr/bin/lpr", X_OK)) { return doConfigurePrinters("/mnt", state->direction); } return INST_NOP; } static int createBootdisk(struct installState * state) { int rc; int i; char * version, * release; char * versionString; int stage = 1; static int beenHere = 0; if (!state->isUpgrade && !beenHere) { writeFstab(&state->fstab); writeModuleConf("/mnt/etc", state->dl, 1); } beenHere = 1; if (kickstart) return INST_NOP; #ifndef __i386__ return INST_NOP; #else if (!testing && (access("/mnt/sbin/mkbootdisk", X_OK) || access("/mnt/sbin/mkinitrd", X_OK))) return INST_NOP; while (stage < 4) { switch (stage) { case 1: rc = newtWinTernary(_("Bootdisk"), _("Yes"), _("No"), _("Back"), _("A custom bootdisk provides a way of booting into your " "Linux system without depending on the normal bootloader. " "This is useful if you don't want to install lilo on your " "system, another operating system removes lilo, or lilo " "doesn't work with your hardware configuration. A custom " "bootdisk can also be used with the Red Hat rescue image, " "making is much easier to recover from severe system " "failures.\n\n" "Would you like to create a bootdisk for your system?")); if (rc == 3) return INST_CANCEL; if (rc == 2) return INST_OKAY; stage = 2; break; case 2: rc = newtWinChoice(_("Bootdisk"), _("Ok"), _("Back"), _("Insert a blank floppy in the first drive " "/dev/fd0.")); if (rc == 2) stage = 1; else stage = 3; break; case 3: for (i = 0; i < state->ps.numPackages; i++) { if (!strncmp(state->ps.packages[i]->name, "kernel", 6)) break; } if (i == state->ps.numPackages) { errorWindow("I couldn't find a kernel!"); return INST_ERROR; } headerGetEntry(state->ps.packages[i]->h, RPMTAG_VERSION, NULL, (void *) &version, NULL); headerGetEntry(state->ps.packages[i]->h, RPMTAG_RELEASE, NULL, (void *) &release, NULL); versionString = alloca(strlen(version) + strlen(release) + 2); sprintf(versionString, "%s-%s", version, release); rc = makeBootdisk("/mnt", versionString); if (rc == INST_ERROR) return INST_ERROR; stage++; break; } } return INST_OKAY; #endif } int main(int argc, char ** argv) { char ** argptr; int step = STEP_FIRST; int rc = 0; struct installState state; int i; int isForce = 0; int len = strlen(argv[0]); char * spaces; int isRescue = 0; DIR * dir; struct dirent * ent; char * kickstartFile = NULL; spaces = strdup(" "); if (!strcmp(argv[0] + len - 6, "umount")) { return umountCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "mount")) { return mountCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "mkdir")) { return mkdirCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "mknod")) { return mknodCommand(argc, argv); } else if (!strcmp(argv[0] + len - 3, "cat")) { return catCommand(argc, argv); } else if (!strcmp(argv[0] + len - 2, "ls")) { return lsCommand(argc, argv); } else if (!strcmp(argv[0] + len - 2, "ln")) { return lnCommand(argc, argv); } else if (!strcmp(argv[0] + len - 2, "rm")) { return rmCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "chmod")) { return chmodCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "lsmod")) { return lsmodCommand(argc, argv); } else if (!strcmp(argv[0] + len - 6, "mkswap")) { return mkswapCommand(argc, argv); } else if (!strcmp(argv[0] + len - 6, "swapon")) { return swaponCommand(argc, argv); } else if (!strcmp(argv[0] + len - 6, "uncpio")) { return uncpioCommand(argc, argv); } else if (!strcmp(argv[0] + len - 6, "insmod")) { return ourInsmodCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "rmmod")) { return rmmod_main(argc, argv); } /* if this fails, it's okay -- it might help with free space though */ unlink("/sbin/install"); newtSetSuspendCallback(doSuspend); memset(&state, 0, sizeof(state)); state.steps = installSteps; /* blind guess */ argptr = argv + 1; while (*argptr) { if (!strcmp(*argptr, "--method")) { argptr++; if (!*argptr) { fprintf(stderr, "--method requires argument\n"); exit(1); } state.method = findInstallMethod(*argptr); if (!state.method) { fprintf(stderr, "unknown install method: %s\n", *argptr); exit(1); } } else if (!strcmp(*argptr, "--force")) { isForce = 1; } else if (!strcmp(*argptr, "--kickstart") || !strcmp(*argptr, "--ks")) { argptr++; if (!*argptr) fprintf(stderr, "--kickstart requires argument\n"); kickstartFile = *argptr; } else if (!strcmp(*argptr, "--rescue")) { isRescue = 1; } else if (!strcmp(*argptr, "--expert")) { expert = 1; } else if (!strcmp(*argptr, "--test")) { testing = 1; } else if (!strcmp(*argptr, "--pcmcia")) { argptr++; if (!*argptr) { fprintf(stderr, "--pcmcia requires argument\n"); exit(1); } state.pcmcia = *argptr; } else if (!strcmp(*argptr, "--kernel")) { argptr++; if (!*argptr) { fprintf(stderr, "--kernel requires argument\n"); exit(1); } state.kernel = *argptr; } else { /* skipping unknown arguments allows for future expansion */ fprintf(stderr, "unknown argument: %s\n", *argptr); } argptr++; } if (!isRescue && !state.method) { fprintf(stderr, "--method argument is required\n"); exit(1); } if (!testing && !isForce && (getpid() > 50)) { fprintf(stderr, "you're running me on a live system! that's "); fprintf(stderr, "incredibly stupid.\n"); exit(1); } fprintf(stderr, "in second stage install\n"); /* translate some tables */ for (i = 0; installSteps[i].next != STEP_DONE; i++) installSteps[i].name = _(installSteps[i].name); installSteps[i].name = _(installSteps[i].name); for (i = 0; upgradeSteps[i].next != STEP_DONE; i++) upgradeSteps[i].name = _(upgradeSteps[i].name); upgradeSteps[i].name = _(upgradeSteps[i].name); openLog(); logMessage("second stage install running (version " VERSION " built " __DATE__ " " __TIME__ ")"); logDebugMessage(("extra log messages are enabled")); spawnShell(); newtInit(); newtCls(); newtDrawRootText(0, 0, "Red Hat Linux (C) 1998 Red Hat Software"); newtPushHelpLine(NULL); setDefaultLanguage(2); if (isRescue) { do { rc = setupSCSIInterfaces(0, &state.dl); } while (rc); /* cut! we're out of here */ newtFinished(); execl("/bin/sh", "-/bin/sh", NULL); fprintf(stderr, "ack! I couldn't manage to execl() /bin/sh: %s", strerror(errno)); while (1); } readNetConfig("/tmp", &state.netc); dir = opendir("/tmp"); if (!dir) logMessage("failed to open directory /tmp: %s", strerror(errno)); else { errno = 0; while ((ent = readdir(dir))) { if (!strncmp("ifcfg-", ent->d_name, 6)) break; } if (!ent && errno) { logMessage("error reading directory entry: %s", strerror(errno)); } else if (ent) { logMessage("found network config file %s", ent->d_name); readNetInterfaceConfig("/tmp", ent->d_name + 6, &state.intf); } } closedir(dir); logMessage("reading /usr/lib/rpmrc"); rpmReadConfigFiles(NULL, NULL, NULL, 0); logMessage("\tdone"); readModuleConf("/tmp", &state.dl); /* make sure we don't pick up any gunk from the outside world */ putenv("PATH=/usr/bin:/bin:/sbin:/usr/sbin"); putenv("LD_LIBRARY_PATH="); if (kickstartFile) { if (ksReadCommands(kickstartFile)) kickstartFile = NULL; else kickstart = 1; #ifndef __sparc__ setupKeyboard(&state.keyboard); #endif } else { #ifndef __sparc__ readKbdConfig("/tmp", &state.keyboard); #endif } while (step != STEP_DONE) { i = strlen(state.steps[step].name); newtDrawRootText(0, 0 - i, state.steps[step].name); newtRefresh(); rc = state.steps[step].fn(&state); if (!rc) state.steps[step].completed = 1; spaces[i] = '\0'; newtDrawRootText(0, 0 - i, spaces); spaces[i] = ' '; step = getNextStep(&state, step, rc); } if (kickstart) ksRunPost(); newtDrawRootText(0, 72, _("Complete")); newtWinMessage(_("Done"), _("Ok"), _("Congratulations, installation is complete.\n\n" "Remove the floppy from the drive and " "press return to reboot. For information on fixes which are " "available for this release of Red Hat Linux, consult the " "Errata available from http://www.redhat.com.\n\n" "Information on configuring your system is available in the post " "install chapter of the Official Red Hat Linux User's Guide.")); newtFinished(); return 0; }