/* * 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 1996 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 "config.h" #include "devices.h" #include "doit.h" #include "fs.h" #include "hd.h" #include "install.h" #include "kernel.h" #include "lilo.h" #include "log.h" #include "methods.h" #include "net.h" #include "perror.h" #include "pkgs.h" #include "run.h" #include "scsi.h" #include "upgrade.h" #include "windows.h" int testing = 0; #define STEP_FIRST 0 #define STEP_PATH 0 #define STEP_SCSI 1 #define STEP_FDISK 2 #define STEP_SWAP 3 #define STEP_FINDPKGS 4 #define STEP_MTAB 5 #define STEP_FORMAT 6 #define STEP_PICKPKGS 7 #define STEP_DOIT 8 #define STEP_FINISHNET 9 #define STEP_TIMECONFIG 10 #define STEP_KBDCONFIG 11 #define STEP_ROOTPW 12 #define STEP_LILO 13 #define STEP_UPG_SCSI 1 #define STEP_UPG_MTAB 2 #define STEP_UPG_FFILES 3 #define STEP_UPG_PKGS 4 #define STEP_UPG_DOIT 5 #define STEP_UPG_FINISHNET 6 #define STEP_UPG_LILO 7 #define STEP_DONE 1000 struct installState { int isUpgrade, lastChoice; char * pcmcia, * kernel; struct partitionTable table; struct fstab fstab; struct pkgSet ps; struct componentSet cs; struct installMethod * method; struct netInterface intf; struct netConfig netc; struct driversLoaded * dl; struct installStep * steps; } ; typedef int (*installStepFn)(struct installState * state); 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 setupFilesystem(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 configureKeyboard(struct installState * state); static int configureTimezone(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); struct installStep { char * name; int prev, next; installStepFn fn; int skipOnCancel; int completed; }; struct installStep installSteps[] = { { "Select installation path", -1, STEP_SCSI, selectPath, 0, 0 }, { "Setup SCSI", STEP_PATH, STEP_FDISK, setupSCSI, 0, 0 }, { "Partition disks", STEP_SCSI, STEP_SWAP, partitionDisks, 0, 0 }, { "Setup up swap space", STEP_FDISK, STEP_FINDPKGS, setupSwap, 0, 0 }, { "Find installation files", STEP_SWAP, STEP_MTAB, findInstallFiles, 1, 0 }, { "Setup filesystems", STEP_SWAP, STEP_FORMAT, setupFilesystem, 0, 0 }, { "Choose partitions to format", STEP_MTAB, STEP_PICKPKGS, formatPartitions, 0, 0 }, { "Choose packages to install", STEP_FORMAT, STEP_DOIT, choosePackages, 0, 0 }, { "Install system", -1, STEP_FINISHNET, doInstallStep, 0, 0 }, { "Configure networking", -1, STEP_TIMECONFIG, finishNetworking, 0, 0 }, { "Configure timezone", STEP_FINISHNET, STEP_KBDCONFIG, configureTimezone, 0, 0 }, { "Configure keyboard", STEP_TIMECONFIG,STEP_ROOTPW, configureKeyboard, 0, 0 }, { "Set root password", STEP_KBDCONFIG, STEP_LILO, setRootPassword, 0, 0 }, { "Install bootloader", STEP_ROOTPW, STEP_DONE, setupBootloader, 0, 0 }, }; struct installStep upgradeSteps[] = { { "Select installation path", -1, STEP_UPG_SCSI, selectPath, 0, 0 }, { "Setup SCSI", STEP_PATH, STEP_UPG_MTAB, setupSCSI, 0, 0 }, { "Find current installation", STEP_UPG_SCSI, STEP_UPG_FFILES, upgrFindInstall, 0, 0 }, { "Find installation files", STEP_UPG_MTAB, STEP_UPG_PKGS, findInstallFiles, 1, 0 }, { "Choose packages to install", STEP_UPG_FFILES,STEP_UPG_DOIT, upgrChoosePackages, 0, 0 }, { "Upgrade system", -1, STEP_UPG_FINISHNET, doInstallStep, 0, 0 }, { "Install bootloader", STEP_UPG_FINISHNET, 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); execl("/usr/bin/sh", "/usr/bin/sh", NULL); logMessage(perrorstr("exec of /usr/bin/sh failed")); } close(fd); } } static int setupSCSI(struct installState * state) { return setupSCSIInterfaces(0, &state->dl); } static int partitionDisks(struct installState * state) { int rc; if (!state->isUpgrade) { rc = partitionDrives(); if (rc) return rc; } return findAllPartitions(&state->table); } static int findInstallFiles(struct installState * state) { int rc; if (state->method->prepareRoot) { rc = state->method->prepareRoot(state->method, state->table, &state->netc, &state->intf, &state->dl); if (rc) return rc; } if ((rc = state->method->getPackageSet(state->method, &state->ps))) return rc; if ((state->method->getComponentSet(state->method, &state->ps, &state->cs)) ) return rc; return 0; } static int setupFilesystem(struct installState * state) { return setupMountTable(state->table, &state->fstab); } static int formatPartitions(struct installState * state) { 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; if (!state->isUpgrade) { winMessage(15, 7, 50, 10, "Install log", "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."); rc = formatFilesystems(&state->fstab); if (rc) return rc; rc = mountFilesystems(&state->fstab); if (rc) return rc; } else { winMessage(15, 7, 50, 10, "Upgrade log", "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."); } rc = doInstall(state->method, state->cs.preskel, &state->ps, state->isUpgrade); 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, text, pw1Entry, pw2Entry; char * pw1, * pw2; int done = 0; char salt[3]; char cmd[200]; struct timeval time1, time2; char * pw; gettimeofday(&time1, NULL); newtOpenWindow(15, 4, 50, 14, "Root Password"); form = newtForm(NULL, NULL, 0); text = newtTextbox(1, 1, 47, 5, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, "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!"); newtFormAddComponent(form, newtLabel(3, 7, "Password :")); newtFormAddComponent(form, newtLabel(3, 8, "Password (again):")); pw1Entry = newtEntry(21, 7, "", 24, &pw1, NEWT_ENTRY_HIDDEN); pw2Entry = newtEntry(21, 8, "", 24, &pw2, NEWT_ENTRY_HIDDEN); newtFormAddComponents(form, text, pw1Entry, pw2Entry, NULL); newtFormAddComponent(form, newtButton(20, 10, "Ok")); do { newtFormSetCurrent(form, pw1Entry); newtRunForm(form); if (strcmp(pw1, pw2)) { winMessage(40, 11, 30, 9, "Password Mismatch", "The passwords you entered were different. Please " "try again."); newtEntrySet(pw1Entry, "", 0); newtEntrySet(pw2Entry, "", 0); } else if (strlen(pw1) < 6) { winMessage(40, 10, 30, 9, "Password Mismatch", "The root password must be at least 6 characters " "long."); newtEntrySet(pw1Entry, "", 0); newtEntrySet(pw2Entry, "", 0); } else done = 1; } while (!done); newtPopWindow(); if (testing) return 0; gettimeofday(&time2, NULL); salt[0] = mksalt(time1.tv_usec); salt[1] = mksalt(time2.tv_usec); salt[2] = '\0'; pw = crypt(pw1, salt); logMessage("salt: %s crypt: %s\n", salt, pw); sprintf(cmd, "/mnt/bin/sed 's&root::&root:%s:&' < /mnt/etc/passwd > " "/mnt/etc/passwd.new", pw); newtFormDestroy(form); system(cmd); unlink("/mnt/etc/passwd"); rename("/mnt/etc/passwd.new", "/mnt/etc/passwd"); return 0; } static int configureKeyboard(struct installState * state) { return kbdConfig(); } static int configureTimezone(struct installState * state) { return timeConfig(); } static int setupBootloader(struct installState * state) { static int first = 1; int rc; if (!state->isUpgrade && first) writeFstab(&state->fstab); writeModuleConf("/mnt/etc", state->dl); #ifdef __alpha__ if (first) { rc = kernelCopy(state->kernel); if (rc) return rc; } first = 0; return INST_NOP; #else /* installLilo installs silo on the SPARC */ return installLilo("/mnt/etc", state->table, state->fstab); #endif } static int finishNetworking(struct installState * state) { int rc; rc = checkNetConfig(&state->intf, &state->netc, &state->dl); if (rc) return rc; writeNetConfig("/mnt/etc/sysconfig", &state->netc, &state->intf, 0); writeNetInterfaceConfig("/mnt/etc/sysconfig/network-scripts", &state->intf); writeResolvConf("/mnt/etc", &state->netc); writeHosts("/mnt/etc", &state->netc, &state->intf); return 0; } static int selectPath(struct installState * state) { newtComponent install, upgrade, text, f, answer; memset(state, 0, sizeof(state)); newtOpenWindow(15, 6, 50, 10, "Installation Path"); text = newtTextbox(1, 1, 47, 3, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, "Would you like to install a new system or upgrade a system which " "already contains Red Hat 2.0, 2.1, or 3.0.3?"); install = newtButton(10, 6, "Install"); upgrade = newtButton(30, 6, "Upgrade"); f = newtForm(NULL, NULL, 0); newtFormAddComponents(f, text, install, upgrade, NULL); answer = newtRunForm(f); if (answer == f) answer = newtFormGetCurrent(f); newtFormDestroy(f); newtPopWindow(); if (answer == upgrade) { state->steps = upgradeSteps; state->isUpgrade = 1; } else state->steps = installSteps; return 0; } static int upgrFindInstall(struct installState * state) { int rc; findAllPartitions(&state->table); /* this also turns on swap for us */ rc = readMountTable(state->table, &state->fstab); if (rc) return rc; if (!testing) mountFilesystems(&state->fstab); return 0; } static int upgrChoosePackages(struct installState * state) { int firstTime = 1; char * upgradeList, * 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, 1)) { 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..."); if (state->method->getFile(state->method, "uglist", &upgradeList, 1)) return INST_ERROR; rc = ugFindUpgradePackages(&state->ps, path, upgradeList); if (state->method->rmFiles) unlink(upgradeList); 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]; if (wasCancelled) { sprintf(textBuf, "You cancelled step \"%s\".\n\n", name); newtOpenWindow(15, 3, 50, 16, "Cancelled"); } else { sprintf(textBuf, "An error occured during step \"%s\" of the " "install.\n\n", name); newtOpenWindow(15, 3, 50, 16, "Error"); } form = newtForm(NULL, NULL, 0); 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?"); text = newtTextbox(1, 1, 48, 10, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, textBuf); if (testing) { previous = newtButton(1, 12, "Previous"); retry = newtButton(15, 12, "Retry"); menu = newtButton(28, 12, "Menu"); exitb = newtButton(39, 12, "Exit"); newtFormAddComponents(form, text, previous, retry, menu, exitb, NULL); } else { previous = newtButton(5, 12, "Previous"); retry = newtButton(20, 12, "Retry"); menu = newtButton(38, 12, "Menu"); newtFormAddComponents(form, text, previous, retry, menu, NULL); } 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) { newtComponent form, listbox, okay, text; int firstStep = currStep, i; int numChoices, listHeight; char buf[200]; newtOpenWindow(15, 3, 50, 16, "Installation Steps"); while (state->steps[firstStep].prev != -1) firstStep = state->steps[firstStep].prev; form = newtForm(NULL, NULL, 0); i = firstStep, numChoices = 0; do { numChoices++; i = state->steps[i].next; } while (state->steps[i].prev != -1 && state->steps[i].next != STEP_DONE); numChoices++; if (numChoices > 6) listHeight = 6; else listHeight = 0; listbox = newtListbox(10, 4, listHeight, NEWT_LISTBOX_RETURNEXIT); text = newtTextbox(1, 1, 48, 3, NEWT_TEXTBOX_WRAP); newtTextboxSetText(text, "What step would you like to run? Steps with a * next " "to them have already been completed."); newtListboxAddEntry(listbox, " Continue with install", (void *) STEP_DONE + 1); for (i = firstStep; i < (firstStep + numChoices); i++) { if (state->steps[i].completed) strcpy(buf, "* "); else strcpy(buf, " "); strcat(buf, state->steps[i].name); newtListboxAddEntry(listbox, buf, (void *) i); } okay = newtButton(23, 11, "Ok"); newtFormAddComponents(form, text, listbox, okay, NULL); newtRunForm(form); i = (int) newtListboxGetCurrent(listbox); newtFormDestroy(form); newtPopWindow(); if (i == STEP_DONE + 1) return -1; 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); } else if (lastrc == INST_CANCEL) { 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 (state->steps[nextStep].skipOnCancel && state->steps[nextStep].prev != -1) nextStep = state->steps[nextStep].prev; if (state->steps[nextStep].prev == -1) { messageWindow("Cancelled", "I can't go to the previous step" " from here. You will have to try again."); nextStep = lastStep; } break; case DO_RETRY: nextStep = lastStep; break; case DO_MENU: nextStep = stepMenu(state, lastStep); if (nextStep == -1) { choice = DO_NEXT; if (lastrc) nextStep = lastStep; else nextStep = state->steps[lastStep].next; } break; case DO_NEXT: default: nextStep = state->steps[lastStep].next; break; } state->lastChoice = choice; return nextStep; } void main(int argc, char ** argv) { char ** argptr; int step = STEP_FIRST; int rc = 0; struct installState state; int i; char * spaces; spaces = strdup(" "); /* if this fails, it's okay -- it might help with free space though */ unlink("/sbin/install"); 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, "--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 { fprintf(stderr, "unknown argument: %s\n", *argptr); exit(1); } argptr++; } if (!state.method) { fprintf(stderr, "--method argument is required\n"); exit(1); } if (!testing && (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"); openLog(); logMessage("second stage install running (version %s)", VERSION); spawnShell(); newtInit(); newtCls(); newtDrawRootText(0, 0, "Red Hat Linux (C) 1996 Red Hat Software"); newtPushHelpLine(NULL); readNetConfig("/tmp", &state.netc); readNetInterfaceConfig("/tmp", "eth0", &state.intf); readModuleConf("/tmp", &state.dl); 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); } newtDrawRootText(0, 72, "Complete"); winMessage(10, 3, 60, 16, "Done", "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."); umountFilesystems(&state.fstab); newtFinished(); }