/* * loader.c * * This is the installer loader. Its job is to somehow load the rest * of the installer into memory and run it. This may require setting * up some devices and networking, etc. The main point of this code is * to stay SMALL! Remember that, live by that, and learn to like it. * * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 2007 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Author(s): Erik Troan * Matt Wilson * Michael Fulbright * Jeremy Katz */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_MTRACE #include #endif #include "copy.h" #include "getparts.h" #include "loader.h" #include "loadermisc.h" /* JKFIXME: functions here should be split out */ #include "log.h" #include "lang.h" #include "fwloader.h" #include "kbd.h" #include "kickstart.h" #include "windows.h" /* module stuff */ #include "modules.h" #include "moduleinfo.h" #include "driverdisk.h" /* hardware stuff */ #include "hardware.h" /* install method stuff */ #include "method.h" #include "cdinstall.h" #include "nfsinstall.h" #include "hdinstall.h" #include "urls.h" #include "urlinstall.h" #include "net.h" #include "telnetd.h" #include #include "selinux.h" #include "../isys/imount.h" #include "../isys/isys.h" #include "../isys/stubs.h" #include "../isys/lang.h" #include "../isys/eddsupport.h" #include "../isys/str.h" /* maximum number of extra arguments that can be passed to the second stage */ #define MAX_EXTRA_ARGS 128 static char * extraArgs[MAX_EXTRA_ARGS]; static int hasGraphicalOverride(); static int newtRunning = 0; /* boot flags -- we need these in a lot of places */ uint64_t flags = LOADER_FLAGS_SELINUX; #ifdef INCLUDE_LOCAL #include "cdinstall.h" #include "hdinstall.h" #endif #ifdef INCLUDE_NETWORK #include "nfsinstall.h" #include "urlinstall.h" #endif int num_link_checks = 5; int post_link_sleep = 0; static pid_t init_pid = 1; static int init_sig = SIGUSR1; /* default to shutdown=halt */ static const char *LANG_DEFAULT = "en_US.UTF-8"; static struct installMethod installMethods[] = { { N_("Local CD/DVD"), 0, DEVICE_CDROM, mountCdromImage }, { N_("Hard drive"), 0, DEVICE_DISK, mountHardDrive }, { N_("NFS directory"), 1, DEVICE_NETWORK, mountNfsImage }, { "URL", 1, DEVICE_NETWORK, mountUrlImage }, }; static int numMethods = sizeof(installMethods) / sizeof(struct installMethod); static int expected_exit = 0; void doExit(int result) { expected_exit = 1; exit(result); } void doSuspend(void) { newtFinished(); doExit(1); } void doShell(void) { pid_t child; int status; newtSuspend(); child = fork(); if (child == 0) { if (execl("/sbin/bash", "/sbin/bash", "-i", NULL) == -1) { logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); _exit(1); } } else if (child == -1) { logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); newtResume(); } else { if (waitpid(child, &status, 0) == -1) { logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); } newtResume(); } } void doGdbserver(struct loaderData_s *loaderData) { int child, fd; char *pid; iface_t iface; /* If gdbserver is found, go ahead and run it on the loader process now * before anything bad happens. */ if (loaderData->gdbServer && !access("/usr/bin/gdbserver", X_OK)) { pid_t loaderPid = getpid(); iface_init_iface_t(&iface); if (kickstartNetworkUp(loaderData, &iface)) { logMessage(ERROR, "can't run gdbserver due to no network"); return; } checked_asprintf(&pid, "%d", loaderPid); if (!(child = fork())) { logMessage(INFO, "starting gdbserver: %s %s %s %s", "/usr/bin/gdbserver", "--attach", loaderData->gdbServer, pid); fd = open("/dev/null", O_RDONLY); close(STDIN_FILENO); dup2(fd, STDIN_FILENO); close(fd); fd = open("/dev/null", O_WRONLY); close(STDOUT_FILENO); dup2(fd, STDOUT_FILENO); close(STDERR_FILENO); dup2(fd, STDERR_FILENO); close(fd); if (execl("/usr/bin/gdbserver", "/usr/bin/gdbserver", "--attach", loaderData->gdbServer, pid, NULL) == -1) logMessage(ERROR, "error running gdbserver: %m"); _exit(1); } } } void startNewt(void) { if (!newtRunning) { char *buf; char *arch = getProductArch(); checked_asprintf(&buf, _("Welcome to %s for %s"), getProductName(), arch); /* * Because currently initrd.img only has got the default English locale * support, pretend for newtInit() it is actually the used LANG so Newt * knows how to compute character widths etc. */ char *lang = getenv("LANG"); if (lang) { lang = strdup(lang); } setenv("LANG", LANG_DEFAULT, 1); newtInit(); unsetenv("LANG"); /* restore the original LANG value */ if (lang) { setenv("LANG", lang, 1); free(lang); } newtCls(); newtDrawRootText(0, 0, buf); free(buf); newtPushHelpLine(_(" / between elements | selects | next screen ")); newtRunning = 1; if (!access("/bin/sh", X_OK)) newtSetSuspendCallback((void *) doShell, NULL); } } void stopNewt(void) { if (newtRunning) newtFinished(); newtRunning = 0; } static gchar *productName = NULL; static gchar *productPath = NULL; static gchar *productArch = NULL; static void initProductInfo(void) { gchar *contents = NULL; gchar **lines = NULL, **stamp = NULL; GError *fileErr = NULL; if (!g_file_get_contents("/.buildstamp", &contents, NULL, &fileErr)) { logMessage(ERROR, "error reading .buildstamp: %s", fileErr->message); g_error_free(fileErr); productName = g_strdup("anaconda"); productArch = g_strdup("unknown architecture"); productPath = g_strdup("anaconda"); return; } /* .buildstamp uses the first 3 lines in this format: * STAMP.productArch * productName * productPath */ lines = g_strsplit(contents, "\n", 0); g_free(contents); if ((lines != NULL) && (g_strv_length(lines) >= 3)) { /* STAMP.productArch */ stamp = g_strsplit(lines[0], ".", 0); if ((stamp != NULL) && (g_strv_length(stamp) == 2)) { productArch = g_strdup(stamp[1]); } else { productArch = g_strdup("unknown architecture"); } if (stamp) { g_strfreev(stamp); } productName = g_strdup(lines[1]); productPath = g_strdup(lines[2]); } else { productName = g_strdup("anaconda"); productArch = g_strdup("unknown architecture"); productPath = g_strdup("anaconda"); } if (lines) { g_strfreev(lines); } return; } char * getProductName(void) { if (!productName) { initProductInfo(); } return productName; } char * getProductArch(void) { if (!productArch) { initProductInfo(); } return productArch; } char * getProductPath(void) { if (!productPath) { initProductInfo(); } return productPath; } void initializeConsole() { /* enable UTF-8 console */ setenv("LANG", LANG_DEFAULT, 1); printf("\033%%G"); fflush(stdout); isysLoadFont(); isysSetUnicodeKeymap(); } /* fbcon is buggy and resets our color palette if we allocate a terminal * after initializing it, so we initialize 9 of them before we need them. * If it doesn't work, the user gets to suffer through having an ugly palette, * but things are still usable. */ static void initializeTtys(void) { int fd, n; char dev[] = "/dev/ttyX"; for (n = 9; n > 0; n--) { sprintf(dev, "/dev/tty%d", n); mknod(dev, 0600 | S_IFCHR, makedev(4, n)); fd = open(dev, O_RDWR|O_NOCTTY); if (fd >= 0) { ioctl(fd, VT_ACTIVATE, n); if (n == 1) ioctl(fd, VT_WAITACTIVE, n); close(fd); } else logMessage(ERROR, "failed to initialize %s", dev); } } static void spawnShell(void) { pid_t pid; if (FL_SERIAL(flags) || FL_NOSHELL(flags)) { logMessage(INFO, "not spawning a shell"); return; } else if (access("/bin/sh", X_OK)) { logMessage(ERROR, "cannot open shell - /bin/sh doesn't exist"); return; } if (!(pid = fork())) { int fd; fd = open("/dev/tty2", O_RDWR|O_NOCTTY); if (fd < 0) { logMessage(ERROR, "cannot open /dev/tty2 -- no shell will be provided"); return; } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd); setsid(); /* enable UTF-8 console */ printf("\033%%G"); fflush(stdout); isysLoadFont(); if (ioctl(0, TIOCSCTTY, NULL)) { logMessage(ERROR, "could not set new controlling tty"); } signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_DFL); if (!access("/tmp/updates/pyrc.py", R_OK|X_OK)) setenv("PYTHONSTARTUP", "/tmp/updates/pyrc.py", 1); else if (!access("/usr/lib/anaconda-runtime/pyrc.py", R_OK|X_OK)) setenv("PYTHONSTARTUP", "/usr/lib/anaconda-runtime/pyrc.py", 1); setenv("LD_LIBRARY_PATH", LIBPATH, 1); setenv("LANG", "C", 1); if (execl("/bin/sh", "-/bin/sh", NULL) == -1) { logMessage(CRITICAL, "exec of /bin/sh failed: %m"); exit(1); } } return; } static void copyWarnFn (char *msg) { logMessage(WARNING, msg); } static void copyErrorFn (char *msg) { newtWinMessage(_("Error"), _("OK"), _(msg)); } void loadUpdates(struct loaderData_s *loaderData) { char *device = NULL, *part = NULL, *buf; char **devNames = NULL; enum { UPD_DEVICE, UPD_PART, UPD_PROMPT, UPD_LOAD, UPD_DONE } stage = UPD_DEVICE; int rc, num = 0; int dir = 1; while (stage != UPD_DONE) { switch (stage) { case UPD_DEVICE: { rc = getRemovableDevices(&devNames); if (rc == 0) return; /* we don't need to ask which to use if they only have one */ if (rc == 1) { device = strdup(devNames[0]); free(devNames); devNames = NULL; if (dir == -1) return; stage = UPD_PART; break; } dir = 1; startNewt(); rc = newtWinMenu(_("Update Disk Source"), _("You have multiple devices which could serve " "as sources for an update disk. Which would " "you like to use?"), 40, 10, 10, rc < 6 ? rc : 6, devNames, &num, _("OK"), _("Cancel"), NULL); if (rc == 2) { free(devNames); devNames = NULL; return; } device = strdup(devNames[num]); free(devNames); devNames = NULL; stage = UPD_PART; } case UPD_PART: { char ** part_list = getPartitionsList(device); int nump = 0, num = 0; if (part != NULL) { free(part); part = NULL; } if ((nump = lenPartitionsList(part_list)) == 0) { if (dir == -1) { stage = UPD_DEVICE; } else { checked_asprintf(&part, "/dev/%s", device); stage = UPD_PROMPT; } break; } dir = 1; startNewt(); rc = newtWinMenu(_("Update Disk Source"), _("There are multiple partitions on this device " "which could contain the update disk image. " "Which would you like to use?"), 40, 10, 10, nump < 6 ? nump : 6, part_list, &num, _("OK"), _("Back"), NULL); if (rc == 2) { freePartitionsList(part_list); stage = UPD_DEVICE; dir = -1; break; } part = strdup(part_list[num]); stage = UPD_LOAD; } case UPD_PROMPT: checked_asprintf(&buf, _("Insert your updates disk into %s and " "press \"OK\" to continue."), part); rc = newtWinChoice(_("Updates Disk"), _("OK"), _("Back"), buf); free(buf); buf = NULL; if (rc == 2) { stage = UPD_PART; dir = -1; break; } stage = UPD_LOAD; break; case UPD_LOAD: logMessage(INFO, "UPDATES device is %s", part); if (doPwMount(part, "/tmp/update-disk", "auto", "ro", NULL)) { newtWinMessage(_("Error"), _("OK"), _("Failed to mount updates disk")); stage = UPD_PROMPT; break; } else { /* Copy everything to /tmp/updates so we can unmount the disk */ winStatus(40, 3, _("Updates"), _("Reading anaconda updates")); if (!copyDirectory("/tmp/update-disk", "/tmp/updates", copyWarnFn, copyErrorFn)) { dir = 1; stage = UPD_DONE; } newtPopWindow(); umount("/tmp/update-disk"); } case UPD_DONE: break; } } return; } static char *newUpdatesLocation(const char *origLocation) { const char *location; char *retval = NULL; newtComponent f, okay, cancel, answer, locationEntry; newtGrid grid, buttons; startNewt(); locationEntry = newtEntry(-1, -1, NULL, 60, &location, NEWT_FLAG_SCROLL); newtEntrySet(locationEntry, origLocation, 1); /* button bar at the bottom of the window */ buttons = newtButtonBar(_("OK"), &okay, _("Cancel"), &cancel, NULL); grid = newtCreateGrid(1, 3); newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, newtTextboxReflowed(-1, -1, _("Unable to download the updates image. Please modify the updates location below or press Cancel to proceed without updates.."), 60, 0, 0, 0), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, locationEntry, 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, 0, 1, 0, 0, 0, NEWT_GRID_FLAG_GROWX); f = newtForm(NULL, NULL, 0); newtGridAddComponentsToForm(grid, f, 1); newtGridWrappedWindow(grid, _("Error downloading updates image")); newtGridFree(grid, 1); /* run the form */ answer = newtRunForm(f); if (answer != cancel) retval = strdup(location); newtFormDestroy(f); newtPopWindow(); return retval; } static int loadUpdatesFromRemote(char * url, struct loaderData_s * loaderData) { int rc = getFileFromUrl(url, "/tmp/updates.img", loaderData); if (rc != 0) { char *newLocation = newUpdatesLocation(url); if (!newLocation) return rc; else return loadUpdatesFromRemote(newLocation, loaderData); } copyUpdatesImg("/tmp/updates.img"); unlink("/tmp/updates.img"); return 0; } static void writeVNCPasswordFile(char *pfile, char *password) { FILE *f; f = fopen(pfile, "w+"); fprintf(f, "%s\n", password); fclose(f); } /* XXX: read information from /etc/sysconfig/network-scripts/ifcfg-$INTERFACE * (written by linuxrc), the linuxrc mess should be firing up NM too */ static void readNetInfo(struct loaderData_s ** ld) { int i; struct loaderData_s * loaderData = *ld; DIR *dp = NULL; FILE *f = NULL; struct dirent *ent = NULL; char *cfgfile = NULL; int bufsiz = 100; char buf[bufsiz]; char *vname = NULL; char *vparm = NULL; /* when this function is called, we can assume only one network device * config file has been written to /etc/sysconfig/network-scripts, so * find it and read it */ dp = opendir("/etc/sysconfig/network-scripts"); if (dp == NULL) { return; } while ((ent = readdir(dp)) != NULL) { if (!strncmp(ent->d_name, "ifcfg-", 6)) { checked_asprintf(&cfgfile, "/etc/sysconfig/network-scripts/%s", ent->d_name); break; } } if (dp != NULL) { if (closedir(dp) == -1) { logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__); abort(); } } if (cfgfile == NULL) { logMessage(DEBUGLVL, "no ifcfg files found in /etc/sysconfig/network-scripts"); return; } if ((f = fopen(cfgfile, "r")) == NULL) { logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__); free(cfgfile); return; } if ((vname = (char *) malloc(sizeof(char) * 15)) == NULL) { logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__); abort(); } if ((vparm = (char *) malloc(sizeof(char) * 85)) == NULL) { logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__); abort(); } /* make sure everything is NULL before we begin copying info */ loaderData->ipv4 = NULL; loaderData->netmask = NULL; loaderData->gateway = NULL; loaderData->dns = NULL; loaderData->peerid = NULL; loaderData->subchannels = NULL; loaderData->portname = NULL; loaderData->nettype = NULL; loaderData->ctcprot = NULL; loaderData->layer2 = NULL; loaderData->portno = NULL; loaderData->macaddr = NULL; #ifdef ENABLE_IPV6 loaderData->ipv6 = NULL; loaderData->gateway6 = NULL; #endif /* * The /tmp/netinfo file is written out by /sbin/init on s390x (which is * really the linuxrc.s390 script). It's a shell-sourcable file with * various system settings needing for the system instance. * * The goal of this function is to read in only the network settings * and populate the loaderData structure. */ while(fgets(buf, bufsiz, f)) { /* trim whitespace from end */ i = 0; while (!isspace(buf[i]) && i < (bufsiz-1)) i++; buf[i] = '\0'; /* break up var name and value */ if (strstr(buf, "=")) { vname = strtok(buf, "="); if (vname == NULL) continue; vparm = strtok(NULL, "="); if (vparm == NULL) continue; if (!strncmp(vname, "IPADDR", 6)) loaderData->ipv4 = strdup(vparm); if (!strncmp(vname, "NETMASK", 7)) loaderData->netmask = strdup(vparm); if (!strncmp(vname, "GATEWAY", 7)) loaderData->gateway = strdup(vparm); if (!strncmp(vname, "DNS", 3)) loaderData->dns = strdup(vparm); if (!strncmp(vname, "MTU", 3)) { errno = 0; loaderData->mtu = strtol(vparm, NULL, 10); if ((errno == ERANGE && (loaderData->mtu == LONG_MIN || loaderData->mtu == LONG_MAX)) || (errno != 0 && loaderData->mtu == 0)) { logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); abort(); } } if (!strncmp(vname, "PEERID", 6)) loaderData->peerid = strdup(vparm); if (!strncmp(vname, "SUBCHANNELS", 12)) loaderData->subchannels = strdup(vparm); if (!strncmp(vname, "PORTNAME", 8)) loaderData->portname = strdup(vparm); if (!strncmp(vname, "NETTYPE", 7)) loaderData->nettype = strdup(vparm); if (!strncmp(vname, "CTCPROT", 7)) loaderData->ctcprot = strdup(vparm); if (!strncmp(vname, "LAYER2", 6)) loaderData->layer2 = strdup(vparm); if (!strncmp(vname, "PORTNO", 6)) loaderData->portno = strdup(vparm); if (!strncmp(vname, "MACADDR", 7)) loaderData->macaddr = strdup(vparm); if (!strncmp(vname, "HOSTNAME", 8)) loaderData->hostname = strdup(vparm); } } if (loaderData->ipv4 && loaderData->netmask) { flags |= LOADER_FLAGS_HAVE_CMSCONF; } if (fclose(f) == -1) { logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); abort(); } if (cfgfile != NULL) { free(cfgfile); } return; } /* parse anaconda or pxelinux-style ip= arguments * pxelinux format: ip=::: * anaconda format: ip= netmask= gateway= */ static void parseCmdLineIp(struct loaderData_s * loaderData, char *argv) { /* Detect pxelinux */ if (strstr(argv, ":") != NULL) { char *start, *end; /* IP */ start = argv + 3; end = strstr(start, ":"); loaderData->ipv4 = strndup(start, end-start); loaderData->ipinfo_set = 1; /* Boot server */ if (end + 1 == '\0') return; start = end + 1; end = strstr(start, ":"); if (end == NULL) return; /* Gateway */ if (end + 1 == '\0') return; start = end + 1; end = strstr(start, ":"); if (end == NULL) { loaderData->gateway = strdup (start); return; } else { loaderData->gateway = strndup(start, end-start); } /* Netmask */ if (end + 1 == '\0') return; start = end + 1; loaderData->netmask = strdup(start); } else { loaderData->ipv4 = strdup(argv + 3); loaderData->ipinfo_set = 1; } if (loaderData->ipinfo_set) flags |= LOADER_FLAGS_IP_PARAM; } #ifdef ENABLE_IPV6 /* * parse anaconda ipv6= arguments */ static void parseCmdLineIpv6(struct loaderData_s * loaderData, char *argv) { /* right now we only accept ipv6= arguments equal to: * dhcp DHCPv6 call * auto RFC 2461 neighbor discovery */ loaderData->ipv6 = NULL; if (!strncmp(str2lower(argv), "ipv6=dhcp", 9)) { loaderData->ipv6 = strdup("dhcp"); } else if (!strncmp(str2lower(argv), "ipv6=auto", 9)) { loaderData->ipv6 = strdup("auto"); } if (loaderData->ipv6 != NULL) { loaderData->ipv6info_set = 1; flags |= LOADER_FLAGS_IPV6_PARAM; } return; } #endif static long argToLong(char *arg, int offset) { long retval; errno = 0; retval = strtol(arg+offset, NULL, 10); if ((errno == ERANGE && (retval == LONG_MIN || retval == LONG_MAX)) || (errno != 0 && retval == 0)) { logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); abort(); } return retval; } /* parses /proc/cmdline for any arguments which are important to us. * NOTE: in test mode, can specify a cmdline with --cmdline */ static void parseCmdLineFlags(struct loaderData_s * loaderData, char * cmdLine) { int fd; char buf[1024]; int len; gint argc = 0; gchar **argv = NULL; GError *optErr = NULL; int numExtraArgs = 0; int i; char *front; /* we want to default to graphical and allow override with 'text' */ flags |= LOADER_FLAGS_GRAPHICAL; /* if we have any explicit cmdline (probably test mode), we don't want * to parse /proc/cmdline */ if (!cmdLine) { if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) return; len = read(fd, buf, sizeof(buf) - 1); close(fd); if (len <= 0) { logMessage(INFO, "kernel command line was empty"); return; } buf[len] = '\0'; cmdLine = buf; } logMessage(INFO, "kernel command line: %s", cmdLine); if (!g_shell_parse_argv(cmdLine, &argc, &argv, &optErr)) { g_error_free(optErr); return; } for (i=0; i < argc; i++) { if (!strcasecmp(argv[i], "askmethod")) flags |= LOADER_FLAGS_ASKMETHOD; else if (!strcasecmp(argv[i], "asknetwork")) flags |= LOADER_FLAGS_ASKNETWORK; else if (!strcasecmp(argv[i], "noshell")) flags |= LOADER_FLAGS_NOSHELL; else if (!strcasecmp(argv[i], "nokill")) flags |= LOADER_FLAGS_NOKILL; else if (!strcasecmp(argv[i], "mediacheck")) flags |= LOADER_FLAGS_MEDIACHECK; else if (!strcasecmp(argv[i], "allowwireless")) flags |= LOADER_FLAGS_ALLOW_WIRELESS; else if (!strcasecmp(argv[i], "telnet")) flags |= LOADER_FLAGS_TELNETD; else if (!strcasecmp(argv[i], "noprobe")) flags |= LOADER_FLAGS_NOPROBE; else if (!strcasecmp(argv[i], "text")) { logMessage(INFO, "text mode forced from cmdline"); flags |= LOADER_FLAGS_TEXT; flags &= ~LOADER_FLAGS_GRAPHICAL; } else if (!strcasecmp(argv[i], "graphical")) { logMessage(INFO, "graphical mode forced from cmdline"); flags |= LOADER_FLAGS_GRAPHICAL; } else if (!strcasecmp(argv[i], "cmdline")) { logMessage(INFO, "cmdline mode forced from cmdline"); flags |= LOADER_FLAGS_CMDLINE; } else if (!strncasecmp(argv[i], "updates=", 8)) loaderData->updatessrc = strdup(argv[i] + 8); else if (!strncasecmp(argv[i], "updates", 7)) flags |= LOADER_FLAGS_UPDATES; else if (!strncasecmp(argv[i], "dogtail=", 8)) loaderData->dogtailurl = strdup(argv[i] + 8); else if (!strncasecmp(argv[i], "dd=", 3) || !strncasecmp(argv[i], "driverdisk=", 11)) { loaderData->ddsrc = strdup(argv[i] + (argv[i][1] == 'r' ? 11 : 3)); } else if (!strcasecmp(argv[i], "dd") || !strcasecmp(argv[i], "driverdisk")) flags |= LOADER_FLAGS_MODDISK; else if (!strcasecmp(argv[i], "dlabel=on")) flags |= LOADER_FLAGS_AUTOMODDISK; else if (!strcasecmp(argv[i], "dlabel=off")) flags &= ~LOADER_FLAGS_AUTOMODDISK; else if (!strcasecmp(argv[i], "rescue")) flags |= LOADER_FLAGS_RESCUE; else if (!strcasecmp(argv[i], "nopass")) flags |= LOADER_FLAGS_NOPASS; else if (!strcasecmp(argv[i], "serial")) flags |= LOADER_FLAGS_SERIAL; else if (!strcasecmp(argv[i], "noipv4")) flags |= LOADER_FLAGS_NOIPV4; #ifdef ENABLE_IPV6 else if (!strcasecmp(argv[i], "noipv6")) flags |= LOADER_FLAGS_NOIPV6; #endif else if (!strcasecmp(argv[i], "kssendmac")) flags |= LOADER_FLAGS_KICKSTART_SEND_MAC; else if (!strcasecmp(argv[i], "kssendsn")) flags |= LOADER_FLAGS_KICKSTART_SEND_SERIAL; /* deprecated hardware bits */ else if (!strcasecmp(argv[i], "nousbstorage")) mlAddBlacklist("usb-storage"); else if (!strcasecmp(argv[i], "nousb")) { mlAddBlacklist("ehci-hcd"); mlAddBlacklist("ohci-hcd"); mlAddBlacklist("uhci-hcd"); } else if (!strcasecmp(argv[i], "nofirewire")) mlAddBlacklist("firewire-ohci"); else if (!strncasecmp(argv[i], "loglevel=", 9)) { if (!strcasecmp(argv[i]+9, "debug")) { loaderData->logLevel = strdup(argv[i]+9); setLogLevel(DEBUGLVL); } else if (!strcasecmp(argv[i]+9, "info")) { loaderData->logLevel = strdup(argv[i]+9); setLogLevel(INFO); } else if (!strcasecmp(argv[i]+9, "warning")) { loaderData->logLevel = strdup(argv[i]+9); setLogLevel(WARNING); } else if (!strcasecmp(argv[i]+9, "error")) { loaderData->logLevel = strdup(argv[i]+9); setLogLevel(ERROR); } else if (!strcasecmp(argv[i]+9, "critical")) { loaderData->logLevel = strdup(argv[i]+9); setLogLevel(CRITICAL); } } else if (!strncasecmp(argv[i], "ksdevice=", 9)) { loaderData->netDev = strdup(argv[i] + 9); loaderData->netDev_set = 1; } else if (!strncmp(argv[i], "BOOTIF=", 7)) { /* +10 so that we skip over the leading 01- */ loaderData->bootIf = strdup(argv[i] + 10); /* scan the BOOTIF value and replace '-' with ':' */ front = loaderData->bootIf; if (front) { while (*front != '\0') { if (*front == '-') *front = ':'; front++; } } loaderData->bootIf_set = 1; } else if (!strncasecmp(argv[i], "dhcpclass=", 10)) { loaderData->netCls = strdup(argv[i] + 10); loaderData->netCls_set = 1; } else if (!strcasecmp(argv[i], "ks") || !strncasecmp(argv[i], "ks=", 3)) loaderData->ksFile = strdup(argv[i]); else if (!strncasecmp(argv[i], "display=", 8)) setenv("DISPLAY", argv[i] + 8, 1); else if ((!strncasecmp(argv[i], "lang=", 5)) && (strlen(argv[i]) > 5)) { loaderData->lang = strdup(argv[i] + 5); loaderData->lang_set = 1; } else if (!strncasecmp(argv[i], "keymap=", 7) && (strlen(argv[i]) > 7)) { loaderData->kbd = strdup(argv[i] + 7); loaderData->kbd_set = 1; } else if (!strncasecmp(argv[i], "method=", 7)) { logMessage(WARNING, "method= is deprecated. Please use repo= instead."); loaderData->instRepo = strdup(argv[i] + 7); } else if (!strncasecmp(argv[i], "repo=", 5)) loaderData->instRepo = strdup(argv[i] + 5); else if (!strncasecmp(argv[i], "stage2=", 7)) setStage2LocFromCmdline(argv[i] + 7, loaderData); else if (!strncasecmp(argv[i], "hostname=", 9)) loaderData->hostname = strdup(argv[i] + 9); else if (!strncasecmp(argv[i], "ip=", 3)) parseCmdLineIp(loaderData, argv[i]); #ifdef ENABLE_IPV6 else if (!strncasecmp(argv[i], "ipv6=", 5)) parseCmdLineIpv6(loaderData, argv[i]); #endif else if (!strncasecmp(argv[i], "netmask=", 8)) loaderData->netmask = strdup(argv[i] + 8); else if (!strncasecmp(argv[i], "gateway=", 8)) loaderData->gateway = strdup(argv[i] + 8); else if (!strncasecmp(argv[i], "dns=", 4)) loaderData->dns = strdup(argv[i] + 4); else if (!strncasecmp(argv[i], "ethtool=", 8)) loaderData->ethtool = strdup(argv[i] + 8); else if (!strncasecmp(argv[i], "essid=", 6)) loaderData->essid = strdup(argv[i] + 6); else if (!strncasecmp(argv[i], "mtu=", 4)) loaderData->mtu = argToLong(argv[i], 4); else if (!strncasecmp(argv[i], "wepkey=", 7)) loaderData->wepkey = strdup(argv[i] + 7); else if (!strncasecmp(argv[i], "linksleep=", 10)) num_link_checks = argToLong(argv[i], 10); else if (!strncasecmp(argv[i], "nicdelay=", 9)) post_link_sleep = argToLong(argv[i], 9); else if (!strncasecmp(argv[i], "dhcptimeout=", 12)) loaderData->dhcpTimeout = argToLong(argv[i], 12); else if (!strncasecmp(argv[i], "selinux=0", 9)) flags &= ~LOADER_FLAGS_SELINUX; else if (!strncasecmp(argv[i], "selinux", 7)) flags |= LOADER_FLAGS_SELINUX; else if (!strncasecmp(argv[i], "gdb=", 4)) loaderData->gdbServer = strdup(argv[i] + 4); else if (!strncasecmp(argv[i], "proxy=", 6)) splitProxyParam(argv[i]+6, &loaderData->proxyUser, &loaderData->proxyPassword, &loaderData->proxy); else if (numExtraArgs < (MAX_EXTRA_ARGS - 1)) { /* go through and append args we just want to pass on to */ /* the anaconda script, but don't want to represent as a */ /* LOADER_FLAGS_XXX since loader doesn't care about these */ /* particular options. */ /* do vncpassword case first */ if (!strncasecmp(argv[i], "vncpassword=", 12)) { writeVNCPasswordFile("/tmp/vncpassword.dat", argv[i]+12); } else if (!strncasecmp(argv[i], "resolution=", 11) || !strncasecmp(argv[i], "nomount", 7) || !strncasecmp(argv[i], "vnc", 3) || !strncasecmp(argv[i], "vncconnect=", 11) || !strncasecmp(argv[i], "headless", 8) || !strncasecmp(argv[i], "usefbx", 6) || !strncasecmp(argv[i], "mpath", 6) || !strncasecmp(argv[i], "nompath", 8) || !strncasecmp(argv[i], "dmraid", 6) || !strncasecmp(argv[i], "nodmraid", 8) || !strncasecmp(argv[i], "xdriver=", 8) || !strncasecmp(argv[i], "vesa", 4) || !strncasecmp(argv[i], "syslog=", 7)) { /* vnc implies graphical */ if (!strncasecmp(argv[i], "vnc", 3)) { logMessage(INFO, "vnc forced graphical mode from cmdline"); flags |= LOADER_FLAGS_GRAPHICAL; } /* the following things require networking to be configured * by loader, so an active connection is ready once we get * to anaconda */ if (!strncasecmp(argv[i], "syslog", 6) || !strncasecmp(argv[i], "vnc", 3) || isKickstartFileRemote(loaderData->ksFile)) { logMessage(INFO, "early networking required for %s", argv[i]); flags |= LOADER_FLAGS_EARLY_NETWORKING; } if (!strncasecmp(argv[i], "vesa", 4)) { checked_asprintf(&extraArgs[numExtraArgs], "--xdriver=vesa"); logMessage(WARNING, "\"vesa\" command line argument is deprecated. use \"xdriver=vesa\"."); } else { checked_asprintf(&extraArgs[numExtraArgs],"--%s", argv[i]); } numExtraArgs += 1; if (numExtraArgs > (MAX_EXTRA_ARGS - 2)) { logMessage(WARNING, "Too many command line arguments (max " "allowed is %d), rest will be dropped.", MAX_EXTRA_ARGS); } } } } readNetInfo(&loaderData); /* NULL terminates the array of extra args */ extraArgs[numExtraArgs] = NULL; return; } /* make sure they have enough ram */ static void checkForRam(void) { if (totalMemory() < MIN_RAM) { char *buf; checked_asprintf(&buf, _("You do not have enough RAM to install %s " "on this machine."), getProductName()); startNewt(); newtWinMessage(_("Error"), _("OK"), buf); free(buf); stopNewt(); doExit(0); } } static int haveDeviceOfType(int type) { struct device ** devices; devices = getDevices(type); if (devices) { return 1; } return 0; } static char *doLoaderMain(struct loaderData_s *loaderData, moduleInfoSet modInfo) { enum { STEP_LANG, STEP_KBD, STEP_METHOD, STEP_DRIVER, STEP_DRIVERDISK, STEP_NETWORK, STEP_IFACE, STEP_IP, STEP_STAGE2, STEP_DONE } step; char *url = NULL, *ret = NULL, *devName = NULL, *kbdtype = NULL; static iface_t iface; int i, rc = LOADER_NOOP, dir = 1; int needsNetwork = 0, class = -1; int skipMethodDialog = 0, skipLangKbd = 0; char *installNames[10]; int numValidMethods = 0; int validMethods[10]; for (i = 0; i < numMethods; i++, numValidMethods++) { installNames[numValidMethods] = installMethods[i].name; validMethods[numValidMethods] = i; } installNames[numValidMethods] = NULL; /* Before anything else, see if there's a CD/DVD with a stage2 image on * it. However if stage2= was given, use that value as an override here. * That will also then bypass any method selection UI in loader. */ if (!FL_ASKMETHOD(flags)) { int stage2ok = 0; // marmarek hack for USB install if (loaderData->method == METHOD_HD) { char *location = "/mnt/stage2"; char *device = ((struct hdInstallData *)loaderData->stage2Data)->partition; char *stage2loc = "/mnt/stage2/images/install.img"; char *stage2img; int stage2inram; if (!(rc=doPwMount(device, location, "auto", "ro", NULL))) { if (!access(stage2loc, R_OK)) { char *updpath; //TODO? queryCDMediaCheck(device, location); /* if in rescue mode lets copy stage 2 into RAM so we can */ /* free up the CD drive and user can have it avaiable to */ /* aid system recovery. */ if (FL_RESCUE(flags) && !FL_TEXT(flags) && totalMemory() > MIN_GUI_RAM ) { rc = copyFile(stage2loc, "/tmp/install.img"); stage2img = strdup("/tmp/install.img"); stage2inram = 1; } else { stage2img = strdup(stage2loc); stage2inram = 0; } rc = mountStage2(stage2img); free(stage2img); if (!rc) { checked_asprintf(&updpath, "%s/images/updates.img", location); logMessage(INFO, "Looking for updates in %s", updpath); copyUpdatesImg(updpath); free(updpath); checked_asprintf(&updpath, "%s/images/product.img", location); logMessage(INFO, "Looking for product in %s", updpath); copyProductImg(updpath); free(updpath); /* if in rescue mode and we copied stage2 to RAM */ /* we can now unmount the CD */ if (FL_RESCUE(flags) && stage2inram) { umount(location); } stage2ok = 1; checked_asprintf(&url, "file://%s", location); } else { logMessage(INFO, "mounting stage2 failed"); umount(location); } } else { /* this wasnt the CD we were looking for, clean up and */ /* try the next CD drive */ umount(location); } } else { logMessage(WARNING, "Mount failed for %s at %s", device, location); } } if (!url) url = findAnacondaCD("/mnt/stage2"); if (url) { if (!stage2ok) setStage2LocFromCmdline(url, loaderData); skipMethodDialog = 1; logMessage(INFO, "Detected stage 2 image on CD (url: %s)", url); winStatus(50, 3, _("Media Detected"), _("Found local installation media"), 0); sleep(3); newtPopWindow(); skipLangKbd = 1; flags |= LOADER_FLAGS_NOPASS; } else if (!loaderData->stage2Data && loaderData->instRepo) { /* If no CD/DVD with a stage2 image was found and we were given a * repo=/method= parameter, try to piece together a valid setting * for the stage2= parameter based on that. */ char *tmp; checked_asprintf(&tmp, "%s/images/install.img", loaderData->instRepo); logMessage(INFO, "no stage2= given, assuming %s", tmp); setStage2LocFromCmdline(tmp, loaderData); free(tmp); /* If we had to infer a stage2= location, but the repo= parameter * we based this guess on was wrong, we need to correct the typo * in both places. Unfortunately we can't really know what the * user meant, so the best we can do is take the results of * running stage2= through the UI and chop off any /images/whatever * path that's at the end of it. */ loaderData->inferredStage2 = 1; skipMethodDialog = 1; } else if (loaderData->stage2Data) { skipMethodDialog = 1; } } else { /* Needed because they have already been set when parsing cmdline. * (Leaks a little.) */ loaderData->method = -1; loaderData->stage2Data = NULL; } i = 0; step = STEP_LANG; while (step != STEP_DONE) { switch(step) { case STEP_LANG: { if (loaderData->lang && (loaderData->lang_set == 1)) setLanguage(loaderData->lang, 1); else if (FL_RESCUE(flags) || !skipLangKbd) chooseLanguage(&loaderData->lang); step = STEP_KBD; dir = 1; break; } case STEP_KBD: { if (loaderData->kbd && (loaderData->kbd_set == 1)) { /* JKFIXME: this is broken -- we should tell of the * failure; best by pulling code out in kbd.c to use */ if (isysLoadKeymap(loaderData->kbd)) { logMessage(WARNING, "requested keymap %s is not valid, asking", loaderData->kbd); loaderData->kbd = NULL; loaderData->kbd_set = 0; break; } rc = LOADER_NOOP; } else if (FL_RESCUE(flags) || !skipLangKbd) { /* JKFIXME: should handle kbdtype, too probably... but it * just matters for sparc */ if (!FL_CMDLINE(flags)) rc = chooseKeyboard(loaderData, &kbdtype); else rc = LOADER_NOOP; } else { step = STEP_METHOD; dir = 1; } if (rc == LOADER_NOOP) { if (dir == -1) step = STEP_LANG; else step = STEP_METHOD; break; } if (rc == LOADER_BACK) { step = STEP_LANG; dir = -1; } else { step = STEP_METHOD; dir = 1; } break; } case STEP_METHOD: { if (loaderData->method != -1) skipMethodDialog = 1; else if (FL_CMDLINE(flags)) { fprintf(stderr, "No method given for cmdline mode, aborting\n"); doExit(EXIT_FAILURE); } /* If we already found a stage2 image, skip the prompt. */ if (skipMethodDialog) { if (dir == 1) rc = 1; else rc = -1; } else { /* we need to set these each time through so that we get * updated for language changes (#83672) */ for (i = 0; i < numMethods; i++) { installNames[i] = _(installMethods[i].name); } installNames[i] = NULL; rc = newtWinMenu(FL_RESCUE(flags) ? _("Rescue Method") : _("Installation Method"), FL_RESCUE(flags) ? _("What type of media contains the rescue " "image?") : _("What type of media contains the installation " "image?"), 30, 10, 20, 6, installNames, &loaderData->method, _("OK"), _("Back"), NULL); if (rc == 2) { loaderData->method = -1; } } if (rc && (rc != 1)) { step = STEP_KBD; dir = -1; } else { class = installMethods[validMethods[loaderData->method]].type; step = STEP_DRIVER; dir = 1; } break; } case STEP_DRIVER: { if ((FL_EARLY_NETWORKING(flags) && haveDeviceOfType(DEVICE_NETWORK)) || (class == -1 || haveDeviceOfType(class))) { step = STEP_NETWORK; dir = 1; class = -1; break; } if (skipLangKbd) { skipLangKbd = 0; step = STEP_KBD; break; } rc = newtWinTernary(_("No driver found"), _("Select driver"), _("Use a driver disk"), _("Back"), _("Unable to find any devices of the type " "needed for this installation type. " "Would you like to manually select your " "driver or use a driver disk?")); if (rc == 2) { step = STEP_DRIVERDISK; dir = 1; break; } else if (rc == 3) { step = STEP_METHOD; loaderData->method = -1; dir = -1; break; } chooseManualDriver(installMethods[validMethods[loaderData->method]].type, loaderData); /* it doesn't really matter what we return here; we just want * to reprobe and make sure we have the driver */ step = STEP_DRIVER; break; } case STEP_DRIVERDISK: { if (skipLangKbd) { skipLangKbd = 0; step = STEP_KBD; break; } rc = loadDriverFromMedia(class, loaderData, 0, 0); if (rc == LOADER_BACK) { step = STEP_DRIVER; dir = -1; break; } /* need to come back to driver so that we can ensure that we found * the right kind of driver after loading the driver disk */ step = STEP_DRIVER; break; } case STEP_NETWORK: { if (((installMethods[validMethods[loaderData->method]].type != DEVICE_NETWORK) && (!hasGraphicalOverride()) && !FL_ASKNETWORK(flags) && !FL_EARLY_NETWORKING(flags)) || (is_nm_connected())) { needsNetwork = 0; if (dir == 1) step = STEP_STAGE2; else if (dir == -1) step = STEP_METHOD; break; } needsNetwork = 1; if (!haveDeviceOfType(DEVICE_NETWORK)) { class = DEVICE_NETWORK; step = STEP_DRIVER; break; } logMessage(INFO, "need to set up networking"); memset(&iface, 0, sizeof(iface)); /* fall through to interface selection */ } case STEP_IFACE: { logMessage(INFO, "going to pick interface"); /* skip configureTCPIP() screen for kickstart (#260621) */ if (loaderData->ksFile) flags |= LOADER_FLAGS_IS_KICKSTART; if (FL_HAVE_CMSCONF(flags)) { loaderData->ipinfo_set = 1; #ifdef ENABLE_IPV6 loaderData->ipv6info_set = 1; #endif } rc = chooseNetworkInterface(loaderData); if ((rc == LOADER_BACK) || (rc == LOADER_ERROR) || ((dir == -1) && (rc == LOADER_NOOP))) { /* don't skip method dialog iff we don't have url from ks or boot params */ if (!loaderData->stage2Data) { loaderData->method = -1; } step = STEP_METHOD; dir = -1; break; } devName = loaderData->netDev; strcpy(iface.device, devName); /* continue to ip config */ step = STEP_IP; dir = 1; break; } case STEP_IP: { if (!needsNetwork || dir == -1) { step = STEP_METHOD; /* only hit going back */ break; } if ((ret = malloc(INET6_ADDRSTRLEN+1)) == NULL) { logMessage(ERROR, "malloc failure for ret in STEP_IP"); doExit(EXIT_FAILURE); } logMessage(INFO, "going to do getNetConfig"); /* s390 provides all config info by way of the CMS conf file */ if (FL_HAVE_CMSCONF(flags)) { loaderData->ipinfo_set = 1; #ifdef ENABLE_IPV6 loaderData->ipv6info_set = 1; #endif } /* populate netDev based on any kickstart data */ setupIfaceStruct(&iface, loaderData); rc = readNetConfig(devName, &iface, loaderData->netCls, loaderData->method); /* set the hostname if we have that */ if (loaderData->hostname) { if (sethostname(loaderData->hostname, strlen(loaderData->hostname))) { logMessage(ERROR, "error setting hostname to %s", loaderData->hostname); } } free(ret); ret = NULL; if ((rc == LOADER_BACK) || ((dir == -1) && (rc == LOADER_NOOP))) { needsNetwork = 1; step = STEP_IFACE; dir = -1; break; } /* retry */ if (rc == LOADER_ERROR) { needsNetwork = 1; break; } writeEnabledNetInfo(&iface); step = STEP_STAGE2; dir = 1; break; } case STEP_STAGE2: { if (url) { logMessage(INFO, "stage2 url is %s", url); return url; } logMessage(INFO, "starting STEP_STAGE2"); url = installMethods[validMethods[loaderData->method]].mountImage( installMethods + validMethods[loaderData->method], "/mnt/stage2", loaderData); if (!url) { step = STEP_IP; loaderData->ipinfo_set = 0; #ifdef ENABLE_IPV6 loaderData->ipv6info_set = 0; #endif loaderData->method = -1; skipMethodDialog = 0; dir = -1; } else { logMessage(INFO, "got stage2 at url %s", url); step = STEP_DONE; dir = 1; if (loaderData->invalidRepoParam) { char *newInstRepo; /* Doesn't contain /images? Let's not even try. */ if (strstr(url, "/images") == NULL) break; checked_asprintf(&newInstRepo, "%.*s", (int) (strstr(url, "/images")-url), url); free(loaderData->instRepo); loaderData->instRepo = newInstRepo; logMessage(INFO, "reset repo= parameter to %s", loaderData->instRepo); } } break; } case STEP_DONE: break; } } return url; } static int manualDeviceCheck(struct loaderData_s *loaderData) { char ** devices; int i, j, rc, num = 0; unsigned int width = 40; char * buf; do { /* FIXME */ devices = malloc(1 * sizeof(*devices)); j = 0; devices[j] = NULL; if (width > 70) width = 70; if (j > 0) { buf = _("The following devices have been found on your system."); } else { buf = _("No device drivers have been loaded for your system. " "Would you like to load any now?"); } rc = newtWinMenu(_("Devices"), buf, width, 10, 20, (j > 6) ? 6 : j, devices, &num, _("Done"), _("Add Device"), NULL); /* no leaky */ for (i = 0; i < j; i++) free(devices[j]); free(devices); if (rc != 2) break; chooseManualDriver(DEVICE_ANY, loaderData); } while (1); return 0; } /* JKFIXME: I don't really like this, but at least it isolates the ifdefs */ /* Either move dirname to %s_old or unlink depending on arch (unlink on all * !s390{,x} arches). symlink to /mnt/runtime/dirname. dirname *MUST* start * with a '/' */ static void migrate_runtime_directory(char * dirname) { char * runtimedir; int ret; checked_asprintf(&runtimedir, "/mnt/runtime%s", dirname); if (!access(runtimedir, X_OK)) { if (unlink(dirname) == -1) { char * olddir; checked_asprintf(&olddir, "%s_old", dirname); ret = rename(dirname, olddir); free(olddir); } ret = symlink(runtimedir, dirname); } free(runtimedir); } static int hasGraphicalOverride() { int i; if (getenv("DISPLAY")) return 1; for (i = 0; extraArgs[i] != NULL; i++) { if (!strncasecmp(extraArgs[i], "--vnc", 5)) return 1; } return 0; } void loaderSegvHandler(int signum) { void *array[30]; size_t i; const char const * const errmsgs[] = { "loader received SIG", "! Backtrace:\n", "Loader exited unexpectedly! Backtrace:\n", }; /* XXX This should really be in a glibc header somewhere... */ extern const char *const sys_sigabbrev[NSIG]; signal(signum, SIG_DFL); /* back to default */ newtFinished(); if (signum == 0) { i = write(STDERR_FILENO, errmsgs[2], strlen(errmsgs[2])); } else { i = write(STDERR_FILENO, errmsgs[0], strlen(errmsgs[0])); i = write(STDERR_FILENO, sys_sigabbrev[signum], strlen(sys_sigabbrev[signum])); i = write(STDERR_FILENO, errmsgs[1], strlen(errmsgs[1])); } i = backtrace (array, 30); backtrace_symbols_fd(array, i, STDERR_FILENO); _exit(1); } void loaderExitHandler(void) { if (expected_exit) return; loaderSegvHandler(0); } static void setupBacktrace(void) { void *array; signal(SIGSEGV, loaderSegvHandler); signal(SIGABRT, loaderSegvHandler); atexit(loaderExitHandler); /* Turns out, there's an initializer at the top of backtrace() that * (on some arches) calls dlopen(). dlopen(), unsurprisingly, calls * malloc(). So, call backtrace() early in signal handler setup so * we can later safely call it from the signal handler itself. */ backtrace(&array, 1); } void loaderUsrXHandler(int signum) { logMessage(INFO, "Remembering signal %d\n", signum); init_sig = signum; } static int anaconda_trace_init(void) { #ifdef USE_MTRACE setenv("MALLOC_TRACE","/malloc",1); mtrace(); #endif /* We have to do this before we init bogl(), which doLoaderMain will do * when setting fonts for different languages. It's also best if this * is well before we might take a SEGV, so they'll go to tty8 */ initializeTtys(); /* set up signal handler */ setupBacktrace(); return 0; } static void add_to_path_env(const char *env, const char *val) { char *oldenv, *newenv; oldenv = getenv(env); if (oldenv) { checked_asprintf(&newenv, "%s:%s", val, oldenv); oldenv = strdupa(newenv); free(newenv); newenv = oldenv; } else { newenv = strdupa(val); } setenv(env, newenv, 1); } static void loadScsiDhModules(void) { struct utsname utsname; char *modules = NULL; char *tmp = NULL; struct dirent *ent = NULL; uname(&utsname); checked_asprintf(&tmp, "/lib/modules/%s/kernel/drivers/scsi/device_handler", utsname.release); DIR *dir = opendir(tmp); free(tmp); if (!dir) return; int fd = dirfd(dir); while ((ent = readdir(dir)) != NULL) { struct stat sb; if (fstatat(fd, ent->d_name, &sb, 0) < 0) continue; size_t len = strlen(ent->d_name) - 3; if (strcmp(ent->d_name+len, ".ko")) continue; if (S_ISREG(sb.st_mode)) { char modname[len+1]; strncpy(modname, ent->d_name, len); modname[len] = '\0'; if (modules && modules[0]) { checked_asprintf(&tmp, "%s:%s", modules, modname); } else { checked_asprintf(&tmp, "%s", modname); } free(modules); modules = tmp; } } closedir(dir); mlLoadModuleSet(modules); free(modules); } int main(int argc, char ** argv) { int rc, ret, pid, status; struct stat sb; struct serial_struct si; char * arg; FILE *f; char twelve = 12; moduleInfoSet modInfo; char *url = NULL; char ** argptr, ** tmparg; char * anacondaArgs[50]; struct loaderData_s loaderData; char *path, *fmt; GSList *dd, *dditer; gchar *cmdLine = NULL, *ksFile = NULL, *virtpcon = NULL; gboolean mediacheck = FALSE; gchar **remaining = NULL; GOptionContext *optCon = g_option_context_new(NULL); GError *optErr = NULL; GOptionEntry optionTable[] = { { "cmdline", 0, 0, G_OPTION_ARG_STRING, &cmdLine, NULL, NULL }, { "ksfile", 0, 0, G_OPTION_ARG_STRING, &ksFile, NULL, NULL }, { "mediacheck", 0, 0, G_OPTION_ARG_NONE, &mediacheck, NULL, NULL }, { "virtpconsole", 0, 0, G_OPTION_ARG_STRING, &virtpcon, NULL, NULL }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, NULL }, { NULL }, }; /* get init PID if we have it */ if ((f = fopen("/var/run/init.pid", "r")) != NULL) { char linebuf[256]; while (fgets(linebuf, sizeof(linebuf), f) != NULL) { errno = 0; init_pid = strtol(linebuf, NULL, 10); if (errno == EINVAL || errno == ERANGE) { logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); init_pid = 1; } } fclose(f); } signal(SIGUSR1, loaderUsrXHandler); signal(SIGUSR2, loaderUsrXHandler); /* Make sure sort order is right. */ setenv ("LC_COLLATE", "C", 1); /* Very first thing, set up tracebacks and debug features. */ rc = anaconda_trace_init(); /* now we parse command line options */ g_option_context_set_help_enabled(optCon, FALSE); g_option_context_add_main_entries(optCon, optionTable, NULL); if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) { fprintf(stderr, "bad option: %s\n", optErr->message); g_error_free(optErr); g_option_context_free(optCon); doExit(1); } g_option_context_free(optCon); if (remaining) { fprintf(stderr, "unexpected argument: %s\n", remaining[0]); g_strfreev(remaining); doExit(1); } g_strfreev(remaining); if (!access("/var/run/loader.run", R_OK)) { printf(_("loader has already been run. Starting shell.\n")); execl("/bin/sh", "-/bin/sh", NULL); doExit(0); } f = fopen("/var/run/loader.run", "w+"); fprintf(f, "%d\n", getpid()); fclose(f); /* The fstat checks disallows serial console if we're running through a pty. This is handy for Japanese. */ fstat(0, &sb); if (major(sb.st_rdev) != 3 && major(sb.st_rdev) != 136 && (virtpcon == NULL)){ if ((ioctl (0, TIOCLINUX, &twelve) < 0) && (ioctl(0, TIOCGSERIAL, &si) != -1)) flags |= LOADER_FLAGS_SERIAL; } if (mediacheck) flags |= LOADER_FLAGS_MEDIACHECK; if (ksFile) flags |= LOADER_FLAGS_KICKSTART; if (virtpcon) flags |= LOADER_FLAGS_VIRTPCONSOLE; /* uncomment to send mac address in ks=http:/ header by default*/ flags |= LOADER_FLAGS_KICKSTART_SEND_MAC; /* JKFIXME: I do NOT like this... it also looks kind of bogus */ #if defined(__s390__) || defined(__s390x__) flags |= LOADER_FLAGS_NOSHELL; #endif openLog(); /* XXX if RHEL, enable the AUTODD feature by default, * but we should come with more general way how to control this */ if (!strncmp(getProductName(), "Red Hat", 7)) { flags |= LOADER_FLAGS_AUTOMODDISK; } memset(&loaderData, 0, sizeof(loaderData)); loaderData.method = -1; loaderData.fw_loader_pid = -1; loaderData.fw_search_pathz_len = -1; loaderData.dhcpTimeout = -1; extraArgs[0] = NULL; parseCmdLineFlags(&loaderData, cmdLine); logMessage(INFO, "anaconda version %s on %s starting", VERSION, getProductArch()); if ((FL_SERIAL(flags) || FL_VIRTPCONSOLE(flags)) && !hasGraphicalOverride()) { logMessage(INFO, "text mode forced due to serial/virtpconsole"); flags |= LOADER_FLAGS_TEXT; } set_fw_search_path(&loaderData, "/firmware:/lib/firmware"); start_fw_loader(&loaderData); arg = "/lib/modules/module-info"; modInfo = newModuleInfoSet(); if (readModuleInfo(arg, modInfo, NULL, 0)) { fprintf(stderr, "failed to read %s\n", arg); sleep(5); stop_fw_loader(&loaderData); doExit(1); } initializeConsole(); checkForRam(); /* iSeries vio console users will be ssh'ing in to the primary partition, so use a terminal type that is appripriate */ if (isVioConsole()) setenv("TERM", "vt100", 1); mlLoadModuleSet("cramfs:squashfs:iscsi_tcp"); loadScsiDhModules(); #if !defined(__s390__) && !defined(__s390x__) mlLoadModuleSet("floppy:edd:pcspkr:iscsi_ibft"); #endif #ifdef ENABLE_IPV6 if (!FL_NOIPV6(flags)) mlLoadModule("ipv6", NULL); #endif /* now let's do some initial hardware-type setup */ dasdSetup(); #if defined(__powerpc__) mlLoadModule("spufs", NULL); #endif if (loaderData.lang && (loaderData.lang_set == 1)) { setLanguage(loaderData.lang, 1); } /* FIXME: this is a bit of a hack */ loaderData.modInfo = modInfo; /* Setup depmod & modprobe so we can load multiple DDs */ modprobeDDmode(); /* If there is /.rundepmod file present, rerun depmod */ if (!access("/.rundepmod", R_OK)){ if (system("depmod -a")) { /* this is not really fatal error, it might still work, log it */ logMessage(ERROR, "Error running depmod -a for initrd overlay"); } } if (FL_AUTOMODDISK(flags)) { /* Load all autodetected DDs */ logMessage(INFO, "Trying to detect vendor driver discs"); dd = findDriverDiskByLabel(); dditer = dd; while(dditer) { /* load the DD */ if (loadDriverDiskFromPartition(&loaderData, (char*)(dditer->data))) { logMessage(ERROR, "Automatic driver disk loader failed for %s.", (char*)(dditer->data)); } else { logMessage(INFO, "Automatic driver disk loader succeeded for %s.", (char*)(dditer->data)); } /* clean the device record */ free((char*)(dditer->data)); dditer->data = NULL; /* next DD */ dditer = g_slist_next(dditer); } g_slist_free(dd); } if (FL_MODDISK(flags)) { startNewt(); loadDriverDisks(DEVICE_ANY, &loaderData); } if (!access("/dd.img", R_OK)) { logMessage(INFO, "found /dd.img, loading drivers"); getDDFromSource(&loaderData, "path:/dd.img"); } /* Reset depmod & modprobe to normal mode and get the rest of drivers*/ modprobeNormalmode(); /* this allows us to do an early load of modules specified on the * command line to allow automating the load order of modules so that * eg, certain scsi controllers are definitely first. * FIXME: this syntax is likely to change in a future release * but is done as a quick hack for the present. */ if (!mlInitModuleConfig()) { logMessage(ERROR, "unable to initialize kernel module loading"); abort(); } earlyModuleLoad(0); busProbe(FL_NOPROBE(flags)); /* Disable all network interfaces in NetworkManager by default */ #if !defined(__s390__) && !defined(__s390x__) int i; if ((i = writeDisabledNetInfo()) != 0) { logMessage(ERROR, "writeDisabledNetInfo failure: %d", i); } #endif /* Start NetworkManager now so it's always available to talk to. */ if (iface_start_NetworkManager()) logMessage(INFO, "failed to start NetworkManager"); if (!FL_CMDLINE(flags)) startNewt(); /* can't run gdbserver until after network modules are loaded */ doGdbserver(&loaderData); /* JKFIXME: we'd really like to do this before the busprobe, but then * we won't have network devices available (and that's the only thing * we support with this right now */ if (loaderData.ddsrc != NULL) { getDDFromSource(&loaderData, loaderData.ddsrc); } /* JKFIXME: loaderData->ksFile is set to the arg from the command line, * and then getKickstartFile() changes it and sets FL_KICKSTART. * kind of weird. */ if (loaderData.ksFile || ksFile) { logMessage(INFO, "getting kickstart file"); if (!ksFile) getKickstartFile(&loaderData); if (FL_KICKSTART(flags) && (ksReadCommands((ksFile)?ksFile:loaderData.ksFile)!=LOADER_ERROR)) { runKickstart(&loaderData); } } if (FL_TELNETD(flags)) startTelnetd(&loaderData); url = doLoaderMain(&loaderData, modInfo); /* unlink dirs and link to the ones in /mnt/runtime */ migrate_runtime_directory("/usr"); migrate_runtime_directory("/lib"); migrate_runtime_directory("/lib64"); ret = symlink("/mnt/runtime/etc/selinux", "/etc/selinux"); copyDirectory("/mnt/runtime/etc","/etc", NULL, copyErrorFn); copyDirectory("/mnt/runtime/var","/var", NULL, copyErrorFn); /* now load SELinux policy before exec'ing anaconda and the shell * (if we're using SELinux) */ if (FL_SELINUX(flags)) { if (mount("/selinux", "/selinux", "selinuxfs", 0, NULL)) { logMessage(ERROR, "failed to mount /selinux: %m, disabling SELinux"); flags &= ~LOADER_FLAGS_SELINUX; } else { if (loadpolicy() == 0) { setexeccon(ANACONDA_CONTEXT); } else { logMessage(ERROR, "failed to load policy, disabling SELinux"); flags &= ~LOADER_FLAGS_SELINUX; } } } logMessage(INFO, "getting ready to spawn shell now"); spawnShell(); /* we can attach gdb now :-) */ if (FL_NOPROBE(flags) && !loaderData.ksFile) { startNewt(); manualDeviceCheck(&loaderData); } if (loaderData.updatessrc) loadUpdatesFromRemote(loaderData.updatessrc, &loaderData); else if (FL_UPDATES(flags)) loadUpdates(&loaderData); /* make sure /tmp/updates exists so that magic in anaconda to */ /* symlink rhpl/ will work */ if (access("/tmp/updates", F_OK)) mkdirChain("/tmp/updates"); add_fw_search_dir(&loaderData, "/tmp/updates/firmware"); add_fw_search_dir(&loaderData, "/tmp/product/firmware"); add_to_path_env("PYTHONPATH", "/tmp/updates"); add_to_path_env("PYTHONPATH", "/tmp/updates/iw"); add_to_path_env("PYTHONPATH", "/tmp/updates/textw"); add_to_path_env("PYTHONPATH", "/tmp/product"); add_to_path_env("LD_LIBRARY_PATH", "/tmp/updates"); add_to_path_env("LD_LIBRARY_PATH", "/tmp/product"); add_to_path_env("PATH", "/tmp/updates"); add_to_path_env("PATH", "/tmp/product"); stop_fw_loader(&loaderData); start_fw_loader(&loaderData); mlLoadModuleSet("raid0:raid1:raid5:raid6:raid456:raid10:linear:dm-mod:dm-zero:dm-mirror:dm-snapshot:dm-multipath:dm-round-robin:dm-crypt:cbc:sha256:lrw:xts"); if (!access("/mnt/runtime/usr/lib/libunicode-lite.so.1", R_OK)) setenv("LD_PRELOAD", "/mnt/runtime/usr/lib/libunicode-lite.so.1", 1); if (!access("/mnt/runtime/usr/lib64/libunicode-lite.so.1", R_OK)) setenv("LD_PRELOAD", "/mnt/runtime/usr/lib64/libunicode-lite.so.1", 1); argptr = anacondaArgs; path = getenv("PATH"); while (path && path[0]) { int n = strcspn(path, ":"); char c, *binpath; c = path[n]; path[n] = '\0'; checked_asprintf(&binpath, "%s/anaconda", path); path[n] = c; if (!access(binpath, X_OK)) { *argptr++ = strdupa(binpath); free(binpath); break; } free(binpath); path += n + 1; } logMessage(INFO, "Running anaconda script %s", *(argptr-1)); *argptr++ = "--stage2"; if (strncmp(url, "ftp:", 4)) { *argptr++ = url; } else { int fd, ret; fd = open("/tmp/ftp-stage2", O_CREAT | O_TRUNC | O_RDWR, 0600); ret = write(fd, url, strlen(url)); ret = write(fd, "\r", 1); close(fd); *argptr++ = "@/tmp/ftp-stage2"; } /* add extra args - this potentially munges extraArgs */ tmparg = extraArgs; while (*tmparg) { char *idx; logMessage(DEBUGLVL, "adding extraArg %s", *tmparg); idx = strchr(*tmparg, '='); if (idx && ((idx-*tmparg) < strlen(*tmparg))) { *idx = '\0'; *argptr++ = *tmparg; *argptr++ = idx+1; } else { *argptr++ = *tmparg; } tmparg++; } if (FL_AUTOMODDISK(flags)) *argptr++ = "--dlabel"; if (FL_NOIPV4(flags)) *argptr++ = "--noipv4"; #ifdef ENABLE_IPV6 if (FL_NOIPV6(flags)) *argptr++ = "--noipv6"; #endif #if defined(__s390__) || defined(__s390x__) *argptr++ = "--headless"; #endif if (FL_KICKSTART(flags)) { *argptr++ = "--kickstart"; *argptr++ = loaderData.ksFile; } if (FL_SERIAL(flags)) *argptr++ = "--serial"; if (FL_RESCUE(flags)) { *argptr++ = "--rescue"; } else { if (FL_TEXT(flags)) *argptr++ = "-T"; else if (FL_GRAPHICAL(flags)) *argptr++ = "--graphical"; if (FL_CMDLINE(flags)) *argptr++ = "-C"; if (!FL_SELINUX(flags)) *argptr++ = "--noselinux"; else if (FL_SELINUX(flags)) *argptr++ = "--selinux"; if (FL_VIRTPCONSOLE(flags)) { *argptr++ = "--virtpconsole"; *argptr++ = virtpcon; } if (loaderData.updatessrc && FL_UPDATES(flags)) { *argptr++ = "--updates"; *argptr++ = loaderData.updatessrc; } if (loaderData.dogtailurl) { *argptr++ = "--dogtail"; *argptr++ = loaderData.dogtailurl; } if ((loaderData.lang) && !FL_NOPASS(flags)) { *argptr++ = "--lang"; *argptr++ = loaderData.lang; } if ((loaderData.kbd) && !FL_NOPASS(flags)) { *argptr++ = "--keymap"; *argptr++ = loaderData.kbd; } if (loaderData.logLevel) { *argptr++ = "--loglevel"; *argptr++ = loaderData.logLevel; } if (loaderData.instRepo) { *argptr++ = "--repo"; if (strncmp(loaderData.instRepo, "ftp:", 4)) { *argptr++ = loaderData.instRepo; } else { int fd, ret; fd = open("/tmp/ftp-repo", O_CREAT | O_TRUNC | O_RDWR, 0600); ret = write(fd, loaderData.instRepo, strlen(loaderData.instRepo)); ret = write(fd, "\r", 1); close(fd); *argptr++ = "@/tmp/ftp-repo"; } } if (loaderData.proxy && strcmp("", loaderData.proxy)) { *argptr++ = "--proxy"; *argptr++ = strdup(loaderData.proxy); if (loaderData.proxyUser && strcmp(loaderData.proxyUser, "")) { int fd, ret; fd = open("/tmp/proxy", O_CREAT|O_TRUNC|O_RDWR, 0600); ret = write(fd, loaderData.proxyUser, strlen(loaderData.proxyUser)); ret = write(fd, "\r\n", 2); if (loaderData.proxyPassword && strcmp(loaderData.proxyPassword, "")) { ret = write(fd, loaderData.proxyPassword, strlen(loaderData.proxyPassword)); ret = write(fd, "\r\n", 2); } close(fd); *argptr++ = "--proxyAuth"; *argptr++ = "/tmp/proxy"; } } } *argptr = NULL; stopNewt(); closeLog(); if (FL_RESCUE(flags)) { fmt = _("Running anaconda %s, the %s rescue mode - please wait.\n"); } else { fmt = _("Running anaconda %s, the %s system installer - please wait.\n"); } printf(fmt, VERSION, getProductName()); if (!(pid = fork())) { if (execv(anacondaArgs[0], anacondaArgs) == -1) { fprintf(stderr,"exec of anaconda failed: %m\n"); doExit(1); } } waitpid(pid, &status, 0); if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) { rc = 1; } else { rc = 0; } if ((rc == 0) && (FL_POWEROFF(flags) || FL_HALT(flags))) { if (!(pid = fork())) { char * cmd = (FL_POWEROFF(flags) ? strdup("/sbin/poweroff") : strdup("/sbin/halt")); if (execl(cmd, cmd, NULL) == -1) { fprintf(stderr, "exec of poweroff failed: %m\n"); doExit(1); } } waitpid(pid, &status, 0); } stop_fw_loader(&loaderData); #if defined(__s390__) || defined(__s390x__) /* at the latest possibility signal init=linuxrc.s390 to reboot/halt */ logMessage(INFO, "Sending signal %d to process %d\n", init_sig, init_pid); kill(init_pid, init_sig); #endif doExit(rc); doExit(1); } /* vim:set sw=4 sts=4 et: */