/* * 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 #include #ifdef USE_MTRACE #include #endif #include "serial.h" #include "copy.h" #include "getparts.h" #include "loader.h" #include "loadermisc.h" /* JKFIXME: functions here should be split out */ #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 "ibft.h" #include "net.h" #include "readvars.h" #include "unpack.h" #ifdef USESELINUX #include #endif #include "selinux.h" #include "../pyanaconda/isys/imount.h" #include "../pyanaconda/isys/isys.h" #include "../pyanaconda/isys/lang.h" #include "../pyanaconda/isys/eddsupport.h" #include "../pyanaconda/isys/log.h" #include "../pyanaconda/isys/mem.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 int init_sig = SIGUSR1; /* default to shutdown=halt */ static char *VIRTIO_PORT = "/dev/virtio-ports/org.fedoraproject.anaconda.log.0"; static struct installMethod installMethods[] = { { N_("Local CD/DVD"), "METHOD_CDROM", 0, DEVICE_CDROM, promptForCdrom, loadCdromImages}, { N_("Hard drive"), "METHOD_HD", 0, DEVICE_DISK, promptForHardDrive, loadHdImages }, { N_("NFS directory"), "METHOD_NFS", 1, DEVICE_NETWORK, promptForNfs, loadNfsImages }, { "URL", "METHOD_URL", 1, DEVICE_NETWORK, promptForUrl, loadUrlImages}, }; static int numMethods = sizeof(installMethods) / sizeof(struct installMethod); static int expected_exit = 0; static GHashTable *cmdline = NULL; 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 (chdir("/root") == 0) { if (execl("/bin/bash", "/bin/bash", "-i", NULL) == -1) { logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); _exit(1); } } else { logMessage(ERROR, "missing /root, cannot run shell"); } } 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 *productArch = NULL; /* The product info comes from ./.buildstamp, which is an .ini-style file * written out by scripts/mk-images and put into every initrd. It contains * information about the product being booted, which is what differentiates it * from .treeinfo. A valid file looks like this: * * [Main] * BugURL=http://bugzilla.redhat.com * IsFinal=true|false * Product= * UUID=