/* * install.c * * This is the first half of the install. It does just enough to get the * second half going. It, and everything it needs, has to fit on one floppy * along with a kernel and modules. Needless to say, it's a bit tight. * * Erik Troan (ewt@redhat.com) * * 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. * */ #include #include #include #include #include /* for ntohl */ #include #include #include #include #include #include #include #include #include #include #include #include "cpio.h" #include "devices.h" #include "fs.h" #include "install.h" #include "otherinsmod.h" #include "intl.h" #include "kbd.h" #include "kickstart.h" #include "lang.h" #include "log.h" #include "methods.h" #include "net.h" #include "newt.h" #include "run.h" #include "windows.h" #include "pcmcia-probing/pcmcia-probe.h" #define KS_NONE 0 #define KS_FLOPPY 1 #define KS_BOOTP 2 #define KS_FILE 3 #ifdef SINGLE_INSTALL #define main install_main #else int testing = 0; int expert = 0; int kickstart = 0; #endif int debug = 1; char * ksFile; /* hack */ int rmmod_main(int argc, char ** argv); void welcome(void) { if (!testing && !kickstart) { newtWinMessage("Red Hat Linux", _("Ok"), _("Welcome to Red Hat Linux!\n\n" "This installation process is outlined in detail in the " "Official Red Hat Linux Installation Guide available from " "Red Hat Software. If you have access to this manual, you " "should read the installation section before continuing.\n\n" "If you have purchased Official Red Hat Linux, be sure to " "register your purchase through our web site, " "http://www.redhat.com.")); } } void mountFloppy() { if (!testing) { logMessage("mounting ext2 fs on floppy"); doMount("fd0", "/tmp/bootdisk", "ext2", 1, 0); logMessage("floppy filesystem mounted on /tmp/bootdisk"); } } int expandPcmciaArchive() { CFD_t cfd; char * failedFile; int rc; cfd.cpioIoType = cpioIoTypeGzFd; cfd.cpioGzFd = gzdOpen("/tmp/image/pcmcia.cgz", "r"); if (gzdFileno(cfd.cpioGzFd) == NULL) { logMessage("gzdOpen failed to read pcmcia.cgz: %s", strerror(errno)); return INST_ERROR; } rc = cpioInstallArchive(&cfd, NULL, 0, NULL, NULL, &failedFile); if (rc) { logMessage("cpio expansion failed on file %s, error %d\n", failedFile, rc); gzdClose(cfd.cpioGzFd); return INST_ERROR; } gzdClose(cfd.cpioGzFd); return 0; } #ifdef __i386__ extern int loadPCMCIADisk(void); int setupPCMCIA(char ** arg, int direction) { int rc; struct driversLoaded * dl = NULL; int status; char * probeOutput; static char pcic[20]; int argc; char **argv; poptContext optCon; struct poptOption ksPcOptions[] = { { 0, 0, 0, 0, 0 } }; /* just probe and if pcmcia controller exists we load support automatically */ probeOutput = pcmciaProbeController(); if (probeOutput == NULL) { if (direction < 0) return INST_CANCEL; return INST_OKAY; } logMessage("pcmcia probe returned: %s", probeOutput); if (strstr(probeOutput, "TCIC")) { strcpy(pcic, "tcic"); } else strcpy(pcic, "i82365"); logMessage("pcmcia pcic type: %s", pcic); /* go ahead and tell the rest of the install we found a PCMCIA controller */ *arg = pcic; if (kickstart) { if (!ksGetCommand(KS_CMD_PCMCIA, argv, &argc, &argv)) { char *t; optCon = poptGetContext(NULL, argc, argv, ksPcOptions, 0); poptGetNextOpt(optCon); t = poptGetArg(optCon); if (t && *t && (!strcasecmp(t,"no") || !strcasecmp(t,"off") || !strcmp(t, "0"))) return INST_OKAY; else if (!(t && *t && (!strcasecmp(t,"yes") || !strcasecmp(t,"on") || !strcmp(t, "1")))) { newtWinMessage("PCMCIA Kickstart", "Ok", "bad argument to kickstart " "pcmcia command: %s.\nMust be " "'on', '1', or 'yes' to enable, " "or 'off', '0', or 'no' to disable.",t); kickstart=0; } } } else { rc = newtWinTernary(_("PCMCIA Support"), _("Yes"), _("No"), _("Back"), _("Do you need to use PCMCIA devices during the " "install? Answer no to this question if only " "need PCMCIA support after the install. You do " "not need install-time PCMCIA support if you " "are installing Red Hat Linux on a laptop with " "a built-in CDROM drive.")); if (rc == 2) return INST_OKAY; if (rc == 3) return INST_CANCEL; } do { if (!kickstart) { rc = newtWinChoice(_("PCMCIA Support Disk"), _("Ok"), _("Back"), _("PCMCIA support requires a PCMCIA support disk. " "Please remove the boot disk currently in your " "drive and replace it with the Red Hat " "PCMCIA support disk.")); if (rc == 2) return INST_CANCEL; } if ((rc = loadPCMCIADisk())) kickstart = 0; } while (rc); winStatus(40, 3, "PCMCIA", _("Starting PCMCIA services...")); loadModule("pcmcia_core", DRIVER_PCMCIA, DRIVER_MINOR_NONE, &dl); loadModule(pcic, DRIVER_PCMCIA, DRIVER_MINOR_NONE, &dl); loadModule("ds", DRIVER_PCMCIA, DRIVER_MINOR_NONE, &dl); if (!fork()) { pid_t child; if (!(child = fork())) { execl("/sbin/cardmgr", "/sbin/cardmgr", "-m", "/tmp/pcmcia/lib/modules/preferred", NULL); exit(-1); } exit(child); } wait(&status); /* if cardmgr a chance to get going */ sleep(5); newtPopWindow(); return 0; } #endif #ifdef SINGLE_INSTALL extern void doSuspend(void); #else void doSuspend(void) { pid_t pid; int status; if (testing) { newtFinished(); exit(1); } else if (access("/bin/sh", X_OK)) { return; } 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(); } #endif void doKickstart(struct intfInfo * intf, struct netInfo * netc, struct driversLoaded ** dl) { char * file; char * ksPath; int rc; #ifndef DISABLE_NETWORK if (kickstart == KS_BOOTP) { if ((bringUpNetworking(intf, netc, dl, 1))) { kickstart = 0; } else if (!(intf->set & INTFINFO_HAS_BOOTSERVER)) { newtWinMessage(_("Kickstart Error"), _("Ok"), _("No kickstart " "configuration file server can be found.")); kickstart = 0; } else { if (!(intf->set & INTFINFO_HAS_BOOTFILE)) { file = "/kickstart/"; logMessage("bootp: no bootfile received"); } else { file = intf->bootFile; } ksPath = malloc(strlen(file) + strlen(netc->hostname) + 70); strcpy(ksPath, inet_ntoa(intf->bootServer)); strcat(ksPath, ":"); strcat(ksPath, file); if (ksPath[strlen(ksPath) - 1] == '/') { ksPath[strlen(ksPath) - 1] = '\0'; file = malloc(30); sprintf(file, "%s-kickstart", inet_ntoa(intf->ip)); } else { file = strrchr(ksPath, '/'); if (!file) { file = ksPath; ksPath = "/"; } else { *file++ = '\0'; } } logMessage("ks server: %s file: %s", ksPath, file); loadFilesystem("nfs", "nfs", dl); if ((rc = doMount(ksPath, "/tmp/ks", "nfs", 1, 0))) { newtWinMessage(_("Error"), _("Ok"), _("I could not mount the kickstart path %s.\n"), ksPath); kickstart = 0; } else { ksFile = malloc(strlen(file) + 20); sprintf(ksFile, "/tmp/ks/%s", file); if (ksReadCommands(ksFile)) kickstart = 0; } } } #endif if (kickstart) chooseLanguage(); } #ifdef SPAWN_SHELL 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); } } #endif int main(int argc, char ** argv) { char ** argptr, * arg; struct installMethod * method; int rc; char * pcmciaArg = NULL; int isSerial; int force = 0; struct stat sb; char * childArgs[30]; struct intfInfo intf; struct netInfo netc; struct driversLoaded * dl = NULL; char * ksMode = NULL; int stage, direction; char * keymap = NULL; char * kbdtype = NULL; poptContext optCon; int cont = 0; int forceSupp = 0; int network = 0; int local = 0; struct poptOption optionTable[] = { { "expert", '\0', POPT_ARG_NONE, &expert, 0 }, { "force", '\0', POPT_ARG_NONE, &force, 0 }, { "forcesupp", '\0', POPT_ARG_NONE, &forceSupp, 0 }, { "kickstart", '\0', POPT_ARG_STRING, &ksMode, 0 }, { "ks", '\0', POPT_ARG_STRING, &ksMode, 0 }, { "test", '\0', POPT_ARG_NONE, &testing, 0 }, { "local", '\0', POPT_ARG_NONE, &local, 0 }, { "network", '\0', POPT_ARG_NONE, &network, 0 }, { 0, 0, 0, 0, 0 } }; #ifdef SPAWN_SHELL spawnShell(); #endif #ifndef __alpha__ if (!strcmp(argv[0] + strlen(argv[0]) - 6, "insmod") || !strcmp(argv[0] + strlen(argv[0]) - 8, "modprobe")) { return ourInsmodCommand(argc, argv); } else if (!strcmp(argv[0] + strlen(argv[0]) - 5, "rmmod")) { return rmmod_main(argc, argv); } else #endif if (!strcmp(argv[0], "install-continue")) { cont = 1; } memset(&intf, 0, sizeof(intf)); memset(&netc, 0, sizeof(netc)); optCon = poptGetContext(NULL, argc, argv, optionTable, 0); if ((rc = poptGetNextOpt(optCon)) < -1) { fprintf(stderr, "bad option %s: %s\n", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(rc)); exit(1); } if ((arg = poptGetArg(optCon))) { fprintf(stderr, "unexpected argument: %s\n", arg); exit(1); } if (ksMode) { if (testing) { kickstart = KS_FILE; ksFile = ksMode; } else if (!strcmp(ksMode, "floppy")) kickstart = KS_FLOPPY; else if (!strcmp(ksMode, "bootp")) kickstart = KS_BOOTP; else { fprintf(stderr, "unknown kickstart option %s\n", ksMode); exit(1); } } if (!testing && !force && (getpid() > 50)) { fprintf(stderr, "you're running me on a live system! that's "); fprintf(stderr, "incredibly stupid.\n"); exit(1); } openLog(testing); /* see if we're on a serial console -- if so, don't setup a keymap */ if (fstat(0, &sb)) { logMessage("error stat'ing stdin: %s", strerror(errno)); return 1; } if (!S_ISCHR(sb.st_mode)) { logMessage("stdin isn't a character device!!! ack!"); return 1; } isSerial = (major(sb.st_rdev) == 4 && minor(sb.st_dev) >= 64) || (major(sb.st_rdev) == 5 && minor(sb.st_dev) >= 64); logMessage("welcome to the Red Hat install " "(first stage, version " INSTALL_VERSION " built " __DATE__ " " __TIME__")"); newtInit(); newtCls(); newtSetSuspendCallback(doSuspend); setDefaultLanguage(1); newtDrawRootText(0, 0, _("Welcome to Red Hat Linux")); newtPushHelpLine(_(" / between elements | selects | next screen ")); #if defined(__i386__) loadModuleDep("/modules/modules.dep"); #endif /* kickstart from floppy needs to be handled before PCMCIA */ if (!cont && kickstart == KS_FLOPPY) { loadFilesystem("vfat", "vfat", &dl); if (devMakeInode("fd0", "/tmp/fd0")) kickstart = 0; else if (doMount("/tmp/fd0", "/tmp/ks", "vfat", 1, 0)) { newtWinMessage(_("Error"), _("Ok"), _("I could not mount the boot floppy.")); kickstart = 0; } else if (access("/tmp/ks/ks.cfg", R_OK)) { newtWinMessage(_("Error"), _("Ok"), _("Cannot find ks.cfg on boot floppy.")); kickstart = 0; } else { int infd = -1, outfd = -1; char buf[4096]; int i; if ((outfd = open("/tmp/ks.cfg", O_CREAT | O_RDWR, 0666)) < 0 || (infd = open("/tmp/ks/ks.cfg", O_RDONLY)) < 0) { newtWinMessage(_("Error"), _("Ok"), _("Error opening files " "for kickstart copy: %s\n"), strerror(errno)); kickstart = 0; } else { while ((i = read(infd, buf, sizeof(buf))) > 0) { if (write(outfd, buf, i) != i) break; } if (infd >= 0) close(infd); if (outfd >= 0) close(outfd); if (!i) { ksFile = alloca(30); strcpy(ksFile, "/tmp/ks.cfg"); if (ksReadCommands(ksFile)) kickstart = 0; } else { newtWinMessage(_("Error"), _("Ok"), _("Error copying kickstart file from floppy.")); kickstart = 0; } } umount("/tmp/ks"); devRemoveInode("/tmp/fd0"); } } else if (cont && kickstart == KS_FLOPPY) { /* continue install after language re-exec - all data was lost */ ksFile = strdup("/tmp/ks.cfg"); if (ksReadCommands(ksFile)) kickstart = 0; } else if (kickstart == KS_FILE) { rc = 0; while (!rc) if ((rc = ksReadCommands(ksFile))) kickstart = 0; } direction = 1; if (cont) stage = 1; else stage = 0; do { switch (stage) { case 0: if (!kickstart) { welcome(); /* kickstart installs do this later */ chooseLanguage(); } newtFinished(); argv[0] = "install-continue"; execv(testing ? "./install" : "/sbin/install", argv); break; case 1: setDefaultLanguage(1); if (isSerial || kickstart) { direction = 1, stage++; break; } rc = setupKeyboard(&keymap, &kbdtype); direction = (rc == INST_CANCEL) ? -1 : 1; stage += direction; break; case 2: #if defined(__i386__) rc = setupPCMCIA(&pcmciaArg, direction); direction = (rc == INST_CANCEL) ? -1 : 1; #endif stage += direction; break; case 3: doKickstart(&intf, &netc, &dl); stage += direction; break; case 4: if (!network && !local) /* if they didn't say, assume both */ network = local = 1; rc = chooseInstallMethod(forceSupp, local, network, &method, &netc, &intf, &dl); direction = (rc == INST_CANCEL) ? -1 : 1; stage += direction; break; } } while (stage < 5); logMessage("method selection completed"); #ifndef DISABLE_NETWORK /* FIXME: is this a decent test? */ if (intf.set) { writeNetInterfaceConfig("/tmp", &intf); writeNetConfig("/tmp", &netc, &intf, 1); } #endif if (dl) writeModuleConf("/tmp", dl, 0); logMessage("state saved to /tmp"); newtFinished(); closeLog(); if (testing) exit(0); argptr = childArgs; *argptr++ = "/usr/bin/runinstall2"; *argptr++ = "--method"; *argptr++ = method->abbrev; if (expert) *argptr++ = "--expert"; if (pcmciaArg) { *argptr++ = "--pcmcia"; *argptr++ = pcmciaArg; } if (kickstart) { *argptr++ = "--ks"; *argptr++ = ksFile; } *argptr++ = NULL; execv(childArgs[0], childArgs); rc = errno; openLog(testing); logMessage("error in exec of second stage loader :-("); logMessage("\terror:%s", strerror(rc)); while (1) ; return 0; }