/*
* 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 .
*
* Author(s): Erik Troan
* Matt Wilson
* Michael Fulbright
* Jeremy Katz
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 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
#include
#include
#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 "../pyanaconda/isys/imount.h"
#include "../pyanaconda/isys/isys.h"
#include "../pyanaconda/isys/log.h"
#include "../pyanaconda/isys/mem.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;
if (FL_NOEJECT(flags)) {
logMessage(INFO, "noeject in effect, not ejecting cdrom");
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) {
char *descr, *tstamp;
closeCdromTray(cddriver);
readStampFileFromIso(cddriver, &tstamp, &descr);
doMediaCheck(cddriver, descr);
if (descr)
free(descr);
if (tstamp)
free(tstamp);
}
/* 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 */
void queryCDMediaCheck(char *instRepo) {
int rc;
char *tmp, *device;
/* dont bother to test in automated installs */
if (FL_KICKSTART(flags) && !FL_MEDIACHECK(flags))
return;
/* Skip over the leading "cdrom://". */
tmp = instRepo+8;
checked_asprintf(&device, "%.*s", (int) (strchr(tmp, ':')-tmp), tmp);
/* 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 installation
* media. Now we need to unmount it to perform the check, then
* remount to pretend nothing ever happened.
*/
umount("/mnt/source");
mediaCheckCdrom(device);
do {
if (doPwMount(device, "/mnt/source", "iso9660", "ro", NULL)) {
ejectCdrom(device);
wrongCDMessage();
continue;
}
if (access("/mnt/source/.discinfo", R_OK)) {
umount("/mnt/source");
ejectCdrom(device);
wrongCDMessage();
continue;
}
break;
} while (1);
}
}
free(device);
}
int findInstallCD(struct loaderData_s *loaderData) {
int i, rc;
struct device **devices;
devices = getDevices(DEVICE_CDROM);
if (!devices) {
logMessage(ERROR, "got to findInstallCD without a CD device");
return LOADER_ERROR;
}
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 /mnt/source",
devices[i]->device);
if (!FL_CMDLINE(flags))
winStatus(60, 3, _("Scanning"), _("Looking for installation media on CD device %s\n"), devices[i]->device);
else
printf(_("Looking for installation media 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, "/mnt/source", "iso9660", "ro", NULL)) == 0) {
if (!access("/mnt/source/.treeinfo", R_OK) && !access("/mnt/source/.discinfo", R_OK)) {
loaderData->method = METHOD_CDROM;
checked_asprintf(&loaderData->instRepo, "cdrom://%s:/mnt/source", devices[i]->device);
return LOADER_OK;
} else {
/* This wasn't the CD we were looking for. Clean up and
* try the next drive.
*/
umount("/mnt/source");
}
}
}
return LOADER_ERROR;
}
int promptForCdrom(struct loaderData_s *loaderData) {
int rc;
do {
rc = findInstallCD(loaderData);
if (loaderData->instRepo && rc == LOADER_OK) {
queryCDMediaCheck(loaderData->instRepo);
return rc;
} else {
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"));
rc = newtWinChoice(_("Disc Not Found"),
_("OK"), _("Back"), buf, _("OK"));
free(buf);
if (rc == 2)
return LOADER_BACK;
}
} while (!loaderData->instRepo);
return LOADER_OK;
}
int loadCdromImages(struct loaderData_s *loaderData) {
char *device = NULL;
char *tmp;
logMessage(DEBUGLVL, "looking for extras for CD/DVD install");
if (!loaderData->instRepo)
return 0;
/* Skip over the leading "cdrom://". */
tmp = loaderData->instRepo+8;
checked_asprintf(&device, "%.*s", (int) (strchr(tmp, ':')-tmp), tmp);
if (doPwMount(device, "/mnt/source", "auto", "ro", NULL))
return 0;
logMessage(INFO, "Looking for updates in /mnt/source/images/updates.img");
copyUpdatesImg("/mnt/source/images/updates.img");
logMessage(INFO, "Looking for product in /mnt/source/images/product.img");
copyProductImg("/mnt/source/images/product.img");
umount("/mnt/source");
return 1;
}
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 */