/* * 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 * Matt Wilson * * Copyright 1999 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 #include "commands.h" #include "setup.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 "hints.h" #include "kbd.h" #include "kernel.h" #include "kickstart.h" #include "lang.h" #include "libfdisk/libfdisk.h" #ifdef __alpha__ #include "milo.h" #endif #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; pid_t syncPid = 0; static int exitOnSuspend; #define STEP_FIRST 0 #define STEP_PATH 0 #define STEP_CLASS 1 #define STEP_SCSI 2 #define STEP_FDISKMTAB 3 #define STEP_SWAP 4 #define STEP_FINDPKGS 5 #define STEP_FORMAT 6 #define STEP_PICKPKGS 7 #define STEP_DOIT 8 #define STEP_MOUSE 9 #define STEP_FINISHNET 10 #define STEP_TIMECONFIG 11 #define STEP_SERVICES 12 #define STEP_PRINTER 13 #define STEP_ROOTPW 14 #define STEP_AUTH 15 #define STEP_BOOTDISK 16 #define STEP_LILO 17 #define STEP_XCONFIG 18 #define STEP_EXIT 19 #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_BOOTDISK 6 #define STEP_UPG_LILO 7 #define STEP_UPG_EXIT 8 #define STEP_DONE 1000 struct installState { int isUpgrade, lastChoice, direction, isLocal; char * pcmcia, * kernel, * keyboard, *kbdtype; 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; char * rootPath; struct hints hints; char * rootPass; } ; typedef int (*installStepFn)(struct installState * state); /* hack */ int rmmod_main(int argc, char ** argv); static int setupSCSI(struct installState * state); static int selectInstallClass(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); static int configureX(struct installState * state); static int configureMouse(struct installState * state); static int configureAuth(struct installState * state); static int exitInstall(struct installState * state); static void killCardServices(); struct installStep { char * name; int prev, next; installStepFn fn; int skipOnCancel, completed; int skipOnLocal; }; /* this table is translated at run time */ static struct installStep installSteps[] = { { N_("Select installation path"), -1, STEP_CLASS, selectPath, 0, 0, 0 }, { N_("Select installation class"), STEP_PATH, STEP_SCSI, selectInstallClass, 0, 0, 0 }, { N_("Setup SCSI"), STEP_CLASS, STEP_FDISKMTAB, setupSCSI, 0, 0, 1 }, { N_("Setup filesystems"), STEP_SCSI, STEP_SWAP, partitionDisks, 0, 0, 1 }, { N_("Setup swap space"), STEP_FDISKMTAB, STEP_FINDPKGS, setupSwap, 0, 0, 1 }, { N_("Find installation files"), STEP_SWAP, STEP_FORMAT, findInstallFiles, 1, 0, 0 }, { N_("Choose partitions to format"),STEP_SWAP, STEP_PICKPKGS, formatPartitions, 0, 0, 1 }, { N_("Choose packages to install"), STEP_FORMAT, STEP_DOIT, choosePackages, 0, 0, 0 }, { N_("Install system"), STEP_PICKPKGS, STEP_MOUSE, doInstallStep, 0, 0, 0 }, { N_("Configure mouse"), -1, STEP_FINISHNET, configureMouse, 0, 0, 0 }, { N_("Configure networking"), STEP_MOUSE, STEP_TIMECONFIG, finishNetworking, 0, 0, 0 }, { N_("Configure timezone"), STEP_FINISHNET, STEP_SERVICES, configureTimezone, 0, 0, 0 }, { N_("Configure services"), STEP_TIMECONFIG,STEP_PRINTER, configureServices, 0, 0, 0 }, { N_("Configure printer"), STEP_SERVICES, STEP_ROOTPW, configurePrinter, 0, 0, 0 }, { N_("Set root password"), STEP_PRINTER, STEP_AUTH, setRootPassword, 0, 0, 0 }, { N_("Configure authentication"), STEP_ROOTPW, STEP_BOOTDISK, configureAuth, 0, 0, 0 }, { N_("Create bootdisk"), STEP_AUTH, STEP_LILO, createBootdisk, 0, 0, 1 }, { N_("Install bootloader"), STEP_BOOTDISK, STEP_XCONFIG, setupBootloader, 0, 0, 1 }, { N_("Configure X"), STEP_LILO, STEP_EXIT, configureX, 0, 0, 0 }, { N_("Exit install"), STEP_XCONFIG, STEP_DONE, exitInstall, 0, 0, 0 }, }; /* this table is translated at run time */ struct installStep upgradeSteps[] = { { N_("Select installation path"), -1, STEP_UPG_SCSI, selectPath, 0, 0, 0 }, { N_("Setup SCSI"), STEP_PATH, STEP_UPG_MTAB, setupSCSI, 0, 0, 0 }, { N_("Find current installation"), STEP_UPG_SCSI, STEP_UPG_FFILES, upgrFindInstall, 0, 0, 0 }, { N_("Find installation files"), STEP_UPG_MTAB, STEP_UPG_PKGS, findInstallFiles, 1, 0, 0 }, { N_("Choose packages to upgrade"), STEP_UPG_FFILES,STEP_UPG_DOIT, upgrChoosePackages, 0, 0, 0 }, { N_("Upgrade system"), STEP_UPG_PKGS, STEP_UPG_BOOTDISK, doInstallStep, 0, 0, 0 }, { N_("Create bootdisk"), -1, STEP_UPG_LILO, createBootdisk, 0, 0, 0 }, { N_("Install bootloader"), STEP_UPG_BOOTDISK,STEP_UPG_EXIT, setupBootloader, 0, 0, 0 }, { N_("Exit install"), STEP_UPG_LILO, STEP_DONE, exitInstall, 0, 0, 0 }, }; /* partition layout for a server */ static struct attemptedPartition serverPartitioning[] = { #if defined(__i386__) { "/boot", 16, LINUX_NATIVE_PARTITION, 0, -1 }, #elif defined(__alpha__) { "/dos", 2, DOS_PRIMARY_lt32MEG_PARTITION, 0, 1 }, #endif { "/", 256, LINUX_NATIVE_PARTITION, 0, -1 }, { "/usr", 512, LINUX_NATIVE_PARTITION, 1, -1 }, { "/var", 256, LINUX_NATIVE_PARTITION, 0, -1 }, { "/home", 512, LINUX_NATIVE_PARTITION, 1, -1 }, { "Swap-auto", 64, LINUX_SWAP_PARTITION, 0, -1 }, { NULL, 0, 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("/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) { struct driversLoaded * drivers; if (state->direction < 0 && scsiDeviceAvailable()) return INST_CANCEL; /* If we have any scsi adapters configured from earlier, then don't bother asking again */ drivers = state->dl; while (drivers && drivers->type != DRIVER_SCSI) drivers = drivers->next; if (drivers) return INST_OKAY; #ifdef __sparc__ readProcModules(DRIVER_SCSI, &state->dl); #endif return setupSCSIInterfaces(0, &state->dl, state->hints.flags & HINT_AUTOSCSI, state->direction); } 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; } static int partitionDisks(struct installState * state) { int rc = 0; char ** drives; static int useNew; int numDrives; int dir = 1; enum { PART_METHOD, PART_RESIZE, PART_DDRUID, PART_FDISK, PART_DONE } stage; if (state->isUpgrade) return findAllPartitions(NULL, &state->table); if (kickstart) { if ((rc = getDriveList(&drives, &numDrives))) return rc; return kickstartPartitioning(&state->table, &state->fstab, drives); } if (state->direction > 0) stage = PART_RESIZE; else stage = PART_DDRUID; while (stage != PART_DONE) { switch (stage) { case PART_RESIZE: #ifdef ENABLE_RESIZE #ifdef __i386__ if (!(state->hints.flags & HINT_SKIPRESIZE)) { if ((rc = getDriveList(&drives, &numDrives))) return rc; if ((rc = FSResizePartitions(&state->table, drives))) return rc; } #endif /* __i386__ */ #else /* ENABLE_RESIZE */ if (dir < 0) return INST_CANCEL; #endif stage = PART_METHOD, dir = 1; break; case PART_METHOD: if (state->hints.flags & HINT_FORCEDDRUID) { if (dir < 0) return INST_CANCEL; useNew = 1; } else { rc = useNewFdisk(&useNew); if (rc == INST_ERROR) return rc; if (rc == INST_CANCEL) { stage = PART_RESIZE, dir = -1; continue; } } stage = useNew ? PART_DDRUID : PART_FDISK; dir = 1; break; case PART_DDRUID: if ((rc = getDriveList(&drives, &numDrives))) return rc; rc = FSEditPartitions(&state->table, &state->fstab, drives, &state->intf, &state->netc, &state->dl, state->hints.partitioning.attempts, (useNew ? 0 : FSEDIT_READONLY) | state->hints.partitioning.flags); if (rc == INST_ERROR) return rc; stage = PART_DONE, dir = 1; if (rc && useNew) stage = PART_METHOD, dir = -1; else if (rc) stage = PART_FDISK; break; case PART_FDISK: if ((rc = partitionDrives()) == INST_ERROR) return rc; stage = PART_DDRUID, dir = 1; if (rc) stage = PART_METHOD, dir = -1; break; case PART_DONE: 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->isUpgrade && 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 (state->hints.flags & HINT_AUTOFORMAT) { if (state->direction < 0) return INST_CANCEL; 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) { if ((state->hints.flags & HINT_AUTOSWAP) && state->direction < 0) return INST_CANCEL; return activeSwapSpace(&state->table, &state->fstab, state->hints.flags & HINT_AUTOSWAP); } static int choosePackages(struct installState * state) { if (state->hints.component && (state->direction < 0)) return INST_CANCEL; return psSelectPackages(&state->ps, &state->cs, state->hints.component, 0, 0); } static void emptyErrorCallback(void) { } static int doInstallStep(struct installState * state) { int rc = 0; char * netSharedPath = NULL; FILE * f; int netSharedLength; int i; char * path; rpmErrorCallBackType old; if (!state->isUpgrade) { if (!(state->hints.flags & HINT_NOLOGMESSAGE) && !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 { do { if (!(state->hints.flags & HINT_NOLOGMESSAGE) && !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; } winStatus(40, 3, _("Rebuilding"), _("Rebuilding RPM database...")); rc = 0; if (!testing) { old = rpmErrorSetCallback(emptyErrorCallback); rpmSetVerbosity(RPMMESS_FATALERROR); rc = rpmdbRebuild(state->rootPath); rpmErrorSetCallback(old); rpmSetVerbosity(RPMMESS_NORMAL); newtPopWindow(); if (rc) { newtWinMessage(_("Error"), _("Ok"), _("Rebuild of RPM " "database failed. You may be out of disk space?")); return INST_ERROR; } } } while (rc); } /* 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); } if (state->method->prepareMedia) { rc = state->method->prepareMedia(state->method, &state->fstab); if (rc) { umountFilesystems(&state->fstab); return rc; } } rc = doInstall(state->method, state->rootPath, &state->ps, netSharedPath, state->keyboard, state->kbdtype, state->isUpgrade); if (rc) { /* this may not be a good idea for upgrades */ /* umountFilesystems(&state->fstab); */ return rc; } path = alloca(strlen(state->rootPath) + 200); sprintf(path, "%s/etc/rpmrc", state->rootPath); if (netSharedPath && access(path, X_OK)) { logMessage("creating /etc/rpmrc for netshared info (as none exists)"); f = fopen(path, "w"); if (!f) { newtWinMessage(_("Error"), _("Ok"), "error creating path: %s", path, strerror(errno)); } else { fprintf(f, "netsharedpath: %s\n", netSharedPath); fclose(f); } } /* why not? */ sync(); sync(); if (!rc) psFreeComponentSet(&state->cs); configPCMCIA(state->rootPath, 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; char * path, * newpath; 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); } state->rootPass = strdup(pw1); 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(state->rootPath); chdir("/"); exit(system(cmd)); } waitpid(pid, &status, 0); path = alloca(strlen(state->rootPath) + 30); newpath = alloca(strlen(state->rootPath) + 30); sprintf(path, "%s/etc/passwd", state->rootPath); sprintf(newpath, "%s/etc/passwd.new", state->rootPath); unlink(path); rename(newpath, path); beenSet = 1; return 0; } static int configureTimezone(struct installState * state) { return timeConfig(state->rootPath); } static int configureServices(struct installState * state) { if (state->hints.flags & HINT_SKIPNTSYSV) return (state->direction < 0) ? INST_CANCEL : INST_OKAY; else return servicesConfig(state->rootPath); } static int setupBootloader(struct installState * state) { static int first = 1; int rc; char * versionString; int append = 0; char * version, * release; int i; if (!state->isUpgrade && first) { setupSerialConsole(); } 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); #ifdef __alpha__ /* We have generic alpha kernels now, no need to copy from the boot disk */ /* if (first) { first = 0; rc = kernelCopy(state->kernel); if (rc) return rc; } */ writeMiloConf(state->rootPath, state->fstab, versionString); #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, state->hints.bootloader.flags, state->hints.bootloader.options); #endif } static int finishNetworking(struct installState * state) { int rc; char * path = alloca(strlen(state->rootPath) + 50); rc = checkNetConfig(&state->intf, &state->netc, &state->intfFinal, &state->netcFinal, &state->dl, state->direction); if (rc) return rc; sprintf(path, "%s/etc/sysconfig", state->rootPath); writeNetConfig(path, &state->netcFinal, &state->intfFinal, 0); strcat(path, "/network-scripts"); writeNetInterfaceConfig(path, &state->intfFinal); sprintf(path, "%s/etc", state->rootPath); writeResolvConf(path, &state->netcFinal); /* this is a bit of a hack */ writeHosts(path, &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 selectInstallClass(struct installState * state) { int rc, i; static int choice = 2; char * choices[] = { N_("Workstation"), N_("Server"), N_("Custom"), NULL }; char * transChoices[sizeof(choices) / sizeof(*choices)]; if (expert || kickstart) { return state->direction < 0 ? INST_CANCEL : INST_OKAY; } for (i = 0; choices[i]; i++) transChoices[i] = _(choices[i]); transChoices[i] = NULL; rc = newtWinMenu(_("Installation Class"), _("What type of machine are you installing? For " "maximum flexibility, choose \"Custom\"."), 30, 10, 0, 5, transChoices, &choice, _("Ok"), _("Back"), NULL); if (rc == 2) return INST_CANCEL; memset(&state->hints, 0, sizeof(state->hints)); if (choice == 2) return 0; state->hints.bootloader.flags = LILO_ON_MBR | LILO_USE_LINEAR; state->hints.flags = HINT_SKIPNTSYSV | HINT_AUTOFORMAT | HINT_NOLOGMESSAGE | HINT_AUTH | HINT_AUTOSWAP | HINT_SKIPBOOTLOADER | HINT_FORCEDDRUID; state->hints.auth = AUTHHINT_SHADOW | AUTHHINT_MD5; state->hints.component = choices[choice]; if (choice == 1) { /* server */ state->hints.flags |= HINT_SKIPPRINTER; state->hints.flags |= HINT_SKIPRESIZE; state->hints.partitioning.flags |= FSEDIT_CLEARALL; state->hints.partitioning.attempts = serverPartitioning; } else { /* workstation */ state->hints.flags |= HINT_AUTOSCSI; #ifdef __i386__ state->hints.partitioning.flags |= FSEDIT_CLEARLINUX; #else state->hints.partitioning.flags |= FSEDIT_CLEARALL; #endif } return INST_OKAY; } static int upgrFindInstall(struct installState * state) { int rc; 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; } umountFilesystems(&state->fstab); /* rootpath upgrade support */ if (strcmp(state->rootPath ,"/mnt")) return INST_OKAY; /* 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) { static int firstTime = 1; char * rpmconvertbin; int rc; char * path; char * argv[] = { NULL, NULL }; char buf[128]; if (testing) path = "/"; else path = state->rootPath; if (firstTime) { snprintf(buf, sizeof(buf), "%s%s", state->rootPath, "/var/lib/rpm/packages.rpm"); if (access(buf, R_OK)) { snprintf(buf, sizeof(buf), "%s%s", state->rootPath, "/var/lib/rpm/packages"); if (access(buf, 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, NULL, 0, 1); } #define DO_RETRY 1 #define DO_NEXT 2 #define DO_PREV 3 #define DO_MENU 4 static int errcanChoices(char * name) { int rc; rc = newtWinTernary(_("Error"), _("Previous"), _("Retry"), _("Menu"), _("An error occured during step \"%s\" of the install.\n\n" "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?"), name); if (rc == 1 || !rc) return DO_PREV; else if (rc == 2) return DO_RETRY; return DO_MENU; } 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); kickstart = 0; } else if (lastrc == INST_CANCEL) { choice = DO_PREV; } 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].skipOnLocal && state->isLocal)) && 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; while (nextStep != STEP_DONE && state->isLocal && state->steps[nextStep].skipOnLocal) nextStep = state->steps[nextStep].next; state->direction = 1; break; } state->lastChoice = choice; return nextStep; } void doSuspend(void) { pid_t pid; int status; if (exitOnSuspend) { 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) { char * path = alloca(strlen(state->rootPath) + 50); sprintf(path, "%s/usr/bin/lpr", state->rootPath); if (state->hints.flags & HINT_SKIPPRINTER) return state->direction < 0 ? INST_CANCEL : INST_OKAY; else if (testing) { return doConfigurePrinters("/", state->direction); } else if (!access(path, X_OK)) { return doConfigurePrinters(state->rootPath, 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 (state->hints.flags & HINT_SKIPBOOTDISK) 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 it 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; DIR * dir; struct dirent * ent; char * kickstartFile = NULL; int localInstall = 0; char * s; /* This is a nice way to find malloc/free troubles. -- msw */ /* if (!getenv("MALLOC_CHECK_")) { putenv("MALLOC_CHECK_=2"); execv(argv[0], argv); } */ spaces = strdup(" "); #ifdef SINGLE_INSTALL if ((len >= 7 && !strcmp(argv[0] + len - 7, "install")) || (len >= 16 && !strcmp(argv[0] + len - 16, "install-continue"))) { extern int install_main(int, char **); return install_main(argc, argv); } #endif 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") || !strcmp(argv[0] + len - 8, "modprobe")) { return ourInsmodCommand(argc, argv); } else if (!strcmp(argv[0] + len - 5, "rmmod")) { return rmmod_main(argc, argv); } else if (!strcmp(argv[0] + len - 6, "depmod")) { printf("depmod is not available.\n"); return 0; } /* 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 */ state.rootPath = "/mnt"; 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, "--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 if (!strcmp(*argptr, "--rootpath")) { argptr++; if (!*argptr) { fprintf(stderr, "--rootpath requires argument\n"); exit(1); } state.rootPath = *argptr; localInstall = 1; } else { /* skipping unknown arguments allows for future expansion */ fprintf(stderr, "unknown argument: %s\n", *argptr); } argptr++; } if (!state.method) { fprintf(stderr, "--method argument is required\n"); exit(1); } if (!testing && !localInstall && !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"); if (!testing && !localInstall && !(syncPid = fork())) { while (1) { sleep(30); sync(); } } /* 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); state.isLocal = localInstall; exitOnSuspend = localInstall || testing; newtSetSuspendCallback(doSuspend); openLog(testing || localInstall); logMessage("second stage install running (version " INSTALL_VERSION " built " __DATE__ " " __TIME__ ")"); logDebugMessage(("extra log messages are enabled")); if (!strcmp(state.rootPath ,"/mnt")) spawnShell(); newtInit(); newtCls(); /* Please don't remove this (C) line -- it's a legal nicety. Feel free to add whatever (C) information you like though - for example - "UltraPenguin 1.2 Linux (C) 1998 Red Hat Software, 1999 Ultra Penguin Team" If you have any questions, feel free to contact Matt Wilson, */ newtDrawRootText(0, 0, "Red Hat Linux (C) 1999 Red Hat Software"); newtPushHelpLine(_(" / between elements | selects | next screen ")); setDefaultLanguage(2); readNetConfig("/tmp", &state.netc); #if defined(__i386__) loadModuleDep("/modules/modules.dep"); #endif 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/rpm/rpmrc"); rpmReadConfigFiles(NULL, NULL); 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; ksToHints(&state.hints); } setupKeyboard(&state.keyboard, &state.kbdtype); } else { readKbdConfig("/tmp", &state.keyboard, &state.kbdtype); } if (kickstart) { state.hints.flags |= HINT_SKIPNTSYSV | HINT_AUTOFORMAT | HINT_SKIPPRINTER | HINT_SKIPBOOTDISK | HINT_AUTOSWAP | HINT_AUTOSCSI | HINT_NOLOGMESSAGE; } while (step != STEP_DONE) { s = _(state.steps[step].name); i = strlen(s); newtDrawRootText(0 - i, 0, s); newtRefresh(); rc = state.steps[step].fn(&state); if (!rc) state.steps[step].completed = 1; spaces[i] = '\0'; newtDrawRootText(0 - i, 0, spaces); spaces[i] = ' '; step = getNextStep(&state, step, rc); } if (kickstart) ksRunPost(); if (!(state.hints.flags & HINT_REBOOT)) { newtDrawRootText(72, 0, _("Complete")); newtWinMessage(_("Done"), _("Ok"), _("Congratulations, installation is complete.\n\n" "Remove the boot media 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(); killCardServices(); return 0; } static void killCardServices() { int fd; struct stat sb; char * buf; pid_t p; if ((fd = open("/tmp/cardmgr.pid", O_RDONLY)) < 0) return; fstat(fd, &sb); buf = alloca(sb.st_size); read(fd, buf, sb.st_size); buf[sb.st_size - 1] = '\0'; /* cut off the \n */ close(fd); p = atoi(buf); kill(p, SIGTERM); } static int configureX(struct installState * state) { if (kickstart) { int argc; char **argv; poptContext optCon; struct poptOption ksPcOptions[] = { { 0, 0, 0, 0, 0 } }; if (!ksGetCommand(KS_CMD_SKIPX, NULL, &argc, &argv)) { char *t; optCon = poptGetContext(NULL, argc, argv, ksPcOptions, 0); poptGetNextOpt(optCon); t = poptGetArg(optCon); if (t && *t && (!strcasecmp(t,"yes") || !strcasecmp(t,"on") || !strcmp(t, "1"))) return INST_OKAY; else if (!(t && *t && (!strcasecmp(t,"no") || !strcasecmp(t,"off") || !strcmp(t, "0")))) { newtWinMessage("Xconfig Kickstart", "Ok", "bad argument to skipx " "command: %s.\nMust be " "'on', '1', or 'yes' to enable, " "or 'off', '0', or 'no' to disable.",t); kickstart = 0; } } } return setupXfree(state->method, state->rootPath, &state->ps); } static int configureMouse(struct installState * state) { return mouseConfig(state->rootPath); } static int configureAuth(struct installState * state) { char cmd[255]; int rc; int child, status; if (state->hints.flags & HINT_AUTH) return (state->direction < 0) ? INST_CANCEL : INST_OKAY; else { if ((rc = authConfig(state->rootPath, &state->hints))) return rc; } if (!testing && state->rootPass) { if (!(child = fork())) { snprintf(cmd, sizeof(cmd), "(echo \"%s\"; sleep 1; echo \"%s\") | passwd root > /dev/null 2>&1", state->rootPass, state->rootPass); if (!testing) chroot(state->rootPath); chdir("/"); system(cmd); exit(0); } waitpid(child, &status, 0); } return INST_OKAY; } static int exitInstall(struct installState * state) { return INST_OKAY; }