diff options
Diffstat (limited to 'loader/loader.c')
-rw-r--r-- | loader/loader.c | 2208 |
1 files changed, 2208 insertions, 0 deletions
diff --git a/loader/loader.c b/loader/loader.c new file mode 100644 index 000000000..d4e6d26ae --- /dev/null +++ b/loader/loader.c @@ -0,0 +1,2208 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * Author(s): Erik Troan <ewt@redhat.com> + * Matt Wilson <msw@redhat.com> + * Michael Fulbright <msf@redhat.com> + * Jeremy Katz <katzj@redhat.com> + */ + +#include <ctype.h> +#include <errno.h> +#include <execinfo.h> +#include <fcntl.h> +#include <newt.h> +#include <popt.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <syslog.h> +#include <unistd.h> +#include <stdint.h> +#include <arpa/inet.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <linux/fb.h> +#include <linux/serial.h> +#include <linux/vt.h> + +#ifdef USE_MTRACE +#include <mcheck.h> +#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 "urlinstall.h" + +#include "net.h" +#include "telnetd.h" + +#include <selinux/selinux.h> +#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 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); + +void doSuspend(void) { + newtFinished(); + exit(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(); + + if (kickstartNetworkUp(loaderData, &iface)) { + logMessage(ERROR, "can't run gdbserver due to no network"); + return; + } + + if (asprintf(&pid, "%d", loaderPid) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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(); + + if (asprintf(&buf, _("Welcome to %s for %s"), getProductName(), + arch) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + newtInit(); + newtCls(); + newtDrawRootText(0, 0, buf); + free(buf); + + newtPushHelpLine(_(" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen ")); + + newtRunning = 1; + if (FL_TESTING(flags)) + newtSetSuspendCallback((void *) doSuspend, NULL); + else if (!access("/sbin/busybox", X_OK)) + newtSetSuspendCallback((void *) doShell, NULL); + } +} + +void stopNewt(void) { + if (newtRunning) newtFinished(); + newtRunning = 0; +} + +static char * productName = NULL; +static char * productPath = NULL; +static char * productArch = NULL; +static char * productStamp = NULL; + +static void initProductInfo(void) { + FILE *f; + int i; + + f = fopen("/.buildstamp", "r"); + if (!f) { + productName = strdup("anaconda"); + productPath = strdup("anaconda"); + } else { + productStamp = malloc(256); + productName = malloc(256); + productPath = malloc(256); + productStamp = fgets(productStamp, 256, f); /* stamp time and architecture */ + productArch = strstr(productStamp, "."); /* architecture is separated by dot */ + if(productArch) productArch++; + + productName = fgets(productName, 256, f); /* product name */ + productPath = fgets(productPath, 256, f); /* product version */ + productPath = fgets(productPath, 256, f); /* product path */ + + i = strlen(productName) - 1; + while (isspace(*(productName + i))) { + *(productName + i) = '\0'; + i--; + } + i = strlen(productPath) - 1; + while (isspace(*(productPath + i))) { + *(productPath + i) = '\0'; + i--; + } + i = strlen(productArch) - 1; + while (isspace(*(productArch + i))) { + *(productArch + i) = '\0'; + i--; + } + } + + if(!productArch) productArch = strdup("unknown architecture"); + + fclose(f); +} + +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 */ + printf("\033%%G"); + fflush(stdout); + + isysLoadFont(); + if (!FL_TESTING(flags)) + 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); + 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); + return; + } + + device = strdup(devNames[num]); + free(devNames); + stage = UPD_PART; + } + + case UPD_PART: { + char ** part_list = getPartitionsList(device); + int nump = 0, num = 0; + + if (part != NULL) free(part); + + if ((nump = lenPartitionsList(part_list)) == 0) { + if (dir == -1) + stage = UPD_DEVICE; + else { + if (asprintf(&part, "/dev/%s", device) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + 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: + if (asprintf(&buf, _("Insert your updates disk into %s and " + "press \"OK\" to continue."), part) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + rc = newtWinChoice(_("Updates Disk"), _("OK"), _("Back"), buf); + free(buf); + + 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", "ext2", "ro", NULL) && + doPwMount(part, "/tmp/update-disk", "vfat", "ro", NULL) && + doPwMount(part, "/tmp/update-disk", "iso9660", "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); +} + +/* read information from /tmp/netinfo (written by linuxrc) */ +static void readNetInfo(struct loaderData_s ** ld) { + int i; + struct loaderData_s * loaderData = *ld; + FILE *f; + /* FIXME: arbitrary size that works, but could blow up in the future */ + int bufsiz = 100; + char buf[bufsiz], *vname, *vparm; + + f = fopen("/tmp/netinfo", "r"); + if (!f) + return; + + /* FIXME: static buffers lead to pain */ + vname = (char *)malloc(sizeof(char)*15); + vparm = (char *)malloc(sizeof(char)*85); + + /* 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->macaddr = NULL; + + /* + * 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, "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; + + fclose(f); +} + +/* parse anaconda or pxelinux-style ip= arguments + * pxelinux format: ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask> + * anaconda format: ip=<client-ip> netmask=<netmask> gateway=<gw-ip> +*/ +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 = 0; + + /* 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 = 0; + } + + if (loaderData->ipinfo_set || !strncmp(loaderData->ipv4, "dhcp", 4)) + flags |= LOADER_FLAGS_IP_PARAM; +} + +/* + * 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; +} + +/* 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; + char ** argv; + int argc; + int numExtraArgs = 0; + int i; + char *front, *stage2param = NULL; + + /* 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 (poptParseArgvString(cmdLine, &argc, (const char ***) &argv)) + 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], "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], "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; + else if (!strcasecmp(argv[i], "noipv6")) + flags |= LOADER_FLAGS_NOIPV6; + else if (!strcasecmp(argv[i], "kssendmac")) + flags |= LOADER_FLAGS_KICKSTART_SEND_MAC; + /* 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)) { + stage2param = strdup(argv[i]+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]); + else if (!strncasecmp(argv[i], "ipv6=", 5)) + parseCmdLineIpv6(loaderData, argv[i]); + 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)) { + errno = 0; + loaderData->mtu = strtol(argv[i] + 4, 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(); + } + } + else if (!strncasecmp(argv[i], "wepkey=", 7)) + loaderData->wepkey = strdup(argv[i] + 7); + else if (!strncasecmp(argv[i], "linksleep=", 10)) { + errno = 0; + num_link_checks = strtol(argv[i] + 10, NULL, 10); + + if ((errno == ERANGE && (num_link_checks == LONG_MIN || + num_link_checks == LONG_MAX)) || + (errno != 0 && num_link_checks == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + else if (!strncasecmp(argv[i], "nicdelay=", 9)) { + errno = 0; + post_link_sleep = strtol(argv[i] + 9, NULL, 10); + + if ((errno == ERANGE && (post_link_sleep == LONG_MIN || + post_link_sleep == LONG_MAX)) || + (errno != 0 && post_link_sleep == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + else if (!strncasecmp(argv[i], "dhcptimeout=", 12)) { + errno = 0; + loaderData->dhcpTimeout = strtol(argv[i] + 12, NULL, 10); + + if ((errno == ERANGE && (loaderData->dhcpTimeout == LONG_MIN || + loaderData->dhcpTimeout == LONG_MAX)) || + (errno != 0 && loaderData->dhcpTimeout == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + 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 (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)) { + if (!FL_TESTING(flags)) + writeVNCPasswordFile("/tmp/vncpassword.dat", argv[i]+12); + } + else if (!strncasecmp(argv[i], "resolution=", 11) || + !strncasecmp(argv[i], "lowres", 6) || + !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 cmdline mode from cmdline"); + flags |= LOADER_FLAGS_GRAPHICAL; + } + + if (!strncasecmp(argv[i], "vesa", 4)) { + if (asprintf(&extraArgs[numExtraArgs], + "--xdriver=vesa") == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(WARNING, "\"vesa\" command line argument is deprecated. use \"xdriver=vesa\"."); + } else { + if (asprintf(&extraArgs[numExtraArgs],"--%s", + argv[i]) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + 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; +} + + +#if 0 +/* determine if we are using a framebuffer console. return 1 if so */ +static int checkFrameBuffer() { + int fd; + int rc = 0; + struct fb_fix_screeninfo fix; + + if ((fd = open("/dev/fb0", O_RDONLY)) == -1) { + return 0; + } + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) >= 0) { + rc = 1; + } + close(fd); + return rc; +} +#endif + + +/* make sure they have enough ram */ +static void checkForRam(void) { + if (totalMemory() < MIN_RAM) { + char *buf; + + if (asprintf(&buf, _("You do not have enough RAM to install %s " + "on this machine."), getProductName()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + startNewt(); + newtWinMessage(_("Error"), _("OK"), buf); + free(buf); + stopNewt(); + exit(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, 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; + + if (!FL_CMDLINE(flags)) + startNewt(); + + /* 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) && !loaderData->stage2Data) { + url = findAnacondaCD("/mnt/stage2"); + if (url) { + setStage2LocFromCmdline(url, loaderData); + skipMethodDialog = 1; + + logMessage(INFO, "Detected stage 2 image on CD (url: %s)", url); + winStatus(50, 3, _("Media Detected"), + _("Local installation media detected..."), 0); + sleep(3); + newtPopWindow(); + + skipLangKbd = 1; + flags |= LOADER_FLAGS_NOPASS; + } else if (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; + + if (asprintf(&tmp, "%s/images/install.img", + loaderData->instRepo) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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; + } + } + + /* Disable all network interfaces in NetworkManager by default */ + if ((i = writeDisabledNetInfo()) != 0) { + logMessage(ERROR, "writeDisabledNetInfo failure: %d", i); + } + + 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 (!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 (!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"); + exit(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 && rc != 1) { + step = STEP_KBD; + dir = -1; + } else { + class = installMethods[validMethods[loaderData->method]].type; + step = STEP_DRIVER; + dir = 1; + } + break; + } + + case STEP_DRIVER: { + if (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; + 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)) { + 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; + loaderData->ipv6info_set = 1; + } else { + loaderData->ipinfo_set = 0; + loaderData->ipv6info_set = 0; + } + + rc = chooseNetworkInterface(loaderData); + if ((rc == LOADER_BACK) || (rc == LOADER_ERROR) || + ((dir == -1) && (rc == LOADER_NOOP))) { + 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"); + exit(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; + loaderData->ipv6info_set = 1; + } + + /* populate netDev based on any kickstart data */ + if (loaderData->ipinfo_set) { + iface.flags |= IFACE_FLAGS_IS_PRESET; + } + setupNetworkDeviceConfig(&iface, loaderData); + + rc = readNetConfig(devName, &iface, loaderData->netCls, loaderData->method); + if (FL_NOIPV4(flags)) { + loaderData->ipinfo_set = 0; + } else { + if (loaderData->ipv4 == NULL) { + if (iface_have_in_addr(&iface.ipaddr)) { + ret = (char *) inet_ntop(AF_INET, &iface.ipaddr, + ret, INET_ADDRSTRLEN); + } else { + ret = NULL; + iface.flags |= IFACE_FLAGS_IS_DYNAMIC; + } + + if (IFACE_IS_DYNAMIC(iface.flags) || ret == NULL) { + loaderData->ipv4 = strdup("dhcp"); + } else { + loaderData->ipv4 = strdup(ret); + } + } + + loaderData->ipinfo_set = 1; + } + + if (FL_NOIPV6(flags)) { + loaderData->ipv6info_set = 0; + } else { + if (loaderData->ipv6 == NULL) { + if (iface_have_in6_addr(&iface.ip6addr)) { + ret = (char *) inet_ntop(AF_INET6, &iface.ip6addr, + ret, INET6_ADDRSTRLEN); + } else { + ret = NULL; + iface.flags |= IFACE_FLAGS_IS_DYNAMIC; + } + + if (IFACE_IS_DYNAMIC(iface.flags) || ret == NULL) { + loaderData->ipv6 = strdup("dhcpv6"); + } else { + loaderData->ipv6 = strdup(ret); + } + } + + loaderData->ipv6info_set = 1; + } + + /* 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) || (rc == LOADER_ERROR) || + ((dir == -1) && (rc == LOADER_NOOP))) { + step = STEP_IFACE; + dir = -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; + loaderData->ipv6info_set = 0; + 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; + + if (asprintf(&newInstRepo, "%.*s", + (int) (strstr(url, "/images")-url), url) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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; + + if (asprintf(&runtimedir, "/mnt/runtime%s", dirname) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (!access(runtimedir, X_OK)) { + if (unlink(dirname) == -1) { + char * olddir; + + if (asprintf(&olddir, "%s_old", dirname) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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", + }; + + /* 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(); + 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); +} + +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(); + +#if 0 + int fd = open("/dev/tty8", O_RDWR); + if (fd != STDERR_FILENO) { + close(STDERR_FILENO); + dup2(fd, STDERR_FILENO); + close(fd); + } +#endif + + /* set up signal handler */ + signal(SIGSEGV, loaderSegvHandler); + signal(SIGABRT, loaderSegvHandler); + + return 0; +} + +static void add_to_path_env(const char *env, const char *val) +{ + char *oldenv, *newenv; + + oldenv = getenv(env); + if (oldenv) { + if (asprintf(&newenv, "%s:%s", val, oldenv) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + oldenv = strdupa(newenv); + free(newenv); + newenv = oldenv; + } else { + newenv = strdupa(val); + } + + setenv(env, newenv, 1); +} + +int main(int argc, char ** argv) { + int rc; + + 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; + char * cmdLine = NULL; + char * ksFile = NULL; + int testing = 0; + int mediacheck = 0; + char * virtpcon = NULL; + poptContext optCon; + struct poptOption optionTable[] = { + { "cmdline", '\0', POPT_ARG_STRING, &cmdLine, 0, NULL, NULL }, + { "ksfile", '\0', POPT_ARG_STRING, &ksFile, 0, NULL, NULL }, + { "test", '\0', POPT_ARG_NONE, &testing, 0, NULL, NULL }, + { "mediacheck", '\0', POPT_ARG_NONE, &mediacheck, 0, NULL, NULL}, + { "virtpconsole", '\0', POPT_ARG_STRING, &virtpcon, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + /* 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 */ + optCon = poptGetContext(NULL, argc, (const char **) 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 = (char *) poptGetArg(optCon))) { + fprintf(stderr, "unexpected argument: %s\n", arg); + exit(1); + } + + if (!testing && !access("/var/run/loader.run", R_OK)) { + printf(_("loader has already been run. Starting shell.\n")); + execl("/bin/sh", "-/bin/sh", NULL); + exit(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 (testing) flags |= LOADER_FLAGS_TESTING; + 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 | LOADER_FLAGS_NOUSB; +#endif + + openLog(FL_TESTING(flags)); + if (!FL_TESTING(flags)) + openlog("loader", 0, LOG_LOCAL0); + + 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 = FL_TESTING(flags) ? "./module-info" : "/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); + exit(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:vfat:nfs:loop:floppy:edd:pcspkr:squashfs:ext4dev:ext3:ext2:iscsi_tcp"); + + if (!FL_NOIPV6(flags)) + mlLoadModule("ipv6", NULL); + + /* 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; + + 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"); + } + + /* 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. + */ + mlInitModuleConfig(); + earlyModuleLoad(0); + + busProbe(FL_NOPROBE(flags)); + + /* 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); + + if (!FL_TESTING(flags)) { + int ret; + + /* 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/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("md:raid0:raid1:raid5:raid6:raid456:raid10:linear:fat:msdos:jbd:lock_nolock:gfs2:reiserfs:jfs:xfs:dm-mod:dm-zero:dm-mirror:dm-snapshot:dm-multipath:dm-round-robin:dm-emc:dm-crypt:crypto_blkcipher:cbc:aes:sha256"); + + 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'; + if (asprintf(&binpath, "%s/anaconda", path) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + 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/method", O_CREAT | O_TRUNC | O_RDWR, 0600); + ret = write(fd, url, strlen(url)); + ret = write(fd, "\r", 1); + close(fd); + *argptr++ = "@/tmp/method"; + } + + /* 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_NOIPV4(flags)) + *argptr++ = "--noipv4"; + + if (FL_NOIPV6(flags)) + *argptr++ = "--noipv6"; + + 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"; + *argptr++ = loaderData.instRepo; + } + } + + *argptr = NULL; + + stopNewt(); + closeLog(); + + if (!FL_TESTING(flags)) { + int pid, status, rc; + char *fmt; + + 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())) { + setenv("ANACONDAVERSION", VERSION, 1); + if (execv(anacondaArgs[0], anacondaArgs) == -1) { + fprintf(stderr,"exec of anaconda failed: %m\n"); + exit(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"); + exit(1); + } + } + waitpid(pid, &status, 0); + } + +#if defined(__s390__) || defined(__s390x__) + /* FIXME: we have to send a signal to linuxrc on s390 so that shutdown + * can happen. this is ugly */ + FILE * f; + f = fopen("/var/run/init.pid", "r"); + if (!f) { + logMessage(WARNING, "can't find init.pid, guessing that init is pid 1"); + pid = 1; + } else { + char * buf = malloc(256); + char *ret; + + ret = fgets(buf, 256, f); + errno = 0; + pid = strtol(buf, NULL, 10); + + if ((errno == ERANGE && (pid == LONG_MIN || pid == LONG_MAX)) || + (errno != 0 && pid == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + free(buf); + fclose(f); + } + + kill(pid, SIGUSR2); +#endif + stop_fw_loader(&loaderData); + return rc; + } +#if 0 + else { + char **args = anacondaArgs; + printf("would have run "); + while (*args) + printf("%s ", *args++); + printf("\n"); + printf("LANGKEY=%s\n", getenv("LANGKEY")); + printf("LANG=%s\n", getenv("LANG")); + } +#endif + return 1; +} + +/* vim:set sw=4 sts=4 et: */ |