aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'loader/cdinstall.c')
-rw-r--r--loader/cdinstall.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/loader/cdinstall.c b/loader/cdinstall.c
new file mode 100644
index 0000000..9d5cee1
--- /dev/null
+++ b/loader/cdinstall.c
@@ -0,0 +1,509 @@
+/*
+ * cdinstall.c - code to set up cdrom installs
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <newt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+/* FIXME Remove hack when: https://bugzilla.redhat.com/show_bug.cgi?id=478663
+ is resolved */
+/* Hack both __BIG_ENDIAN and __LITTLE_ENDIAN get defined by glibc, the
+ kernel headers we need do not like this! */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#undef __BIG_ENDIAN
+#else
+#undef __LITTLE_ENDIAN
+#endif
+#include <asm/types.h>
+#include <limits.h>
+#include <linux/cdrom.h>
+
+#include "kickstart.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "lang.h"
+#include "modules.h"
+#include "method.h"
+#include "cdinstall.h"
+#include "mediacheck.h"
+#include "windows.h"
+
+#include "../isys/imount.h"
+#include "../isys/isys.h"
+#include "../isys/log.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+/* ejects the CD device the device node points at */
+static void ejectCdrom(char *device) {
+ int ejectfd;
+
+ if (!device) return;
+ logMessage(INFO, "ejecting %s...",device);
+ if ((ejectfd = open(device, O_RDONLY | O_NONBLOCK, 0)) >= 0) {
+ ioctl(ejectfd, CDROM_LOCKDOOR, 0);
+ if (ioctl(ejectfd, CDROMEJECT, 0))
+ logMessage(ERROR, "eject failed on device %s: %m", device);
+ close(ejectfd);
+ } else {
+ logMessage(ERROR, "could not open device %s: %m", device);
+ }
+}
+
+static char *cdrom_drive_status(int rc) {
+ struct {
+ int code;
+ char *str;
+ } status_codes[] =
+ {
+ { CDS_NO_INFO, "CDS_NO_INFO" },
+ { CDS_NO_DISC, "CDS_NO_DISC" },
+ { CDS_TRAY_OPEN, "CDS_TRAY_OPEN" },
+ { CDS_DRIVE_NOT_READY, "CDS_DRIVE_NOT_READY" },
+ { CDS_DISC_OK, "CDS_DISC_OK" },
+ { CDS_AUDIO, "CDS_AUDIO" },
+ { CDS_DATA_1, "CDS_DATA_1" },
+ { CDS_DATA_2, "CDS_DATA_2" },
+ { CDS_XA_2_1, "CDS_XA_2_1" },
+ { CDS_XA_2_2, "CDS_XA_2_2" },
+ { CDS_MIXED, "CDS_MIXED" },
+ { INT_MAX, NULL },
+ };
+ int i;
+
+ if (rc < 0)
+ return strerror(-rc);
+
+ for (i = 0; status_codes[i].code != INT_MAX; i++) {
+ if (status_codes[i].code == rc)
+ return status_codes[i].str;
+ }
+ return NULL;
+}
+
+static int waitForCdromTrayClose(int fd) {
+ int rc;
+ int prev = INT_MAX;
+
+ do {
+ char *status = NULL;
+ rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ if (rc < 0)
+ rc = -errno;
+
+ /* only bother to print the status if it changes */
+ if (prev == INT_MAX || prev != rc) {
+ status = cdrom_drive_status(rc);
+ if (status != NULL) {
+ logMessage(INFO, "drive status is %s", status);
+ } else {
+ logMessage(INFO, "drive status is unknown status code %d", rc);
+ }
+ }
+ prev = rc;
+ if (rc == CDS_DRIVE_NOT_READY)
+ usleep(100000);
+ } while (rc == CDS_DRIVE_NOT_READY);
+ return rc;
+}
+
+static void closeCdromTray(char *device) {
+ int fd;
+
+ if (!device || !*device)
+ return;
+
+ logMessage(INFO, "closing CD tray on %s .", device);
+ if ((fd = open(device, O_RDONLY | O_NONBLOCK, 0)) >= 0) {
+ if (ioctl(fd, CDROMCLOSETRAY, 0)) {
+ logMessage(ERROR, "closetray failed on device %s: %m", device);
+ } else {
+ waitForCdromTrayClose(fd);
+ ioctl(fd, CDROM_LOCKDOOR, 1);
+ }
+ close(fd);
+ } else {
+ logMessage(ERROR, "could not open device %s: %m", device);
+ }
+}
+
+/* Given cd device cddriver, this function will attempt to check its internal
+ * checksum.
+ */
+static void mediaCheckCdrom(char *cddriver) {
+ int rc;
+ int first;
+
+ first = 1;
+ do {
+ char *descr;
+ char *tstamp;
+ int ejectcd;
+
+ /* init every pass */
+ ejectcd = 0;
+ descr = NULL;
+
+ closeCdromTray(cddriver);
+
+ /* if first time through, see if they want to eject the CD */
+ /* currently in the drive (most likely the CD they booted from) */
+ /* and test a different disk. Otherwise just test the disk in */
+ /* the drive since it was inserted in the previous pass through */
+ /* this loop, so they want it tested. */
+ if (first) {
+ first = 0;
+ rc = newtWinChoice(_("Media Check"), _("Test"), _("Eject Disc"),
+ _("Choose \"%s\" to test the disc currently in "
+ "the drive, or \"%s\" to eject the disc and "
+ "insert another for testing."), _("Test"),
+ _("Eject Disc"));
+
+ if (rc == 2)
+ ejectcd = 1;
+ }
+
+ if (!ejectcd) {
+ /* XXX MSFFIXME: should check return code for error */
+ readStampFileFromIso(cddriver, &tstamp, &descr);
+ doMediaCheck(cddriver, descr);
+
+ if (descr)
+ free(descr);
+ }
+
+ ejectCdrom(cddriver);
+
+ rc = newtWinChoice(_("Media Check"), _("Test"), _("Continue"),
+ _("If you would like to test additional media, "
+ "insert the next disc and press \"%s\". "
+ "Testing each disc is not strictly required, however "
+ "it is highly recommended. Minimally, the discs should "
+ "be tested prior to using them for the first time. "
+ "After they have been successfully tested, it is not "
+ "required to retest each disc prior to using it again."),
+ _("Test"), _("Continue"));
+
+ if (rc == 2) {
+ closeCdromTray(cddriver);
+ return;
+ } else {
+ continue;
+ }
+ } while (1);
+}
+
+/* output an error message when CD in drive is not the correct one */
+/* Used by mountCdromStage2() */
+static void wrongCDMessage(void) {
+ newtWinMessage(_("Error"), _("OK"),
+ _("The %s disc was not found "
+ "in any of your drives. Please insert "
+ "the %s disc and press %s to retry."),
+ getProductName(), getProductName(), _("OK"));
+}
+
+/* ask about doing media check */
+static void queryCDMediaCheck(char *dev, char *location) {
+ int rc;
+ char *stage2loc;
+
+ /* dont bother to test in automated installs */
+ if (FL_KICKSTART(flags) && !FL_MEDIACHECK(flags))
+ return;
+
+ /* see if we should check image(s) */
+ /* in rescue mode only test if they explicitly asked to */
+ if (!FL_RESCUE(flags) || FL_MEDIACHECK(flags)) {
+ startNewt();
+ rc = newtWinChoice(_("Disc Found"), _("OK"), _("Skip"),
+ _("To begin testing the media before installation press %s.\n\n"
+ "Choose %s to skip the media test and start the installation."),
+ _("OK"), _("Skip"));
+
+ if (rc != 2) {
+ /* We already mounted the CD earlier to verify there's at least a
+ * stage2 image. Now we need to unmount to perform the check, then
+ * remount to pretend nothing ever happened.
+ */
+ umount(location);
+ mediaCheckCdrom(dev);
+
+ do {
+ if (doPwMount(dev, location, "iso9660", "ro", NULL)) {
+ ejectCdrom(dev);
+ wrongCDMessage();
+ continue;
+ }
+
+ checked_asprintf(&stage2loc, "%s/images/install.img",
+ location);
+
+ if (access(stage2loc, R_OK)) {
+ free(stage2loc);
+ umount(location);
+ ejectCdrom(dev);
+ wrongCDMessage();
+ continue;
+ }
+
+ free(stage2loc);
+ break;
+ } while (1);
+ }
+ }
+}
+
+/* Set up a CD/DVD drive to mount the stage2 image from. If successful, the
+ * stage2 image will be left mounted on /mnt/runtime.
+ *
+ * location: Where to mount the media at (usually /mnt/stage2)
+ * loaderData: The usual, can be NULL if no info
+ * interactive: Whether or not to prompt about questions/errors
+ * mediaCheck: Do we run media check or not?
+ */
+static char *setupCdrom(char *location, struct loaderData_s *loaderData,
+ int interactive, int mediaCheck) {
+ int i, rc;
+ int stage2inram = 0;
+ char *retbuf = NULL, *stage2loc, *stage2img;
+ struct device ** devices;
+ char *cddev = NULL;
+
+ devices = getDevices(DEVICE_CDROM);
+ if (!devices) {
+ logMessage(ERROR, "got to setupCdrom without a CD device");
+ return NULL;
+ }
+
+ checked_asprintf(&stage2loc, "%s/images/install.img", location);
+
+ /* JKFIXME: ASSERT -- we have a cdrom device when we get here */
+ do {
+ for (i = 0; devices[i]; i++) {
+ char *tmp = NULL;
+ int fd;
+
+ if (!devices[i]->device)
+ continue;
+
+ if (strncmp("/dev/", devices[i]->device, 5)) {
+ checked_asprintf(&tmp, "/dev/%s", devices[i]->device);
+
+ free(devices[i]->device);
+ devices[i]->device = tmp;
+ }
+
+ logMessage(INFO, "trying to mount CD device %s on %s",
+ devices[i]->device, location);
+
+ if (!FL_CMDLINE(flags))
+ winStatus(60, 3, _("Scanning"), _("Looking for installation images on CD device %s\n"), devices[i]->device);
+ else
+ printf(_("Looking for installation images on CD device %s"), devices[i]->device);
+
+ fd = open(devices[i]->device, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ logMessage(ERROR, "Couldn't open %s: %m", devices[i]->device);
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+ continue;
+ }
+
+ rc = waitForCdromTrayClose(fd);
+ close(fd);
+ switch (rc) {
+ case CDS_NO_INFO:
+ logMessage(ERROR, "Drive tray reports CDS_NO_INFO");
+ break;
+ case CDS_NO_DISC:
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+ continue;
+ case CDS_TRAY_OPEN:
+ logMessage(ERROR, "Drive tray reports open when it should be closed");
+ break;
+ default:
+ break;
+ }
+
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+
+ if (!(rc=doPwMount(devices[i]->device, location, "iso9660", "ro", NULL))) {
+ cddev = devices[i]->device;
+ if (!access(stage2loc, R_OK)) {
+ char *updpath;
+
+ if (mediaCheck)
+ queryCDMediaCheck(devices[i]->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) {
+ logMessage(INFO, "mounting stage2 failed");
+ umount(location);
+ continue;
+ }
+
+ 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);
+ }
+
+ checked_asprintf(&retbuf, "cdrom://%s:%s",
+ devices[i]->device, location);
+ } else {
+ /* this wasnt the CD we were looking for, clean up and */
+ /* try the next CD drive */
+ umount(location);
+ }
+ }
+ }
+
+ if (!retbuf) {
+ if (interactive) {
+ char * buf;
+
+ checked_asprintf(&buf, _("The %s disc was not found in any of your "
+ "CDROM drives. Please insert the %s disc "
+ "and press %s to retry."),
+ getProductName(), getProductName(), _("OK"));
+
+ ejectCdrom(cddev);
+ rc = newtWinChoice(_("Disc Not Found"),
+ _("OK"), _("Back"), buf, _("OK"));
+ free(buf);
+ if (rc == 2)
+ goto err;
+ } else {
+ /* we can't ask them about it, so just return not found */
+ goto err;
+ }
+ }
+ } while (!retbuf);
+
+err:
+ free(stage2loc);
+ return retbuf;
+}
+
+/* try to find a install CD non-interactively */
+char * findAnacondaCD(char *location) {
+ return setupCdrom(location, NULL, 0, 1);
+}
+
+/* look for a CD and mount it. if we have problems, ask */
+char * mountCdromImage(struct installMethod * method,
+ char * location, struct loaderData_s * loaderData) {
+ return setupCdrom(location, loaderData, 1, 1);
+}
+
+void setKickstartCD(struct loaderData_s * loaderData, int argc, char ** argv) {
+
+ logMessage(INFO, "kickstartFromCD");
+ loaderData->method = METHOD_CDROM;
+}
+
+int kickstartFromCD(char *kssrc) {
+ int rc, i;
+ char *p, *kspath;
+ struct device ** devices;
+
+ logMessage(INFO, "getting kickstart file from first CDROM");
+
+ devices = getDevices(DEVICE_CDROM);
+ /* usb can take some time to settle, even with the various hacks we
+ * have in place. some systems use portable USB CD-ROM drives, try to
+ * make sure there really isn't one before bailing */
+ for (i = 0; !devices && i < 10; ++i) {
+ logMessage(INFO, "sleeping to wait for a USB CD-ROM");
+ sleep(2);
+ devices = getDevices(DEVICE_CDROM);
+ }
+ if (!devices) {
+ logMessage(ERROR, "No CDROM devices found!");
+ return 1;
+ }
+
+ /* format is cdrom:[/path/to/ks.cfg] */
+ kspath = "";
+ p = strchr(kssrc, ':');
+ if (p)
+ kspath = p + 1;
+
+ if (!p || strlen(kspath) < 1)
+ kspath = "/ks.cfg";
+
+ for (i=0; devices[i]; i++) {
+ if (!devices[i]->device)
+ continue;
+
+ rc = getKickstartFromBlockDevice(devices[i]->device, kspath);
+ if (rc == 0)
+ return 0;
+ }
+
+ startNewt();
+ newtWinMessage(_("Error"), _("OK"),
+ _("Cannot find kickstart file on CDROM."));
+ return 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 et */