diff options
author | David Cantrell <dcantrell@redhat.com> | 2008-08-25 17:13:37 -1000 |
---|---|---|
committer | David Cantrell <dcantrell@redhat.com> | 2008-08-25 17:13:37 -1000 |
commit | 80713e3f73e48856221c667f32b94b0a023ebecc (patch) | |
tree | aff4d9170fc24d2f1acc238a2d8908159a71d3dd /loader | |
parent | ef5fbf7bc72572f3a6326b12f9187a5438e58e4c (diff) | |
download | anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.tar.gz anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.tar.xz anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.zip |
Renamed loader2 subdirectory to loader (hooray for git)
Diffstat (limited to 'loader')
71 files changed, 16996 insertions, 0 deletions
diff --git a/loader/.gitignore b/loader/.gitignore new file mode 100644 index 000000000..021a9622c --- /dev/null +++ b/loader/.gitignore @@ -0,0 +1,12 @@ +ctype.c +mkctype +loader +init +debug.log +loader.tr +.depend +font.bgf.gz +loader.po +shutdown +checkisomd5 +tr diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 000000000..ff56e501a --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,156 @@ +# +# Makefile +# +# Copyright (C) 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/>. +# + +include ../Makefile.inc +VERSION := $(shell awk '/Version:/ { print $$2 }' ../anaconda.spec) + +ifeq (.depend,$(wildcard .depend)) +TARGET=$(PROGS) +else +TARGET=depend $(PROGS) +endif + +LIBS = -lnewt -lslang -lz -lpopt ../isys/libisys.a -lcheckisomd5 + +# devmapper +LIBS += $(shell pkg-config --libs devmapper) +CFLAGS += $(shell pkg-config --cflags devmapper) + +# libnl +LIBS += $(shell pkg-config --libs libnl-1) +CFLAGS += $(shell pkg-config --cflags libnl-1) + +# NetworkManager +CFLAGS += $(shell pkg-config --cflags NetworkManager) + +# D-Bus +LIBS += $(shell pkg-config --libs dbus-1) +CFLAGS += $(shell pkg-config --cflags dbus-1) + +ifeq (1, $(USESELINUX)) +LIBS += -lselinux -lsepol +endif + +# These libs need to be on the end of the link list +LIBS += -lresolv -lm + +BINS = loader + +HWOBJS = hardware.o +METHOBJS = method.o cdinstall.o hdinstall.o nfsinstall.o urlinstall.o +OBJS = copy.o log.o moduleinfo.o loadermisc.o modules.o windows.o \ + lang.o kbd.o driverdisk.o selinux.o \ + mediacheck.o kickstart.o driverselect.o \ + getparts.o dirbrowser.o fwloader.o \ + $(HWOBJS) $(METHOBJS) +LOADEROBJS = loader.o loader-pcmcia.o +NETOBJS = net.o urls.o ftp.o telnet.o telnetd.o +SOURCES = $(subst .o,.c,$(OBJS)) loader.c + +LIBS += + +CFLAGS += -DUSE_LOGDEV -DVERSION='"$(VERSION)"' +STATIC = +REALCC=gcc + +# linuxrc + shutdown on s390, init everywhere else +ifneq (,$(filter s390 s390x,$(ARCH))) +BINS += linuxrc.s390 shutdown +SHUTDOWNOPTS = -DAS_SHUTDOWN=1 +else +BINS += init +endif + +# translation stuff +LANGS = $(shell cut -f 2 ../lang-table | egrep -v '(^en$$)') + +TR = $(patsubst %,tr/%.tr,$(LANGS)) +TRFILES = $(patsubst %,%.tr,$(LANGS)) + +all: $(BINS) loader.tr + +loader.tr: $(TR) ../lang-table + (cd tr; ls $(TRFILES) | cpio --quiet -Hcrc -o |gzip -9) > $@ + +tr/%.tr: ../po/%.po loader.po + msgmerge -q $< loader.po | msgconv -t utf-8 | ./simplemot > $@ + +loader.po: $(wildcard *.c) + xgettext --default-domain=loader --add-comments \ + --keyword=_ --keyword=N_ *.c + sed -i 's/charset=CHARSET/charset=UTF-8/' $@ + +linuxrc.s390: + @echo "Nothing to do for $@" + +init: init.o undomounts.o shutdown.o copy.o + $(CC) $(STATIC) $(CFLAGS) $(LDFLAGS) -o $@ $^ + +shutdown: shutdown.o undomounts.o + $(CC) $(STATIC) $(CFLAGS) $(SHUTDOWNOPTS) $(LDFLAGS) -o $@ $^ + +init.o: init.c devices.h + $(CC) $(CFLAGS) -c -o init.o init.c + +undomounts.o: undomounts.c + $(CC) $(CFLAGS) -c -o undomounts.o undomounts.c + +shutdown.o: shutdown.c + $(CC) $(CFLAGS) $(SHUTDOWNOPTS) -c -o shutdown.o shutdown.c + +mkctype: mkctype.c + $(REALCC) $(CFLAGS) -o mkctype mkctype.c + +ctype.c: mkctype + ./mkctype > ctype.c + +loader.o: loader.c + $(CC) -DINCLUDE_LOCAL -DINCLUDE_NETWORK $(CFLAGS) -o $@ -c $< + +loader-local.o: loader.c + $(CC) -DINCLUDE_LOCAL $(CFLAGS) -o $@ -c $< + +loader-net.o: loader.c + $(CC) -DINCLUDE_NETWORK $(CFLAGS) -o $@ -c $< + +loader: loader.o $(OBJS) $(NETOBJS) + $(CC) -g $(STATIC) -o $@ $^ $(LIBS) $(LDFLAGS) + +clean: + rm -f *.o *~ .depend init ctype.c mkctype \ + loader + +depend: $(CTYPEDEP) + $(CPP) $(CFLAGS) -DHAVE_CONFIG_H -M $(SOURCES) > .depend + +install: all + mkdir -p $(DESTDIR)/$(RUNTIMEDIR)/loader + for n in $(BINS); do \ + install -m 755 $$n $(DESTDIR)/$(RUNTIMEDIR)/loader; \ + done + if [ -f keymaps-$(ARCH) ]; then cp keymaps-$(ARCH) $(DESTDIR)/$(RUNTIMEDIR)/keymaps-override-$(ARCH) ; fi + install -m 644 unicode-linedraw-chars.txt $(DESTDIR)/$(RUNTIMEDIR)/loader + install -m 644 loader.tr $(DESTDIR)/$(RUNTIMEDIR)/loader + +dirbrowser: dirbrowser.c + gcc -DSTANDALONE -D_FORTIFY_SOURCE=2 -Wall -Werror -ggdb -o dirbrowser dirbrowser.c -lnewt -lslang + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/loader/cdinstall.c b/loader/cdinstall.c new file mode 100644 index 000000000..f0028d3f5 --- /dev/null +++ b/loader/cdinstall.c @@ -0,0 +1,508 @@ +/* + * 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> +#include <asm/types.h> +#include <limits.h> +#include <linux/cdrom.h> + +#include "kickstart.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.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" + +/* 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(DEBUGLVL, "drive status is %s", status); + } else { + logMessage(DEBUGLVL, "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) { + 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; + } + + if (asprintf(&stage2loc, "%s/images/install.img", + location) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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; + } + + if (asprintf(&stage2loc, "%s/images/install.img", location) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + /* JKFIXME: ASSERT -- we have a cdrom device when we get here */ + do { + for (i = 0; devices[i]; i++) { + char *tmp = NULL; + int j; + int fd; + + if (!devices[i]->device) + continue; + + if (strncmp("/dev/", devices[i]->device, 5)) { + if (asprintf(&tmp, "/dev/%s", devices[i]->device) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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"), 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) { + waitForCdromTrayClose(fd); + close(fd); + } + + for (j = 0; j < 450; j++) { + fd = open(devices[i]->device, O_RDONLY); + if (fd >= 0) { + close(fd); + break; + } else if (errno == ENOMEDIUM) { + logMessage(DEBUGLVL, "%s reported %m", devices[i]->device); + usleep(100000); + } else { + 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() > 128000) { + 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; + } + + if (asprintf(&updpath, "%s/images/updates.img", location) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(INFO, "Looking for updates in %s", updpath); + copyUpdatesImg(updpath); + free(updpath); + + if (asprintf(&updpath, "%s/images/product.img", location) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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); + } + + if (asprintf(&retbuf, "cdrom://%s:%s", + devices[i]->device, location) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } 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; + + if (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")) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + 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); + 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 */ diff --git a/loader/cdinstall.h b/loader/cdinstall.h new file mode 100644 index 000000000..a0ecbc31a --- /dev/null +++ b/loader/cdinstall.h @@ -0,0 +1,34 @@ +/* + * cdinstall.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_CDINSTALL +#define H_CDINSTALL + +#include "method.h" + +char * mountCdromImage(struct installMethod * method, + char * location, struct loaderData_s * loaderData); + +char * findAnacondaCD(char * location); + +void setKickstartCD(struct loaderData_s * loaderData, int argc, + char ** argv); + +int kickstartFromCD(char *kssrc); +#endif diff --git a/loader/copy.c b/loader/copy.c new file mode 100644 index 000000000..1c612337c --- /dev/null +++ b/loader/copy.c @@ -0,0 +1,141 @@ +/* + * copy.c - functions for copying files and directories + * + * Copyright (C) 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/>. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "lang.h" + +/* Recursive */ +int copyDirectory(char * from, char * to, void (*warnFn)(char *), + void (*errorFn)(char *)) { + char *msg; + DIR * dir; + struct dirent * ent; + int fd, outfd; + char buf[4096]; + int i; + struct stat sb; + char filespec[256]; + char filespec2[256]; + char link[1024]; + + mkdir(to, 0755); + + if (!(dir = opendir(from))) { + if (errorFn) { + if (asprintf(&msg, N_("Failed to read directory %s: %m"), from) == -1) { + fprintf(stderr, "%s: %d: %m\n", __func__, __LINE__); + fflush(stderr); + abort(); + } + + errorFn(msg); + free(msg); + } + + return 1; + } + + errno = 0; + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + + sprintf(filespec, "%s/%s", from, ent->d_name); + sprintf(filespec2, "%s/%s", to, ent->d_name); + + lstat(filespec, &sb); + + if (S_ISDIR(sb.st_mode)) { + if (copyDirectory(filespec, filespec2, warnFn, errorFn)) { + closedir(dir); + return 1; + } + } else if (S_ISLNK(sb.st_mode)) { + i = readlink(filespec, link, sizeof(link) - 1); + link[i] = '\0'; + if (symlink(link, filespec2)) { + if (warnFn) { + if (asprintf(&msg, "Failed to symlink %s to %s: %m", + filespec2, link) == -1) { + fprintf(stderr, "%s: %d: %m\n", __func__, __LINE__); + fflush(stderr); + abort(); + } + + warnFn(msg); + free(msg); + } + } + } else { + fd = open(filespec, O_RDONLY); + if (fd == -1) { + if (errorFn) { + if (asprintf(&msg, "Failed to open %s: %m", filespec) == -1) { + fprintf(stderr, "%s: %d: %m\n", __func__, __LINE__); + fflush(stderr); + abort(); + } + + errorFn(msg); + free(msg); + } + + closedir(dir); + return 1; + } + outfd = open(filespec2, O_RDWR | O_TRUNC | O_CREAT, 0644); + if (outfd == -1) { + if (warnFn) { + if (asprintf(&msg, "Failed to create %s: %m", filespec2) == -1) { + fprintf(stderr, "%s: %d: %m\n", __func__, __LINE__); + fflush(stderr); + abort(); + } + + warnFn(msg); + free(msg); + } + } else { + fchmod(outfd, sb.st_mode & 07777); + + while ((i = read(fd, buf, sizeof(buf))) > 0) + i = write(outfd, buf, i); + close(outfd); + } + + close(fd); + } + + errno = 0; + } + + closedir(dir); + + return 0; +} diff --git a/loader/copy.h b/loader/copy.h new file mode 100644 index 000000000..1153bf816 --- /dev/null +++ b/loader/copy.h @@ -0,0 +1,26 @@ +/* + * copy.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_COPY +#define H_COPY + +int copyDirectory (char *from, char *to, void (*warnFn)(char *), + void (*errorFn)(char *)); + +#endif diff --git a/loader/devices.h b/loader/devices.h new file mode 100644 index 000000000..974e79288 --- /dev/null +++ b/loader/devices.h @@ -0,0 +1,103 @@ +/* + * devices.h: handle declaration of devices to be created under /dev + * + * Copyright (C) 2004 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/>. + */ + +#ifndef LOADER_INIT_DEVICES_H +#define LOADER_INIT_DEVICES_H + +struct devnode { + char * devname; + int type; + int major; + int minor; + int perms; + char * owner; + char * group; +}; + +#define CHARDEV 0 +#define BLOCKDEV 1 +#define DIRTYPE 2 + +struct devnode devnodes[] = { + /* consoles */ + {"console", CHARDEV, 5, 1, 0600, "root", "root"}, + {"ttyS0", CHARDEV, 4, 64, 0600, "root", "root"}, + {"ttyS1", CHARDEV, 4, 65, 0600, "root", "root"}, + {"ttyS2", CHARDEV, 4, 66, 0600, "root", "root"}, + {"ttyS3", CHARDEV, 4, 67, 0600, "root", "root"}, +#ifdef __ia64__ + {"ttySG0", CHARDEV, 204, 40, 0600, "root", "root"}, +#endif +#ifdef __powerpc__ + {"hvsi0", CHARDEV, 229, 128, 0600, "root", "root"}, + {"hvsi1", CHARDEV, 229, 129, 0600, "root", "root"}, + {"hvsi2", CHARDEV, 229, 130, 0600, "root", "root"}, +#endif + {"hvc0", CHARDEV, 229, 0, 0600, "root", "root"}, +#if defined(__i386__) || defined(__x86_64__) || defined(__ia64__) + {"xvc0", CHARDEV, 204, 191, 0600, "root", "root"}, +#endif + /* base unix */ + {"null", CHARDEV, 1, 3, 0666, "root", "root"}, + {"zero", CHARDEV, 1, 5, 0666, "root", "root"}, + {"mem", CHARDEV, 1, 1, 0600, "root", "root"}, + /* ttys */ + {"pts", DIRTYPE, 0, 0, 0755, "root", "root"}, + {"ptmx", CHARDEV, 5, 2, 0666, "root", "root"}, + {"tty", CHARDEV, 5, 0, 0666, "root", "root"}, + {"tty0", CHARDEV, 4, 0, 0600, "root", "tty"}, + {"tty1", CHARDEV, 4, 1, 0600, "root", "tty"}, + {"tty2", CHARDEV, 4, 2, 0600, "root", "tty"}, + {"tty3", CHARDEV, 4, 3, 0600, "root", "tty"}, + {"tty4", CHARDEV, 4, 4, 0600, "root", "tty"}, + {"tty5", CHARDEV, 4, 5, 0600, "root", "tty"}, + {"tty6", CHARDEV, 4, 6, 0600, "root", "tty"}, + {"tty7", CHARDEV, 4, 7, 0600, "root", "tty"}, + {"tty8", CHARDEV, 4, 8, 0600, "root", "tty"}, + {"tty9", CHARDEV, 4, 9, 0600, "root", "tty"}, + /* fb */ + {"fb0", CHARDEV, 29, 0, 0600, "root", "tty"}, + /* sparc specific */ +#ifdef __sparc__ + {"openprom", CHARDEV, 10, 139, 0644, "root", "root"}, + {"sunmouse", CHARDEV, 10, 6, 0644, "root", "root"}, + {"kbd", CHARDEV, 11, 0, 0644, "root", "root"}, +#endif + /* X */ + {"agpgart", CHARDEV, 10, 175, 0664, "root", "root"}, + {"psaux", CHARDEV, 10, 1, 0644, "root", "root"}, + {"input", DIRTYPE, 0, 0, 0755, "root", "root"}, + {"input/mice", CHARDEV, 13, 63, 0664, "root", "root"}, + /* floppies */ + {"fd0", BLOCKDEV, 2, 0, 0644, "root", "root"}, + {"fd1", BLOCKDEV, 2, 1, 0644, "root", "root"}, + /* random */ + {"random", CHARDEV, 1, 8, 0644, "root", "root"}, + {"urandom", CHARDEV, 1, 9, 0644, "root", "root"}, + /* mac stuff */ +#ifdef __powerpc__ + {"nvram", CHARDEV, 10, 144, 0644, "root", "root"}, + {"adb", CHARDEV, 56, 0, 0644, "root", "root"}, + {"iseries", DIRTYPE, 0, 0, 0755, "root", "root" }, +#endif + {"rtc", CHARDEV, 10, 135, 0644, "root", "root"}, + { NULL, 0, 0, 0, 0, NULL, NULL }, +}; + +#endif diff --git a/loader/devt.h b/loader/devt.h new file mode 100644 index 000000000..364a13428 --- /dev/null +++ b/loader/devt.h @@ -0,0 +1,39 @@ +/* + * devt.h: handle declaration of dev_t to be sane for loopback purposes + * + * Copyright (C) 1996, 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/>. + */ + +#ifndef DEVT_H +#define DEVT_H + +/* Need to tell loop.h what the actual dev_t type is. */ +#undef dev_t +#if defined(__alpha) || (defined(__sparc__) && defined(__arch64__)) +#define dev_t unsigned int +#else +#if defined(__x86_64__) +#define dev_t unsigned long +#else +#define dev_t unsigned short +#endif +#endif +#include <linux/loop.h> +#undef dev_t +#define dev_t dev_t + +#endif diff --git a/loader/dirbrowser.c b/loader/dirbrowser.c new file mode 100644 index 000000000..4d9c25cab --- /dev/null +++ b/loader/dirbrowser.c @@ -0,0 +1,215 @@ +/* + * dirbrowser.c - newt-based directory browser to get a file name + * + * Copyright (C) 2004 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): Jeremy Katz <katzj@redhat.com> + */ + +#include <newt.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <string.h> +#include <sys/stat.h> + +#ifndef STANDALONE +#include "log.h" +#include "loader.h" +#include "loadermisc.h" +#include "lang.h" +#endif + +#ifdef STANDALONE +#define _(x) x + +static int simpleStringCmp(const void * a, const void * b) { + const char * first = *((const char **) a); + const char * second = *((const char **) b); + + return strcmp(first, second); +} +#endif + +#if 0 +/* sample filter function */ +/* return 1 if a dir, 0 if not */ +int getOnlyDirs(char * dir, struct dirent *entry) { + struct stat sb; + char * fn = alloca(strlen(dir) + strlen(entry->d_name) + 2); + + sprintf(fn, "%s/%s", dir, entry->d_name); + stat(fn, &sb); + + if (!S_ISDIR(sb.st_mode)) { + return 1; + } + return 0; +} +#endif + +#define FSTEP 10 + +static char ** get_file_list(char * dirname, + int (*filterfunc)(char *, struct dirent *)) { + DIR * dir; + struct dirent *entry; + char ** files; + int numfiles = FSTEP, i = 0; + + dir = opendir(dirname); + if (dir == NULL) { + fprintf(stderr, "error opening %s: %m", dirname); + return NULL; + } + + files = malloc(numfiles * sizeof(char *)); + + while ((entry = readdir(dir))) { + if ((strlen(entry->d_name) == 1) && !strncmp(entry->d_name, ".", 1)) + continue; + if ((strlen(entry->d_name) == 2) && !strncmp(entry->d_name, "..", 2)) + continue; + if (filterfunc && filterfunc(dirname, entry)) + continue; + + files[i] = strdup(entry->d_name); + if (i++ >= (numfiles - 1)) { + numfiles += FSTEP; + files = realloc(files, numfiles * sizeof(char *)); + } + } + files[i] = NULL; + closedir(dir); + + qsort(files, i, sizeof(*files), simpleStringCmp); + return files; +} + +/* Browse through a directory structure looking for a file. + * Returns the full path to the file. + * + * Parameters: + * title: Title for newt dialog window + * dirname: Directory to use for root of browsing. NOTE: you cannot go + * up above this root. + * filterfunc: An (optional) function to filter out files based on whatever + * criteria you want. Returns 1 if it passes, 0 if not. + * Function should take arguments of the directory name and + * the dirent for the file. + */ +char * newt_select_file(char * title, char * text, char * dirname, + int (*filterfunc)(char *, struct dirent *)) { + char ** files; + char * fn = NULL; + int i, done = 0; + char * topdir = dirname; + char * dir = malloc(PATH_MAX); + char * path = NULL; + newtGrid grid, buttons; + newtComponent f, tb, listbox, ok, cancel; + struct stat sb; + struct newtExitStruct es; + + dir = realpath(dirname, dir); + + do { + files = get_file_list(dir, filterfunc); + + f = newtForm(NULL, NULL, 0); + grid = newtCreateGrid(1, 4); + + tb = newtTextboxReflowed(-1, -1, text, 60, 0, 10, 0); + + listbox = newtListbox(12, 65, 10, + NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT); + + newtListboxSetWidth(listbox, 55); + buttons = newtButtonBar(_("OK"), &ok, _("Cancel"), &cancel, NULL); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, tb, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, listbox, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + /* if this isn't our topdir, we want to let them go up a dir */ + if (strcmp(topdir, dir)) + newtListboxAppendEntry(listbox, "../", ".."); + + for (i = 0; (files[i] != NULL); i++) { + if ((files[i] == NULL) || (strlen(files[i]) == 0)) continue; + path = malloc(strlen(files[i]) + strlen(dir) + 2); + sprintf(path, "%s/%s", dir, files[i]); + stat(path, &sb); + free(path); + if (S_ISDIR(sb.st_mode)) { + char *dir = malloc(strlen(files[i]) + 2); + sprintf(dir, "%s/", files[i]); + newtListboxAppendEntry(listbox, dir, files[i]); + } else { + newtListboxAppendEntry(listbox, files[i], files[i]); + } + } + + newtGridWrappedWindow(grid, title); + newtGridAddComponentsToForm(grid, f, 1); + newtFormRun(f, &es); + + if (es.reason == NEWT_EXIT_COMPONENT && es.u.co == cancel) { + fn = NULL; + done = -1; + } else { + fn = (char *) newtListboxGetCurrent(listbox); + path = malloc(strlen(fn) + strlen(dir) + 2); + sprintf(path, "%s/%s", dir, fn); + + stat(path, &sb); + if (!S_ISDIR(sb.st_mode)) { + fn = path; + done = 1; + } else { + dir = realpath(path, dir); + free(path); + } + } + + newtGridFree(grid, 1); + newtFormDestroy(f); + newtPopWindow(); + } while (done == 0); + + return fn; +} + +#ifdef STANDALONE +int main(int argc, char ** argv) { + char * foo; + + newtInit(); + newtCls(); + + foo = newt_select_file("Get File Name", "foo, blah blah blah", + "/etc", NULL); + newtFinished(); + printf("got %s\n", foo); + return 0; +} +#endif diff --git a/loader/dirbrowser.h b/loader/dirbrowser.h new file mode 100644 index 000000000..7fb22f292 --- /dev/null +++ b/loader/dirbrowser.h @@ -0,0 +1,28 @@ +/* + * dirbrowser.h + * + * Copyright (C) 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/>. + */ + +#ifndef DIRBROWSER_H +#define DIRBROWSER_H + +#include <dirent.h> + +char * newt_select_file(char * title, char * text, char * dirname, + int (*filterfunc)(char *, struct dirent *)); + +#endif diff --git a/loader/driverdisk.c b/loader/driverdisk.c new file mode 100644 index 000000000..ce6f8ce91 --- /dev/null +++ b/loader/driverdisk.c @@ -0,0 +1,642 @@ +/* + * driverdisk.c - driver disk functionality + * + * Copyright (C) 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): Jeremy Katz <katzj@redhat.com> + */ + +#include <errno.h> +#include <fcntl.h> +#include <newt.h> +#include <popt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "copy.h" +#include "loader.h" +#include "log.h" +#include "loadermisc.h" +#include "lang.h" +#include "fwloader.h" +#include "method.h" +#include "modules.h" +#include "moduleinfo.h" +#include "windows.h" +#include "hardware.h" +#include "driverdisk.h" +#include "getparts.h" +#include "dirbrowser.h" + +#include "nfsinstall.h" +#include "urlinstall.h" + +#include "../isys/isys.h" +#include "../isys/imount.h" +#include "../isys/eddsupport.h" + +/* boot flags */ +extern uint64_t flags; + +static char * driverDiskFiles[] = { "modinfo", "modules.dep", + "modules.cgz", "modules.alias", NULL }; + +static int verifyDriverDisk(char *mntpt) { + char ** fnPtr; + char file[200]; + struct stat sb; + + for (fnPtr = driverDiskFiles; *fnPtr; fnPtr++) { + sprintf(file, "%s/%s", mntpt, *fnPtr); + if (access(file, R_OK)) { + logMessage(ERROR, "cannot find %s, bad driver disk", file); + return LOADER_BACK; + } + } + + /* check for both versions */ + sprintf(file, "%s/rhdd", mntpt); + if (access(file, R_OK)) { + logMessage(DEBUGLVL, "not a new format driver disk, checking for old"); + sprintf(file, "%s/rhdd-6.1", mntpt); + if (access(file, R_OK)) { + logMessage(ERROR, "can't find either driver disk identifier, bad " + "driver disk"); + } + } + + /* side effect: file is still mntpt/ddident */ + stat(file, &sb); + if (!sb.st_size) + return LOADER_BACK; + + return LOADER_OK; +} + +static void copyWarnFn (char *msg) { + logMessage(WARNING, msg); +} + +static void copyErrorFn (char *msg) { + newtWinMessage(_("Error"), _("OK"), _(msg)); +} + +/* this copies the contents of the driver disk to a ramdisk and loads + * the moduleinfo, etc. assumes a "valid" driver disk mounted at mntpt */ +static int loadDriverDisk(struct loaderData_s *loaderData, char *mntpt) { + moduleInfoSet modInfo = loaderData->modInfo; + char file[200], dest[200]; + char *title; + char *fwdir = NULL; + struct moduleBallLocation * location; + struct stat sb; + static int disknum = 0; + int version = 1; + int fd, ret; + + /* check for both versions */ + sprintf(file, "%s/rhdd", mntpt); + if (access(file, R_OK)) { + version = 0; + sprintf(file, "%s/rhdd-6.1", mntpt); + if (access(file, R_OK)) { + /* this can't happen, we already verified it! */ + return LOADER_BACK; + } + } + stat(file, &sb); + title = malloc(sb.st_size + 1); + + fd = open(file, O_RDONLY); + ret = read(fd, title, sb.st_size); + if (title[sb.st_size - 1] == '\n') + sb.st_size--; + title[sb.st_size] = '\0'; + close(fd); + + sprintf(file, "/tmp/DD-%d", disknum); + mkdirChain(file); + + if (!FL_CMDLINE(flags)) { + startNewt(); + winStatus(40, 3, _("Loading"), _("Reading driver disk...")); + } + + sprintf(dest, "/tmp/DD-%d", disknum); + copyDirectory(mntpt, dest, copyWarnFn, copyErrorFn); + + location = malloc(sizeof(struct moduleBallLocation)); + location->title = strdup(title); + location->version = version; + + if (asprintf(&location->path, "/tmp/DD-%d/modules.cgz", disknum) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (asprintf(&fwdir, "/tmp/DD-%d/firmware", disknum) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (!access(fwdir, R_OK|X_OK)) { + add_fw_search_dir(loaderData, fwdir); + stop_fw_loader(loaderData); + start_fw_loader(loaderData); + } + free(fwdir); + + sprintf(file, "%s/modinfo", mntpt); + readModuleInfo(file, modInfo, location, 1); + + if (!FL_CMDLINE(flags)) + newtPopWindow(); + + disknum++; + return 0; +} + +/* Get the list of removable devices (floppy/cdrom) available. Used to + * find suitable devices for update disk / driver disk source. + * Returns the number of devices. ***devNames will be a NULL-terminated list + * of device names + */ +int getRemovableDevices(char *** devNames) { + struct device **devs; + int numDevices = 0; + int i = 0; + + devs = getDevices(DEVICE_DISK | DEVICE_CDROM); + + for (i = 0; devs[i] ; i++) { + if (devs[i]->priv.removable) { + *devNames = realloc(*devNames, (numDevices + 2) * sizeof(char *)); + (*devNames)[numDevices] = strdup(devs[i]->device); + (*devNames)[numDevices+1] = NULL; + numDevices ++; + } + } + if (!numDevices) { + logMessage(ERROR, "no devices found to load drivers from"); + } + return numDevices; +} + +/* Prompt for loading a driver from "media" + * + * class: type of driver to load. + * usecancel: if 1, use cancel instead of back + */ +int loadDriverFromMedia(int class, struct loaderData_s *loaderData, + int usecancel, int noprobe) { + char * device = NULL, * part = NULL, * ddfile = NULL; + char ** devNames = NULL; + enum { DEV_DEVICE, DEV_PART, DEV_CHOOSEFILE, DEV_LOADFILE, + DEV_INSERT, DEV_LOAD, DEV_PROBE, + DEV_DONE } stage = DEV_DEVICE; + int rc, num = 0; + int dir = 1; + int found = 0, before = 0; + + while (stage != DEV_DONE) { + switch(stage) { + case DEV_DEVICE: + rc = getRemovableDevices(&devNames); + if (rc == 0) + return LOADER_BACK; + + /* 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 LOADER_BACK; + + stage = DEV_PART; + break; + } + dir = 1; + + startNewt(); + rc = newtWinMenu(_("Driver Disk Source"), + _("You have multiple devices which could serve " + "as sources for a driver disk. Which would " + "you like to use?"), 40, 10, 10, + rc < 6 ? rc : 6, devNames, + &num, _("OK"), + (usecancel) ? _("Cancel") : _("Back"), NULL); + + if (rc == 2) { + free(devNames); + return LOADER_BACK; + } + device = strdup(devNames[num]); + free(devNames); + + stage = DEV_PART; + case DEV_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 = DEV_DEVICE; + else + stage = DEV_INSERT; + break; + } + dir = 1; + + startNewt(); + rc = newtWinMenu(_("Driver Disk Source"), + _("There are multiple partitions on this device " + "which could contain the driver 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 = DEV_DEVICE; + dir = -1; + break; + } + + part = strdup(part_list[num]); + stage = DEV_CHOOSEFILE; + + } + + case DEV_CHOOSEFILE: { + if (part == NULL) { + logMessage(ERROR, "somehow got to choosing file with a NULL part, going back"); + stage = DEV_PART; + break; + } + /* make sure nothing is mounted when we get here */ + num = umount("/tmp/dpart"); + if (num == -1) { + logMessage(ERROR, "error unmounting: %m"); + if ((errno != EINVAL) && (errno != ENOENT)) + exit(1); + } + + logMessage(INFO, "trying to mount %s as partition", part); + if (doPwMount(part, "/tmp/dpart", "vfat", "ro", NULL)) { + if (doPwMount(part, "/tmp/dpart", "ext2", "ro", NULL)) { + if (doPwMount(part, "/tmp/dpart", "iso9660", "ro", NULL)) { + newtWinMessage(_("Error"), _("OK"), + _("Failed to mount partition.")); + stage = DEV_PART; + break; + } + } + } + + ddfile = newt_select_file(_("Select driver disk image"), + _("Select the file which is your driver " + "disk image."), + "/tmp/dpart", NULL); + if (ddfile == NULL) { + umount("/tmp/dpart"); + stage = DEV_PART; + dir = -1; + break; + } + dir = 1; + + stage = DEV_LOADFILE; + } + + case DEV_LOADFILE: { + if(ddfile == NULL) { + logMessage(DEBUGLVL, "trying to load dd from NULL"); + stage = DEV_CHOOSEFILE; + break; + } + if (dir == -1) { + umountLoopback("/tmp/drivers", "/dev/loop6"); + unlink("/tmp/drivers"); + ddfile = NULL; + stage = DEV_CHOOSEFILE; + break; + } + if (mountLoopback(ddfile, "/tmp/drivers", "/dev/loop6")) { + newtWinMessage(_("Error"), _("OK"), + _("Failed to load driver disk from file.")); + stage = DEV_CHOOSEFILE; + break; + } + stage = DEV_LOAD; + break; + } + + case DEV_INSERT: { + char * buf; + + if (asprintf(&buf, + _("Insert your driver disk into /dev/%s " + "and press \"OK\" to continue."), device) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + rc = newtWinChoice(_("Insert Driver Disk"), _("OK"), _("Back"), + buf); + free(buf); + if (rc == 2) { + stage = DEV_DEVICE; + dir = -1; + break; + } + dir = 1; + + logMessage(INFO, "trying to mount %s", device); + if (doPwMount(device, "/tmp/drivers", "vfat", "ro", NULL)) { + if (doPwMount(device, "/tmp/drivers", "ext2", "ro", NULL)) { + if (doPwMount(device, "/tmp/drivers", "iso9660", "ro", NULL)) { + newtWinMessage(_("Error"), _("OK"), + _("Failed to mount driver disk.")); + stage = DEV_INSERT; + break; + } + } + } + + rc = verifyDriverDisk("/tmp/drivers"); + if (rc == LOADER_BACK) { + newtWinMessage(_("Error"), _("OK"), + _("Driver disk is invalid for this " + "release of %s."), getProductName()); + umount("/tmp/drivers"); + stage = DEV_INSERT; + break; + } + + stage = DEV_LOAD; + break; + } + case DEV_LOAD: { + struct device ** devices; + + before = 0; + found = 0; + + devices = getDevices(class); + if (devices) + for(; devices[before]; before++); + + rc = loadDriverDisk(loaderData, "/tmp/drivers"); + umount("/tmp/drivers"); + if (rc == LOADER_BACK) { + dir = -1; + if (ddfile != NULL) + stage = DEV_CHOOSEFILE; + else + stage = DEV_INSERT; + break; + } + /* fall through to probing */ + stage = DEV_PROBE; + + if (ddfile != NULL) { + umountLoopback("/tmp/drivers", "/dev/loop6"); + unlink("/tmp/drivers"); + umount("/tmp/dpart"); + } + } + + case DEV_PROBE: { + struct device ** devices; + + /* if they didn't specify that we should probe, then we should + * just fall out */ + if (noprobe) { + stage = DEV_DONE; + break; + } + + busProbe(0); + + devices = getDevices(class); + if (devices) + for(; devices[found]; found++); + + if (found > before) { + stage = DEV_DONE; + break; + } + + /* we don't have any more modules of the proper class. ask + * them to manually load */ + rc = newtWinTernary(_("Error"), _("Manually choose"), + _("Continue"), _("Load another disk"), + _("No devices of the appropriate type were " + "found on this driver disk. Would you " + "like to manually select the driver, " + "continue anyway, or load another " + "driver disk?")); + + if (rc == 2) { + /* if they choose to continue, just go ahead and continue */ + stage = DEV_DONE; + } else if (rc == 3) { + /* if they choose to load another disk, back to the + * beginning with them */ + stage = DEV_DEVICE; + } else { + rc = chooseManualDriver(class, loaderData); + /* if they go back from a manual driver, we'll ask again. + * if they load something, assume it's what we need */ + if (rc == LOADER_OK) { + stage = DEV_DONE; + } + } + + break; + } + + case DEV_DONE: + break; + } + } + + return LOADER_OK; +} + + +/* looping way to load driver disks */ +int loadDriverDisks(int class, struct loaderData_s *loaderData) { + int rc; + + rc = newtWinChoice(_("Driver disk"), _("Yes"), _("No"), + _("Do you have a driver disk?")); + if (rc != 1) + return LOADER_OK; + + rc = loadDriverFromMedia(DEVICE_ANY, loaderData, 1, 0); + if (rc == LOADER_BACK) + return LOADER_OK; + + do { + rc = newtWinChoice(_("More Driver Disks?"), _("Yes"), _("No"), + _("Do you wish to load any more driver disks?")); + if (rc != 1) + break; + loadDriverFromMedia(DEVICE_ANY, loaderData, 0, 0); + } while (1); + + return LOADER_OK; +} + +static void loadFromLocation(struct loaderData_s * loaderData, char * dir) { + if (verifyDriverDisk(dir) == LOADER_BACK) { + logMessage(ERROR, "not a valid driver disk"); + return; + } + + loadDriverDisk(loaderData, dir); + busProbe(0); +} + +void getDDFromSource(struct loaderData_s * loaderData, char * src) { + char *path = "/tmp/dd.img"; + int unlinkf = 0; + + if (!strncmp(src, "nfs:", 4)) { + unlinkf = 1; + if (getFileFromNfs(src + 4, "/tmp/dd.img", loaderData)) { + logMessage(ERROR, "unable to retrieve driver disk: %s", src); + return; + } + } else if (!strncmp(src, "ftp://", 6) || !strncmp(src, "http://", 7)) { + unlinkf = 1; + if (getFileFromUrl(src, "/tmp/dd.img", loaderData)) { + logMessage(ERROR, "unable to retrieve driver disk: %s", src); + return; + } + /* FIXME: this is a hack so that you can load a driver disk from, eg, + * scsi cdrom drives */ +#if !defined(__s390__) && !defined(__s390x__) + } else if (!strncmp(src, "cdrom", 5)) { + loadDriverDisks(DEVICE_ANY, loaderData); + return; +#endif + } else if (!strncmp(src, "path:", 5)) { + path = src + 5; + } else { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Unknown driver disk kickstart source: %s"), src); + return; + } + + if (!mountLoopback(path, "/tmp/drivers", "/dev/loop6")) { + loadFromLocation(loaderData, "/tmp/drivers"); + umountLoopback("/tmp/drivers", "/dev/loop6"); + unlink("/tmp/drivers"); + if (unlinkf) unlink(path); + } + +} + +static void getDDFromDev(struct loaderData_s * loaderData, char * dev, + char * fstype); + +void useKickstartDD(struct loaderData_s * loaderData, + int argc, char ** argv) { + char * fstype = NULL; + char * dev = NULL; + char * src = NULL; + + char * biospart = NULL, * p = NULL; + int usebiosdev = 0; + + poptContext optCon; + int rc; + struct poptOption ksDDOptions[] = { + { "type", '\0', POPT_ARG_STRING, &fstype, 0, NULL, NULL }, + { "source", '\0', POPT_ARG_STRING, &src, 0, NULL, NULL }, + { "biospart", '\0', POPT_ARG_NONE, &usebiosdev, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + optCon = poptGetContext(NULL, argc, (const char **) argv, ksDDOptions, 0); + if ((rc = poptGetNextOpt(optCon)) < -1) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("The following invalid argument was specified for " + "the kickstart driver disk command: %s:%s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + return; + } + + dev = (char *) poptGetArg(optCon); + + if (!dev && !src) { + logMessage(ERROR, "bad arguments to kickstart driver disk command"); + return; + } + + if (usebiosdev != 0) { + p = strchr(dev,'p'); + if (!p){ + logMessage(ERROR, "Bad argument for biospart"); + return; + } + *p = '\0'; + + biospart = getBiosDisk(dev); + if (biospart == NULL) { + logMessage(ERROR, "Unable to locate BIOS dev %s",dev); + return; + } + dev = malloc(strlen(biospart) + strlen(p + 1) + 2); + sprintf(dev, "%s%s", biospart, p + 1); + } + + if (dev) { + return getDDFromDev(loaderData, dev, fstype); + } else { + return getDDFromSource(loaderData, src); + } +} + +static void getDDFromDev(struct loaderData_s * loaderData, char * dev, + char * fstype) { + if (fstype) { + if (doPwMount(dev, "/tmp/drivers", fstype, "ro", NULL)) { + logMessage(ERROR, "unable to mount %s as %s", dev, fstype); + return; + } + } else if (doPwMount(dev, "/tmp/drivers", "vfat", "ro", NULL)) { + if (doPwMount(dev, "/tmp/drivers", "ext2", "ro", NULL)) { + if (doPwMount(dev, "/tmp/drivers", "iso9660", "ro", NULL)) { + logMessage(ERROR, "unable to mount driver disk %s", dev); + return; + } + } + } + + loadFromLocation(loaderData, "/tmp/drivers"); + umount("/tmp/drivers"); + unlink("/tmp/drivers"); +} diff --git a/loader/driverdisk.h b/loader/driverdisk.h new file mode 100644 index 000000000..25a446e88 --- /dev/null +++ b/loader/driverdisk.h @@ -0,0 +1,40 @@ +/* + * driverdisk.h + * + * Copyright (C) 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/>. + */ + +#ifndef DRIVERDISK_H +#define DRIVERDISK_H + +#include "loader.h" +#include "modules.h" +#include "moduleinfo.h" + +int loadDriverFromMedia(int class, struct loaderData_s *loaderData, + int usecancel, int noprobe); + +int loadDriverDisks(int class, struct loaderData_s *loaderData); + +int getRemovableDevices(char *** devNames); + +int chooseManualDriver(int class, struct loaderData_s *loaderData); +void useKickstartDD(struct loaderData_s * loaderData, int argc, + char ** argv); + +void getDDFromSource(struct loaderData_s * loaderData, char * src); + +#endif diff --git a/loader/driverselect.c b/loader/driverselect.c new file mode 100644 index 000000000..28f5ffd42 --- /dev/null +++ b/loader/driverselect.c @@ -0,0 +1,278 @@ +/* + * driverselect.c - functionality for manually selecting drivers + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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> + * Jeremy Katz <katzj@redhat.com> + */ + +#include <ctype.h> +#include <newt.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "modules.h" +#include "moduleinfo.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "lang.h" +#include "driverdisk.h" + +struct sortModuleList { + int index; + moduleInfoSet modInfo; +}; + +static int sortDrivers(const void * a, const void * b) { + const struct sortModuleList * one = a; + const struct sortModuleList * two = b; + + return strcmp(one->modInfo->moduleList[one->index].description, + one->modInfo->moduleList[two->index].description); +} + +static int getManualModuleArgs(struct moduleInfo * mod, char *** moduleArgs) { + newtGrid grid, buttons; + newtComponent text, f, ok, back, entry; + struct newtExitStruct es; + int done = 0, i; + char * buf; + char *argsEntry = ""; + + if (*moduleArgs) { + for (i = 0; (*moduleArgs)[i]; i++) + argsEntry = strcat(argsEntry, (*moduleArgs)[i]); + } + + f = newtForm(NULL, NULL, 0); + if (asprintf(&buf, + _("Please enter any parameters which you wish to pass " + "to the %s module separated by spaces. If you don't " + "know what parameters to supply, skip this screen " + "by pressing the \"OK\" button."), mod->moduleName) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + text = newtTextboxReflowed(-1, -1, buf, 60, 0, 10, 0); + entry = newtEntry(-1, -1, argsEntry, 50, (const char **) &argsEntry, + NEWT_ENTRY_SCROLL); + + newtFormAddHotKey(f, NEWT_KEY_F12); + + buttons = newtButtonBar(_("OK"), &ok, _("Back"), &back, NULL); + + grid = newtCreateGrid(1, 3); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, entry, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + newtGridWrappedWindow(grid, _("Enter Module Parameters")); + newtGridAddComponentsToForm(grid, f, 1); + + do { + newtFormRun(f, &es); + + if (es.reason == NEWT_EXIT_COMPONENT && es.u.co == back) { + done = -1; + } else { + done = 1; + } + } while (done == 0); + + free(buf); + newtGridFree(grid, 1); + + if (done == -1) { + newtFormDestroy(f); + newtPopWindow(); + + return LOADER_BACK; + } + logMessage(INFO, "specified args of %s for %s", argsEntry, mod->moduleName); + + if (strlen(argsEntry) > 0) { + int numAlloced = 5; + char * start; + char * end; + + i = 0; + + *moduleArgs = malloc((numAlloced + 1) * sizeof(*moduleArgs)); + start = argsEntry; + while (start && *start) { + end = start; + while (!isspace(*end) && *end) end++; + *end = '\0'; + (*moduleArgs)[i++] = strdup(start); + start = end + 1; + *end = ' '; + start = strchr(end, ' '); + if (start) start++; + + if (i >= numAlloced) { + numAlloced += 5; + *moduleArgs = realloc(*moduleArgs, + sizeof(*moduleArgs) * (numAlloced + 1)); + } + } + (*moduleArgs)[i] = NULL; + } + + newtFormDestroy(f); + newtPopWindow(); + + return LOADER_OK; +} + +int chooseManualDriver(int class, struct loaderData_s *loaderData) { + int i, numSorted, num = 0, done = 0; + enum driverMajor type; + struct sortModuleList * sortedOrder; + char giveArgs = ' '; + char ** moduleArgs = NULL; + moduleInfoSet modInfo = loaderData->modInfo; + + newtComponent text, f, ok, back, argcheckbox, listbox; + newtGrid grid, buttons; + struct newtExitStruct es; + + if (class == DEVICE_NETWORK) + type = DRIVER_NET; + else if (class == DEVICE_DISK || class == DEVICE_CDROM) + type = DRIVER_SCSI; + else + type = DRIVER_ANY; + + do { + sortedOrder = malloc(sizeof(*sortedOrder) * modInfo->numModules); + numSorted = 0; + + for (i = 0; i < modInfo->numModules; i++) { + sortedOrder[numSorted].index = i; + sortedOrder[numSorted++].modInfo = modInfo; + } + + if (numSorted == 0) { + i = newtWinChoice(_("No drivers found"), _("Load driver disk"), + _("Back"), _("No drivers were found to manually " + "insert. Would you like to use " + "a driver disk?")); + if (i != 1) + return LOADER_BACK; + + loadDriverFromMedia(class, loaderData, 1, 1); + continue; + } else { + break; + } + } while (1); + + qsort(sortedOrder, numSorted, sizeof(*sortedOrder), sortDrivers); + + f = newtForm(NULL, NULL, 0); + + text = newtTextboxReflowed(-1, -1, + _("Please select the driver below which you " + "wish to load. If it does not appear and " + "you have a driver disk, press F2."), + 60, 0, 10, 0); + + listbox = newtListbox(-1, -1, 6, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT); + newtListboxSetWidth(listbox, 55); + + buttons = newtButtonBar(_("OK"), &ok, _("Back"), &back, NULL); + argcheckbox = newtCheckbox(-1, -1, _("Specify optional module arguments"), + giveArgs, NULL, &giveArgs); + + newtFormAddHotKey(f, NEWT_KEY_F2); + newtFormAddHotKey(f, NEWT_KEY_F12); + + for (i = 0; i < numSorted; i++) { + char *buf = NULL; + + if (asprintf(&buf, "%s (%s)", + modInfo->moduleList[sortedOrder[i].index].description, + modInfo->moduleList[sortedOrder[i].index].moduleName) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + newtListboxAppendEntry(listbox, buf, + INT_TO_POINTER(sortedOrder[i].index)); + } + + grid = newtCreateGrid(1, 4); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, listbox, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_COMPONENT, argcheckbox, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + newtGridWrappedWindow(grid, _("Select Device Driver to Load")); + + newtGridAddComponentsToForm(grid, f, 1); + + do { + newtFormRun(f, &es); + + num = POINTER_TO_INT(newtListboxGetCurrent(listbox)); + + if (es.reason == NEWT_EXIT_COMPONENT && es.u.co == back) { + done = -1; + } else if (es.reason == NEWT_EXIT_HOTKEY && es.u.key == NEWT_KEY_F2) { + done = -2; + } else { + if (giveArgs != ' ') { + i = getManualModuleArgs(&(modInfo->moduleList[num]), + &moduleArgs); + if (i == LOADER_BACK) + done = 0; + else + done = 1; + } else { + done = 1; + } + } + } while (done == 0); + + newtGridFree(grid, 1); + newtFormDestroy(f); + newtPopWindow(); + + if (done == -1) + return LOADER_BACK; + if (done == -2) { + loadDriverFromMedia(class, loaderData, 1, 1); + return chooseManualDriver(class, loaderData); + } + + mlLoadModule(modInfo->moduleList[num].moduleName, moduleArgs); + free(sortedOrder); + + return LOADER_OK; +} + + diff --git a/loader/ftp.c b/loader/ftp.c new file mode 100644 index 000000000..ce6deb5d4 --- /dev/null +++ b/loader/ftp.c @@ -0,0 +1,785 @@ +/* + * ftp.c - ftp code + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + * 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> + * Jeremy Katz <katzj@redhat.com> + * David Cantrell <dcantrell@redhat.com> + */ + +#define HAVE_ALLOCA_H 1 +#define HAVE_NETINET_IN_SYSTM_H 1 +#define HAVE_SYS_SOCKET_H 1 + +#if HAVE_ALLOCA_H +# include <alloca.h> +#endif + +#if HAVE_SYS_SOCKET_H +# include <sys/socket.h> +#endif + +#if HAVE_NETINET_IN_SYSTM_H +# include <sys/types.h> +# include <netinet/in_systm.h> +#endif + +#if ! HAVE_HERRNO +extern int h_errno; +#endif + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#define TIMEOUT_SECS 60 +#define BUFFER_SIZE 4096 + +#ifndef IPPORT_FTP +# define IPPORT_FTP 21 +#endif + +#include "ftp.h" +#include "log.h" +#include "net.h" + +static int ftpCheckResponse(int sock, char ** str); +static int ftpCommand(int sock, char * command, ...); +static int getHostAddress(const char * host, void * address, int family); + +static int ftpCheckResponse(int sock, char ** str) { + static char buf[BUFFER_SIZE + 1]; + int bufLength = 0; + fd_set emptySet, readSet; + char * chptr, * start; + struct timeval timeout; + int bytesRead, rc = 0; + int doesContinue = 1; + char errorCode[4]; + + errorCode[0] = '\0'; + + do { + FD_ZERO(&emptySet); + FD_ZERO(&readSet); + FD_SET(sock, &readSet); + + timeout.tv_sec = TIMEOUT_SECS; + timeout.tv_usec = 0; + + rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout); + if (rc < 1) { + if (rc==0) + return FTPERR_BAD_SERVER_RESPONSE; + else + rc = FTPERR_UNKNOWN; + } else { + rc = 0; + } + + bytesRead = read(sock, buf + bufLength, sizeof(buf) - bufLength - 1); + + bufLength += bytesRead; + + buf[bufLength] = '\0'; + + /* divide the response into lines, checking each one to see if + we are finished or need to continue */ + + start = chptr = buf; + + do { + while (*chptr != '\n' && *chptr) chptr++; + + if (*chptr == '\n') { + *chptr = '\0'; + if (*(chptr - 1) == '\r') *(chptr - 1) = '\0'; + if (str) *str = start; + + if (errorCode[0]) { + if (!strncmp(start, errorCode, 3) && start[3] == ' ') + doesContinue = 0; + } else { + strncpy(errorCode, start, 3); + errorCode[3] = '\0'; + if (start[3] != '-') { + doesContinue = 0; + } + } + + start = chptr + 1; + chptr++; + } else { + chptr++; + } + } while (*chptr); + + if (doesContinue && chptr > start) { + memcpy(buf, start, chptr - start - 1); + bufLength = chptr - start - 1; + } else { + bufLength = 0; + } + } while (doesContinue && !rc); + + if (*errorCode == '4' || *errorCode == '5') { + if (!strncmp(errorCode, "421", 3)) { + return FTPERR_TOO_MANY_CONNECTIONS; + } else if (!strncmp(errorCode, "550", 3)) { + return FTPERR_FILE_NOT_FOUND; + } + + return FTPERR_BAD_SERVER_RESPONSE; + } + + if (rc) return rc; + + return 0; +} + +int ftpCommand(int sock, char * command, ...) { + va_list ap; + int len; + char * s; + char * buf; + int rc; + + va_start(ap, command); + len = strlen(command) + 2; + s = va_arg(ap, char *); + while (s) { + len += strlen(s) + 1; + s = va_arg(ap, char *); + } + va_end(ap); + + buf = alloca(len + 1); + + va_start(ap, command); + strcpy(buf, command); + strcat(buf, " "); + s = va_arg(ap, char *); + while (s) { + strcat(buf, s); + strcat(buf, " "); + s = va_arg(ap, char *); + } + va_end(ap); + + buf[len - 2] = '\r'; + buf[len - 1] = '\n'; + buf[len] = '\0'; + + if (write(sock, buf, len) != len) { + return FTPERR_SERVER_IO_ERROR; + } + + if ((rc = ftpCheckResponse(sock, NULL))) + return rc; + + return 0; +} + +static int getHostAddress(const char * host, void * address, int family) { + char *hostname, *port; + struct hostent *hostent; + + splitHostname((char *) host, &hostname, &port); + + if (family == AF_INET) { + if (isdigit(host[0])) { + if (inet_pton(AF_INET, hostname, (struct in_addr *)address) >= 1) { + return 0; + } else { + return FTPERR_BAD_HOST_ADDR; + } + } else { + if ((hostent = gethostbyname(hostname)) != NULL) { + memcpy((struct in_addr *) address, hostent->h_addr_list[0], hostent->h_length); + return 0; + } else { + errno = h_errno; + return FTPERR_BAD_HOSTNAME; + } + } + } else if (family == AF_INET6) { + if (strchr(hostname, ':')) { + if (inet_pton(AF_INET6, hostname, (struct in_addr6 *)address) >= 1) { + return 0; + } else + return FTPERR_BAD_HOST_ADDR; + } else { + if ((hostent = gethostbyname(hostname)) != NULL) { + memcpy((struct in_addr6 *) address, hostent->h_addr_list[0], hostent->h_length); + return 0; + } else { + errno = h_errno; + return FTPERR_BAD_HOSTNAME; + } + } + } else { + return FTPERR_UNSUPPORTED_FAMILY; + } +} + +int ftpOpen(char *host, int family, char *name, char *password, + int port) { + static int sock; + struct in_addr addr; + struct in6_addr addr6; + struct sockaddr_in destPort; + struct sockaddr_in6 destPort6; + struct passwd * pw; + int rc = 0; + + if (port < 0) port = IPPORT_FTP; + + if (!name) + name = "anonymous"; + + if (!password) { + password = "root@"; + if (getuid()) { + pw = getpwuid(getuid()); + if (pw) { + password = alloca(strlen(pw->pw_name) + 2); + strcpy(password, pw->pw_name); + strcat(password, "@"); + } + } + } + + if (family == AF_INET) + rc = getHostAddress(host, &addr, AF_INET); + else if (family == AF_INET6) + rc = getHostAddress(host, &addr6, AF_INET6); + + if (rc) + return rc; + + sock = socket(family, SOCK_STREAM, IPPROTO_IP); + if (sock < 0) { + return FTPERR_FAILED_CONNECT; + } + + if (family == AF_INET) { + destPort.sin_family = family; + destPort.sin_port = htons(port); + destPort.sin_addr = addr; + + if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) { + close(sock); + return FTPERR_FAILED_CONNECT; + } + } else if (family == AF_INET6) { + destPort6.sin6_family = family; + destPort6.sin6_port = htons(port); + destPort6.sin6_addr = addr6; + + if (connect(sock, (struct sockaddr *) &destPort6, sizeof(destPort6))) { + close(sock); + return FTPERR_FAILED_CONNECT; + } + } + + /* ftpCheckResponse() assumes the socket is nonblocking */ + if (fcntl(sock, F_SETFL, O_NONBLOCK)) { + close(sock); + return FTPERR_FAILED_CONNECT; + } + + if ((rc = ftpCheckResponse(sock, NULL))) { + return rc; + } + + if ((rc = ftpCommand(sock, "USER", name, NULL))) { + close(sock); + return rc; + } + + if ((rc = ftpCommand(sock, "PASS", password, NULL))) { + close(sock); + return rc; + } + + if ((rc = ftpCommand(sock, "TYPE", "I", NULL))) { + close(sock); + return rc; + } + + return sock; +} + +/* + * FTP specification: + * RFC 959 FILE TRANSFER PROTOCOL (FTP) + * RFC 2428 FTP Extensions for IPv6 and NATs + */ +int ftpGetFileDesc(int sock, struct in6_addr host, int family, + char * remotename) { + int dataSocket; + struct sockaddr_in dataAddress; + struct sockaddr_in6 dataAddress6; + int i, j; + char * passReply; + char * chptr; + char * retrCommand; + int rc; + + if (family == AF_INET) { + if (write(sock, "PASV\r\n", 6) != 6) { + return FTPERR_SERVER_IO_ERROR; + } + } else if (family == AF_INET6) { + if (write(sock, "EPSV\r\n", 6) != 6) { + return FTPERR_SERVER_IO_ERROR; + } + } + + if ((rc = ftpCheckResponse(sock, &passReply))) { + return FTPERR_PASSIVE_ERROR; + } + + /* get IP address and port number from server response */ + if (family == AF_INET) { + /* we have a PASV response of the form: + * 227 Entering Passive Mode (209,132,176,30,57,229) + * where 209.132.176.30 is the IP, and 57 & 229 are the ports + */ + chptr = passReply; + while (*chptr && *chptr != '(') chptr++; + if (*chptr != '(') { + return FTPERR_PASSIVE_ERROR; + } + chptr++; + passReply = chptr; + while (*chptr && *chptr != ')') chptr++; + if (*chptr != ')') { + return FTPERR_PASSIVE_ERROR; + } + *chptr-- = '\0'; + while (*chptr && *chptr != ',') chptr--; + if (*chptr != ',') { + return FTPERR_PASSIVE_ERROR; + } + chptr--; + while (*chptr && *chptr != ',') chptr--; + if (*chptr != ',') { + return FTPERR_PASSIVE_ERROR; + } + *chptr++ = '\0'; + + /* now passReply points to the IP portion + * and chptr points to the port number portion + */ + if (sscanf(chptr, "%d,%d", &i, &j) != 2) { + return FTPERR_PASSIVE_ERROR; + } + } else if (family == AF_INET6) { + /* we have an EPSV response of the form: + * 229 Entering Extended Passive Mode (|||51626|) + * where 51626 is the port + */ + chptr = passReply; + while (*chptr && *chptr != '(') chptr++; + if (*chptr != '(') { + return FTPERR_PASSIVE_ERROR; + } + chptr++; + while (*chptr && *chptr == '|') chptr++; + passReply = chptr; + while (*chptr && *chptr != '|') chptr++; + *chptr = '\0'; + chptr = passReply; + + /* now chptr contains our port number */ + if (sscanf(chptr, "%d", &i) != 1) { + return FTPERR_PASSIVE_ERROR; + } + } + + /* build our sockaddr */ + if (family == AF_INET) { + dataAddress.sin_family = family; + dataAddress.sin_port = htons((i << 8) + j); + + /* passReply contains the IP address, but with commands insteaad of + * periods, so change those + */ + chptr = passReply; + while (*chptr++) { + if (*chptr == ',') *chptr = '.'; + } + + if (!inet_pton(family, passReply, &dataAddress.sin_addr)) { + return FTPERR_PASSIVE_ERROR; + } + } else if (family == AF_INET6) { + dataAddress6.sin6_family = family; + dataAddress6.sin6_port = htons(i); + + /* we don't get this in an EPSV reply, but we got it as a param */ + memset(&dataAddress6.sin6_addr, 0, sizeof(struct in6_addr)); + memcpy(&dataAddress6.sin6_addr, &host, sizeof(host)); + } + + dataSocket = socket(family, SOCK_STREAM, IPPROTO_IP); + if (dataSocket < 0) { + return FTPERR_FAILED_CONNECT; + } + + retrCommand = alloca(strlen(remotename) + 20); + sprintf(retrCommand, "RETR %s\r\n", remotename); + i = strlen(retrCommand); + + if (write(sock, retrCommand, i) != i) { + return FTPERR_SERVER_IO_ERROR; + } + + if (family == AF_INET) { + if (connect(dataSocket, (struct sockaddr *) &dataAddress, + sizeof(dataAddress))) { + close(dataSocket); + return FTPERR_FAILED_DATA_CONNECT; + } + } else if (family == AF_INET6) { + if (connect(dataSocket, (struct sockaddr *) &dataAddress6, + sizeof(dataAddress6))) { + close(dataSocket); + return FTPERR_FAILED_DATA_CONNECT; + } + } + + if ((rc = ftpCheckResponse(sock, NULL))) { + close(dataSocket); + return rc; + } + + return dataSocket; +} + +int ftpGetFileDone(int sock) { + if (ftpCheckResponse(sock, NULL)) { + return FTPERR_BAD_SERVER_RESPONSE; + } + + return 0; +} + +const char *ftpStrerror(int errorNumber, urlprotocol protocol) { + switch (errorNumber) { + case FTPERR_PERMISSION_DENIED: + return(protocol == URL_METHOD_FTP ? "FTP permission denied" : + "HTTP permission denied"); + + case FTPERR_BAD_SERVER_RESPONSE: + return(protocol == URL_METHOD_FTP ? "Bad FTP server response" : + "Bad HTTP server response"); + + case FTPERR_SERVER_IO_ERROR: + return(protocol == URL_METHOD_FTP ? "FTP IO error" : + "HTTP IO error"); + + case FTPERR_SERVER_TIMEOUT: + return(protocol == URL_METHOD_FTP ? "FTP server timeout" : + "HTTP server timeout"); + + case FTPERR_BAD_HOST_ADDR: + return(protocol == URL_METHOD_FTP ? + "Unable to lookup FTP server host address" : + "Unable to lookup HTTP server host address"); + + case FTPERR_BAD_HOSTNAME: + return(protocol == URL_METHOD_FTP ? + "Unable to lookup FTP server host name" : + "Unable to lookup HTTP server host name"); + + case FTPERR_FAILED_CONNECT: + return(protocol == URL_METHOD_FTP ? + "Failed to connect to FTP server" : + "Failed to connect to HTTP server"); + + case FTPERR_FAILED_DATA_CONNECT: + return(protocol == URL_METHOD_FTP ? + "Failed to establish data connection to FTP server" : + "Failed to establish data connection to HTTP server"); + + case FTPERR_FILE_IO_ERROR: + return("IO error to local file"); + + case FTPERR_PASSIVE_ERROR: + return("Error setting remote server to passive mode"); + + case FTPERR_FILE_NOT_FOUND: + return("File not found on server"); + + case FTPERR_TOO_MANY_CONNECTIONS: + return(protocol == URL_METHOD_FTP ? + "Too many connections to FTP server" : + "Too many connections to HTTP server"); + + case FTPERR_UNSUPPORTED_FAMILY: + return(protocol == URL_METHOD_FTP ? + "Unsupported address family on FTP server" : + "Unsupported address family on HTTP server"); + + case FTPERR_UNKNOWN: + default: + return("Unknown or unexpected error"); + } +} + +static int read_headers (char **headers, fd_set *readSet, int sock) +{ + char *nextChar; + struct timeval timeout; + int n = 4096; + int rc; + + *headers = malloc(n); + nextChar = *headers; + + *nextChar = '\0'; + while (!strstr(*headers, "\r\n\r\n")) { + FD_ZERO(readSet); + FD_SET(sock, readSet); + + timeout.tv_sec = TIMEOUT_SECS; + timeout.tv_usec = 0; + + rc = select(sock + 1, readSet, NULL, NULL, &timeout); + if (rc == 0) { + close(sock); + free(*headers); + *headers = NULL; + return FTPERR_SERVER_TIMEOUT; + } else if (rc < 0) { + close(sock); + free(*headers); + *headers = NULL; + return FTPERR_SERVER_IO_ERROR; + } + + if (read(sock, nextChar, 1) != 1) { + close(sock); + free(*headers); + *headers = NULL; + return FTPERR_SERVER_IO_ERROR; + } + + nextChar++; + *nextChar = '\0'; + + if (nextChar - *headers == n) { + n += 4096; + *headers = realloc (*headers, sizeof(**headers)*n); + } + } + + return 0; +} + +static char *find_header (char *headers, char *to_find) +{ + char *start, *end, *searching_for, *retval; + + if (asprintf(&searching_for, "\r\n%s:", to_find) == -1) + return NULL; + + if ((start = strstr(headers, searching_for)) == NULL) { + free(searching_for); + return NULL; + } + + /* Trim off what we were searching for so we only return the value. */ + start += strlen(searching_for); + free(searching_for); + while (isspace(*start) && *start) start++; + + if (start == NULL) + return NULL; + + /* Now find the end of the header. */ + end = strstr (start, "\r\n"); + + if (end == NULL) + return NULL; + + retval = strndup (start, end-start); + return retval; +} + +static char *find_status_code (char *headers) +{ + char *start, *end, *retval; + + start = headers; + + /* Skip ahead to the first whitespace in the header. */ + while (!isspace(*start) && *start) start++; + if (start == NULL) + return NULL; + + /* Now skip over the whitespace. What's next is the status code number, + * followed by a text description of the code. + */ + while (isspace(*start) && *start) start++; + if (start == NULL) + return NULL; + + if ((end = strstr (start, "\r\n")) == NULL) + return NULL; + + retval = strndup (start, end-start); + return retval; +} + +/* extraHeaders is either NULL or a string with extra headers separated + * by '\r\n', ending with '\r\n'. + */ +int httpGetFileDesc(char * hostname, int port, char * remotename, + char *extraHeaders) { + char * buf, *headers = NULL; + char *status; + char *hstr; + int family; + struct in_addr addr; + struct in6_addr addr6; + int sock; + int rc; + struct sockaddr_in destPort; + struct sockaddr_in6 destPort6; + fd_set readSet; + + if (port < 0) + port = 80; + + family = AF_INET; + rc = getHostAddress(hostname, &addr, family); + if (rc) { + family = AF_INET6; + rc = getHostAddress(hostname, &addr6, family); + if (rc) + return rc; + } + + sock = socket(family, SOCK_STREAM, IPPROTO_IP); + if (sock < 0) { + return FTPERR_FAILED_CONNECT; + } + + if (family == AF_INET) { + destPort.sin_family = family; + destPort.sin_port = htons(port); + destPort.sin_addr = addr; + + if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) { + close(sock); + return FTPERR_FAILED_CONNECT; + } + } else if (family == AF_INET6) { + destPort6.sin6_family = family; + destPort6.sin6_port = htons(port); + destPort6.sin6_addr = addr6; + + if (connect(sock, (struct sockaddr *) &destPort6, sizeof(destPort6))) { + close(sock); + return FTPERR_FAILED_CONNECT; + } + } + + if (extraHeaders) + hstr = extraHeaders; + else + hstr = ""; + + buf = alloca(strlen(remotename) + strlen(hostname) + strlen(hstr) + 25); + sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n%s\r\n", remotename, hostname, hstr); + rc = write(sock, buf, strlen(buf)); + + rc = read_headers (&headers, &readSet, sock); + + if (rc < 0) + return rc; + + status = find_status_code (headers); + + if (status == NULL) { + close(sock); + if (status) free(status); + if (headers) free(headers); + return FTPERR_SERVER_IO_ERROR; + } else if (!strncmp(status, "200", 3)) { + if (status) free(status); + if (headers) free(headers); + return sock; + } else if (!strncmp(status, "301", 3) || !strncmp(status, "302", 3) || + !strncmp(status, "303", 3) || !strncmp(status, "307", 3)) { + struct iurlinfo ui; + char *redir_loc = find_header (headers, "Location"); + int retval; + + if (status) free(status); + if (headers) free(headers); + + if (redir_loc == NULL) { + logMessage(WARNING, "got a redirect response, but Location header is NULL"); + close(sock); + return FTPERR_FILE_NOT_FOUND; + } + + logMessage(INFO, "redirecting to %s", redir_loc); + convertURLToUI(redir_loc, &ui); + retval = httpGetFileDesc (ui.address, -1, ui.prefix, extraHeaders); + free(redir_loc); + return retval; + } else if (!strncmp(status, "403", 3)) { + if (status) free(status); + if (headers) free(headers); + close(sock); + return FTPERR_PERMISSION_DENIED; + } else if (!strncmp(status, "404", 3)) { + if (status) free(status); + if (headers) free(headers); + close(sock); + return FTPERR_FILE_NOT_FOUND; + } else { + if (status) free(status); + if (headers) free(headers); + close(sock); + logMessage(ERROR, "bad HTTP response code: %s", status); + return FTPERR_BAD_SERVER_RESPONSE; + } +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/ftp.h b/loader/ftp.h new file mode 100644 index 000000000..c1e7fcb87 --- /dev/null +++ b/loader/ftp.h @@ -0,0 +1,53 @@ +/* + * ftp.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_FTP +#define H_FTP + +#include "urls.h" + +const char * ftpStrerror(int ftpErrno, urlprotocol protocol); + +#define FTPERR_BAD_SERVER_RESPONSE -1 +#define FTPERR_SERVER_IO_ERROR -2 +#define FTPERR_SERVER_TIMEOUT -3 +#define FTPERR_BAD_HOST_ADDR -4 +#define FTPERR_BAD_HOSTNAME -5 +#define FTPERR_FAILED_CONNECT -6 +#define FTPERR_FILE_IO_ERROR -7 +#define FTPERR_PASSIVE_ERROR -8 +#define FTPERR_FAILED_DATA_CONNECT -9 +#define FTPERR_FILE_NOT_FOUND -10 +#define FTPERR_TOO_MANY_CONNECTIONS -11 +#define FTPERR_BAD_URL -12 +#define FTPERR_TOO_MANY_REDIRECTS -13 +#define FTPERR_UNSUPPORTED_FAMILY -14 +#define FTPERR_PERMISSION_DENIED -15 +#define FTPERR_UNKNOWN -100 + +int ftpOpen(char * host, int family, char * name, char * password, + int port); +int ftpGetFile(int sock, char * remotename, int dest); +int ftpGetFileDesc(int sock, struct in6_addr host, int family, + char * remotename); +int ftpGetFileDone(int sock); + +int httpGetFileDesc(char * hostname, int port, char * remotename, char *extraHeaders); + +#endif diff --git a/loader/fwloader.c b/loader/fwloader.c new file mode 100644 index 000000000..2ab8c7fb4 --- /dev/null +++ b/loader/fwloader.c @@ -0,0 +1,680 @@ +/* + * fwloader.c -- a small firmware loader. + * + * Copyright (C) 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): Peter Jones (pjones@redhat.com) + */ + +#define _GNU_SOURCE 1 + +#include <argz.h> +#include <envz.h> +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <asm/types.h> +#include <linux/netlink.h> + +#include "loader.h" +#include "fwloader.h" +#include "udelay.h" +#include "log.h" + +#ifndef FWDEBUG +#define logMessage(x, ...) +#endif + +struct fw_loader { + int netlinkfd; + sigset_t sigmask; + char *fw_pathz; + size_t fw_pathz_len; + struct pollfd *fds; +}; + +int done = 0; + +static inline int set_fd_coe(int fd, int enable) +{ + int rc; + long flags = 0; + + rc = fcntl(fd, F_GETFD, &flags); + if (rc < 0) + return rc; + + if (enable) + flags |= FD_CLOEXEC; + else + flags &= ~FD_CLOEXEC; + + rc = fcntl(fd, F_SETFD, flags); + return rc; +} + +static int open_uevent_socket(struct fw_loader *fwl) +{ + int fd, rc; + struct sockaddr_nl sa; + + fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (fd < 0) + return -1; + set_fd_coe(fd, 1); + + memset(&sa, '\0', sizeof (sa)); + sa.nl_family = AF_NETLINK; + sa.nl_pid = getpid(); + sa.nl_groups = -1; + + if (bind(fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) { + close(fd); + return -1; + } + + fwl->netlinkfd = fd; + + fd = open("/proc/sys/kernel/hotplug", O_RDWR); + if (fd >= 0) { + rc = ftruncate(fd, 0); + rc = write(fd, "\n", 1); + close(fd); + } + + fd = open("/sys/class/firmware/timeout", O_RDWR); + if (fd >= 0) { + rc = write(fd, "10", 2); + close(fd); + } + + return 0; +} + +extern void loaderSegvHandler(int signum); + +static void kill_hotplug_signal(int signum) +{ + signal(signum, kill_hotplug_signal); + logMessage(DEBUGLVL, "fwloader: got exit signal, quitting"); + done = 1; +} + +static int daemonize(struct fw_loader *fwl) +{ + int fd; + int rc; + +#if 0 + int pid; + pid = fork(); + if (pid != 0) + return pid; +#endif + + signal(SIGTERM, kill_hotplug_signal); + signal(SIGSEGV, loaderSegvHandler); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + + sigfillset(&fwl->sigmask); + sigdelset(&fwl->sigmask, SIGTERM); + sigdelset(&fwl->sigmask, SIGSEGV); + sigemptyset(&fwl->sigmask); + + prctl(PR_SET_NAME, "hotplug", 0, 0, 0); + rc = chdir("/"); + + fd = open("/proc/self/oom_adj", O_RDWR); + if (fd >= 0) { + rc = write(fd, "-17", 3); + close(fd); + } + + for (fd = 0; fd < getdtablesize(); fd++) { + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) + continue; + if (fd == tty_logfd || fd == file_logfd) + continue; + close(fd); + } + + setsid(); + fd = open("/dev/null", O_RDONLY); + close(STDIN_FILENO); + dup2(fd, STDIN_FILENO); + set_fd_coe(STDIN_FILENO, 1); + close(fd); + fd = open("/dev/null", O_WRONLY); + close(STDOUT_FILENO); + dup2(fd, STDOUT_FILENO); + set_fd_coe(STDOUT_FILENO, 1); + close(STDERR_FILENO); + dup2(fd, STDERR_FILENO); + set_fd_coe(STDERR_FILENO, 1); + close(fd); + + logMessage(DEBUGLVL, "fwloader: starting up (pid %d)", getpid()); + return 0; +} + +struct uevent { + char *msg; + char *path; + char *envz; + size_t envz_len; +}; + +static int get_netlink_msg(struct fw_loader *fwl, struct uevent *uevent) +{ + size_t len; + ssize_t size; + static char buffer[2560]; + char *pos; + char *msg = NULL, *path = NULL, *envz = NULL; + char *argv[] = { NULL }; + size_t envz_len; + error_t errnum; + + size = recv(fwl->netlinkfd, &buffer, sizeof (buffer), 0); + if (size < 0) + return -1; + + if ((size_t)size > sizeof (buffer) - 1) + size = sizeof (buffer) - 1; + buffer[size] = '\0'; + + len = strcspn(buffer, "@"); + if (!buffer[len]) + return -1; + + if ((errnum = argz_create(argv, &envz, &envz_len)) > 0) + goto err; + + pos = buffer; + msg = strndup(pos, len++); + pos += len; + path = strdup(pos); + + pos += strlen(pos) + 1; + if (len < size + 1) { + while (pos[0]) { + char *value = strchr(pos, '='); + if (value) + *(value++) = '\0'; + + if ((errnum = envz_add(&envz, &envz_len, pos, value)) > 0) + goto err; + pos += strlen(pos) + 1; + if (*pos) + pos += strlen(pos) + 1; + } + } + + uevent->msg = msg; + uevent->path = path; + uevent->envz = envz; + uevent->envz_len = envz_len; + return 0; +err: + if (msg) + free(msg); + if (path) + free(path); + while(envz) + argz_delete(&envz, &envz_len, envz); + errno = errnum; + return -1; +} + +/* Set the 'loading' attribute for a firmware device. + * 1 == currently loading + * 0 == done loading + * -1 == error + */ +static int +get_loading_fd(const char *device) +{ + int fd = -1; + char *loading_path = NULL; + + if (asprintf(&loading_path, "%s/loading", device) < 0) { + logMessage(ERROR, "fwloader: device %s: asprintf: %m", device); + return -1; + } + logMessage(DEBUGLVL, "fwloader: looking for loading file at %s", loading_path); + fd = open(loading_path, O_RDWR | O_SYNC ); + if (fd < 0) + logMessage(ERROR, "fwloader: open %s: %m", loading_path); + free(loading_path); + return fd; +} + +static int +set_loading(int fd, int value) +{ + int rc = 0; + + if (value == -1) + rc = write(fd, "-1", 3); + else if (value == 0) + rc = write(fd, "0", 2); + else if (value == 1) + rc = write(fd, "1", 2); + fsync(fd); + fdatasync(fd); + + return rc < 0 ? rc : 0; +} + +static int +fd_map(int fd, char **buf, size_t *bufsize) +{ + struct stat stats; + int en = 0; + + if (fstat(fd, &stats) < 0) { + en = errno; + close(fd); + errno = en; + return -1; + } + + *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (*buf == MAP_FAILED) { + *buf = NULL; + en = errno; + close(fd); + errno = en; + return -1; + } + *bufsize = stats.st_size; + return 0; +} + +static int +file_map(const char *filename, char **buf, size_t *bufsize, int flags) +{ + int fd, en, rc = 0; + + if ((fd = open(filename, flags ? flags : O_RDONLY)) < 0) + return -1; + + if (fd_map(fd, buf, bufsize) < 0) + rc = -1; + + en = errno; + close(fd); + errno = en; + + return rc; +} + +static void +file_unmap(void *buf, size_t bufsize) +{ + munmap(buf, bufsize); +} + +static int +fetcher(char *inpath, int outfd) +{ + char *inbuf = NULL; + size_t inlen; + int count; + int en = 0; + int rc; + + errno = 0; + if (access(inpath, F_OK)) + goto out; + + if (file_map(inpath, &inbuf, &inlen, O_RDONLY) < 0) + goto out; + + lseek(outfd, 0, SEEK_SET); + rc = ftruncate(outfd, 0); + rc = ftruncate(outfd, inlen); + + count = 0; + while (count < inlen) { + ssize_t c; + c = write(outfd, inbuf + count, inlen - count); + if (c <= 0) + goto out; + count += c; + } + +out: + en = errno; + if (inbuf) + file_unmap(inbuf, inlen); + if (en) { + errno = en; + return -1; + } + return 0; +} + + +static int +_load_firmware(struct fw_loader *fwl, int fw_fd, char *sysdir, int timeout) +{ + int rc = 0; + char *fw_buf = NULL, *data = NULL; + size_t fw_len = 0; + int dfd = -1, lfd = -1; + int loading = -2; + size_t count; + + logMessage(DEBUGLVL, "fwloader: waiting for firmware dir at %s", sysdir); + timeout *= 1000000; + while (access(sysdir, F_OK) && timeout) { + udelay(100); + timeout -= 100; + } + if (!timeout) { + logMessage(ERROR, "fwloader: never found firmware dir at %s", sysdir); + return -ENOENT; + } + + if ((lfd = get_loading_fd(sysdir)) < 0) + return lfd; + + set_loading(lfd, 1); + loading = -1; + + if (fd_map(fw_fd, &fw_buf, &fw_len) < 0) { + rc = -errno; + goto out; + } + + if (asprintf(&data, "%s/data", sysdir) < 0) { + rc = -errno; + goto out; + } + if ((dfd = open(data, O_RDWR)) < 0) { + rc = -errno; + goto out; + } + count = 0; + while (count < fw_len) { + ssize_t c; + if ((c = write(dfd, fw_buf + count, fw_len - count)) <= 0) + goto out; + count += c; + } + loading = 0; + +out: + if (dfd >= 0) + close(dfd); + if (fw_buf) + file_unmap(fw_buf, fw_len); + if (loading != -2) + set_loading(lfd, loading); + if (lfd >= 0) + close(lfd); + if (data) + free(data); + + return rc; +} + +static void load_firmware(struct fw_loader *fwl, struct uevent *uevent) +{ + char *devpath = NULL, *firmware = NULL, *timeout; + char *fw_file = NULL, *sys_file = NULL; + char *entry; + int timeout_secs; + char *tempfile; + int fd = -1; + + tempfile = strdup("/tmp/fw-XXXXXX"); + fd = mkstemp(tempfile); + if (fd < 0) { + logMessage(ERROR, "fwloader: mkstemp(\"%s\") failed: %m", tempfile); + free(tempfile); + return; + } + unlink(tempfile); + free(tempfile); + + devpath = envz_get(uevent->envz, uevent->envz_len, "DEVPATH"); + firmware = envz_get(uevent->envz, uevent->envz_len, "FIRMWARE"); + timeout = envz_get(uevent->envz, uevent->envz_len, "TIMEOUT"); + + if (!devpath || !firmware) { + argz_stringify(uevent->envz, uevent->envz_len, ' '); + logMessage(ERROR, "fwloader: environment: %s", uevent->envz); + return; + } + + errno = 0; + timeout_secs = strtol(timeout, NULL, 10); + + if ((errno == ERANGE && (timeout_secs == LONG_MIN || + timeout_secs == LONG_MAX)) || + (errno != 0 && timeout_secs == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + /* find the file */ + for (entry = fwl->fw_pathz; entry; + entry = argz_next(fwl->fw_pathz, fwl->fw_pathz_len, entry)) { + if (asprintf(&fw_file, "%s/%s", entry, firmware) < 0) + return; + + logMessage(INFO, "fwloader: trying to find %s at %s", firmware, fw_file); + + if (fetcher(fw_file, fd) >= 0) + break; + + free(fw_file); + fw_file = NULL; + if (errno == ENOENT || errno == EPERM) + continue; + break; + } + if (!fw_file) + goto out; + + if (asprintf(&sys_file, "/sys%s/", devpath) < 0) + goto out; + + _load_firmware(fwl, fd, sys_file, timeout_secs); + +out: + if (fw_file) + free(fw_file); + if (sys_file) + free(sys_file); + if (fd != -1) + close(fd); +} + +static void handle_single_uevent(struct fw_loader *fwl, struct uevent *uevent) +{ + char *action = NULL; + char *subsystem = NULL; + + action = envz_get(uevent->envz, uevent->envz_len, "ACTION"); + subsystem = envz_get(uevent->envz, uevent->envz_len, "SUBSYSTEM"); + + logMessage(DEBUGLVL, "fwloader: subsystem %s got action %s", subsystem, action); + if (!strcmp(action, "add") && !strcmp(subsystem, "firmware")) + load_firmware(fwl, uevent); +} + +static void handle_events(struct fw_loader *fwl) +{ + int rc; + struct uevent uevent; + if (fwl->fds == NULL) + fwl->fds = calloc(1, sizeof (struct pollfd)); + + do { + do { + if (done) + exit(0); + fwl->fds[0].events = POLLIN | POLLPRI; + fwl->fds[0].revents = 0; + fwl->fds[0].fd = fwl->netlinkfd; + + //logMessage(DEBUGLVL, "fwloader: polling on netlink socket"); + errno = 0; + rc = poll(fwl->fds, 1, -1); + //logMessage(DEBUGLVL, "fwloader: poll returned %d", rc); + + if (done) + exit(0); + } while (rc < 1 || (rc < 0 && errno == EINTR)); + + memset(&uevent, '\0', sizeof (uevent)); + if (get_netlink_msg(fwl, &uevent) < 0) + continue; + + handle_single_uevent(fwl, &uevent); + } while (1); + + if (fwl->fds) { + free(fwl->fds); + fwl->fds = NULL; + } +} + +void set_fw_search_path(struct loaderData_s *loaderData, char *path) +{ + char *old = loaderData->fw_search_pathz, *new = NULL; + size_t old_len = loaderData->fw_search_pathz_len; + + loaderData->fw_search_pathz = NULL; + loaderData->fw_search_pathz_len = -1; + if (!path) { + if (old) + free(old); + return; + } + + if ((new = strdup(path)) == NULL) + goto out; + + loaderData->fw_search_pathz = NULL; + loaderData->fw_search_pathz_len = 0; + if (argz_create_sep(new, ':', &loaderData->fw_search_pathz, + &loaderData->fw_search_pathz_len) != 0) + goto out; + + if (old) + free(old); + + return; +out: + if (new) + free(new); + loaderData->fw_search_pathz = old; + loaderData->fw_search_pathz_len = old_len; + + return; +} + +void add_fw_search_dir(struct loaderData_s *loaderData, char *dir) +{ + argz_add(&loaderData->fw_search_pathz, &loaderData->fw_search_pathz_len, + dir); +} + +void do_fw_loader(struct loaderData_s *loaderData) +{ + struct fw_loader fwl; + int rc; + + memset(&fwl, '\0', sizeof (fwl)); + fwl.netlinkfd = -1; + + fwl.fw_pathz = loaderData->fw_search_pathz; + fwl.fw_pathz_len = loaderData->fw_search_pathz_len; + + logMessage(INFO, "fwloader: starting firmware loader"); + + rc = daemonize(&fwl); + if (rc < 0) { + logMessage(ERROR, "fwloader: daemonize() failed with %d: %m", rc); + exit(1); + } + + if (open_uevent_socket(&fwl) < 0) { + logMessage(ERROR, "fwloader: open_uevent_socket() failed: %m"); + exit(1); + } + + logMessage(DEBUGLVL, "fwloader: entering event loop"); + handle_events(&fwl); + + exit(1); +} + + +void start_fw_loader(struct loaderData_s *loaderData) { + pid_t loader; + + loader = fork(); + if (loader > 0) + loaderData->fw_loader_pid = loader; + if (loader != 0) + return; + + do_fw_loader(loaderData); +} + +void stop_fw_loader(struct loaderData_s *loaderData) { + int x = 0, rc; + siginfo_t siginfo; + if (loaderData->fw_loader_pid > 0) + kill(loaderData->fw_loader_pid, SIGTERM); + while (x <= 100) { + if (x > 90) + kill(loaderData->fw_loader_pid, SIGKILL); + memset(&siginfo, '\0', sizeof (siginfo)); + rc = waitid(P_PID, loaderData->fw_loader_pid, &siginfo, WNOHANG|WEXITED); + if (rc < 0 && errno == ECHILD) + return; + else if (rc == 0 && siginfo.si_pid != 0) + return; + else if (rc == 0) + x++; + usleep(10000); + } + return; +} + + +/* + * vim:ts=8:sw=4:sts=4:et + */ diff --git a/loader/fwloader.h b/loader/fwloader.h new file mode 100644 index 000000000..e0b0fe8a2 --- /dev/null +++ b/loader/fwloader.h @@ -0,0 +1,35 @@ +/* + * fwloader.h -- a small firmware loader. + * + * Copyright (C) 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): Peter Jones <pjones@redhat.com> + */ + +#ifndef FWLOADER_H +#define FWLOADER_H 1 + +#include "loader.h" + +extern void set_fw_search_path(struct loaderData_s *loaderData, char *path); +extern void add_fw_search_dir(struct loaderData_s *loaderData, char *dir); +extern void start_fw_loader(struct loaderData_s *loaderData); +extern void stop_fw_loader(struct loaderData_s *loaderData); + +#endif /* FWLOADER_H */ +/* + * vim:ts=8:sw=4:sts=4:et + */ diff --git a/loader/getparts.c b/loader/getparts.c new file mode 100644 index 000000000..0fbae76c5 --- /dev/null +++ b/loader/getparts.c @@ -0,0 +1,176 @@ +/* + * getparts.c - functions associated with getting partitions for a disk + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 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): Michael Fulbright <msf@redhat.com> + * Jeremy Katz <katzj@redhat.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> + +#include "log.h" + +/* see if this is a partition name or not */ +static int isPartitionName(char *pname) { + + /* if it doesnt start with a alpha its not one */ + if (!isalpha(*pname) || strstr(pname, "ram")) + return 0; + + /* if it has a '/' in it then treat it specially */ + if (strchr(pname, '/') && !strstr(pname, "iseries") && + !strstr(pname, "i2o")) { + /* assume its either a /dev/ida/ or /dev/cciss device */ + /* these have form of c?d?p? if its a partition */ + return strchr(pname, 'p') != NULL; + } else { + /* if it ends with a digit we're ok */ + return isdigit(pname[strlen(pname)-1]); + } +} + +/* return NULL terminated array of pointers to names of partitons in + * /proc/partitions + */ +char **getPartitionsList(char * disk) { + FILE *f; + int numfound = 0; + char **rc=NULL; + + f = fopen("/proc/partitions", "r"); + if (!f) { + logMessage(ERROR, "getPartitionsList: could not open /proc/partitions"); + return NULL; + } + + /* read through /proc/partitions and parse out partitions */ + while (1) { + char *tmpptr, *pptr; + char tmpstr[4096]; + + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + if (tmpptr) { + char *a, *b; + int toknum = 0; + + a = tmpstr; + while (1) { + b = strsep(&a, " \n"); + + /* if no fields left abort */ + if (!b) + break; + + /* if field was empty means we hit another delimiter */ + if (!*b) + continue; + + /* make sure this is a valid partition line, should start */ + /* with a numeral */ + if (toknum == 0) { + if (!isdigit(*b)) + break; + } else if (toknum == 2) { + /* if size is exactly 1 then ignore it as an extended */ + if (!strcmp(b, "1")) + break; + } else if (toknum == 3) { + /* this should be the partition name */ + /* now we need to see if this is the block device or */ + /* actually a partition name */ + if (!isPartitionName(b)) + break; + + /* make sure that either we don't care about the disk + * or it's this one */ + if ((disk != NULL) && (strncmp(disk, b, strlen(disk)))) + break; + + /* we found a partition! */ + pptr = (char *) malloc(strlen(b) + 7); + sprintf(pptr, "/dev/%s", b); + + if (!rc) { + rc = (char **) malloc(2*sizeof(char *)); + rc[0] = pptr; + rc[1] = NULL; + } else { + int idx; + + rc = (char **) realloc(rc, (numfound+2)*sizeof(char *)); + idx = 0; + while (idx < numfound) { + if (strcmp(pptr, rc[idx]) < 0) + break; + + idx++; + } + + /* move existing out of way if necessary */ + if (idx != numfound) + memmove(rc+idx+1, rc+idx, (numfound-idx)*sizeof(char *)); + + rc[idx] = pptr; + rc[numfound+1] = NULL; + } + numfound++; + break; + } + toknum++; + } + } else { + break; + } + } + + fclose(f); + + return rc; +} + +/* returns length of partitionlist */ +int lenPartitionsList(char **list) { + char **part; + int rc; + + if (!list) return 0; + for (rc = 0, part = list; *part; rc++, part++); + + return rc; +} + +/* frees partition list */ +void freePartitionsList(char **list) { + char **part; + + if (!list) + return; + + for (part = list; *part; part++) + if (*part) + free(*part); + + free(list); +} diff --git a/loader/getparts.h b/loader/getparts.h new file mode 100644 index 000000000..b672a77fd --- /dev/null +++ b/loader/getparts.h @@ -0,0 +1,27 @@ +/* + * getparts.h + * + * Copyright (C) 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/>. + */ + +#ifndef GETPARTS_H +#define GETPARTS_H + +char **getPartitionsList(char * disk); +int lenPartitionsList(char **list); +void freePartitionsList(char **list); + +#endif diff --git a/loader/hardware.c b/loader/hardware.c new file mode 100644 index 000000000..202c64187 --- /dev/null +++ b/loader/hardware.c @@ -0,0 +1,201 @@ +/* + * hardware.c - various hardware probing functionality + * + * 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 <errno.h> +#include <fcntl.h> +#include <popt.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/wait.h> + +#include "loader.h" +#include "hardware.h" +#include "log.h" + +/* FIXME: for turning off dma */ +#include <sys/ioctl.h> +#include <linux/hdreg.h> +#include "../isys/isys.h" + +/* boot flags */ +extern uint64_t flags; + +static int detectHardware() { + int child, rc, status; + int timeout = 0; /* FIXME: commandline option for this */ + + fprintf(stderr, "detecting hardware...\n"); + logMessage(DEBUGLVL, "probing buses"); + + if (!(child = fork())) { + int fd = open("/dev/tty3", O_RDWR); + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + + rc = execl("/sbin/udevadm", "udevadm", "trigger", NULL); + _exit(1); + } + + waitpid(child, &status, 0); + if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) { + rc = 1; + } else { + rc = 0; + } + + fprintf(stderr, "waiting for hardware to initialize...\n"); + logMessage(DEBUGLVL, "waiting for hardware to initialize"); + + if (!(child = fork())) { + char *args[] = { "/sbin/udevsettle", "udevsettle", NULL, NULL }; + int fd = open("/dev/tty3", O_RDWR); + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + + if (timeout) { + if (asprintf(&args[2],"--timeout=%d",timeout) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + rc = execv("/sbin/udevsettle",args); + _exit(1); + } + + waitpid(child, &status, 0); + if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) { + rc = 1; + } else { + rc = 0; + } + if (rc) { + return LOADER_ERROR; + } + return LOADER_OK; +} + +/* 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. + */ +int earlyModuleLoad(int justProbe) { + int fd, len, i; + char buf[1024], *cmdLine; + int argc; + char ** argv; + + /* FIXME: reparsing /proc/cmdline to avoid major loader changes. + * should probably be done in loader.c:parseCmdline() like everything + * else + */ + if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) return 1; + len = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (len <= 0) return 1; + + buf[len] = '\0'; + cmdLine = buf; + + if (poptParseArgvString(cmdLine, &argc, (const char ***) &argv)) + return 1; + + for (i=0; i < argc; i++) { + if (!strncasecmp(argv[i], "driverload=", 11)) { + logMessage(INFO, "loading %s early", argv[i] + 11); + mlLoadModuleSet(argv[i] + 11); + } + } + return 0; +} + +int busProbe(int justProbe) { + /* autodetect whatever we can */ + if (justProbe) + return 0; + return detectHardware(); +} + +/* check if the system has been booted with dasd parameters */ +/* These parameters define the order in which the DASDs */ +/* are visible to Linux. Otherwise load dasd modules probeonly, */ +/* then parse proc to find active DASDs */ +/* Reload dasd_mod with correct range of DASD ports */ +void dasdSetup() { +#if !defined(__s390__) && !defined(__s390x__) + return; +#else + char **dasd_parms; + char *line; + char *parms = NULL, *parms_end; + FILE *fd; + + dasd_parms = malloc(sizeof(*dasd_parms) * 2); + dasd_parms[0] = NULL; + dasd_parms[1] = NULL; + + fd = fopen ("/tmp/dasd_ports", "r"); + if(fd) { + line = (char *)malloc(sizeof(char) * 200); + while (fgets (line, 199, fd) != NULL) { + if((parms = strstr(line, "dasd=")) || + (parms = strstr(line, "DASD="))) { + strncpy(parms, "dasd", 4); + parms_end = parms; + while(*parms_end && !(isspace(*parms_end))) parms_end++; + *parms_end = '\0'; + break; + } + } + fclose(fd); + if (strlen(parms) > 5) + dasd_parms[0] = strdup(parms); + free(line); + } + if(dasd_parms[0]) { + mlLoadModule("dasd_mod", dasd_parms); + + mlLoadModuleSet("dasd_diag_mod:dasd_fba_mod:dasd_eckd_mod"); + free(dasd_parms); + return; + } else { + dasd_parms[0] = "dasd=autodetect"; + mlLoadModule("dasd_mod", dasd_parms); + mlLoadModuleSet("dasd_diag_mod:dasd_fba_mod:dasd_eckd_mod"); + free(dasd_parms); + } +#endif +} diff --git a/loader/hardware.h b/loader/hardware.h new file mode 100644 index 000000000..d49980d99 --- /dev/null +++ b/loader/hardware.h @@ -0,0 +1,30 @@ +/* + * hardware.h + * + * Copyright (C) 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/>. + */ + +#ifndef LOADERHW_H +#define LOADERHW_H + +#include "modules.h" + +int earlyModuleLoad(int justProbe); +int busProbe(int justProbe); + +void dasdSetup(); + +#endif diff --git a/loader/hdinstall.c b/loader/hdinstall.c new file mode 100644 index 000000000..3e56e0d02 --- /dev/null +++ b/loader/hdinstall.c @@ -0,0 +1,519 @@ +/* + * hdinstall.c - code to set up hard drive 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 <errno.h> +#include <fcntl.h> +#include <newt.h> +#include <popt.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <unistd.h> + +#include "driverdisk.h" +#include "hdinstall.h" +#include "getparts.h" +#include "kickstart.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "lang.h" +#include "modules.h" +#include "method.h" +#include "mediacheck.h" +#include "cdinstall.h" +#include "windows.h" + +#include "../isys/imount.h" +#include "../isys/isys.h" +#include "../isys/eddsupport.h" + +/* boot flags */ +extern uint64_t flags; + +/* given a partition device and directory, tries to mount hd install image */ +static char * setupIsoImages(char * device, char * dirName, char * location) { + int rc = 0; + char *url = NULL, *dirspec, *updpath, *path; + char *typetry[] = {"ext3", "ext2", "vfat", NULL}; + char **type; + + logMessage(INFO, "mounting device %s for hard drive install", device); + + if (!FL_TESTING(flags)) { + for (type=typetry; *type; type++) { + if (!doPwMount(device, "/mnt/isodir", *type, "ro", NULL)) + break; + } + + if (!type) + return NULL; + + if (asprintf(&dirspec, "/mnt/isodir%.*s", + (int) (strrchr(dirName, '/') - dirName), dirName) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (asprintf(&path, "/mnt/isodir%s", dirName) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (path) { + logMessage(INFO, "Path to stage2 image is %s", path); + + rc = copyFile(path, "/tmp/install.img"); + rc = mountStage2("/tmp/install.img"); + + free(path); + + if (rc) { + umountLoopback("/mnt/runtime", "/dev/loop0"); + umount("/mnt/isodir"); + goto err; + } + + if (asprintf(&updpath, "%s/updates.img", dirspec) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(INFO, "Looking for updates for HD in %s", updpath); + copyUpdatesImg(updpath); + free(updpath); + + if (asprintf(&updpath, "%s/product.img", dirspec) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(INFO, "Looking for product for HD in %s", updpath); + copyProductImg(updpath); + + free(updpath); + free(dirspec); + umount("/mnt/isodir"); + + if (asprintf(&url, "hd:%s:/%s", device, + dirName ? dirName : ".") == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + return url; + } else { + free(dirspec); + free(path); + + if (rc) { + umount("/mnt/isodir"); + return NULL; + } + } + } else { + /* in test mode I dont know what to do - just pretend I guess */ + type = typetry; + } + +err: + newtWinMessage(_("Error"), _("OK"), + _("An error occured finding the installation image " + "on your hard drive. Please check your images and " + "try again.")); + return NULL; +} + +/* setup hard drive based install from a partition with a filesystem and + * ISO images on that filesystem + */ +char * mountHardDrive(struct installMethod * method, + char * location, struct loaderData_s * loaderData) { + int rc; + int i; + + newtComponent listbox, label, dirEntry, form, okay, back, text; + struct newtExitStruct es; + newtGrid entryGrid, grid, buttons; + + int done = 0; + char * dir = strdup(""); + char * tmpDir; + char * url = NULL; + char * buf, *substr; + int numPartitions; + + char **partition_list; + char *selpart; + char *kspartition, *ksdirectory; + + /* handle kickstart/stage2= data first if available */ + if (loaderData->method == METHOD_HD && loaderData->stage2Data) { + kspartition = ((struct hdInstallData *)loaderData->stage2Data)->partition; + ksdirectory = ((struct hdInstallData *)loaderData->stage2Data)->directory; + logMessage(INFO, "partition is %s, dir is %s", kspartition, ksdirectory); + + /* if exist, duplicate */ + if (kspartition) + kspartition = strdup(kspartition); + if (ksdirectory) + ksdirectory = strdup(ksdirectory); + + if (!kspartition || !ksdirectory) { + logMessage(ERROR, "missing partition or directory specification"); + loaderData->method = -1; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + } else { + /* if we start with /dev, strip it (#121486) */ + char *kspart = kspartition; + if (!strncmp(kspart, "/dev/", 5)) + kspart = kspart + 5; + + url = setupIsoImages(kspart, ksdirectory, location); + if (!url) { + logMessage(ERROR, "unable to find %s installation images on hd", + getProductName()); + loaderData->method = -1; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + } else { + free(kspartition); + free(ksdirectory); + return url; + } + } + } else { + kspartition = NULL; + ksdirectory = NULL; + } + + /* if we're here its either because this is interactive, or the */ + /* hd kickstart directive was faulty and we have to prompt for */ + /* location of harddrive image */ + + partition_list = NULL; + while (!done) { + /* if we're doing another pass free this up first */ + if (partition_list) + freePartitionsList(partition_list); + + partition_list = getPartitionsList(NULL); + numPartitions = lenPartitionsList(partition_list); + + /* no partitions found, try to load a device driver disk for storage */ + if (!numPartitions) { + rc = newtWinChoice(_("Hard Drives"), _("Yes"), _("Back"), + _("You don't seem to have any hard drives on " + "your system! Would you like to configure " + "additional devices?")); + if (rc == 2) + return NULL; + + rc = loadDriverFromMedia(DEVICE_DISK, loaderData, 0, 0); + if (rc == LOADER_BACK) + return NULL; + + continue; + } + + /* now find out which partition has the stage2 image */ + if (asprintf(&buf, _("What partition and directory on that " + "partition holds the installation image " + "for %s? If you don't see the disk drive " + "you're using listed here, press F2 to " + "configure additional devices."), + getProductName()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + text = newtTextboxReflowed(-1, -1, buf, 62, 5, 5, 0); + free(buf); + + listbox = newtListbox(-1, -1, numPartitions > 5 ? 5 : numPartitions, + NEWT_FLAG_RETURNEXIT | + (numPartitions > 5 ? NEWT_FLAG_SCROLL : 0)); + + for (i = 0; i < numPartitions; i++) + newtListboxAppendEntry(listbox,partition_list[i],partition_list[i]); + + /* if we had ks data around use it to prime entry, then get rid of it*/ + if (kspartition) { + newtListboxSetCurrentByKey(listbox, kspartition); + free(kspartition); + kspartition = NULL; + } + + label = newtLabel(-1, -1, _("Directory holding image:")); + + dirEntry = newtEntry(28, 11, dir, 28, (const char **) &tmpDir, + NEWT_ENTRY_SCROLL); + + /* if we had ks data around use it to prime entry, then get rid of it*/ + if (ksdirectory) { + newtEntrySet(dirEntry, ksdirectory, 1); + free(ksdirectory); + ksdirectory = NULL; + } + + entryGrid = newtGridHStacked(NEWT_GRID_COMPONENT, label, + NEWT_GRID_COMPONENT, dirEntry, + NEWT_GRID_EMPTY); + + buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL); + + grid = newtCreateGrid(1, 4); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, listbox, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, entryGrid, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + newtGridWrappedWindow(grid, _("Select Partition")); + + form = newtForm(NULL, NULL, 0); + newtFormAddHotKey(form, NEWT_KEY_F2); + newtFormAddHotKey(form, NEWT_KEY_F12); + + newtGridAddComponentsToForm(grid, form, 1); + newtGridFree(grid, 1); + + newtFormRun(form, &es); + + selpart = newtListboxGetCurrent(listbox); + + free(dir); + if (tmpDir && *tmpDir) { + /* Protect from form free. */ + dir = strdup(tmpDir); + } else { + dir = strdup(""); + } + + newtFormDestroy(form); + newtPopWindow(); + + if (es.reason == NEWT_EXIT_COMPONENT && es.u.co == back) { + return NULL; + } else if (es.reason == NEWT_EXIT_HOTKEY && es.u.key == NEWT_KEY_F2) { + rc = loadDriverFromMedia(DEVICE_DISK, loaderData, 0, 0); + if (rc == LOADER_BACK) + return NULL; + + continue; + } + + logMessage(INFO, "partition %s selected", selpart); + + /* If the user-provided URL points at a repo instead of a stage2 + * image, fix that up now. + */ + substr = strstr(dir, ".img"); + if (!substr || (substr && *(substr+4) != '\0')) { + if (asprintf(&dir, "%s/install.img", dir) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + loaderData->invalidRepoParam = 1; + + url = setupIsoImages(selpart, dir, location); + if (!url) { + newtWinMessage(_("Error"), _("OK"), + _("Device %s does not appear to contain " + "an installation image."), selpart, getProductName()); + continue; + } + + done = 1; + } + + free(dir); + + return url; +} + +void setKickstartHD(struct loaderData_s * loaderData, int argc, + char ** argv) { + char *biospart = NULL, *partition = NULL, *dir = NULL, *p; + poptContext optCon; + int rc; + + struct poptOption ksHDOptions[] = { + { "biospart", '\0', POPT_ARG_STRING, &biospart, 0, NULL, NULL }, + { "partition", '\0', POPT_ARG_STRING, &partition, 0, NULL, NULL }, + { "dir", '\0', POPT_ARG_STRING, &dir, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + + logMessage(INFO, "kickstartFromHD"); + optCon = poptGetContext(NULL, argc, (const char **) argv, ksHDOptions, 0); + if ((rc = poptGetNextOpt(optCon)) < -1) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to HD kickstart method " + "command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + return; + } + + if (biospart) { + char * dev; + + p = strchr(biospart,'p'); + if(!p){ + logMessage(ERROR, "Bad argument for --biospart"); + return; + } + *p = '\0'; + dev = getBiosDisk(biospart); + if (dev == NULL) { + logMessage(ERROR, "Unable to location BIOS partition %s", biospart); + return; + } + partition = malloc(strlen(dev) + strlen(p + 1) + 2); + sprintf(partition, "%s%s", dev, p + 1); + } + + loaderData->method = METHOD_HD; + loaderData->stage2Data = calloc(sizeof(struct hdInstallData *), 1); + if (partition) + ((struct hdInstallData *)loaderData->stage2Data)->partition = partition; + if (dir) + ((struct hdInstallData *)loaderData->stage2Data)->directory = dir; + + logMessage(INFO, "results of hd ks, partition is %s, dir is %s", partition, + dir); +} + +int kickstartFromHD(char *kssrc) { + int rc; + char *p, *np = NULL, *tmpstr, *ksdev, *kspath; + + logMessage(INFO, "getting kickstart file from harddrive"); + + /* format is hd:[device]:/path/to/ks.cfg */ + /* split up pieces */ + tmpstr = strdup(kssrc); + p = strchr(tmpstr, ':'); + if (p) + np = strchr(p+1, ':'); + + /* no second colon, assume its the old format of */ + /* hd:[device]/path/to/ks.cfg */ + /* this format is bad however because some devices have '/' in them! */ + if (!np) + np = strchr(p+1, '/'); + + if (!p || !np) { + logMessage(WARNING, "Format of command line is ks=hd:[device]:/path/to/ks.cfg"); + free(tmpstr); + return 1; + } + + *np = '\0'; + ksdev = p+1; + kspath = np+1; + + logMessage(INFO, "Loading ks from device %s on path %s", ksdev, kspath); + if ((rc=getKickstartFromBlockDevice(ksdev, kspath))) { + if (rc == 3) { + startNewt(); + newtWinMessage(_("Error"), _("OK"), + _("Cannot find kickstart file on hard drive.")); + } + return 1; + } + + return 0; +} + + +int kickstartFromBD(char *kssrc) { + int rc; + char *p, *np = NULL, *r = NULL, *tmpstr, *ksdev, *kspath, *biosksdev; + + logMessage(INFO, "getting kickstart file from biosdrive"); + + /* format is bd:[device]:/path/to/ks.cfg */ + /* split of pieces */ + tmpstr = strdup(kssrc); + p = strchr(tmpstr, ':'); + if (p) + np = strchr(p+1, ':'); + + if (!p || !np) { + logMessage(WARNING, "Format of command line is ks=bd:device:/path/to/ks.cfg"); + free(tmpstr); + return 1; + } + + *np = '\0'; + kspath = np+1; + + r = strchr(p+1,'p'); + if(!r){ + logMessage(INFO, "Format of biosdisk is 80p1"); + free(tmpstr); + return 1; + } + + *r = '\0'; + biosksdev = getBiosDisk((p + 1)); + if(!biosksdev){ + startNewt(); + newtWinMessage(_("Error"), _("OK"), + _("Cannot find hard drive for BIOS disk %s"), + p + 1); + return 1; + } + + + ksdev = malloc(strlen(biosksdev) + 3); + sprintf(ksdev, "%s%s", biosksdev, r + 1); + logMessage(INFO, "Loading ks from device %s on path %s", ksdev, kspath); + if ((rc=getKickstartFromBlockDevice(ksdev, kspath))) { + if (rc == 3) { + startNewt(); + newtWinMessage(_("Error"), _("OK"), + _("Cannot find kickstart file on hard drive.")); + } + return 1; + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/hdinstall.h b/loader/hdinstall.h new file mode 100644 index 000000000..44351a312 --- /dev/null +++ b/loader/hdinstall.h @@ -0,0 +1,38 @@ +/* + * hdinstall.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_HDINSTALL +#define H_HDINSTALL + +#include "method.h" + +struct hdInstallData { + char * partition; + char * directory; +}; + + +void setKickstartHD(struct loaderData_s * loaderData, int argc, + char ** argv); +char * mountHardDrive(struct installMethod * method, + char * location, struct loaderData_s * loaderData); +int kickstartFromHD(char *kssrc); +int kickstartFromBD(char *kssrc); + +#endif diff --git a/loader/init.c b/loader/init.c new file mode 100644 index 000000000..403789383 --- /dev/null +++ b/loader/init.c @@ -0,0 +1,798 @@ +/* + * init.c: This is the install type init + * + * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 + * 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) + * Jeremy Katz (katzj@redhat.com) + */ + +#if USE_MINILIBC +#include "minilibc.h" +#ifndef SOCK_STREAM +# define SOCK_STREAM 1 +#endif +#else +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <net/if.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/klog.h> +#include <sys/mount.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/swap.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/reboot.h> +#include <linux/vt.h> +#include <termios.h> +#include <libgen.h> + +#include "copy.h" +#include "devt.h" +#include "devices.h" + +#define syslog klogctl +#endif + +#include <asm/types.h> +#include <linux/cdrom.h> +#include <linux/serial.h> + +#ifndef MS_REMOUNT +#define MS_REMOUNT 32 +#endif + +#define ENV_PATH 0 +#define ENV_LD_LIBRARY_PATH 1 +#define ENV_HOME 2 +#define ENV_TERM 3 +#define ENV_DEBUG 4 +#define ENV_TERMINFO 5 +#define ENV_PYTHONPATH 6 +#define ENV_MALLOC_CHECK 7 +#define ENV_MALLOC_PERTURB 8 + +char * env[] = { + "PATH=/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sysimage/bin:" + "/mnt/sysimage/usr/bin:/mnt/sysimage/usr/sbin:/mnt/sysimage/sbin:" + "/mnt/sysimage/usr/X11R6/bin", + + /* we set a nicer ld library path specifically for bash -- a full + one makes anaconda unhappy */ +#if defined(__x86_64__) || defined(__s390x__) || defined(__ppc64__) + "LD_LIBRARY_PATH=/lib64:/usr/lib64:/lib:/usr/lib", +#else + "LD_LIBRARY_PATH=/lib:/usr/lib", +#endif + "HOME=/", + "TERM=linux", + "DEBUG=", + "TERMINFO=/etc/linux-terminfo", + "PYTHONPATH=/tmp/updates", + "MALLOC_CHECK_=2", + "MALLOC_PERTURB_=204", + NULL +}; + +/* + * this needs to handle the following cases: + * + * 1) run from a CD root filesystem + * 2) run from a read only nfs rooted filesystem + * 3) run from a floppy + * 4) run from a floppy that's been loaded into a ramdisk + * + */ + +int testing=0; +void unmountFilesystems(void); +void disableSwap(void); +void shutDown(int noKill, int doReboot, int doPowerOff); +static int getNoKill(void); +struct termios ts; + +static void printstr(char * string) { + int ret; + ret = write(1, string, strlen(string)); +} + +static void fatal_error(int usePerror) { +/* FIXME */ +#if 0 + if (usePerror) + perror("failed:"); + else +#endif + printf("failed.\n"); + + printf("\nI can't recover from this.\n"); + if (testing) + exit(0); +#if !defined(__s390__) && !defined(__s390x__) + while (1) ; +#endif +} + +static int logChunk(int len, char *inbuf, char *outbuf) { + int inctr, outctr; + + for (inctr = 0, outctr = 0; inctr < len; inctr++) { + /* If the character is a NULL that's immediately followed by a open + * bracket, we've found the beginning of a new kernel message. Put in + * a line separator. + */ + if (inbuf[inctr] == '\0' && inctr+1 < len && inbuf[inctr+1] == '<') { + outbuf[outctr] = '\n'; + outctr++; + } + + /* Or, if we see a NULL right before the end of the chunk, that's also + * a good place to add a separator. + */ + else if (inbuf[inctr] == '\0' && inctr+1 == len) { + outbuf[outctr] = '\n'; + outctr++; + } + + /* Otherwise, simply output the character as long as it's not NULL. */ + else if (inbuf[inctr] != '\0') { + outbuf[outctr] = inbuf[inctr]; + outctr++; + } + } + + return outctr; +} + +static void doklog(char * fn) { + fd_set readset, unixs; + int in, out, i; + int log; + socklen_t s; + int sock = -1; + struct sockaddr_un sockaddr; + char inbuf[1024], outbuf[1024]; + int readfd; + int ret; + + in = open("/proc/kmsg", O_RDONLY,0); + if (in < 0) { + /* FIXME: was perror */ + printstr("open /proc/kmsg"); + return; + } + + out = open(fn, O_WRONLY, 0); + if (out < 0) + printf("couldn't open %s for syslog -- still using /tmp/syslog\n", fn); + + log = open("/tmp/syslog", O_WRONLY | O_CREAT, 0644); + if (log < 0) { + /* FIXME: was perror */ + printstr("error opening /tmp/syslog"); + sleep(5); + + close(in); + return; + } + + /* if we get this far, we should be in good shape */ + + if (fork()) { + /* parent */ + close(in); + close(out); + close(log); + return; + } + close(0); + close(1); + close(2); + + dup2(1, log); + +#if defined(USE_LOGDEV) + /* now open the syslog socket */ + sockaddr.sun_family = AF_UNIX; + strcpy(sockaddr.sun_path, "/dev/log"); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + printf("error creating socket: %d\n", errno); + sleep(5); + } + printstr("got socket\n"); + if (bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr.sun_family) + + strlen(sockaddr.sun_path))) { + printf("bind error: %d\n", errno); + sleep(5); + } + printstr("bound socket\n"); + chmod("/dev/log", 0666); + if (listen(sock, 5)) { + printf("listen error: %d\n", errno); + sleep(5); + } +#endif + + syslog(8, NULL, 1); + + FD_ZERO(&unixs); + while (1) { + memcpy(&readset, &unixs, sizeof(unixs)); + + if (sock >= 0) + FD_SET(sock, &readset); + + FD_SET(in, &readset); + + i = select(20, &readset, NULL, NULL, NULL); + if (i <= 0) continue; + + if (FD_ISSET(in, &readset)) { + i = read(in, inbuf, sizeof(inbuf)); + if (i > 0) { + int loggedLen = logChunk(i, inbuf, outbuf); + + if (out >= 0) + ret = write(out, outbuf, loggedLen); + ret = write(log, outbuf, loggedLen); + } + } + + for (readfd = 0; readfd < 20; ++readfd) { + if (FD_ISSET(readfd, &readset) && FD_ISSET(readfd, &unixs)) { + i = read(readfd, inbuf, sizeof(inbuf)); + if (i > 0) { + int loggedLen = logChunk(i, inbuf, outbuf); + + if (out >= 0) + ret = write(out, outbuf, loggedLen); + + ret = write(log, outbuf, loggedLen); + } else if (i == 0) { + /* socket closed */ + close(readfd); + FD_CLR(readfd, &unixs); + } + } + } + + if (sock >= 0 && FD_ISSET(sock, &readset)) { + s = sizeof(sockaddr); + readfd = accept(sock, (struct sockaddr *) &sockaddr, &s); + if (readfd < 0) { + if (out >= 0) + ret = write(out, "error in accept\n", 16); + ret = write(log, "error in accept\n", 16); + close(sock); + sock = -1; + } else { + FD_SET(readfd, &unixs); + } + } + } +} + +static int setupTerminal(int fd) { + struct winsize winsize; + int fdn, len; + char buf[65535]; + + if (ioctl(fd, TIOCGWINSZ, &winsize)) { + printf("failed to get winsize"); + fatal_error(1); + } + + winsize.ws_row = 24; + winsize.ws_col = 80; + + if (ioctl(fd, TIOCSWINSZ, &winsize)) { + printf("failed to set winsize"); + fatal_error(1); + } + + /* use the no-advanced-video vt100 definition */ + env[ENV_TERM] = "TERM=vt100-nav"; + + /* unless the user specifies that they want utf8 */ + if ((fdn = open("/proc/cmdline", O_RDONLY, 0)) != -1) { + len = read(fdn, buf, sizeof(buf) - 1); + close(fdn); + if ((len > 0) && strstr(buf, "utf8")) + env[ENV_TERM] = "TERM=vt100"; + } + + return 0; +} + +#if !defined(__s390__) && !defined(__s390x__) +static int termcmp(struct termios *a, struct termios *b) { + if (a->c_iflag != b->c_iflag || a->c_oflag != b->c_oflag || + a->c_cflag != b->c_cflag || a->c_lflag != b->c_lflag || + a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed) + return 1; + return memcmp(a->c_cc, b->c_cc, sizeof(a->c_cc)); +} +#endif + +static void createDevices(void) { + int i; + + for (i = 0; devnodes[i].devname != NULL; i++) { + char devname[64]; + int type = -1; + + snprintf(devname, 63, "/dev/%s", devnodes[i].devname); + switch (devnodes[i].type) { + case DIRTYPE: + if (mkdir(devname, devnodes[i].perms) < 0) { + fprintf(stderr, "Unable to create directory %s: %m\n", + devname); + } + break; + case CHARDEV: + type = S_IFCHR; + break; + case BLOCKDEV: + type = S_IFBLK; + break; + } + if (type == -1) continue; + + if (mknod(devname, type | devnodes[i].perms, + makedev(devnodes[i].major, devnodes[i].minor)) < 0) + fprintf(stderr, "Unable to create device %s: %m\n", devname); + } +} + +static void termReset(void) { + /* change to tty1 */ + ioctl(0, VT_ACTIVATE, 1); + /* reset terminal */ + tcsetattr(0, TCSANOW, &ts); + /* Shift in, default color, move down 100 lines */ + /* ^O ^[[0m ^[[100E */ + printf("\017\033[0m\033[100E\n"); +} + +/* reboot handler */ +static void sigintHandler(int signum) { + termReset(); + shutDown(getNoKill(), 1, 0); +} + +/* halt handler */ +static void sigUsr1Handler(int signum) { + termReset(); + shutDown(getNoKill(), 0, 0); +} + +/* poweroff handler */ +static void sigUsr2Handler(int signum) { + termReset(); + shutDown(getNoKill(), 0, 1); +} + +static int getNoKill(void) { + int fd; + int len; + char buf[1024]; + + /* look through /proc/cmdline for special options */ + if ((fd = open("/proc/cmdline", O_RDONLY,0)) > 0) { + len = read(fd, buf, sizeof(buf) - 1); + close(fd); + if ((len > 0) && strstr(buf, "nokill")) + return 1; + } + return 0; +} + +static int getInitPid(void) { + int fd = 0, pid = -1, ret; + char * buf = calloc(1, 10); + + fd = open("/var/run/init.pid", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Unable to find pid of init!!!\n"); + return -1; + } + ret = read(fd, buf, 9); + close(fd); + ret = sscanf(buf, "%d", &pid); + return pid; +} + +static void copyErrorFn (char *msg) { + printf(msg); +} + +int main(int argc, char **argv) { + pid_t installpid, childpid; + int waitStatus; + int fd = -1; + int doReboot = 0; + int doShutdown =0; + int isSerial = 0; + char * console = NULL; + int noKill = 0; + char * argvc[15]; + char ** argvp = argvc; + char twelve = 12; + struct serial_struct si; + int i, disable_keys; + + if (!strncmp(basename(argv[0]), "poweroff", 8)) { + printf("Running poweroff...\n"); + fd = getInitPid(); + if (fd > 0) + kill(fd, SIGUSR2); + exit(0); + } else if (!strncmp(basename(argv[0]), "halt", 4)) { + printf("Running halt...\n"); + fd = getInitPid(); + if (fd > 0) + kill(fd, SIGUSR1); + exit(0); + } else if (!strncmp(basename(argv[0]), "reboot", 6)) { + printf("Running reboot...\n"); + fd = getInitPid(); + if (fd > 0) + kill(fd, SIGINT); + exit(0); + } + +#if !defined(__s390__) && !defined(__s390x__) + testing = (getppid() != 0) && (getppid() != 1); +#endif + + if (!testing) { + /* turn off screen blanking */ + printstr("\033[9;0]"); + printstr("\033[8]"); + } else { + printstr("(running in test mode).\n"); + } + + umask(022); + + printstr("\nGreetings.\n"); + + printf("anaconda installer init version %s starting\n", VERSION); + + printf("mounting /proc filesystem... "); + if (!testing) { + if (mount("/proc", "/proc", "proc", 0, NULL)) + fatal_error(1); + } + printf("done\n"); + + printf("creating /dev filesystem... "); + if (!testing) { + if (mount("/dev", "/dev", "tmpfs", 0, NULL)) + fatal_error(1); + createDevices(); + printf("starting udev..."); + if (fork() == 0) { + execl("/sbin/udevd", "/sbin/udevd","--daemon",NULL); + exit(1); + } + } + printf("done\n"); + + printf("mounting /dev/pts (unix98 pty) filesystem... "); + if (!testing) { + if (mount("/dev/pts", "/dev/pts", "devpts", 0, NULL)) + fatal_error(1); + } + printf("done\n"); + + printf("mounting /sys filesystem... "); + if (!testing) { + if (mount("/sys", "/sys", "sysfs", 0, NULL)) + fatal_error(1); + } + printf("done\n"); + + /* these args are only for testing from commandline */ + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "serial")) { + isSerial = 1; + break; + } + } + + noKill = getNoKill(); + +#if !defined(__s390__) && !defined(__s390x__) + static struct termios orig_cmode; + struct termios cmode, mode; + int cfd; + + cfd = open("/dev/console", O_RDONLY); + tcgetattr(cfd,&orig_cmode); + close(cfd); + + cmode = orig_cmode; + cmode.c_lflag &= (~ECHO); + + cfd = open("/dev/console", O_WRONLY); + tcsetattr(cfd,TCSANOW,&cmode); + close(cfd); + + /* handle weird consoles */ +#if defined(__powerpc__) + char * consoles[] = { "/dev/hvc0", /* hvc for JS20 */ + + "/dev/hvsi0", "/dev/hvsi1", + "/dev/hvsi2", /* hvsi for POWER5 */ + NULL }; +#elif defined (__ia64__) + char * consoles[] = { "/dev/ttySG0", "/dev/xvc0", "/dev/hvc0", NULL }; +#elif defined (__i386__) || defined (__x86_64__) + char * consoles[] = { "/dev/xvc0", "/dev/hvc0", NULL }; +#else + char * consoles[] = { NULL }; +#endif + for (i = 0; consoles[i] != NULL; i++) { + if ((fd = open(consoles[i], O_RDWR)) >= 0 && !tcgetattr(fd, &mode) && !termcmp(&cmode, &mode)) { + printf("anaconda installer init version %s using %s as console\n", + VERSION, consoles[i]); + isSerial = 3; + console = strdup(consoles[i]); + break; + } + close(fd); + } + + cfd = open("/dev/console", O_WRONLY); + tcsetattr(cfd,TCSANOW,&orig_cmode); + close(cfd); + + if ((fd < 0) && (ioctl (0, TIOCLINUX, &twelve) < 0)) { + isSerial = 2; + + if (ioctl(0, TIOCGSERIAL, &si) == -1) { + isSerial = 0; + } + } + + if (isSerial && (isSerial != 3)) { + char *device = "/dev/ttyS0"; + + printf("anaconda installer init version %s using a serial console\n", + VERSION); + + if (isSerial == 2) + device = "/dev/console"; + fd = open(device, O_RDWR, 0); + if (fd < 0) + device = "/dev/tts/0"; + + if (fd < 0) { + printf("failed to open %s\n", device); + fatal_error(1); + } + + setupTerminal(fd); + } else if (isSerial == 3) { + setupTerminal(fd); + } else if (fd < 0) { + fd = open("/dev/tty1", O_RDWR, 0); + if (fd < 0) + fd = open("/dev/vc/1", O_RDWR, 0); + + if (fd < 0) { + printf("failed to open /dev/tty1 and /dev/vc/1"); + fatal_error(1); + } + } + + if (testing) + exit(0); + + setsid(); + if (ioctl(0, TIOCSCTTY, NULL)) { + printf("could not set new controlling tty\n"); + } + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); +#else + dup2(0, 1); + dup2(0, 2); +#endif + + /* disable Ctrl+Z, Ctrl+C, etc ... but not in rescue mode */ + disable_keys = 1; + if (argc > 1) + if (strstr(argv[1], "rescue")) + disable_keys = 0; + + if (disable_keys) { + tcgetattr(0, &ts); + ts.c_iflag &= ~BRKINT; + ts.c_iflag |= IGNBRK; + ts.c_iflag &= ~ISIG; + tcsetattr(0, TCSANOW, &ts); + } + + if (!testing) { + int ret; + ret = sethostname("localhost.localdomain", 21); + /* the default domainname (as of 2.0.35) is "(none)", which confuses + glibc */ + ret = setdomainname("", 0); + } + + printf("trying to remount root filesystem read write... "); + if (mount("/", "/", "ext2", MS_REMOUNT | MS_MGC_VAL, NULL)) { + fatal_error(1); + } + printf("done\n"); + + /* we want our /tmp to be ramfs, but we also want to let people hack + * their initrds to add things like a ks.cfg, so this has to be a little + * tricky */ + if (!testing) { + rename("/tmp", "/oldtmp"); + mkdir("/tmp", 0755); + + printf("mounting /tmp as ramfs... "); + if (mount("none", "/tmp", "ramfs", 0, NULL)) + fatal_error(1); + printf("done\n"); + + copyDirectory("/oldtmp", "/tmp", copyErrorFn, copyErrorFn); + unlink("/oldtmp"); + } + + /* Now we have some /tmp space set up, and /etc and /dev point to + it. We should be in pretty good shape. */ + + if (!testing) + doklog("/dev/tty4"); + + /* write out a pid file */ + if ((fd = open("/var/run/init.pid", O_WRONLY|O_CREAT, 0644)) > 0) { + char * buf = malloc(10); + int ret; + + snprintf(buf, 9, "%d", getpid()); + ret = write(fd, buf, strlen(buf)); + close(fd); + free(buf); + } else { + printf("unable to write init.pid (%d): %m\n", errno); + sleep(2); + } + + /* D-Bus */ + if (!testing) { + if (fork() == 0) { + execl("/sbin/dbus-uuidgen", "/sbin/dbus-uuidgen", "--ensure", NULL); + exit(1); + } + + if (fork() == 0) { + execl("/sbin/dbus-daemon", "/sbin/dbus-daemon", "--system", NULL); + exit(1); + } + } + + /* HAL daemon */ + if (!testing) { + if (fork() == 0) { + execl("/sbin/hald", "/sbin/hald", NULL); + exit(1); + } + } + + /* Go into normal init mode - keep going, and then do a orderly shutdown + when: + + 1) /bin/install exits + 2) we receive a SIGHUP + */ + + printf("running install...\n"); + + setsid(); + + if (!(installpid = fork())) { + /* child */ + *argvp++ = "/sbin/loader"; + + if (isSerial == 3) { + *argvp++ = "--virtpconsole"; + *argvp++ = console; + } + + *argvp++ = NULL; + + printf("running %s\n", argvc[0]); + execve(argvc[0], argvc, env); + + shutDown(1, 0, 0); + } + + /* signal handlers for halt/poweroff */ + signal(SIGUSR1, sigUsr1Handler); + signal(SIGUSR2, sigUsr2Handler); + + /* set up the ctrl+alt+delete handler to kill our pid, not pid 1 */ + signal(SIGINT, sigintHandler); + if ((fd = open("/proc/sys/kernel/cad_pid", O_WRONLY)) != -1) { + char buf[7]; + size_t count; + sprintf(buf, "%d", getpid()); + count = write(fd, buf, strlen(buf)); + close(fd); + /* if we succeeded in writing our pid, turn off the hard reboot + ctrl-alt-del handler */ + if (count == strlen(buf) && + (fd = open("/proc/sys/kernel/ctrl-alt-del", O_WRONLY)) != -1) { + int ret; + + ret = write(fd, "0", 1); + close(fd); + } + } + + while (!doShutdown) { + childpid = waitpid(-1, &waitStatus, 0); + + if (childpid == installpid) + doShutdown = 1; + } + + if (!WIFEXITED(waitStatus) || + (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus))) { + printf("install exited abnormally [%d/%d] ", WIFEXITED(waitStatus), + WEXITSTATUS(waitStatus)); + if (WIFSIGNALED(waitStatus)) { + printf("-- received signal %d", WTERMSIG(waitStatus)); + } + printf("\n"); + } else { + doReboot = 1; + } + + if (testing) + exit(0); + + shutDown(noKill, doReboot, 0); + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 ts=4: */ diff --git a/loader/kbd.c b/loader/kbd.c new file mode 100644 index 000000000..94b68583f --- /dev/null +++ b/loader/kbd.c @@ -0,0 +1,187 @@ +/* + * kbd.c - keyboard handling + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 <alloca.h> +#include <errno.h> +#include <newt.h> +#include <stdlib.h> +#include <string.h> + +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "lang.h" +#include "windows.h" + +#include "../isys/stubs.h" +#include "../isys/lang.h" + +/* boot flags */ +extern uint64_t flags; + +int chooseKeyboard(struct loaderData_s * loaderData, char ** kbdtypep) { + int num = -1; + int rc; + gzFile f; + struct kmapHeader hdr; + struct kmapInfo * infoTable; + struct langInfo * languages; + int numLanguages; + char ** kbds; + char buf[16384]; /* I hope this is big enough */ + int i; + char * defkbd = loaderData->kbd ? loaderData->kbd : NULL; + char *lang; + +#if defined(__s390__) || defined(__s390x__) + return LOADER_NOOP; +#endif + + if (FL_SERIAL (flags) || FL_VIRTPCONSOLE(flags)) return LOADER_NOOP; + +#ifdef __sparc__ + { + int fd; + + fd = open("/dev/kbd", O_RDWR); + if (fd < 0) + kbdtype = KBDTYPE_PC; /* if PC keyboard, then there is no driver for /dev/kbd */ + else { + close(fd); + kbdtype = KBDTYPE_SUN; + } + } +#endif /* sparc */ + + numLanguages = getLangInfo(&languages); + + lang = getenv("LANG"); + if (!lang) + lang = loaderData->lang; + + if (!defkbd && lang) { + for (i = 0; i < numLanguages; i++) { + if (!strncmp(languages[i].lc_all, lang, 2)) { + defkbd = languages[i].keyboard; + break; + } + } + } + + if (!defkbd) +#ifdef __sparc__ + if (kbdtype == KBDTYPE_SUN) + defkbd = "sunkeymap"; + else +#endif /* sparc drain bamage */ + defkbd = "us"; + + f = gunzip_open("/etc/keymaps.gz"); + if (!f) { + errorWindow("cannot open /etc/keymaps.gz: %s"); + return LOADER_ERROR; + } + + if (gunzip_read(f, &hdr, sizeof(hdr)) != sizeof(hdr)) { + errorWindow("failed to read keymaps header: %s"); + gunzip_close(f); + return LOADER_ERROR; + } + + logMessage(INFO, "%d keymaps are available", hdr.numEntries); + + i = hdr.numEntries * sizeof(*infoTable); + infoTable = alloca(i); + if (gunzip_read(f, infoTable, i) != i) { + errorWindow("failed to read keymap information: %s"); + gunzip_close(f); + return LOADER_ERROR; + } + + if (num == -1 ) { + kbds = alloca(sizeof(*kbds) * (hdr.numEntries + 1)); + for (i = 0; i < hdr.numEntries; i++) { + kbds[i] = infoTable[i].name; + } + + kbds[i] = NULL; + qsort(kbds, i, sizeof(*kbds), simpleStringCmp); + + for (i = 0; i < hdr.numEntries; i++) + if (!strcmp(kbds[i], defkbd)) + num = i; + + rc = newtWinMenu(_("Keyboard Type"), + _("What type of keyboard do you have?"), + 40, 5, 5, 8, kbds, &num, _("OK"), _("Back"), NULL); + if (rc == 2) return LOADER_BACK; + + /* num needs to index the right keyboard infoTable */ + for (i = 0; i < hdr.numEntries; i++) + if (!strcmp(kbds[num], infoTable[i].name)) break; + num = i; + } + + rc = 0; + + for (i = 0; i < num; i++) { + if (gunzip_read(f, buf, infoTable[i].size) != infoTable[i].size) { + logMessage(ERROR, "error reading %d bytes from file: %m", + infoTable[i].size); + gunzip_close(f); + rc = LOADER_ERROR; + } + } + + if (!rc) rc = loadKeymap(f); + + /* normalize the error condition */ + /* MSWFIXME - do we want to warn the user that setting the + keyboard didn't work? + */ + if (rc != 0) + rc = LOADER_ERROR; + else + gunzip_close(f); + + loaderData->kbd = strdup(infoTable[num].name); + +#ifdef __sparc__ + if (kbdtypep) *kbdtypep = (kbdtype == KBDTYPE_SUN) ? "sun" : "pc"; +#endif + + return rc; +} + +void setKickstartKeyboard(struct loaderData_s * loaderData, int argc, + char ** argv) { + if (argc < 2) { + logMessage(ERROR, "no argument passed to keyboard kickstart command"); + return; + } + + loaderData->kbd = argv[1]; + loaderData->kbd_set = 1; +} diff --git a/loader/kbd.h b/loader/kbd.h new file mode 100644 index 000000000..26c7111e6 --- /dev/null +++ b/loader/kbd.h @@ -0,0 +1,27 @@ +/* + * kbd.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_KBD +#define H_KBD + +int chooseKeyboard(struct loaderData_s * loaderData, char ** kbdtypep); +void setKickstartKeyboard(struct loaderData_s * loaderData, int argc, + char ** argv); + +#endif diff --git a/loader/keymaps-i386 b/loader/keymaps-i386 Binary files differnew file mode 100644 index 000000000..bb6dadc8b --- /dev/null +++ b/loader/keymaps-i386 diff --git a/loader/keymaps-ppc b/loader/keymaps-ppc Binary files differnew file mode 100644 index 000000000..12fa39fae --- /dev/null +++ b/loader/keymaps-ppc diff --git a/loader/keymaps-x86_64 b/loader/keymaps-x86_64 Binary files differnew file mode 100644 index 000000000..bb6dadc8b --- /dev/null +++ b/loader/keymaps-x86_64 diff --git a/loader/kickstart.c b/loader/kickstart.c new file mode 100644 index 000000000..2a3226f39 --- /dev/null +++ b/loader/kickstart.c @@ -0,0 +1,566 @@ +/* + * kickstart.c - kickstart file handling + * + * Copyright (C) 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 <alloca.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <newt.h> +#include <popt.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "loader.h" +#include "loadermisc.h" +#include "lang.h" +#include "log.h" +#include "kickstart.h" + +#include "kbd.h" +#include "driverdisk.h" +#include "net.h" +#include "method.h" + +#include "nfsinstall.h" +#include "urlinstall.h" +#include "cdinstall.h" +#include "hdinstall.h" + +#include "../isys/imount.h" +#include "../isys/isys.h" + +/* boot flags */ +extern uint64_t flags; + +struct ksCommandNames { + int code; + char * name; + void (*setupData) (struct loaderData_s *loaderData, + int argc, char ** argv); +} ; + +struct ksCommand { + int code, argc; + char ** argv; +}; + +static void setTextMode(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setGraphicalMode(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setCmdlineMode(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setSELinux(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setPowerOff(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setHalt(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setShutdown(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setMediaCheck(struct loaderData_s * loaderData, int argc, + char ** argv); +static void setUpdates(struct loaderData_s * loaderData, int argc, + char ** argv); +void loadKickstartModule(struct loaderData_s * loaderData, int argc, + char ** argv); + +struct ksCommandNames ksTable[] = { + { KS_CMD_NFS, "nfs", setKickstartNfs }, + { KS_CMD_CDROM, "cdrom", setKickstartCD }, + { KS_CMD_HD, "harddrive", setKickstartHD }, + { KS_CMD_TEXT, "text", setTextMode }, + { KS_CMD_GRAPHICAL, "graphical", setGraphicalMode }, + { KS_CMD_URL, "url", setKickstartUrl }, + { KS_CMD_NETWORK, "network", setKickstartNetwork }, + { KS_CMD_KEYBOARD, "keyboard", setKickstartKeyboard }, + { KS_CMD_LANG, "lang", setKickstartLanguage }, + { KS_CMD_DD, "driverdisk", useKickstartDD }, + { KS_CMD_DEVICE, "device", loadKickstartModule }, + { KS_CMD_CMDLINE, "cmdline", setCmdlineMode }, + { KS_CMD_SELINUX, "selinux", setSELinux }, + { KS_CMD_POWEROFF, "poweroff", setPowerOff }, + { KS_CMD_HALT, "halt", setHalt }, + { KS_CMD_SHUTDOWN, "shutdown", setShutdown }, + { KS_CMD_MEDIACHECK, "mediacheck", setMediaCheck }, + { KS_CMD_UPDATES, "updates", setUpdates }, + { KS_CMD_NONE, NULL, NULL } +}; + +struct ksCommand * commands = NULL; +int numCommands = 0; + +int ksReadCommands(char * cmdFile) { + int fd; + char * buf; + struct stat sb; + char * start, * end, * chptr; + char oldch; + int line = 0; + char ** argv; + int argc; + int inSection = 0; /* in a section such as %post, %pre or %packages */ + struct ksCommandNames * cmd; + int commandsAlloced = 5; + + if ((fd = open(cmdFile, O_RDONLY)) < 0) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Error opening kickstart file %s: %m"), + cmdFile); + return LOADER_ERROR; + } + + fstat(fd, &sb); + buf = alloca(sb.st_size + 1); + if (read(fd, buf, sb.st_size) != sb.st_size) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Error reading contents of kickstart file %s: %m"), + cmdFile); + close(fd); + return LOADER_ERROR; + } + + close(fd); + + buf[sb.st_size] = '\0'; + + commands = malloc(sizeof(*commands) * commandsAlloced); + + start = buf; + while (*start && !inSection) { + line++; + if (!(end = strchr(start, '\n'))) + end = start + strlen(start); + + oldch = *end; + *end = '\0'; + + while (*start && isspace(*start)) start++; + + chptr = end - 1; + while (chptr > start && isspace(*chptr)) chptr--; + + if (isspace(*chptr)) + *chptr = '\0'; + else + *(chptr + 1) = '\0'; + + if (!*start || *start == '#' || !strncmp(start, "%include", 8)) { + /* keep parsing the file */ + } else if (*start == '%') { + /* assumed - anything starting with %something is a section */ + inSection = 1; + } else if (*chptr == '\\') { + /* JKFIXME: this should be handled better, but at least we + * won't segfault now */ + } else { + if (poptParseArgvString(start, &argc, + (const char ***) &argv) || !argc) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Error in %s on line %d of kickstart file %s."), + argv[0], line, cmdFile); + } else { + for (cmd = ksTable; cmd->name; cmd++) + if (!strcmp(cmd->name, argv[0])) break; + + if (cmd->name) { + if (numCommands == commandsAlloced) { + commandsAlloced += 5; + commands = realloc(commands, + sizeof(*commands) * commandsAlloced); + } + + commands[numCommands].code = cmd->code; + commands[numCommands].argc = argc; + commands[numCommands].argv = argv; + numCommands++; + } + } + } + + if (oldch) + start = end + 1; + else + start = end; + } + + return 0; +} + + +int ksHasCommand(int cmd) { + int i; + + for(i = 0; i < numCommands; i++) + if (commands[i].code == cmd) return 1; + + return 0; +} + +int ksGetCommand(int cmd, char ** last, int * argc, char *** argv) { + int i = 0; + + if (last) { + for (i = 0; i < numCommands; i++) { + if (commands[i].argv == last) break; + } + + i++; + } + + for (; i < numCommands; i++) { + if (commands[i].code == cmd) { + if (argv) *argv = commands[i].argv; + if (argc) *argc = commands[i].argc; + return 0; + } + } + + return 1; +} + +int kickstartFromRemovable(char *kssrc) { + struct device ** devices; + char *p, *kspath; + int i, rc; + + logMessage(INFO, "doing kickstart from removable media"); + devices = getDevices(DEVICE_DISK); + if (!devices) { + logMessage(ERROR, "no disks"); + return 1; + } + + for (i = 0; devices[i]; i++) { + if (devices[i]->priv.removable == 1) { + logMessage(INFO, "first removable media is %s", devices[i]->device); + break; + } + } + + if (!devices[i] || (devices[i]->priv.removable == 0)) { + logMessage(ERROR, "no removable devices"); + return 1; + } + + /* format is floppy:[/path/to/ks.cfg] */ + kspath = ""; + p = strchr(kssrc, ':'); + if (p) + kspath = p + 1; + + if (!p || strlen(kspath) < 1) + kspath = "/ks.cfg"; + + if ((rc=getKickstartFromBlockDevice(devices[i]->device, kspath))) { + if (rc == 3) { + startNewt(); + newtWinMessage(_("Error"), _("OK"), + _("Cannot find ks.cfg on removable media.")); + } + return 1; + } + + return 0; +} + + +/* given a device name (w/o '/dev' on it), try to get ks file */ +/* Error codes: + 1 - could not create device node + 2 - could not mount device as ext2, vfat, or iso9660 + 3 - kickstart file named path not there +*/ +int getKickstartFromBlockDevice(char *device, char *path) { + return getFileFromBlockDevice(device, path, "/tmp/ks.cfg"); +} + +void getHostPathandLogin(char * ksSource, char **host, char ** file, char ** login, char ** password, char * ip) { + char *tmp; + + *host = strdup(ksSource); + tmp = strchr(*host, '/'); + + if (tmp) { + *file = strdup(tmp); + *tmp = '\0'; + } + else { + *file = malloc(sizeof(char *)); + **file = '\0'; + } + + logMessage(DEBUGLVL, "getHostandPath host: |%s|", *host); + logMessage(DEBUGLVL, "getHostandPath file(1): |%s|", *file); + + /* if the filename ends with / or is null, use default kickstart + * name of IP_ADDRESS-kickstart appended to *file + */ + if ((*file) && (((*file)[strlen(*file) - 1] == '/') || + ((*file)[strlen(*file) - 1] == '\0'))) { + if (asprintf(file, "%s%s-kickstart", *file, ip) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(DEBUGLVL, "getHostandPath file(2): |%s|", *file); + } + + /* Do we have a password? */ + tmp = strchr(*host, '@'); + if (tmp != NULL) { + *login = *host; + *tmp = '\0'; + *host = tmp + 1; + + tmp = strchr(*login, ':'); + if (tmp != NULL) { + *password = tmp + 1; + *tmp = '\0'; + } else { + *password = malloc(sizeof(char *)); + **password = '\0'; + } + } else { + *login = malloc(sizeof(char *)); + **login = '\0'; + *password = malloc(sizeof(char *)); + **password = '\0'; + } +} + +void getHostandPath(char * ksSource, char **host, char ** file, char * ip) { + char *password, *login; + getHostPathandLogin (ksSource, host, file, &login, &password, ip); +} + +static char *newKickstartLocation(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 kickstart file. Please modify the kickstart parameter below or press Cancel to proceed as an interactive installation."), 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 kickstart file")); + newtGridFree(grid, 1); + + /* run the form */ + answer = newtRunForm(f); + + if (answer != cancel) + retval = strdup(location); + + newtFormDestroy(f); + newtPopWindow(); + + return retval; +} + +void getKickstartFile(struct loaderData_s *loaderData) { + char *c; + int rc = 1; + + /* Chop off the parameter name, if given. */ + if (!strncmp(loaderData->ksFile, "ks=", 3)) + c = loaderData->ksFile+3; + else + c = loaderData->ksFile; + + while (rc != 0) { + if (!strncmp(c, "ks", 2)) { + rc = kickstartFromNfs(NULL, loaderData); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "http://", 7) || !strncmp(c, "ftp://", 6)) { + rc = kickstartFromUrl(c, loaderData); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "nfs:", 4)) { + rc = kickstartFromNfs(c+4, loaderData); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "floppy", 6)) { + rc = kickstartFromRemovable(c); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "hd:", 3)) { + rc = kickstartFromHD(c); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "bd:", 3)) { + rc = kickstartFromBD(c); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "cdrom", 5)) { + rc = kickstartFromCD(c); + loaderData->ksFile = strdup("/tmp/ks.cfg"); + } else if (!strncmp(c, "file:", 5)) { + loaderData->ksFile = c+5; + break; + } + + if (rc != 0) { + char *newLocation; + + if (!strcmp(c, "ks")) + newLocation = newKickstartLocation(""); + else + newLocation = newKickstartLocation(c); + + if (loaderData->ksFile != NULL) + free(loaderData->ksFile); + + if (newLocation != NULL) { + loaderData->ksFile = strdup(newLocation); + free(newLocation); + return getKickstartFile(loaderData); + } + else + return; + } + } + + flags |= LOADER_FLAGS_KICKSTART; + return; +} + +static void setUpdates(struct loaderData_s * loaderData, int argc, + char ** argv) { + if (argc == 1) + flags |= LOADER_FLAGS_UPDATES; + else if (argc == 2) + loaderData->updatessrc = strdup(argv[1]); + else + logMessage(WARNING, "updates command given with incorrect arguments"); +} + +static void setTextMode(struct loaderData_s * loaderData, int argc, + char ** argv) { + logMessage(INFO, "kickstart forcing text mode"); + flags |= LOADER_FLAGS_TEXT; + return; +} + +static void setGraphicalMode(struct loaderData_s * loaderData, int argc, + char ** argv) { + logMessage(INFO, "kickstart forcing graphical mode"); + flags |= LOADER_FLAGS_GRAPHICAL; + return; +} + +static void setCmdlineMode(struct loaderData_s * loaderData, int argc, + char ** argv) { + logMessage(INFO, "kickstart forcing cmdline mode"); + flags |= LOADER_FLAGS_CMDLINE; + return; +} + +static void setSELinux(struct loaderData_s * loaderData, int argc, + char ** argv) { + flags |= LOADER_FLAGS_SELINUX; + return; +} + +static void setPowerOff(struct loaderData_s * loaderData, int argc, + char ** argv) { + flags |= LOADER_FLAGS_POWEROFF; + return; +} + +static void setHalt(struct loaderData_s * loaderData, int argc, + char ** argv) { + flags |= LOADER_FLAGS_HALT; + return; +} + +static void setShutdown(struct loaderData_s * loaderData, int argc, + char ** argv) { + poptContext optCon; + int reboot = 0, halt = 0, poweroff = 0; + int rc; + + struct poptOption ksOptions[] = { + { "eject", 'e', POPT_ARG_NONE, NULL, 0, NULL, NULL }, + { "reboot", 'r', POPT_ARG_NONE, &reboot, 0, NULL, NULL }, + { "halt", 'h', POPT_ARG_NONE, &halt, 0, NULL, NULL }, + { "poweroff", 'p', POPT_ARG_NONE, &poweroff, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + optCon = poptGetContext(NULL, argc, (const char **) argv, ksOptions, 0); + if ((rc = poptGetNextOpt(optCon)) < -1) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to shutdown kickstart method " + "command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + return; + } + + + if (poweroff) + flags |= LOADER_FLAGS_POWEROFF; + if ((!poweroff && !reboot) || (halt)) + flags |= LOADER_FLAGS_HALT; + + return; +} + +static void setMediaCheck(struct loaderData_s * loaderData, int argc, + char ** argv) { + flags |= LOADER_FLAGS_MEDIACHECK; + return; +} + +void runKickstart(struct loaderData_s * loaderData) { + struct ksCommandNames * cmd; + int argc; + char ** argv; + + logMessage(INFO, "setting up kickstart"); + for (cmd = ksTable; cmd->name; cmd++) { + if ((!ksGetCommand(cmd->code, NULL, &argc, &argv)) && cmd->setupData) { + cmd->setupData(loaderData, argc, argv); + } + } +} diff --git a/loader/kickstart.h b/loader/kickstart.h new file mode 100644 index 000000000..c8d4a7861 --- /dev/null +++ b/loader/kickstart.h @@ -0,0 +1,54 @@ +/* + * kickstart.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_KICKSTART + +#include "loader.h" + +#define KS_CMD_NONE 0 +#define KS_CMD_NFS 1 +#define KS_CMD_CDROM 2 +#define KS_CMD_HD 3 +#define KS_CMD_URL 4 +#define KS_CMD_NETWORK 5 +#define KS_CMD_TEXT 6 +#define KS_CMD_KEYBOARD 7 +#define KS_CMD_LANG 8 +#define KS_CMD_DD 9 +#define KS_CMD_DEVICE 10 +#define KS_CMD_CMDLINE 11 +#define KS_CMD_GRAPHICAL 12 +#define KS_CMD_SELINUX 13 +#define KS_CMD_POWEROFF 14 +#define KS_CMD_HALT 15 +#define KS_CMD_SHUTDOWN 16 +#define KS_CMD_MEDIACHECK 17 +#define KS_CMD_UPDATES 18 + +int ksReadCommands(char * cmdFile); +int ksGetCommand(int cmd, char ** last, int * argc, char *** argv); +int ksHasCommand(int cmd); + +void getKickstartFile(struct loaderData_s * loaderData); +void runKickstart(struct loaderData_s * loaderData); +int getKickstartFromBlockDevice(char *device, char *path); +void getHostandPath(char * ksSource, char **host, char ** file, char * ip); +void getHostPathandLogin(char * ksSource, char **host, char ** file, char ** login, char ** password, char * ip); + +#endif diff --git a/loader/lang.c b/loader/lang.c new file mode 100644 index 000000000..9aefedbc5 --- /dev/null +++ b/loader/lang.c @@ -0,0 +1,404 @@ +/* + * lang.c - determines language, handles translations + * + * 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 <alloca.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <newt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <wchar.h> + +#include "loader.h" +#include "lang.h" +#include "log.h" +#include "loadermisc.h" +#include "windows.h" + +#include "../isys/stubs.h" +#include "../isys/cpio.h" +#include "../isys/lang.h" +#include "../isys/isys.h" + +/* boot flags */ +extern uint64_t flags; + +struct aString { + unsigned int hash; + short length; + char * str; +} ; + +struct aString * strings = NULL; +int numStrings = 0, allocedStrings = 0; + +static int english = 0; + +static char * topLineWelcome = N_("Welcome to %s for %s"); +static char * topLineWelcomeRescue = N_("Welcome to %s for %s - Rescue Mode"); +static char * bottomHelpLine = N_(" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen "); + +static int aStringCmp(const void * a, const void * b) { + const struct aString * first = a; + const struct aString * second = b; + + if (first->hash < second->hash) + return -1; + else if (first->hash == second->hash) + return 0; + + return 1; +} + +char * translateString(char * str) { + unsigned int sum = 0, xor = 0; + int len = 0; + char * chptr; + struct aString * match; + struct aString key; + + for (chptr = str; *chptr; chptr++) { + sum += *chptr; + xor ^= *chptr; + len++; + } + + key.hash = (sum << 16) | ((xor & 0xFF) << 8) | (len & 0xFF); + match = bsearch(&key, strings, numStrings, sizeof(*strings), aStringCmp); + if (!match) + return str; + + return match->str; +} + +static struct langInfo * languages = NULL; +static int numLanguages = 0; + +static void loadLanguageList(void) { + char * file = FL_TESTING(flags) ? "../lang-table" : + "/etc/lang-table"; + FILE * f; + char line[256]; + char name[256], key[256], font[256], code[256], + keyboard[256], timezone[256]; + int lineNum = 0; + + wcwidth(0); + f = fopen(file, "r"); + if (!f) { + newtWinMessage(_("Error"), _("OK"), "cannot open %s: %m", file); + return; + } + + while (fgets(line, sizeof(line), f)) { + lineNum++; + languages = realloc(languages, sizeof(*languages) * (numLanguages + 1)); + if (sscanf(line, "%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\n", + name, key, font, code, keyboard, timezone) != 6) { + printf("bad line %d in lang-table", lineNum); + logMessage(WARNING, "bad line %d in lang-table", lineNum); + } else { + languages[numLanguages].lang = strdup(name); + languages[numLanguages].key = strdup(key); + languages[numLanguages].font = strdup(font); + languages[numLanguages].lc_all = strdup(code); + languages[numLanguages++].keyboard = strdup(keyboard); + } + } + fclose(f); +} + +int getLangInfo(struct langInfo ** langs) { + if (!languages) + loadLanguageList(); + + *langs = languages; + return numLanguages; +} + +void loadLanguage (char * file) { + char filename[200]; + gzFile stream; + int fd, hash, rc; + char * key = getenv("LANGKEY"); + + if (strings) { + free(strings), strings = NULL; + numStrings = allocedStrings = 0; + } + + /* english requires no files */ + if (!strcmp(key, "en")) + return; + + if (!file) { + file = filename; + if (FL_TESTING(flags)) + sprintf(filename, "loader.tr"); + else + sprintf(filename, "/etc/loader.tr"); + } + + stream = gunzip_open(file); + + if (!stream) { + newtWinMessage("Error", "OK", "Translation for %s is not available. " + "The Installation will proceed in English.", key); + return ; + } + + sprintf(filename, "%s.tr", key); + + rc = installCpioFile(stream, filename, "/tmp/translation", 1); + gunzip_close(stream); + + if (rc || access("/tmp/translation", R_OK)) { + newtWinMessage("Error", "OK", "Cannot get translation file %s.\n", + filename); + return; + } + + fd = open("/tmp/translation", O_RDONLY); + if (fd < 0) { + newtWinMessage("Error", "OK", "Failed to open /tmp/translation: %m\n"); + return; + } + + while (read(fd, &hash, 4) == 4) { + if (allocedStrings == numStrings) { + allocedStrings += 10; + strings = realloc(strings, sizeof(*strings) * allocedStrings); + } + + strings[numStrings].hash = ntohl(hash); + rc = read(fd, &strings[numStrings].length, 2); + strings[numStrings].length = ntohs(strings[numStrings].length); + strings[numStrings].str = malloc(strings[numStrings].length + 1); + rc = read(fd, strings[numStrings].str, strings[numStrings].length); + strings[numStrings].str[strings[numStrings].length] = '\0'; + numStrings++; + } + + close(fd); + unlink("/tmp/translation"); + + qsort(strings, numStrings, sizeof(*strings), aStringCmp); +} + + +/* give the index of the language to set to -- sets the appropriate + * lang variables if we have a font. + * + * ASSUMPTION: languages exists + */ +static void setLangEnv (int i) { + if (i > numLanguages) + return; + + if (strcmp(languages[i].font, "latarcyrheb-sun16")) + return; + logMessage(INFO, "setting language to %s", languages[i].lc_all); + + setenv("LANG", languages[i].lc_all, 1); + setenv("LANGKEY", languages[i].key, 1); + setenv("LINGUAS", languages[i].lang, 1); + loadLanguage (NULL); +} + +/* choice is the index of the chosen language in languages */ +static int setupLanguage(int choice, int forced) { + char * buf; + int i; + + logMessage(DEBUGLVL, "going to set language to %s", languages[choice].lc_all); + /* load the language only if it is displayable. if they're using + * a serial console or iSeries vioconsole, we hope it's smart enough */ + if ((strcmp(languages[choice].font, "latarcyrheb-sun16") && !FL_SERIAL(flags) && + !FL_VIRTPCONSOLE(flags) && !isVioConsole())) { + if (forced == 1) return 0; + + newtWinMessage("Language Unavailable", "OK", + "%s display is unavailable in text mode. The " + "installation will continue in English until the " + "display of %s is possible.", languages[choice].lang, + languages[choice].lang); + setLangEnv(english); + return 0; + } + + setLangEnv (choice); + + /* clear out top line */ + buf = alloca(80); + for (i=0; i < 80; i++) + buf[i] = ' '; + newtDrawRootText(0, 0, buf); + + char *fmt = FL_RESCUE(flags) ? _(topLineWelcomeRescue) : _(topLineWelcome); + if (asprintf(&buf, fmt, getProductName(), getProductArch()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + newtDrawRootText(0, 0, buf); + free(buf); + newtPopHelpLine(); + newtPushHelpLine(_(bottomHelpLine)); + + return 0; + +} + +/* this is pretty simple. we want to break down the language specifier + * into its short form (eg, en_US) + */ +static char * getLangShortForm(char * oldLang) { + char * lang; + char * c; + + lang = strdup(oldLang); + + c = strchr(lang, '@'); + if (c) { + *c = '\0'; + } + + c = strchr(lang, '.'); + if (c) { + *c = '\0'; + } + + return lang; +} + +/* return the nick of a language -- eg en_US -> en */ +static char * getLangNick(char * oldLang) { + char * lang; + char * c; + + lang = strdup(oldLang); + + c = strchr(lang, '_'); + if (c) { + *c = '\0'; + } + + return lang; +} + +int setLanguage (char * key, int forced) { + int i; + + if (!languages) loadLanguageList(); + + for (i = 0; i < numLanguages; i++) { + if (!strcmp(languages[i].lc_all, key)) { + return setupLanguage(i, forced | !FL_KICKSTART(flags)); + } + } + + /* we didn't specify anything that's exactly in the lang-table. check + * against short forms and nicks */ + for (i = 0; i < numLanguages; i++) { + if (!strcmp(getLangShortForm(languages[i].lc_all), key)) { + return setupLanguage(i, forced | !FL_KICKSTART(flags)); + } + } + + for (i = 0; i < numLanguages; i++) { + if (!strcmp(getLangNick(languages[i].lc_all), key)) { + return setupLanguage(i, forced | !FL_KICKSTART(flags)); + } + } + + logMessage(ERROR, "unable to set to requested language %s", key); + return -1; +} + +int chooseLanguage(char ** lang) { + int choice = 0; + char ** langs; + int i; + int current = -1; + char * currentLangName = getenv("LANG"); + int numLangs = 0; + char * langPicked; + + if (!languages) loadLanguageList(); + + langs = alloca(sizeof(*langs) * (numLanguages + 1)); + + for (i = 0; i < numLanguages; i++) { + if (!strncmp(languages[i].key, "en", 2)) + english = numLangs; + if (currentLangName && + !strcmp(languages[i].lang, currentLangName)) + current = numLangs; + + langs[numLangs++] = languages[i].lang; + } + + langs[numLangs] = NULL; + + if (current >= 0) + choice = current; + else + choice = english; + + if (!FL_CMDLINE(flags)) + newtWinMenu(_("Choose a Language"), + _("What language would you like to use during the " + "installation process?"), 40, 5, 5, 8, + langs, &choice, _("OK"), NULL); + + langPicked = langs[choice]; + for (i = 0; i < numLanguages; i++) { + if (!strcmp(langPicked, languages[i].lang)) { + *lang = languages[i].lc_all; + choice = i; + break; + } + } + + /* this can't happen */ + if (i == numLanguages) abort(); + + return setupLanguage(choice, 0); +} + +void setKickstartLanguage(struct loaderData_s * loaderData, int argc, + char ** argv) { + if (argc < 2) { + logMessage(ERROR, "no argument passed to lang kickstart command"); + return; + } + + loaderData->lang = argv[1]; + loaderData->lang_set = 1; +} diff --git a/loader/lang.h b/loader/lang.h new file mode 100644 index 000000000..965f5a0ef --- /dev/null +++ b/loader/lang.h @@ -0,0 +1,41 @@ +/* + * lang.h + * + * Copyright (C) 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/>. + */ + +#ifndef _LANG_H_ +#define _LANG_H_ + +#include "loader.h" + +#define _(x) translateString (x) +#define N_(foo) (foo) + +struct langInfo { + char * lang, * key, * font, * lc_all, * keyboard; +} ; + + +int chooseLanguage(char ** lang); +char * translateString(char * str); +int setLanguage (char * key, int forced); +int getLangInfo(struct langInfo **langs); + +void setKickstartLanguage(struct loaderData_s * loaderData, int argc, + char ** argv); + +#endif /* _LANG_H_ */ diff --git a/loader/linuxrc.s390 b/loader/linuxrc.s390 new file mode 100644 index 000000000..7facd3b19 --- /dev/null +++ b/loader/linuxrc.s390 @@ -0,0 +1,674 @@ +#! /bin/sh +# +# Copyright (C) 2000-2004 by +# Bernhard Rosenkraenzer <bero@redhat.com> +# Oliver Paukstadt <opaukstadt@millenux.com> +# Karsten Hopp <karsten@redhat.de> +# Florian La Roche <laroche@redhat.com> +# Nils Philippsen <nils@redhat.de> +# Helge Deller <hdeller@redhat.de> +# David Sainty <dsainty@redhat.com> +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +VERSION=1.1 + +export TEXTDOMAIN=s390installer +export TEXTDOMAINDIR=/usr/lib/locale + +# check IP address format +# param: IP string +# return: 0 (valid IP) or 1 (invalid IP) +checkip() +{ + checkipv6 $1 + a=$? + checkipv4 $1 + b=$? + + if [ $a -eq 1 ] || [ $b -eq 1 ]; then + return 1 + else + return 0 + fi +} + +checkipv6() +{ + ip=$1 + echo $ip | awk -F':' 'BEGIN{ error = 0} { if (NF > 8) error=1; i = 1; while (i++<=NF) {if (!match(toupper($i), "^[0-9A-F]*$")){ error=1}}exit error}' + return $?; +} + +checkipv4() +{ + ip=$1 + echo $ip | awk -F'.' '{ if (NF != 4) { exit 1 } i=1; while (i<=NF) { if ($i>255 || $i<0) { exit 1 }; i=i+1; } exit 0 }' + return $? +} + +doshutdown() +{ + exec /sbin/shutdown + exit 0 +} + +doreboot() +{ + # find out the location of /boot and use it to re-ipl + boot="$(cat /proc/mounts | grep " /mnt/sysimage/boot " | awk -F" " '{print $1}')" + if [ -z $boot ]; then + # use root if /boot not used + boot="$(cat /proc/mounts | grep " /mnt/sysimage " | awk -F" " '{print $1}')" + fi + + # lookup dasd disk + echo $boot | grep "dasd" > /dev/null + if [ $? -eq 0 ]; then + type="ccw" + boot="$(basename $boot)" + # strip partition number from dasd device + boot="$(echo ${boot%[0-9]})" + id="$(basename $(readlink /sys/block/$boot/device))" + echo $type > /sys/firmware/reipl/reipl_type + echo $id > /sys/firmware/reipl/$type/device + else + # scsi re-ipl only supported on newer machines + doshutdown + exit 0 + fi + + echo $"about to exec shutdown -r" + exec /sbin/shutdown -r + exit 0 +} + +sysecho () { + file=$1 + shift + i=1 + while [ $i -le 10 ] ; do + if [ ! -f $file ]; then + sleep 1 + i=$((i+1)) + else + break + fi + done + [ -f $file ] && echo $* > $file +} + + +startinetd() +{ + echo + echo $"Starting telnetd and sshd to allow login over the network." + echo $"Welcome to the anaconda install environment $VERSION for $S390ARCH" > /etc/issue.net + echo $"Welcome to the anaconda install environment $VERSION for $S390ARCH" > /etc/motd + echo >> /etc/motd + + /sbin/xinetd -stayalive -reuse -pidfile /tmp/xinetd.pid + /sbin/sshd + if [ -z "$RUNKS" ]; then + echo + echo $"Connect now to $IPADDR to start the installation." + read + while : ; do + /bin/sh --login + [ $? = 0 ] || break + done + fi +} + +# read file from CMS and write it to /tmp +readcmsfile() # $1=dasdport $2=filename +{ + local dev + if [ $# -ne 2 ]; then return; fi + mknod /dev/dasda b 94 0 + insmod dasd_mod$LO dasd=$1 + insmod dasd_eckd_mod$LO + cmsfscat -d /dev/dasda -a $2 > /tmp/$2 + if [ ${#1} == 3 ]; then + dev="0.0.0${1}" + elif [ ${#1} == 4 ]; then + dev="0.0.${1}" + fi + sysecho /sys/bus/ccw/drivers/dasd-eckd/$dev/online 0 + rmmod dasd_eckd_mod + rmmod dasd_mod +} + +setupdevice() +{ + if [ -z "$SUBCHANNELS" -o -z "$NETTYPE" ]; then + echo $"SUBCHANNELS or NETTYPE empty, cannot continue." + exit 1 + fi + SYSDIR=${SUBCHANNELS//,*/} # get first subchannel. This is where the device can be brought online + sysecho /sys/bus/ccwgroup/drivers/${NETTYPE}/group "$SUBCHANNELS" + if [ -n "$PORTNAME" ]; then + if [ "$NETTYPE" = "lcs" ]; then + sysecho /sys/bus/ccwgroup/drivers/${NETTYPE}/${SYSDIR}/portno "$PORTNAME" + else + sysecho /sys/bus/ccwgroup/drivers/${NETTYPE}/${SYSDIR}/portname "$PORTNAME" + fi + fi + if [ -n "$CTCPROT" -a "$NETTYPE" = "ctc" ]; then + sysecho /sys/bus/ccwgroup/drivers/ctc/${SYSDIR}/protocol "$CTCPROT" + fi + if [ -n "$LAYER2" -a "$NETTYPE" = "qeth" ]; then + sysecho /sys/bus/ccwgroup/drivers/qeth/${SYSDIR}/layer2 "$LAYER2" + fi + sysecho /sys/bus/ccwgroup/drivers/${NETTYPE}/${SYSDIR}/online 1 +} + +createDevices() +{ + awk '{ printf("mknod /dev/%s %s %s %s\n", $1, $2, $3, $4); + printf("chmod %s /dev/%s\n", $5, $1); + printf("chown %s /dev/%s\n", $6, $1); + }' <<EOF | sh + console c 5 1 600 root:root + null c 1 3 666 root:root + zero c 1 5 666 root:root + mem c 1 1 600 root:root + ptmx c 5 2 666 root:root + tty c 5 0 666 root:root + tty0 c 4 0 600 root:tty + tty1 c 4 1 600 root:tty + random c 1 8 644 root:root + urandom c 1 9 644 root:root + rtc c 10 135 644 root:root +EOF + for i in 2 3 4 5 6 7 8 9 ; do + ln -s console /dev/tty$i + done + mkdir /dev/pts +} + +S390ARCH=`uname -m` +if [ "$S390ARCH" = "s390" ]; then + export S390ARCH="S/390" +else + export S390ARCH="zSeries" +fi + +echo $"Starting the $S390ARCH initrd to configure networking. Version is $VERSION" + + +# set up env vars as we do in init.c +if [ `uname -m` = "s390x" ]; then + LD_LIBRARY_PATH=/lib64:/usr/lib64:/usr/X11R6/lib64:/usr/kerberos/lib64:/lib:/usr/lib:/usr/X11R6/lib:/usr/kerberos/lib +else + LD_LIBRARY_PATH=/lib:/usr/lib:/usr/X11R6/lib:/usr/kerberos/lib +fi +export LD_LIBRARY_PATH + +PATH="$PATH:/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sysimage/bin:/mnt/sysimage/usr/bin:/mnt/sysimage/usr/sbin:/mnt/sysimage/sbin:/mnt/sysimage/usr/X11R6/bin" +export PATH +HOME=/ +export HOME +PYTHONPATH=/tmp/updates +export PYTHONPATH + +mount -t proc none /proc +mount -t tmpfs none /dev +createDevices +mount -t devpts /dev/pts /dev/pts +mount -t sysfs none /sys +# remount root fs rw +mount /dev/root / -o remount,rw + +# limit output on x3270 console (well, not really -- need debugging now) +echo "8 4 1 1" > /proc/sys/kernel/printk + +# make /tmp/ramfs +mount -t ramfs none /tmp + +ifconfig lo 127.0.0.1 netmask 255.0.0.0 +route add -host 127.0.0.1 dev lo 2>/dev/null + +echo -e "127.0.0.1\tlocalhost.localdomain localhost" > /etc/hosts +echo -e "::1\t\tlocalhost6.localdomain6 localhost6" >> /etc/hosts + +LO="" + +[ -L /sbin/insmod ] && LO=".ko" + +# Parse configuration +if [ -n "$CMSDASD" -a -n "$CMSCONFFILE" ]; then + readcmsfile $CMSDASD $CMSCONFFILE + source /tmp/$CMSCONFFILE #2>/dev/null +fi + +do_net_install="yes" + +if [ -r /sys/firmware/ipl/ipl_type ]; then + if [ "`cat /sys/firmware/ipl/ipl_type`" = "fcp" ]; then + while [ 1 ]; do + echo $"Your IPL device is set to FCP." + echo $"Would you like to perform a CD-ROM/DVD-ROM installation? (y/n)" + read do_cd_install + case $do_cd_install in + y|Y|[Yy][Ee][Ss]) + # set up FCP cdrom here + CD_DEVICE="`cat /sys/firmware/ipl/device`" + WWPN="`cat /sys/firmware/ipl/wwpn`" + LUN="`cat /sys/firmware/ipl/lun`" + echo 1 > /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/online + echo $WWPN > /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/port_add + echo $LUN > /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/$WWPN/unit_add + do_net_install="no" + break + ;; + n|N|[Nn][Oo]) + do_net_install="yes" + break + ;; + *) + echo + echo $"*** INVALID ANSWER: $do_cd_install" + echo + unset do_cd_install + ;; + esac + done + fi +fi + +if [ "$do_net_install" = "yes" ]; then + # Perform a network installation + # Check for missing parameters, prompt for them if necessary + while [ -z "$NETTYPE" ]; do + echo $"Which kind of network device do you intend to use" + echo $" (e.g. ctc, iucv, qeth, lcs)." + echo $"Enter 'qeth' for OSA-Express Fast Ethernet, Gigabit Ethernet" + echo $" (including 1000Base-T), High Speed Token Ring, and ATM " + echo $" (running Ethernet LAN emulation) features in QDIO mode." + echo $"Enter 'lcs' for OSA2 Ethernet/Token Ring, OSA-Express Fast Ethernet in" + echo $" non-QDIO mode, OSA-Express High Speed Token Ring in non-QDIO mode and" + echo $" Gigabit Ethernet in non-QDIO mode." + read NETTYPE + done + if [ "$NETTYPE" != "iucv" ]; then # iucv is the only interface without ccw config + if [ -n "$CHANDEV" ]; then + echo + echo $"The CHANDEV variable isn't used anymore, please update your " + echo $".parm or the .conf file" + echo + fi + while [ -z "$SUBCHANNELS" ]; do + echo $"Enter the bus ID and the device number of your CCW devices." + echo $"CTC/ESCON and LCS need two subchannels:" + echo $"(e.g. \"0.0.0600,0.0.0601\" will configure the CTC or ESCON interface" + echo $"with the subchannels 0x600 and 0x601)" + echo $"QETH needs three subchannels p.e. 0.0.0300,0.0.0301,0.0.0302" + read SUBCHANNELS + done + SUBCHANNELS=`echo $SUBCHANNELS | /sbin/busybox tr ABCDEF abcdef` + if [ "$NETTYPE" = "qeth" ]; then + if [ -z "$PORTNAME" ]; then + echo $"Portname of the OSA-Express feature in QDIO mode and z/VM Guest LAN" + echo $"This parameter is optional with z/VM 4.4.0 or z/VM 4.3.0 with" + echo $"APARs VM63308 and PQ73878" + echo $"Press enter if you don't want to enter a portname" + read PORTNAME + fi + if [ -z "$LAYER2" ]; then + echo $"Enter the mode of operation for the OSA device" + echo $"0 for layer 3 mode (default)" + echo $"1 for layer 2 mode" + read LAYER2 + fi + if [ "$LAYER2" == 1 ]; then + if [ -z "$VSWITCH" -o "$VSWITCH" == 0 ]; then + if [ -z "$MACADDR" ]; then + echo $"Enter a unique MAC address (eg. 02:00:00:00:00:00)." + echo $"Leave this blank and press enter if connecting to a" + echo $"Layer 2 VSWITCH, as this is automatically assigned" + read MACADDR + fi + fi + fi + fi + fi + + while [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]; do + echo $"Enter the FQDN of your new Linux guest (e.g. s390.redhat.com):" + read HOSTNAME + done + while [ -z "$IPADDR" ]; do + echo $"Enter a valid IP address of your new Linux guest:" + read IPADDR + checkip $IPADDR + ret=$? + if [ $ret -eq 1 ]; then + echo -n "Invalid IP address format. " + unset IPADDR + fi + done + while [ -z "$NETWORK" ]; do + echo $"Enter a valid network address of the new Linux guest:" + read NETWORK + checkip $NETWORK + ret=$? + if [ $ret -eq 1 ]; then + echo -n "Invalid network address format. " + unset NETWORK + fi + done + if [ "$NETTYPE" = "qeth" ] || [ "$NETTYPE" = "lcs" ]; then + while [ -z "$NETMASK" ]; do + echo $"Enter the netmask for the new Linux guest (e.g. 255.255.255.0):" + read NETMASK + checkip $NETMASK + ret=$? + if [ $ret -eq 1 ]; then + echo -n "Invalid netmask format. " + unset NETMASK + fi + done + while [ -z "$BROADCAST" ]; do + echo $"Enter the broadcast address for the new Linux guest:" + read BROADCAST + checkip $BROADCAST + ret=$? + if [ $ret -eq 1 ]; then + echo -n "Invalid broadcast address format. " + unset BROADCAST + fi + done + while [ -z "$GATEWAY" ]; do + echo $"Enter your default gateway:" + read GATEWAY + checkip $GATEWAY + ret=$? + if [ $ret -eq 1 ]; then + echo -n "Invalid gateway address format. " + unset GATEWAY + fi + done + if [ ":$NETTYPE" = ":lcs" ]; then + if [ -n "$RUNKS" -a -z "$PORTNAME" ]; then + PORTNAME=0 + fi + while [ -z "$PORTNAME" ]; do + echo $"Enter the relative port number of your LCS device" + echo $"(required for OSA-Express ATM cards only):" + read PORTNAME + done + fi + else # ctc0, iucv0 + if [ -z "$NETMASK" ]; then + # If the user did not supply netmask, we add the right one. + NETMASK="255.255.255.255" + fi + while [ -z "$GATEWAY" ]; do + echo $"Enter the IP of your CTC / ESCON / IUCV point-to-point partner:" + read GATEWAY + done + + if [ "$NETTYPE" = "ctc" ]; then + if [ -z "$MTU" ]; then + MTU="1500" + fi + if [ -z "$RUNKS" ]; then + if [ -n "$CTCPROT" ]; then + validprot=1 + else + validprot=0 + fi + while [ "$validprot" = "0" ]; do + echo $"Select which protocol should be used for the CTC interface" + echo $"0 for compatibility with p.e. VM TCP service machine (default)" + echo $"1 for enhanced package checking for Linux peers" + echo $"3 for compatibility with OS/390 or z/OS peers" + read CTCPROT + case "x$CTCPROT" in + x|x0) + validprot=1 + unset CTCPROT + ;; + x1|x3) + validprot=1 + ;; + x2) + echo $"CTC tty's are not usable for this installation" + ;; + *) + echo $"Invalid selection" + ;; + esac + done + fi + fi + if [ ":$NETTYPE" = ":iucv" ]; then + while [ -z "$PEERID" ]; do + echo $"Enter the peer id of the VM guest you want to" + echo $"connect to (in capital letters)." + read PEERID + done + fi + fi + # don't ask for MTU, but use it if it has been set in the .parm file + # don't overwrite MMTU if it has been set for CTC + if [ -n "$MTU" -a -z "$MMTU" ]; then + MMTU="mtu $MTU" + fi + + # configure network-interface + if [ ":$NETTYPE" = ":ctc" ]; then + insmod ccwgroup$LO + insmod cu3088$LO + insmod fsm$LO + insmod ctc$LO + setupdevice + DEVICE=${NETTYPE}0 + ifconfig $DEVICE $IPADDR $MMTU pointopoint $GATEWAY + echo "alias $DEVICE ctc" >> /tmp/modprobe.conf + elif [ ":$NETTYPE" = ":iucv" ]; then + insmod fsm$LO + insmod iucv$LO + insmod netiucv$LO + sysecho /sys/bus/iucv/drivers/netiucv/connection $PEERID + DEVICE=${NETTYPE}0 + ifconfig $DEVICE $IPADDR $MMTU pointopoint $GATEWAY + echo "alias $DEVICE netiucv" >> /tmp/modprobe.conf + elif [ "$NETTYPE" = "lcs" ]; then + insmod ccwgroup$LO + insmod cu3088$LO + insmod lcs$LO + setupdevice + # KH FIXME: Workaround for missing sysfs interface + # DEVICE=`cat /sys/devices/lcs/${SUBCHANNELS//,*/}/if_name` + DEVICE=eth0 + ifconfig $DEVICE $IPADDR $MMTU netmask $NETMASK broadcast $BROADCAST + route add -net $NETWORK netmask $NETMASK dev $DEVICE 2>/dev/null + echo "alias $DEVICE lcs" >> /tmp/modprobe.conf + elif [ "$NETTYPE" = "qeth" ]; then + insmod ccwgroup$LO + insmod crypto_api$LO + insmod xfrm_nalgo$LO + insmod qdio$LO + insmod ipv6$LO + insmod qeth$LO + setupdevice + DEVICE=`cat /sys/devices/qeth/${SUBCHANNELS//,*/}/if_name` + if [ -n "$LAYER2" -a -n "$MACADDR" ]; then + ifconfig $DEVICE hw ether $MACADDR + fi + ifconfig $DEVICE $IPADDR $MMTU netmask $NETMASK broadcast $BROADCAST + route add -net $NETWORK netmask $NETMASK dev $DEVICE 2>/dev/null + echo "alias $DEVICE qeth" >> /tmp/modprobe.conf + else + echo $"Unknown network device, aborting installation" + exit 1 + fi + + route add default gw $GATEWAY dev $DEVICE 2>/dev/null + # BH FIXME: Workaround for manual MACADDR, need ping to update arp table + ping -c 1 $GATEWAY > /dev/null + + if [ -z "$DNS" ]; then + echo $"Enter your DNS server(s), separated by colons (:):" + read DNS + fi + if [ -z "$DNS" ]; then + echo $"You might encounter problems without a nameserver, especially" + echo $"with FTP installs" + fi + + if [ -n "$DNS" -a -z "$SEARCHDNS" ]; then + echo $"Enter your DNS search domain(s) (if any), separated by colons (:):" + read SEARCHDNS + fi + + [ -n "$HOSTNAME" ] && hostname $HOSTNAME + + # show interfaces and routing table + ifconfig -a + route -n + + # convert to space-separated lists + if [ -n "$SEARCHDNS" ]; then + SEARCHDNS=`echo $SEARCHDNS |sed -e 's/:/ /g'` + for i in "$SEARCHDNS"; do echo "search $i"; done >> /etc/resolv.conf + fi + if [ -n "$DNS" ]; then + RESOLVDNS=`echo $DNS |sed -e 's/:/ /g'` + for i in $RESOLVDNS; do echo "nameserver $i"; done >> /etc/resolv.conf + fi + + # make sure we have an /etc/hosts file (required for telnetd) + if [ ! -z "$HOSTNAME" -a ! -z "$IPADDR" ]; then + echo -e "$IPADDR\t$HOSTNAME `echo $HOSTNAME | cut -d '.' -f 1`" >> /etc/hosts + fi +fi + +if [ -z "$DASD" ]; then + echo + echo $"Enter DASD range (e.g. 200-203 or 200,201,202,203)" + echo $"Press <Enter> for autoprobing (not recommended):" + echo + read DASD +fi +if [ -n "$DASD" ]; then + echo "DASD=$DASD" > /tmp/dasd_ports +fi + +for i in ${!FCP_*}; do + echo "${!i}" >> /tmp/fcpconfig +done + +grep -q ext3 /proc/filesystems +if [ "$?" != "0" ]; then + insmod jbd$LO + insmod ext3$LO +fi + +# transfer options into install environment +cat > /tmp/install.cfg << EOF +LANG="$LANG" +S390ARCH="$S390ARCH" +TEXTDOMAIN="$TEXTDOMAIN" +TEXTDOMAINDIR="$TEXTDOMAINDIR" +PORTNAME="$PORTNAME" +HOSTNAME="$HOSTNAME" +DEVICE="$DEVICE" +NETTYPE="$NETTYPE" +IPADDR="$IPADDR" +GATEWAY="$GATEWAY" +MTU="$MTU" +NETWORK="$NETWORK" +NETMASK="$NETMASK" +BROADCAST="$BROADCAST" +DNS="`echo $DNS | cut -d ':' -f 1`" +SEARCHDNS="$SEARCHDNS" +PEERID="$PEERID" +SUBCHANNELS="$SUBCHANNELS" +ONBOOT="yes" +CTCPROT="$CTCPROT" +export LANG PORTNAME S390ARCH TEXTDOMAIN TEXTDOMAINDIR +export HOSTNAME DEVICE NETTYPE IPADDR GATEWAY MTU +export NETWORK NETMASK BROADCAST DNS SEARCHDNS +export PEERID ONBOOT SUBCHANNELS CTCPROT +EOF +# immediately read it in again to export these into the shell below +. /tmp/install.cfg +cat /tmp/install.cfg >> /etc/profile +cat > /tmp/netinfo << EOF +DEVICE=$DEVICE +ONBOOT=yes +BOOTPROTO=static +IPADDR=$IPADDR +NETMASK=$NETMASK +GATEWAY=$GATEWAY +BROADCAST=$BROADCAST +HOSTNAME=$HOSTNAME +MTU=$MTU +SUBCHANNELS=$SUBCHANNELS +EOF +[ "$DNS" != "" ] && echo "DNS=`echo $DNS | cut -d ':' -f 1`" >> /tmp/netinfo +[ "$NETTYPE" != "" ] && echo "NETTYPE=$NETTYPE" >> /tmp/netinfo +[ "$PEERID" != "" ] && echo "PEERID=$PEERID" >> /tmp/netinfo +[ "$PORTNAME" != "" ] && echo "PORTNAME=$PORTNAME" >> /tmp/netinfo +[ "$CTCPROT" != "" ] && echo "CTCPROT=$CTCPROT" >> /tmp/netinfo +[ "$LAYER2" != "" ] && echo "LAYER2=$LAYER2" >> /tmp/netinfo +[ "$MACADDR" != "" ] && echo "MACADDR=$MACADDR" >> /tmp/netinfo + +# so that the vars get propagated into the sshd shells +mkdir /.ssh +cat >> /.ssh/environment <<EOF +LD_LIBRARY_PATH=$LD_LIBRARY_PATH +PATH=$PATH +HOME=$HOME +PYTHONPATH=$PYTHONPATH +EOF + +cat >> /etc/profile <<EOF +LD_LIBRARY_PATH=$LD_LIBRARY_PATH +PATH=$PATH +HOME=$HOME +PYTHONPATH=$PYTHONPATH +export LD_LIBRARY_PATH PATH HOME PYTHONPATH +EOF + +if [ -n "$DISPLAY" ]; then + echo "export DISPLAY=$DISPLAY" >> /etc/profile +fi + +# I'm tired of typing this out... +echo "loader" >> /.bash_history + +echo -n $$ > /var/run/init.pid +# shutdown (halt) on SIGUSR1 +trap doshutdown SIGUSR1 +# reboot on SIGUSR2 +trap doreboot SIGUSR2 + +startinetd + +if [ -n "$RUNKS" ]; then + /sbin/loader +fi + +doreboot + +# ;;; Local Variables: *** +# ;;; mode: sh *** +# ;;; tab-width:3 *** +# ;;; end: *** +# vim:ts=3:sw=3 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: */ diff --git a/loader/loader.h b/loader/loader.h new file mode 100644 index 000000000..590632f24 --- /dev/null +++ b/loader/loader.h @@ -0,0 +1,161 @@ +/* + * loader.h + * + * Copyright (C) 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/>. + */ + +#include <stdint.h> + +#ifndef LOADER_H +#define LOADER_H + +#define LOADER_OK 0 +#define LOADER_BACK 1 +#define LOADER_NOOP 2 +#define LOADER_ERROR -1 + +#define LOADER_FLAGS_TESTING (((uint64_t) 1) << 0) +/* #1 unused */ +#define LOADER_FLAGS_TEXT (((uint64_t) 1) << 2) +#define LOADER_FLAGS_RESCUE (((uint64_t) 1) << 3) +#define LOADER_FLAGS_KICKSTART (((uint64_t) 1) << 4) +#define LOADER_FLAGS_KICKSTART_SEND_MAC (((uint64_t) 1) << 5) +#define LOADER_FLAGS_POWEROFF (((uint64_t) 1) << 6) +#define LOADER_FLAGS_NOPROBE (((uint64_t) 1) << 7) +#define LOADER_FLAGS_MODDISK (((uint64_t) 1) << 8) +/* #9 is the most delicious of all flags, also available for use */ +#define LOADER_FLAGS_SERIAL (((uint64_t) 1) << 10) +#define LOADER_FLAGS_UPDATES (((uint64_t) 1) << 11) +#define LOADER_FLAGS_KSFILE (((uint64_t) 1) << 12) +#define LOADER_FLAGS_HALT (((uint64_t) 1) << 13) +#define LOADER_FLAGS_SELINUX (((uint64_t) 1) << 14) +#define LOADER_FLAGS_VIRTPCONSOLE (((uint64_t) 1) << 15) +/* #16 unused */ +#define LOADER_FLAGS_NOSHELL (((uint64_t) 1) << 17) +#define LOADER_FLAGS_NOPCMCIA (((uint64_t) 1) << 18) +#define LOADER_FLAGS_TELNETD (((uint64_t) 1) << 19) +#define LOADER_FLAGS_NOPASS (((uint64_t) 1) << 20) +/* #21 unused */ +#define LOADER_FLAGS_MEDIACHECK (((uint64_t) 1) << 22) +/* #23 unused */ +#define LOADER_FLAGS_ASKMETHOD (((uint64_t) 1) << 24) +#define LOADER_FLAGS_ASKNETWORK (((uint64_t) 1) << 25) +/* #26 unused */ +/* #27 unused */ +#define LOADER_FLAGS_CMDLINE (((uint64_t) 1) << 28) +#define LOADER_FLAGS_GRAPHICAL (((uint64_t) 1) << 29) +#define LOADER_FLAGS_NOIPV4 (((uint64_t) 1) << 31) +#define LOADER_FLAGS_NOIPV6 (((uint64_t) 1) << 32) +#define LOADER_FLAGS_IP_PARAM (((uint64_t) 1) << 33) +#define LOADER_FLAGS_IPV6_PARAM (((uint64_t) 1) << 34) +#define LOADER_FLAGS_IS_KICKSTART (((uint64_t) 1) << 35) +#define LOADER_FLAGS_ALLOW_WIRELESS (((uint64_t) 1) << 36) +#define LOADER_FLAGS_HAVE_CMSCONF (((uint64_t) 1) << 37) + +#define FL_TESTING(a) ((a) & LOADER_FLAGS_TESTING) +#define FL_TEXT(a) ((a) & LOADER_FLAGS_TEXT) +#define FL_RESCUE(a) ((a) & LOADER_FLAGS_RESCUE) +#define FL_KICKSTART(a) ((a) & LOADER_FLAGS_KICKSTART) +#define FL_KICKSTART_SEND_MAC(a) ((a) & LOADER_FLAGS_KICKSTART_SEND_MAC) +#define FL_POWEROFF(a) ((a) & LOADER_FLAGS_POWEROFF) +#define FL_NOPROBE(a) ((a) & LOADER_FLAGS_NOPROBE) +#define FL_MODDISK(a) ((a) & LOADER_FLAGS_MODDISK) +#define FL_SERIAL(a) ((a) & LOADER_FLAGS_SERIAL) +#define FL_UPDATES(a) ((a) & LOADER_FLAGS_UPDATES) +#define FL_KSFILE(a) ((a) & LOADER_FLAGS_KSFILE) +#define FL_NOSHELL(a) ((a) & LOADER_FLAGS_NOSHELL) +#define FL_TELNETD(a) ((a) & LOADER_FLAGS_TELNETD) +#define FL_NOPASS(a) ((a) & LOADER_FLAGS_NOPASS) +#define FL_MEDIACHECK(a) ((a) & LOADER_FLAGS_MEDIACHECK) +#define FL_ASKMETHOD(a) ((a) & LOADER_FLAGS_ASKMETHOD) +#define FL_GRAPHICAL(a) ((a) & LOADER_FLAGS_GRAPHICAL) +#define FL_CMDLINE(a) ((a) & LOADER_FLAGS_CMDLINE) +#define FL_HALT(a) ((a) & LOADER_FLAGS_HALT) +#define FL_SELINUX(a) ((a) & LOADER_FLAGS_SELINUX) +#define FL_VIRTPCONSOLE(a) ((a) & LOADER_FLAGS_VIRTPCONSOLE) +#define FL_ASKNETWORK(a) ((a) & LOADER_FLAGS_ASKNETWORK) +#define FL_NOIPV4(a) ((a) & LOADER_FLAGS_NOIPV4) +#define FL_NOIPV6(a) ((a) & LOADER_FLAGS_NOIPV6) +#define FL_IP_PARAM(a) ((a) & LOADER_FLAGS_IP_PARAM) +#define FL_IPV6_PARAM(a) ((a) & LOADER_FLAGS_IPV6_PARAM) +#define FL_IS_KICKSTART(a) ((a) & LOADER_FLAGS_IS_KICKSTART) +#define FL_ALLOW_WIRELESS(a) ((a) & LOADER_FLAGS_ALLOW_WIRELESS) +#define FL_HAVE_CMSCONF(a) ((a) & LOADER_FLAGS_HAVE_CMSCONF) + +void startNewt(void); +void stopNewt(void); +char * getProductName(void); +char * getProductPath(void); +char * getProductArch(void); + +#include "modules.h" +#include "../isys/devices.h" +/* JKFIXME: I don't like all of the _set attribs, but without them, + * we can't tell if it was explicitly set by kickstart/cmdline or + * if we just got it going through the install. */ +struct loaderData_s { + char * lang; + int lang_set; + char * kbd; + int kbd_set; + char * netDev; + int netDev_set; + char * bootIf; + int bootIf_set; + char * netCls; + int netCls_set; + char *ipv4, *ipv6, *netmask, *gateway, *dns, *hostname, *peerid, *ethtool, *subchannels, *portname, *essid, *wepkey, *nettype, *ctcprot, *layer2, *macaddr; + int mtu; + int noDns; + int dhcpTimeout; + int ipinfo_set; + int ipv6info_set; + char * ksFile; + int method; + char * ddsrc; + void * stage2Data; + char * logLevel; + char * updatessrc; + char * dogtailurl; + char * gdbServer; + char * instRepo; + + pid_t fw_loader_pid; + char *fw_search_pathz; + size_t fw_search_pathz_len; + + moduleInfoSet modInfo; + + int inferredStage2, invalidRepoParam; +}; + +/* 64 bit platforms, definitions courtesy of glib */ +#if defined (__x86_64__) || defined(__ia64__) || defined(__alpha__) || defined(__powerpc64__) || defined(__sparc64__) || defined(__s390x__) +#define POINTER_TO_INT(p) ((int) (long) (p)) +#define INT_TO_POINTER(i) ((void *) (long) (i)) +#else +#define POINTER_TO_INT(p) ((int) (p)) +#define INT_TO_POINTER(i) ((void *) (i)) +#endif + +/* library paths */ +#if defined(__x86_64__) || defined(__s390x__) || defined(__ppc64__) +#define LIBPATH "/lib64:/usr/lib64:/usr/X11R6/lib64:/usr/kerberos/lib64:/mnt/usr/lib64:/mnt/sysimage/lib64:/mnt/sysimage/usr/lib64" +#else +#define LIBPATH "/lib:/usr/lib:/usr/X11R6/lib:/usr/kerberos/lib:/mnt/usr/lib:/mnt/sysimage/lib:/mnt/sysimage/usr/lib" +#endif + +#endif diff --git a/loader/loadermisc.c b/loader/loadermisc.c new file mode 100644 index 000000000..1fd77093f --- /dev/null +++ b/loader/loadermisc.c @@ -0,0 +1,247 @@ +/* + * loadermisc.c - miscellaneous loader functions that don't seem to fit + * anywhere else (yet) (was misc.c) + * JKFIXME: need to break out into reasonable files based on function + * + * Copyright (C) 1999, 2000, 2001, 2002 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 <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "log.h" + +int copyFileFd(int infd, char * dest) { + int outfd; + char buf[4096]; + int i; + int rc = 0; + + outfd = open(dest, O_CREAT | O_RDWR, 0666); + + if (outfd < 0) { + logMessage(ERROR, "failed to open %s: %m", dest); + return 1; + } + + while ((i = read(infd, buf, sizeof(buf))) > 0) { + if (write(outfd, buf, i) != i) { + rc = 1; + break; + } + } + + close(outfd); + + return rc; +} + +int copyFile(char * source, char * dest) { + int infd = -1; + int rc; + + infd = open(source, O_RDONLY); + + if (infd < 0) { + logMessage(ERROR, "failed to open %s: %m", source); + return 1; + } + + rc = copyFileFd(infd, dest); + + close(infd); + + return rc; +} + +char * readLine(FILE * f) { + char buf[1024], *ret; + + ret = fgets(buf, sizeof(buf), f); + + /* chop */ + buf[strlen(buf) - 1] = '\0'; + + return strdup(buf); +} + +/* FIXME: when we only depend on glibc, we could use strvercmp instead */ +/* compare alpha and numeric segments of two versions */ +/* return 1: a is newer than b */ +/* 0: a and b are the same version */ +/* -1: b is newer than a */ +static int rpmvercmp(const char * a, const char * b) +{ + char oldch1, oldch2; + char * str1, * str2; + char * one, * two; + int rc; + int isnum; + + /* easy comparison to see if versions are identical */ + if (!strcmp(a, b)) return 0; + + str1 = alloca(strlen(a) + 1); + str2 = alloca(strlen(b) + 1); + + strcpy(str1, a); + strcpy(str2, b); + + one = str1; + two = str2; + + /* loop through each version segment of str1 and str2 and compare them */ + while (*one && *two) { + while (*one && !isalnum(*one)) one++; + while (*two && !isalnum(*two)) two++; + + str1 = one; + str2 = two; + + /* grab first completely alpha or completely numeric segment */ + /* leave one and two pointing to the start of the alpha or numeric */ + /* segment and walk str1 and str2 to end of segment */ + if (isdigit(*str1)) { + while (*str1 && isdigit(*str1)) str1++; + while (*str2 && isdigit(*str2)) str2++; + isnum = 1; + } else { + while (*str1 && isalpha(*str1)) str1++; + while (*str2 && isalpha(*str2)) str2++; + isnum = 0; + } + + /* save character at the end of the alpha or numeric segment */ + /* so that they can be restored after the comparison */ + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + /* take care of the case where the two version segments are */ + /* different types: one numeric, the other alpha (i.e. empty) */ + if (one == str1) return -1; /* arbitrary */ + /* XXX See patch #60884 (and details) from bugzilla #50977. */ + if (two == str2) return (isnum ? 1 : -1); + + if (isnum) { + /* this used to be done by converting the digit segments */ + /* to ints using atoi() - it's changed because long */ + /* digit segments can overflow an int - this should fix that. */ + + /* throw away any leading zeros - it's a number, right? */ + while (*one == '0') one++; + while (*two == '0') two++; + + /* whichever number has more digits wins */ + if (strlen(one) > strlen(two)) return 1; + if (strlen(two) > strlen(one)) return -1; + } + + /* strcmp will return which one is greater - even if the two */ + /* segments are alpha or if they are numeric. don't return */ + /* if they are equal because there might be more segments to */ + /* compare */ + rc = strcmp(one, two); + if (rc) return (rc < 1 ? -1 : 1); + + /* restore character that was replaced by null above */ + *str1 = oldch1; + one = str1; + *str2 = oldch2; + two = str2; + } + + /* this catches the case where all numeric and alpha segments have */ + /* compared identically but the segment sepparating characters were */ + /* different */ + if ((!*one) && (!*two)) return 0; + + /* whichever version still has characters left over wins */ + if (!*one) return -1; else return 1; +} + +int simpleStringCmp(const void * a, const void * b) { + const char * first = *((const char **) a); + const char * second = *((const char **) b); + + return rpmvercmp(first, second); +} + +/* look for available memory. note: won't ever report more than the + * 900 megs or so supported by the -BOOT kernel due to not using e820 */ +int totalMemory(void) { + int fd; + int bytesRead; + char buf[4096]; + char * chptr, * start; + int total = 0; + + fd = open("/proc/meminfo", O_RDONLY); + if (fd < 0) { + logMessage(ERROR, "failed to open /proc/meminfo: %m"); + return 0; + } + + bytesRead = read(fd, buf, sizeof(buf) - 1); + if (bytesRead < 0) { + logMessage(ERROR, "failed to read from /proc/meminfo: %m"); + close(fd); + return 0; + } + + close(fd); + buf[bytesRead] = '\0'; + + chptr = buf; + while (*chptr && !total) { + if (strncmp(chptr, "MemTotal:", 9)) { + chptr++; + continue; + } + + start = ++chptr ; + while (*chptr && *chptr != '\n') chptr++; + + *chptr = '\0'; + + while (!isdigit(*start) && *start) start++; + if (!*start) { + logMessage(WARNING, "no number appears after MemTotal tag"); + return 0; + } + + chptr = start; + while (*chptr && isdigit(*chptr)) { + total = (total * 10) + (*chptr - '0'); + chptr++; + } + } + + logMessage(INFO, "%d kB are available", total); + + return total; +} diff --git a/loader/loadermisc.h b/loader/loadermisc.h new file mode 100644 index 000000000..8094fa70f --- /dev/null +++ b/loader/loadermisc.h @@ -0,0 +1,31 @@ +/* + * loadermisc.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_LOADER_MISC_H +#define H_LOADER_MISC_H +#include <stdio.h> +#include <stdarg.h> + +int copyFile(char * source, char * dest); +int copyFileFd(int infd, char * dest); +char * readLine(FILE * f); +int simpleStringCmp(const void * a, const void * b); +int totalMemory(void); + +#endif diff --git a/loader/log.c b/loader/log.c new file mode 100644 index 000000000..2872fe845 --- /dev/null +++ b/loader/log.c @@ -0,0 +1,154 @@ +/* + * log.c - logging functionality + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "log.h" + +static FILE * tty_logfile = NULL; +static FILE * file_logfile = NULL; +static int minLevel = INFO; + +static void printLogHeader(int level, FILE *outfile) { + time_t current_time = time(NULL); + struct tm *t = gmtime (¤t_time); + + switch (level) { + case DEBUGLVL: + fprintf (outfile, "%02d:%02d:%02d DEBUG : ", t->tm_hour, + t->tm_min, t->tm_sec); + break; + + case INFO: + fprintf (outfile, "%02d:%02d:%02d INFO : ", t->tm_hour, + t->tm_min, t->tm_sec); + break; + + case WARNING: + fprintf (outfile, "%02d:%02d:%02d WARNING : ", t->tm_hour, + t->tm_min, t->tm_sec); + break; + + case ERROR: + fprintf (outfile, "%02d:%02d:%02d ERROR : ", t->tm_hour, + t->tm_min, t->tm_sec); + break; + + case CRITICAL: + fprintf (outfile, "%02d:%02d:%02d CRITICAL: ", t->tm_hour, + t->tm_min, t->tm_sec); + break; + } +} + +void logMessageV(int level, const char * s, va_list ap) { + + /* Only log to the screen things that are above the minimum level. */ + if (tty_logfile && level >= minLevel) { + va_list apc; + + va_copy(apc, ap); + + printLogHeader(level, tty_logfile); + vfprintf(tty_logfile, s, apc); + fprintf(tty_logfile, "\n"); + fflush(tty_logfile); + + va_end(apc); + } + + /* But log everything to the file. */ + if (file_logfile) { + va_list apc; + + va_copy(apc, ap); + + printLogHeader(level, file_logfile); + vfprintf(file_logfile, s, apc); + fprintf(file_logfile, "\n"); + fflush(file_logfile); + + va_end(apc); + } +} + +void logMessage(int level, const char * s, ...) { + va_list args; + + va_start(args, s); + logMessageV(level, s, args); + va_end(args); +} + +int tty_logfd = -1; +int file_logfd = -1; + +void openLog(int useLocal) { + int flags; + + if (!useLocal) { + tty_logfile = fopen("/dev/tty3", "w"); + file_logfile = fopen("/tmp/anaconda.log", "w"); + } else { + tty_logfile = NULL; + file_logfile = fopen("debug.log", "w"); + } + + if (tty_logfile) { + tty_logfd = fileno(tty_logfile); + flags = fcntl(tty_logfd, F_GETFD, 0) | FD_CLOEXEC; + fcntl(tty_logfd, F_SETFD, flags); + } + + if (file_logfile) { + file_logfd = fileno(file_logfile); + flags = fcntl(file_logfd, F_GETFD, 0) | FD_CLOEXEC; + fcntl(file_logfd, F_SETFD, flags); + } +} + +void closeLog(void) { + if (tty_logfile) + fclose(tty_logfile); + + if (file_logfile) + fclose(file_logfile); +} + +/* set the level. higher means you see more verbosity */ +void setLogLevel(int level) { + minLevel = level; +} + +int getLogLevel(void) { + return minLevel; +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/log.h b/loader/log.h new file mode 100644 index 000000000..78eb44d8c --- /dev/null +++ b/loader/log.h @@ -0,0 +1,44 @@ +/* + * log.h + * + * Copyright (C) 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/>. + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#include <stdio.h> +#include <stdarg.h> + +#define DEBUGLVL 10 +#define INFO 20 +#define WARNING 30 +#define ERROR 40 +#define CRITICAL 50 + +void logMessageV(int level, const char * s, va_list ap) + __attribute__ ((format (printf, 2, 0))); +void logMessage(int level, const char * s, ...) + __attribute__ ((format (printf, 2, 3))); +void openLog(int useLocal); +void closeLog(void); +void setLogLevel(int minLevel); +int getLogLevel(void); + +extern int tty_logfd; +extern int file_logfd; + +#endif /* _LOG_H_ */ diff --git a/loader/mediacheck.c b/loader/mediacheck.c new file mode 100644 index 000000000..d6a0c1d57 --- /dev/null +++ b/loader/mediacheck.c @@ -0,0 +1,126 @@ +/* + * simple program to check implanted md5sum in an iso 9660 image + * + * Copyright (C) 2001, 2005 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): Michael Fulbright <msf@redhat.com> + * Dustin Kirkland <dustin.kirkland@gmail.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <newt.h> +#include <libcheckisomd5.h> + +#include "lang.h" +#include "log.h" + +struct progressCBdata { + newtComponent scale; + newtComponent label; +}; + +static void readCB(void *co, long long pos, long long total) { + struct progressCBdata *data = co; + char tickmark[2] = "-"; + char * ticks = "-\\|/"; + + newtScaleSet(data->scale, pos * 100.0 / total); + *tickmark = ticks[(total / (pos + 1)) % 5]; + + newtLabelSetText(data->label, tickmark); + newtRefresh(); +} + +int doMediaCheck(char *file, char *descr) { + struct progressCBdata data; + newtComponent t, f, scale, label; + int rc; + int dlen; + int llen; + char tmpstr[1024]; + + if (access(file, R_OK) < 0) { + newtWinMessage(_("Error"), _("OK"), _("Unable to find install image " + "%s"), file); + return -1; + } + + if (descr) + snprintf(tmpstr, sizeof(tmpstr), _("Checking \"%s\"..."), descr); + else + snprintf(tmpstr, sizeof(tmpstr), _("Checking media now...")); + + dlen = strlen(tmpstr); + if (dlen > 65) + dlen = 65; + + newtCenteredWindow(dlen+8, 6, _("Media Check")); + t = newtTextbox(1, 1, dlen+4, 3, NEWT_TEXTBOX_WRAP); + + newtTextboxSetText(t, tmpstr); + llen = strlen(tmpstr); + + label = newtLabel(llen+1, 1, "-"); + f = newtForm(NULL, NULL, 0); + newtFormAddComponent(f, t); + scale = newtScale(3, 3, dlen, 100); + newtFormAddComponent(f, scale); + + newtDrawForm(f); + newtRefresh(); + + data.scale = scale; + data.label = label; + + rc = mediaCheckFile(file, readCB, &data); + + newtFormDestroy(f); + newtPopWindow(); + + if (rc == -1) { + logMessage(WARNING, "mediacheck: %s (%s) has no checksum info", file, descr); + newtWinMessage(_("Error"), _("OK"), + _("Unable to read the disc checksum from the " + "primary volume descriptor. This probably " + "means the disc was created without adding the " + "checksum.")); + } else if (rc == 0) { + logMessage(ERROR, "mediacheck: %s (%s) FAILED", file, descr); + newtWinMessage(_("Error"), _("OK"), + _("The image which was just tested has errors. " + "This could be due to a " + "corrupt download or a bad disc. " + "If applicable, please clean the disc " + "and try again. If this test continues to fail you " + "should not continue the install.")); + } else if (rc > 0) { + logMessage(INFO, "mediacheck: %s (%s) PASSED", file, descr); + newtWinMessage(_("Success"), _("OK"), + _("The image which was just tested was successfully " + "verified. It should be OK to install from this " + "media. Note that not all media/drive errors can " + "be detected by the media check.")); + } + + + return rc; +} diff --git a/loader/mediacheck.h b/loader/mediacheck.h new file mode 100644 index 000000000..ab2f887b9 --- /dev/null +++ b/loader/mediacheck.h @@ -0,0 +1,25 @@ +/* + * mediacheck.h + * + * Copyright (C) 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/>. + */ + +#ifndef MEDIACHECK_H +#define MEDIACHECK_H + +int doMediaCheck(char *file, char *descr); + +#endif diff --git a/loader/method.c b/loader/method.c new file mode 100644 index 000000000..26dc2c3fa --- /dev/null +++ b/loader/method.c @@ -0,0 +1,540 @@ +/* + * method.c - generic install method setup functions + * + * Copyright (C) 2002 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 <inttypes.h> +#include <libgen.h> +#include <newt.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <unistd.h> +#include <libgen.h> + +#include "copy.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "lang.h" +#include "mediacheck.h" +#include "method.h" + +#include "../isys/imount.h" +#include "../isys/isys.h" +#include "../isys/cpio.h" + +#include "devt.h" + +#include "nfsinstall.h" +#include "hdinstall.h" +#include "urlinstall.h" + +/* boot flags */ +extern uint64_t flags; + +int umountLoopback(char * mntpoint, char * device) { + int loopfd; + + umount(mntpoint); + + logMessage(INFO, "umounting loopback %s %s", mntpoint, device); + + loopfd = open(device, O_RDONLY); + + if (ioctl(loopfd, LOOP_CLR_FD, 0) == -1) + logMessage(ERROR, "LOOP_CLR_FD failed for %s %s: %m", mntpoint, device); + + close(loopfd); + + return 0; +} + +int mountLoopback(char *fsystem, char *mntpoint, char *device) { + char *opts; + + if (device == NULL) { + logMessage(ERROR, "no loopback device given"); + return LOADER_ERROR; + } + + if (access(fsystem, F_OK) != 0) { + logMessage(ERROR, "file %s is not accessible", fsystem); + return LOADER_ERROR; + } + + if (asprintf(&opts, "ro,loop=%s", device) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (doPwMount(fsystem, mntpoint, "iso9660", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "ext2", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "squashfs", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "cramfs", opts, NULL)) { + if (doPwMount(fsystem, mntpoint, "vfat", opts, NULL)) { + logMessage(ERROR, "failed to mount loopback device %s on %s as %s: %m", + device, mntpoint, fsystem); + return LOADER_ERROR; + } + } + } + } + } + + logMessage(INFO, "mounted loopback device %s on %s as %s", mntpoint, device, fsystem); + + return 0; +} + +/* returns the *absolute* path (malloced) to the #1 iso image */ +/* get timestamp and description of ISO image from stamp file */ +/* returns 0 on success, -1 otherwise */ +int readStampFileFromIso(char *file, char **timestamp, char **releasedescr) { + DIR * dir; + FILE *f; + struct dirent * ent; + struct stat sb; + char *stampfile; + char *descr, *tstamp; + char tmpstr[1024]; + int filetype; + int rc; + + lstat(file, &sb); + if (S_ISBLK(sb.st_mode)) { + filetype = 1; + if (doPwMount(file, "/tmp/testmnt", "iso9660", "ro", NULL)) { + logMessage(ERROR, "Failed to mount device %s to get description", + file); + return -1; + } + } else if (S_ISREG(sb.st_mode)) { + filetype = 2; + if (mountLoopback(file, "/tmp/testmnt", "/dev/loop6")) { + logMessage(ERROR, "Failed to mount iso %s to get description", + file); + return -1; + } + } else { + logMessage(ERROR, "Unknown type of file %s to get description", + file); + return -1; + } + + if (!(dir = opendir("/tmp/testmnt"))) { + umount("/tmp/testmnt"); + if (filetype == 2) + umountLoopback("/tmp/testmnt", "/dev/loop6"); + return -1; + } + + errno = 0; + stampfile = NULL; + while ((ent = readdir(dir))) { + if (!strncmp(ent->d_name, ".discinfo", 9)) { + stampfile = strdup(".discinfo"); + break; + } + } + + closedir(dir); + descr = NULL; + tstamp = NULL; + if (stampfile) { + snprintf(tmpstr, sizeof(tmpstr), "/tmp/testmnt/%s", stampfile); + f = fopen(tmpstr, "r"); + if (f) { + char *tmpptr; + + /* readtime stamp line */ + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + if (tmpptr) + tstamp = strdup(tmpstr); + + /* now read OS description line */ + if (tmpptr) + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + if (tmpptr) + descr = strdup(tmpstr); + + /* skip over arch */ + if (tmpptr) + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + /* now get the CD number */ + if (tmpptr) { + unsigned int len; + char *p, *newstr; + + tmpptr = fgets(tmpstr, sizeof(tmpstr), f); + + /* nuke newline from end of descr, stick number on end*/ + for (p=descr+strlen(descr); p != descr && !isspace(*p); p--); + + *p = '\0'; + len = strlen(descr) + strlen(tmpstr) + 10; + newstr = malloc(len); + strncpy(newstr, descr, len-1); + strncat(newstr, " ", len-1); + + /* is this a DVD or not? If disc id has commas, like */ + /* "1,2,3", its a DVD */ + if (strchr(tmpstr, ',')) + strncat(newstr, "DVD\n", len-1); + else { + strncat(newstr, "disc ", len-1); + strncat(newstr, tmpstr, len-1); + } + + free(descr); + descr = newstr; + } + + fclose(f); + } + } + + free(stampfile); + + umount("/tmp/testmnt"); + if (filetype == 2) + umountLoopback("/tmp/testmnt", "/dev/loop6"); + + if (descr != NULL && tstamp != NULL) { + descr[strlen(descr)-1] = '\0'; + *releasedescr = descr; + + tstamp[strlen(tstamp)-1] = '\0'; + *timestamp = tstamp; + + rc = 0; + } else { + rc = 1; + } + + return rc; +} + +/* XXX this ignores "location", which should be fixed + * + * Given a starting isoFile, will offer choice to mediacheck it and + * all other ISO images in the same directory with the same stamp + */ +void queryIsoMediaCheck(char *isoFile) { + DIR * dir; + struct dirent * ent; + char *isoDir; + char isoImage[1024]; + char tmpmessage[1024]; + char *master_timestamp; + char *tmpstr; + int rc, first; + + /* dont bother to test in automated installs */ + if (FL_KICKSTART(flags) && !FL_MEDIACHECK(flags)) + return; + + /* if they did not specify to mediacheck explicitely then return */ + if (!FL_MEDIACHECK(flags)) + return; + + /* check that file is actually an iso */ + if (!fileIsIso(isoFile)) + return; + + /* get stamp of isoFile, free descr since we dont care */ + readStampFileFromIso(isoFile, &master_timestamp, &tmpstr); + free(tmpstr); + + /* get base path from isoFile */ + tmpstr = strdup(isoFile); + isoDir = strdup(dirname(tmpstr)); + free(tmpstr); + + logMessage(DEBUGLVL, "isoFile = %s", isoFile); + logMessage(DEBUGLVL, "isoDir = %s", isoDir); + logMessage(DEBUGLVL, "Master Timestemp = %s", master_timestamp); + + if (!(dir = opendir(isoDir))) { + newtWinMessage(_("Error"), _("OK"), + _("Failed to read directory %s: %m"), + isoDir); + free(isoDir); + free(master_timestamp); + return; + } + + /* Walk through the directories looking for a CD images. */ + errno = 0; + first = 0; + while (1) { + char *nextname; + char *tdescr, *tstamp; + + if (first) { + first = 1; + nextname = isoFile; + } else { + ent = readdir(dir); + if (!ent) + break; + + nextname = ent->d_name; + } + + /* synthesize name of iso from isoDir and file entry */ + snprintf(isoImage, sizeof(isoImage), "%s/%s", isoDir, nextname); + + /* see if this is an iso image */ + if (!fileIsIso(isoImage)) { + errno = 0; + continue; + } + + /* see if its part of the current CD set */ + readStampFileFromIso(isoImage, &tstamp, &tdescr); + if (strcmp(tstamp, master_timestamp)) { + errno = 0; + continue; + } + + /* found a valid candidate, proceed */ + snprintf(tmpmessage, sizeof(tmpmessage), + _("Would you like to perform a checksum " + "test of the ISO image:\n\n %s?"), isoImage); + + rc = newtWinChoice(_("Checksum Test"), _("Test"), _("Skip"), + tmpmessage); + + if (rc == 2) { + logMessage(INFO, "mediacheck: skipped checking of %s", isoImage); + if (tdescr) + free(tdescr); + continue; + } else { + doMediaCheck(isoImage, tdescr); + if (tdescr) + free(tdescr); + + continue; + } + } + + free(isoDir); + free(master_timestamp); + closedir(dir); +} + +static void copyWarnFn (char *msg) { + logMessage(WARNING, msg); +} + +static void copyErrorFn (char *msg) { + newtWinMessage(_("Error"), _("OK"), _(msg)); +} + +/* + * unpack a gzipped cpio ball into a tree rooted at rootDir + * returns 0 on success, 1 on failure + */ +int unpackCpioBall(char * ballPath, char * rootDir) { + gzFile fd; + char *buf, *cwd; + int rc = 1; + + if (access(ballPath, R_OK)) + return 1; + + if (access(rootDir, R_OK)) + mkdirChain(rootDir); + + buf = (char *)malloc(PATH_MAX); + cwd = getcwd(buf, PATH_MAX); + if ((rc = chdir(rootDir)) == 0) { + fd = gunzip_open(ballPath); + if (fd) { + if (!installCpioFile(fd, NULL, NULL, 0)) { + logMessage(INFO, "copied contents of %s into %s", ballPath, + rootDir); + rc = chdir(cwd); + return 0; + } + gunzip_close(fd); + } + rc = chdir(cwd); + } + + return 1; +} + +void copyUpdatesImg(char * path) { + if (!access(path, R_OK)) { + if (!mountLoopback(path, "/tmp/update-disk", "/dev/loop7")) { + copyDirectory("/tmp/update-disk", "/tmp/updates", copyWarnFn, + copyErrorFn); + umountLoopback("/tmp/update-disk", "/dev/loop7"); + unlink("/tmp/update-disk"); + } else { + unpackCpioBall(path, "/tmp/updates"); + } + } +} + +void copyProductImg(char * path) { + if (!access(path, R_OK)) { + if (!mountLoopback(path, "/tmp/product-disk", "/dev/loop7")) { + copyDirectory("/tmp/product-disk", "/tmp/product", copyWarnFn, + copyErrorFn); + umountLoopback("/tmp/product-disk", "/dev/loop7"); + unlink("/tmp/product-disk"); + } + } +} + +/* unmount a second stage, if mounted. Used for CDs and mediacheck mostly, + so we can eject CDs. */ +void umountStage2(void) { + umountLoopback("/mnt/runtime", "/dev/loop0"); +} + +/* mount a second stage, verify the stamp file, copy updates + * Returns 0 on success, 1 on failure to mount, -1 on bad stamp */ +int mountStage2(char *stage2path) { + if (access(stage2path, R_OK)) { + return 1; + } + + if (mountLoopback(stage2path, "/mnt/runtime", "/dev/loop0")) { + return 1; + } + + return 0; +} + + +/* copies a second stage from fd to dest and mounts on mntpoint */ +int copyFileAndLoopbackMount(int fd, char * dest, + char * device, char * mntpoint) { + int rc; + struct stat sb; + + rc = copyFileFd(fd, dest); + stat(dest, &sb); + logMessage(DEBUGLVL, "copied %" PRId64 " bytes to %s (%s)", sb.st_size, dest, + ((rc) ? " incomplete" : "complete")); + + if (rc) { + /* just to make sure */ + unlink(dest); + return 1; + } + + if (mountLoopback(dest, mntpoint, device)) { + /* JKFIXME: this used to be fatal, but that seems unfriendly */ + logMessage(ERROR, "Error mounting %s on %s: %m", device, mntpoint); + return 1; + } + + return 0; +} + +/* given a device name (w/o '/dev' on it), try to get a file */ +/* Error codes: + 1 - could not create device node + 2 - could not mount device as ext2, vfat, or iso9660 + 3 - file named path not there +*/ +int getFileFromBlockDevice(char *device, char *path, char * dest) { + int rc; + char file[4096]; + + logMessage(INFO, "getFileFromBlockDevice(%s, %s)", device, path); + + if (doPwMount(device, "/tmp/mnt", "vfat", "ro", NULL) && + doPwMount(device, "/tmp/mnt", "ext2", "ro", NULL) && + doPwMount(device, "/tmp/mnt", "iso9660", "ro", NULL)) { + logMessage(ERROR, "failed to mount /dev/%s: %m", device); + return 2; + } + + snprintf(file, sizeof(file), "/tmp/mnt/%s", path); + logMessage(INFO, "Searching for file on path %s", file); + + if (access(file, R_OK)) { + rc = 3; + } else { + copyFile(file, dest); + rc = 0; + logMessage(INFO, "file copied to %s", dest); + } + + umount("/tmp/mnt"); + unlink("/tmp/mnt"); + return rc; +} + +void setStage2LocFromCmdline(char * arg, struct loaderData_s * ld) { + char * c, * dup; + + dup = strdup(arg); + c = dup; + /* : will let us delimit real information on the method */ + if ((c = strtok(c, ":"))) { + c = strtok(NULL, ":"); + + if (!strncmp(arg, "nfs:", 4)) { + ld->method = METHOD_NFS; + ld->stage2Data = calloc(sizeof(struct nfsInstallData *), 1); + + ((struct nfsInstallData *)ld->stage2Data)->mountOpts = NULL; + ((struct nfsInstallData *)ld->stage2Data)->host = strdup(c); + if ((c = strtok(NULL, ":"))) { + ((struct nfsInstallData *)ld->stage2Data)->directory = strdup(c); + } + } else if (!strncmp(arg, "ftp:", 4) || + !strncmp(arg, "http:", 5)) { + ld->method = METHOD_URL; + ld->stage2Data = calloc(sizeof(struct urlInstallData *), 1); + ((struct urlInstallData *)ld->stage2Data)->url = strdup(arg); + } else if (!strncmp(arg, "cdrom:", 6)) { + ld->method = METHOD_CDROM; + } else if (!strncmp(arg, "harddrive:", 10) || + !strncmp(arg, "hd:", 3)) { + ld->method = METHOD_HD; + ld->stage2Data = calloc(sizeof(struct hdInstallData *), 1); + ((struct hdInstallData *)ld->stage2Data)->partition = strdup(c); + if ((c = strtok(NULL, ":"))) { + ((struct hdInstallData *)ld->stage2Data)->directory = strdup(c); + } + } + } + free(dup); +} diff --git a/loader/method.h b/loader/method.h new file mode 100644 index 000000000..5f6c7a83d --- /dev/null +++ b/loader/method.h @@ -0,0 +1,58 @@ +/* + * method.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_METHOD +#define H_METHOD + +#include "loader.h" + +/* method identifiers, needs to match struct installMethod order in loader.c */ +enum { + METHOD_CDROM, + METHOD_HD, + METHOD_NFS, + METHOD_URL +}; + +struct installMethod { + char * name; + int network; + enum deviceType type; + char * (*mountImage)(struct installMethod * method, + char * location, struct loaderData_s * loaderData); +}; + +int umountLoopback(char * mntpoint, char * device); +int mountLoopback(char * fsystem, char * mntpoint, char * device); + +int readStampFileFromIso(char *file, char **descr, char **timestamp); +void queryIsoMediaCheck(char * isoDir); + +void umountStage2(void); +int mountStage2(char *stage2path); +int copyFileAndLoopbackMount(int fd, char *dest, char *device, char *mntpoint); +int getFileFromBlockDevice(char *device, char *path, char * dest); + +int unpackCpioBall(char * ballPath, char * rootDir); +void copyUpdatesImg(char * path); +void copyProductImg(char * path); + +void setStage2LocFromCmdline(char * arg, struct loaderData_s * ld); + +#endif diff --git a/loader/mkctype.c b/loader/mkctype.c new file mode 100644 index 000000000..12eaba067 --- /dev/null +++ b/loader/mkctype.c @@ -0,0 +1,76 @@ +/* + * mkctype.c + * + * Copyright (C) 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/>. + */ + +#include <ctype.h> +#include <stdio.h> + +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) +# define __ctype_b (*__ctype_b_loc()) +# define __ctype_tolower (*__ctype_tolower_loc()) +# define __ctype_toupper (*__ctype_toupper_loc()) +#endif + +int main(int argc, char ** argv) { + int i; + + printf("#include <sys/types.h>\n\n"); + + printf("static const unsigned short int __ctype_b_internal[] = {"); + + for (i = -128; i < 256; i++) { + if (!(i % 8)) { + printf("\n"); + } + + printf("\t0x%x,", __ctype_b[i]); + } + + printf("\n};\n\n"); + printf("const unsigned short int * __ctype_b = __ctype_b_internal + 128;\n\n"); + + printf("const int __ctype_toupper_internal[] = {"); + for (i = -128; i < 256; i++) { + if (!(i % 8)) { + printf("\n"); + } + + printf("\t0x%x,", __ctype_toupper[i]); + } + + printf("\n};\n\n"); + printf("const int * __ctype_toupper = __ctype_toupper_internal + 128;\n\n"); + + printf("const int __ctype_tolower_internal[] = {"); + for (i = -128; i < 256; i++) { + if (!(i % 8)) { + printf("\n"); + } + + printf("\t0x%x,", __ctype_tolower[i]); + } + + printf("\n};\n\n"); + printf("const int * __ctype_tolower = __ctype_tolower_internal + 128;\n\n"); + + printf ("const unsigned short int **__ctype_b_loc (void) { return &__ctype_b; }\n"); + printf ("const int **__ctype_toupper_loc (void) { return &__ctype_toupper; }\n"); + printf ("const int **__ctype_tolower_loc (void) { return &__ctype_tolower; }\n\n"); + + return 0; +}; diff --git a/loader/moduleinfo.c b/loader/moduleinfo.c new file mode 100644 index 000000000..2e0ab7733 --- /dev/null +++ b/loader/moduleinfo.c @@ -0,0 +1,276 @@ +/* + * moduleinfo.c - module info functionality + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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> + */ + +#include <alloca.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <stdio.h> + +#include "moduleinfo.h" + +struct moduleInfo * getModuleList(moduleInfoSet mis, + enum driverMajor major) { + struct moduleInfo * miList, * next; + int i; + + next = miList = malloc(sizeof(*miList) * mis->numModules + 1); + for (i = 0; i < mis->numModules; i++) { + if (mis->moduleList[i].major == major || major == DRIVER_NONE) { + *next = mis->moduleList[i]; + next++; + } + } + + if (next == miList) { + free(next); + return NULL; + } + + next->moduleName = NULL; + next++; + + miList = realloc(miList, sizeof(*miList) * (next - miList)); + return miList; +} + +struct moduleInfo * findModuleInfo(moduleInfoSet mis, + const char * moduleName) { + int i; + struct moduleInfo * found = NULL; + + for (i = 0; i < mis->numModules; i++) { + if (!strcmp(moduleName, mis->moduleList[i].moduleName)) { + if (!found) + found = mis->moduleList + i; + else if (found->locationID && !mis->moduleList[i].locationID) + ; + else + found = mis->moduleList + i; + } + } + + return found; +} + +moduleInfoSet newModuleInfoSet(void) { + return calloc(sizeof(struct moduleInfoSet_s), 1); +} + +/* filename: file to read module-info from + * mis: moduleInfoSet + * location: moduleBallLocation struct describing the location of + * these modules. (may be NULL) + * override: 1 if modules from this module ball should override old ones + * of the same name. + */ +int readModuleInfo(const char * filename, moduleInfoSet mis, + void * location, int override) { + int fd, isIndented; + char * buf, * start, * next = NULL, * chptr; + struct stat sb; + char oldch; + struct moduleInfo * nextModule; + int modulesAlloced; + int i; + int found = 0, skipModule = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) return -1; + + fstat(fd, &sb); + buf = alloca(sb.st_size + 1); + i = read(fd, buf, sb.st_size); + buf[sb.st_size] = '\0'; + close(fd); + + if (i != sb.st_size) + return -1; + + nextModule = NULL; + modulesAlloced = mis->numModules; + + if (strncmp(buf, "Version 0\n", 10)) return -1; + + start = buf + 10; + while (start && *start) { + chptr = strchr(start, '\n'); + if (chptr) { + /* slice and dice */ + next = chptr + 1; + } else { + chptr += strlen(start) - 1; + } + + chptr--; + while (isspace(*chptr)) chptr--; + chptr++; + *chptr = '\0'; + + isIndented = 0; + if (isspace(*start)) { + while (isspace(*start) && *start != '\n') start++; + isIndented = 1; + } + + if (*start != '\n' && *start && *start != '#') { + if (!isIndented) { + if (nextModule && nextModule->moduleName && + nextModule == (mis->moduleList + mis->numModules)) { + mis->numModules++; + } + + if (mis->numModules == modulesAlloced) { + modulesAlloced += 5; + mis->moduleList = realloc(mis->moduleList, + modulesAlloced * sizeof(*mis->moduleList)); + } + + nextModule = NULL; + found = 0; + skipModule = 0; + for (i = 0; i < mis->numModules; i++) { + if (!strcmp(mis->moduleList[i].moduleName, start)) { + if (override) + nextModule = mis->moduleList + i; + else + skipModule = 1; + found = 1; + break; + } + } + + if (!found && !nextModule) { + nextModule = mis->moduleList + mis->numModules; + + nextModule->moduleName = strdup(start); + } + + if (nextModule) { + nextModule->major = DRIVER_NONE; + nextModule->minor = DRIVER_MINOR_NONE; + nextModule->description = NULL; + nextModule->flags = 0; + nextModule->args = NULL; + nextModule->numArgs = 0; + nextModule->locationID = location; + } + } else if (!nextModule && skipModule) { + /* we're skipping this one (not overriding), do nothing */ + } else if (!nextModule && skipModule) { + /* ACK! syntax error */ + fprintf(stderr, "module-info syntax error in %s\n", filename); + return 1; + } else if (nextModule->major == DRIVER_NONE) { + chptr = start + strlen(start) - 1; + while (!isspace(*chptr) && chptr > start) chptr--; + if (chptr != start) chptr++; + + if (!strcmp(chptr, "eth")) { + nextModule->major = DRIVER_NET; + nextModule->minor = DRIVER_MINOR_ETHERNET; + } else if (!strcmp(chptr, "tr")) { + nextModule->major = DRIVER_NET; + nextModule->minor = DRIVER_MINOR_TR; + } else if (!strcmp(chptr, "scsi_hostadapter") || + !strcmp(chptr, "scsi")) { + nextModule->major = DRIVER_SCSI; + } else if (!strcmp(chptr, "pcmcia")) { + nextModule->major = DRIVER_PCMCIA; + } else if (!strcmp(chptr, "fs")) { + nextModule->major = DRIVER_FS; + } else if (!strcmp(chptr, "cdrom")) { + nextModule->major = DRIVER_CDROM; + } else if (!strcmp(chptr, "ide")) { + nextModule->major = DRIVER_IDE; + } else { + nextModule->major = DRIVER_OTHER; + } + } else if (!nextModule->description) { + chptr = start + strlen(start) - 1; + if (*start == '"' && *chptr == '"') { + start++; + *chptr = '\0'; + nextModule->description = strdup(start); + } + } else { + nextModule->args = realloc(nextModule->args, + sizeof(*nextModule->args) * (nextModule->numArgs + 1)); + chptr = start; + while (!isspace(*chptr) && *chptr) chptr++; + if (*chptr) { + oldch = *chptr; + *chptr = '\0'; + nextModule->args[nextModule->numArgs].arg = strdup(start); + + start = chptr + 1; + while (*start && isspace(*start)) start++; + + if (*start == '"') { + start++; + chptr = strchr(start, '"'); + if (chptr) { + *chptr = '\0'; + nextModule->args[nextModule->numArgs].description = + strdup(start); + nextModule->numArgs++; + } + } + } + } + } + + start = next; + } + + /* do we need to add in this last module? */ + if (nextModule && ((nextModule - mis->moduleList) == mis->numModules)) + mis->numModules++; + + return 0; +} + +void freeModuleInfoSet(moduleInfoSet mis) { + int i, j; + + for (i = 0; i < mis->numModules; i++) { + if (mis->moduleList[i].moduleName) + free(mis->moduleList[i].moduleName); + + if (mis->moduleList[i].description) + free(mis->moduleList[i].description); + + for (j = 0; i < mis->moduleList[i].numArgs; j++) { + if (mis->moduleList[i].args[j].arg) + free(mis->moduleList[i].args[j].arg) ; + if (mis->moduleList[i].args[j].description) + free(mis->moduleList[i].args[j].description) ; + } + } + + free(mis); +} diff --git a/loader/moduleinfo.h b/loader/moduleinfo.h new file mode 100644 index 000000000..72f6d7160 --- /dev/null +++ b/loader/moduleinfo.h @@ -0,0 +1,78 @@ +/* + * moduleinfo.h + * + * Copyright (C) 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/>. + */ + +#ifndef MODULEINFO_H +#define MODULEINFO_H + +enum driverMajor { DRIVER_NONE = 0, DRIVER_SCSI, DRIVER_NET, DRIVER_CDROM, + DRIVER_PCMCIA, DRIVER_FS, DRIVER_IDE, DRIVER_OTHER = 1000, + DRIVER_ANY = 5000 }; +enum driverMinor { DRIVER_MINOR_NONE = 0, DRIVER_MINOR_ETHERNET, + DRIVER_MINOR_TR }; + +struct moduleArg { + char * arg; + char * description; +}; + +#define MI_FLAG_NOMISCARGS (1 << 0) + +struct moduleInfo { + char * moduleName; + char * description; + enum driverMajor major; + enum driverMinor minor; + int numArgs; + struct moduleArg * args; + int flags; + void * locationID; +}; + +struct moduleInfoSet_s { + struct moduleInfo * moduleList; + int numModules; +}; + +struct moduleBallLocation { + char * path; /* path to module ball that this driver is from. if NULL, + * implies /modules/modules.cgz */ + char * title; /* title used for driver disk -- may be NULL */ + int version; /* module ball version, used to determine layout */ +}; +#define CURRENT_MODBALLVER 1 + +/* valid moduleball versions + * 0: old single-arch module ball, modules are in uname.release + * 1: multi-arch, modules are in uname.release/arch + */ + +typedef struct moduleInfoSet_s * moduleInfoSet; + +moduleInfoSet newModuleInfoSet(void); +void freeModuleInfoSet(moduleInfoSet mis); +int readModuleInfo(const char * filename, moduleInfoSet mis, void * path, int override); +struct moduleInfo * findModuleInfo(moduleInfoSet mis, + const char * moduleName); + +/* NULL moduleName indicates the end of the list; the list must be freed() */ +struct moduleInfo * getModuleList(moduleInfoSet mis, + enum driverMajor major); + + +#endif diff --git a/loader/modules.c b/loader/modules.c new file mode 100644 index 000000000..705a3ebeb --- /dev/null +++ b/loader/modules.c @@ -0,0 +1,380 @@ +/* + * modules.c - module loading functionality + * + * Copyright (C) 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 <errno.h> +#include <fcntl.h> +#include <newt.h> +#include <popt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "loader.h" +#include "log.h" +#include "modules.h" +#include "windows.h" + +#include "../isys/cpio.h" + +/* boot flags */ +extern uint64_t flags; + +static int writeModulesConf(char *conf); +struct moduleOptions { + char *name; + int numopts; + char **options; +}; + +static struct moduleOptions * modopts = NULL; +static int nummodopts = -1; + +static char ** blacklists = NULL; +static int numblacklists = 0; + +static void readBlacklist() { + int fd; + size_t len = 0; + char buf[1024]; + char *start, *end; + + if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) + return; + + len = read(fd, buf, sizeof(buf) - 1); + close(fd); + buf[len] = '\0'; + start = buf; + + while (start) { + end = strstr(start, " "); + if (end) + *end = '\0'; + if (strncmp(start,"blacklist=",10)) { + if (!end) + break; + start = end + 1; + continue; + } + printf("found %s\n",start); + + blacklists = realloc(blacklists, sizeof(*blacklists) * (numblacklists + 1)); + blacklists[numblacklists] = strdup(start+10); + numblacklists++; + + if (!end) + break; + start = end + 1; + } +} + +void mlAddBlacklist(char *module) { + blacklists = realloc(blacklists, sizeof(*blacklists) * (numblacklists + 1)); + blacklists[numblacklists] = strdup(module); + numblacklists++; + writeModulesConf("/etc/modprobe.d/anaconda"); +} + +static void addOption(const char *module, const char *option) { + int found = 0, i; + + found = 0; + for (i = 0; i < nummodopts; i++) { + if (strncmp(modopts[i].name, module, strlen(modopts[i].name))) + continue; + modopts[i].numopts++; + found = 1; + break; + } + if (found == 0) { + modopts = realloc(modopts, sizeof(*modopts) * (nummodopts + 1)); + modopts[nummodopts].name = strdup(module); + modopts[nummodopts].numopts = 1; + modopts[nummodopts++].options = NULL; + } + modopts[i].options = realloc(modopts[i].options, + sizeof(modopts[i].options) * + (modopts[i].numopts + 1)); + modopts[i].options[modopts[i].numopts - 1] = strdup(option); + modopts[i].options[modopts[i].numopts] = NULL; +} + +static int isValidModule(char *module) { + char mod_name[64], path[512]; + struct utsname utsbuf; + struct stat sbuf; + char *buf; + + uname(&utsbuf); + snprintf(path, 512, "/lib/modules/%s/modules.dep", utsbuf.release); + if (!stat(path, &sbuf)) { + int fd; + + fd = open(path, O_RDONLY); + buf = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (!buf || buf == MAP_FAILED) + return 0; + close(fd); + snprintf(mod_name, 64, "/%s.ko.gz:", module); + if (strstr(buf, mod_name)) { + munmap(buf, sbuf.st_size); + return 1; + } + snprintf(mod_name, 64, "/%s.ko:", module); + if (strstr(buf, mod_name)) { + munmap(buf, sbuf.st_size); + return 1; + } + munmap(buf, sbuf.st_size); + } + return 0; +} + +/* read module options out of /proc/cmdline and into a structure */ +static void readModuleOpts() { + int fd; + size_t len = 0; + char buf[1024]; + char *start, *end, *sep; + + nummodopts = 0; + if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) + return; + + len = read(fd, buf, sizeof(buf) - 1); + close(fd); + buf[len] = '\0'; + start = buf; + + while (start) { + end = strstr(start, " "); + if (end) + *end = '\0'; + sep = strstr(start, "="); + if (sep == NULL) { + if (!end) + break; + start = end + 1; + continue; + } + sep = strstr(start, "."); + if (sep == NULL) { + if (!end) + break; + start = end + 1; + continue; + } + *sep = '\0'; sep++; + + if (isValidModule(start)) + addOption(start, sep); + + if (!end) + break; + start = end + 1; + } +} + +static int doLoadModule(const char *module, char ** args) { + int child; + int status; + + if (!(child = fork())) { + int i, rc; + char **argv = malloc(3 * sizeof(*argv)); + int fd = open("/dev/tty3", O_RDWR); + + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + + argv[0] = "/sbin/modprobe"; + argv[1] = strdup(module); + argv[2] = NULL; + if (args) { + for (i = 0; args[i] ; i++) { + addOption(module, args[i]); + } + writeModulesConf("/etc/modprobe.d/anaconda"); + } + rc = execv("/sbin/modprobe", argv); + _exit(rc); + } + + waitpid(child, &status, 0); + + if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) { + return 1; + } else { + return 0; + } +} + +void mlRemoveBlacklist(char *module) { + int i; + + for (i = 0 ; i < numblacklists ; i++) { + if (!strcmp(blacklists[i], module)) + blacklists[i] = NULL; + } +} + +void mlInitModuleConfig() { + readModuleOpts(); + readBlacklist(); + writeModulesConf("/etc/modprobe.d/anaconda"); +} + +/* load a module with a given list of arguments */ +int mlLoadModule(const char * module, char ** args) { + return doLoadModule(module, args); +} + +/* loads a : separated list of modules */ +int mlLoadModuleSet(const char * modNames) { + char *ptr, *name; + int rc = 0; + + if (!modNames) return 1; + name = strdup(modNames); while (name) { + ptr = strchr(name, ':'); + if (ptr) *ptr = '\0'; + rc |= doLoadModule(name, NULL); + if (ptr) + name = ptr+1; + else + name = NULL; + } + return rc; +} + +static int writeModulesConf(char *conf) { + int i; + char buf[16384]; + int fd, rc; + + if (!conf) + conf = "/tmp/modprobe.conf"; + + fd = open(conf, O_WRONLY | O_CREAT, 0644); + if (fd == -1) { + logMessage(ERROR, "error opening to %s: %m\n", conf); + return 0; + } + strcat(buf, "# Module options and blacklists written by anaconda\n"); + for (i = 0; i < nummodopts ; i++) { + int j; + + strcat(buf, "options "); + strcat(buf, modopts[i].name); + for (j = 0; j < modopts[i].numopts ; j++) { + strcat(buf, " "); + strcat(buf, modopts[i].options[j]); + } + strcat(buf, "\n"); + } + for (i = 0; i < numblacklists ; i++) { + if (blacklists[i]) { + strcat(buf, "blacklist "); + strcat(buf, blacklists[i]); + strcat(buf, "\n"); + } + } + + rc = write(fd, buf, strlen(buf)); + close(fd); + return (rc != strlen(buf)); +} + +void loadKickstartModule(struct loaderData_s * loaderData, int argc, + char ** argv) { + char * opts = NULL; + char * module = NULL; + char ** args = NULL; + poptContext optCon; + int rc; + struct poptOption ksDeviceOptions[] = { + { "opts", '\0', POPT_ARG_STRING, &opts, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + optCon = poptGetContext(NULL, argc, (const char **) argv, + ksDeviceOptions, 0); + if ((rc = poptGetNextOpt(optCon)) < -1) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to device kickstart method " + "command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + return; + } + + module = (char *) poptGetArg(optCon); + + if (!module) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("A module name must be specified for " + "the kickstart device command.")); + return; + } + + if (opts) { + int numAlloced = 5, i = 0; + char * start; + char * end; + + args = malloc((numAlloced + 1) * sizeof(args)); + start = opts; + while (start && *start) { + end = start; + while (!isspace(*end) && *end) end++; + *end = '\0'; + (args)[i++] = strdup(start); + start = end + 1; + *end = ' '; + start = strchr(end, ' '); + if (start) start++; + + if (i >= numAlloced) { + numAlloced += 5; + args = realloc(args, sizeof(args) * (numAlloced + 1)); + } + } + args[i] = NULL; + } + + + mlLoadModule(module, args); +} diff --git a/loader/modules.h b/loader/modules.h new file mode 100644 index 000000000..6c0d8adcd --- /dev/null +++ b/loader/modules.h @@ -0,0 +1,30 @@ +/* + * modules.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_MODULES +#define H_MODULES + +#include "moduleinfo.h" + +void mlInitModuleConfig(); +int mlLoadModule(const char * module, char ** args); +int mlLoadModuleSet(const char * modNames); +void mlAddBlacklist(char *module); +void mlRemoveBlacklist(char *module); +#endif diff --git a/loader/net.c b/loader/net.c new file mode 100644 index 000000000..0acbe83c9 --- /dev/null +++ b/loader/net.c @@ -0,0 +1,2090 @@ +/* + * net.c + * + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + * 2006, 2007, 2008 + * + * 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): David Cantrell <dcantrell@redhat.com> + */ + +#include <netdb.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/utsname.h> +#include <arpa/inet.h> +#include <errno.h> +#include <popt.h> +#include <resolv.h> +#include <net/if.h> +#include <newt.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <dbus/dbus.h> +#include <NetworkManager.h> + +#include "../isys/isys.h" +#include "../isys/ethtool.h" +#include "../isys/iface.h" +#include "../isys/str.h" + +#include "lang.h" +#include "loader.h" +#include "loadermisc.h" +#include "log.h" +#include "method.h" +#include "net.h" +#include "windows.h" + +/* boot flags */ +extern uint64_t flags; + +/** + * Callback function for the CIDR entry boxes on the manual TCP/IP + * configuration window. + * + * @param co The entry field that triggered the callback. + * @param dptr Pointer to intfconfig_s data structure for this field. + * @see intfconfig_s + */ +static void cidrCallback(newtComponent co, void * dptr) { + struct intfconfig_s * data = dptr; + int cidr, upper = 0; + struct in_addr addr; + + if (co == data->cidr4Entry) { + if (data->cidr4 == NULL && data->ipv4 == NULL) + return; + + if (inet_pton(AF_INET, data->cidr4, &addr) >= 1) + return; + + errno = 0; + cidr = strtol(data->cidr4, NULL, 10); + if ((errno == ERANGE && (cidr == LONG_MIN || cidr == LONG_MAX)) || + (errno != 0 && cidr == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (strcmp(data->ipv4, "")) + upper = 32; + } else if (co == data->cidr6Entry) { + if (data->cidr6 == NULL && data->ipv6 == NULL) + return; + + errno = 0; + cidr = strtol(data->cidr6, NULL, 10); + if ((errno == ERANGE && (cidr == LONG_MIN || cidr == LONG_MAX)) || + (errno != 0 && cidr == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (strcmp(data->ipv6, "")) + upper = 128; + } + + if (upper != 0) { + if (cidr < 1 || cidr > upper) { + newtWinMessage(_("Invalid Prefix"), _("Retry"), + _("Prefix must be between 1 and 32 " + "for IPv4 networks or between 1 and 128 " + "for IPv6 networks")); + } + } +} + +static void ipCallback(newtComponent co, void * dptr) { + int i; + char *buf, *octet; + struct intfconfig_s * data = dptr; + + if (co == data->ipv4Entry) { + /* do we need to guess a netmask for the user? */ + if (data->cidr4 == NULL && data->ipv4 != NULL) { + buf = strdup(data->ipv4); + octet = strtok(buf, "."); + errno = 0; + i = strtol(octet, NULL, 10); + + if ((errno == ERANGE && (i == LONG_MIN || i == LONG_MAX)) || + (errno != 0 && i == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + free(buf); + free(octet); + + if (i >= 0 && i <= 127) + newtEntrySet(data->cidr4Entry, "8", 1); + else if (i >= 128 && i <= 191) + newtEntrySet(data->cidr4Entry, "16", 1); + else if (i >= 192 && i <= 222) + newtEntrySet(data->cidr4Entry, "24", 1); + } + + return; + } else if (co == data->ipv6Entry) { + /* users must provide a mask, we can't guess for ipv6 */ + return; + } +} + +static void setMethodSensitivity(void *dptr, int radio_button_count) { + int i = 0; + + for (i = 0; i < radio_button_count; i++) { + newtCheckboxSetFlags(*((newtComponent *) dptr), NEWT_FLAG_DISABLED, + NEWT_FLAGS_TOGGLE); + dptr += sizeof (newtComponent); + } + + return; +} + +static void v4MethodCallback(newtComponent co, void *dptr) { + setMethodSensitivity(dptr, 2); + return; +} + +static void v6MethodCallback(newtComponent co, void *dptr) { + setMethodSensitivity(dptr, 3); + return; +} + +static void parseEthtoolSettings(struct loaderData_s * loaderData) { + char * option, * buf; + ethtool_duplex duplex = ETHTOOL_DUPLEX_UNSPEC; + ethtool_speed speed = ETHTOOL_SPEED_UNSPEC; + + buf = strdup(loaderData->ethtool); + option = strtok(buf, " "); + while (option) { + if (option[strlen(option) - 1] == '\"') + option[strlen(option) - 1] = '\0'; + if (option[0] == '\"') + option++; + if (!strncmp(option, "duplex", 6)) { + if (!strncmp(option + 7, "full", 4)) + duplex = ETHTOOL_DUPLEX_FULL; + else if (!strncmp(option + 7, "half", 4)) + duplex = ETHTOOL_DUPLEX_HALF; + else + logMessage(WARNING, "Unknown duplex setting: %s", option + 7); + option = strtok(NULL, " "); + } else if (!strncmp("speed", option, 5)) { + if (!strncmp(option + 6, "1000", 4)) + speed = ETHTOOL_SPEED_1000; + else if (!strncmp(option + 6, "100", 3)) + speed = ETHTOOL_SPEED_100; + else if (!strncmp(option + 6, "10", 2)) + speed = ETHTOOL_SPEED_10; + else + logMessage(WARNING, "Unknown speed setting: %s", option + 6); + option = strtok(NULL, " "); + } else { + logMessage(WARNING, "Unknown ethtool setting: %s", option); + } + option = strtok(NULL, " "); + } + setEthtoolSettings(loaderData->netDev, speed, duplex); + free(buf); +} + +/* XXX: make this get DNS servers via NM +static int getDnsServers(iface_t * iface) { + int rc; + struct in_addr addr; + struct in6_addr addr6; + char * ns = ""; + struct newtWinEntry entry[] = { { N_("Nameserver IP"), &ns, 0 }, + { NULL, NULL, 0 } }; + + do { + rc = newtWinEntries(_("Missing Nameserver"), + _("Your IP address request returned configuration " + "information, but it did not include a nameserver address. " + "If you do not have this information, you can leave " + "the field blank and the install will continue."), + 61, 0, 0, 45, entry, _("OK"), _("Back"), NULL); + + if (rc == 2) return LOADER_BACK; + + rc = 0; + if (!ns || !*ns) { + iface->numdns = 0; + break; + } else { + if ((inet_pton(AF_INET, ns, &addr) >= 1) || + (inet_pton(AF_INET6, ns, &addr6) >= 1)) { + iface->dns[0] = strdup(ns); + } else { + rc = 2; + } + } + + if (rc) { + newtWinMessage(_("Invalid IP Information"), _("Retry"), + _("You entered an invalid IP address.")); + } else { + iface->numdns = 1; + } + } while (rc == 2); + + return LOADER_OK; +} +*/ + +void printLoaderDataIPINFO(struct loaderData_s *loaderData) { + logMessage(DEBUGLVL, "loaderData->ipinfo_set = |%d|", loaderData->ipinfo_set); + logMessage(DEBUGLVL, "loaderData->ipv4 = |%s|", loaderData->ipv4); + logMessage(DEBUGLVL, "loaderData->ipv6info_set = |%d|", loaderData->ipv6info_set); + logMessage(DEBUGLVL, "loaderData->ipv6 = |%s|", loaderData->ipv6); + logMessage(DEBUGLVL, "loaderData->dhcpTimeout = |%d|", loaderData->dhcpTimeout); + logMessage(DEBUGLVL, "loaderData->netmask = |%s|", loaderData->netmask); + logMessage(DEBUGLVL, "loaderData->gateway = |%s|", loaderData->gateway); + logMessage(DEBUGLVL, "loaderData->dns = |%s|", loaderData->dns); + logMessage(DEBUGLVL, "loaderData->hostname = |%s|", loaderData->hostname); + logMessage(DEBUGLVL, "loaderData->noDns = |%d|", loaderData->noDns); + logMessage(DEBUGLVL, "loaderData->netDev_set = |%d|", loaderData->netDev_set); + logMessage(DEBUGLVL, "loaderData->netDev = |%s|", loaderData->netDev); + logMessage(DEBUGLVL, "loaderData->netCls_set = |%d|", loaderData->netCls_set); + logMessage(DEBUGLVL, "loaderData->netCls = |%s|", loaderData->netCls); +} + +/* given loader data from kickstart, populate network configuration struct */ +void setupNetworkDeviceConfig(iface_t * iface, + struct loaderData_s * loaderData) { + int err; + struct in_addr addr; + struct in6_addr addr6; + char * c; + + /* set to 1 to get ks network struct logged */ +#if 0 + printLoaderDataIPINFO(loaderData); +#endif + + if (loaderData->ethtool) { + parseEthtoolSettings(loaderData); + } + + if (loaderData->netCls_set) { + iface->vendorclass = loaderData->netCls; + } else { + iface->vendorclass = NULL; + } + + if (loaderData->ipinfo_set) { + /* this is how we specify dhcp */ + if (!strncmp(loaderData->ipv4, "dhcp", 4)) { + int ret = 0; + + /* JKFIXME: this soooo doesn't belong here. and it needs to + * be broken out into a function too */ + logMessage(INFO, "sending dhcp request through device %s", + loaderData->netDev); + + if (!FL_TESTING(flags)) { + if (loaderData->noDns) { + iface->flags |= IFACE_FLAGS_NO_WRITE_RESOLV_CONF; + } + + iface->dhcptimeout = loaderData->dhcpTimeout; + + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, + "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return; + } + + ret = get_connection(iface); + newtPopWindow(); + } + + if (ret) { + logMessage(ERROR, "failed to start NetworkManager (%d)", ret); + return; + } + + iface->flags |= IFACE_FLAGS_IS_DYNAMIC | IFACE_FLAGS_IS_PRESET; + } else if (loaderData->ipv4) { + if (inet_pton(AF_INET, loaderData->ipv4, &addr) >= 1) { + iface->ipaddr = addr; + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + iface->flags |= IFACE_FLAGS_IS_PRESET; + } + } else if (loaderData->ipv6) { + if (inet_pton(AF_INET6, loaderData->ipv6, &addr6) >= 1) { + memcpy(&iface->ip6addr, &addr6, sizeof(struct in6_addr)); + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + iface->flags |= IFACE_FLAGS_IS_PRESET; + } + } else { /* invalid ip information, disable the setting of ip info */ + loaderData->ipinfo_set = 0; + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + loaderData->ipv4 = NULL; + loaderData->ipv6 = NULL; + } + } + + if (loaderData->netmask) { + if (inet_pton(AF_INET, loaderData->netmask, &iface->netmask) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (loaderData->gateway) { + if (inet_pton(AF_INET, loaderData->gateway, &iface->gateway) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + /* FIXME: add support for loaderData->gateway6 */ + + if (loaderData->dns) { + char * buf; + char ret[INET6_ADDRSTRLEN+1]; + buf = strdup(loaderData->dns); + + /* Scan the dns parameter for multiple comma-separated IP addresses */ + c = strtok(buf, ","); + while ((iface->numdns < MAXNS) && (c != NULL)) { + if (inet_pton(AF_INET, c, &addr) >= 1) { + iface->dns[iface->numdns] = strdup(c); + iface->numdns++; + + if (inet_ntop(AF_INET, &addr, ret, INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, strerror(errno)); + } else { + logMessage(DEBUGLVL, "adding dns4 %s", ret); + c = strtok(NULL, ","); + } + } else if (inet_pton(AF_INET6, c, &addr6) >= 1) { + iface->dns[iface->numdns] = strdup(c); + iface->numdns++; + + if (inet_ntop(AF_INET6, &addr6, ret, INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, strerror(errno)); + } else { + logMessage(DEBUGLVL, "adding dns6 %s", ret); + c = strtok(NULL, ","); + } + } + } + + logMessage(INFO, "dnsservers is %s", loaderData->dns); + } + + if (loaderData->hostname) { + logMessage(INFO, "setting specified hostname of %s", + loaderData->hostname); + iface->hostname = strdup(loaderData->hostname); + } + + if (loaderData->mtu) { + iface->mtu = loaderData->mtu; + } + + if (loaderData->peerid) { + iface->peerid = strdup(loaderData->peerid); + } + + if (loaderData->subchannels) { + iface->subchannels = strdup(loaderData->subchannels); + } + + if (loaderData->ctcprot) { + iface->ctcprot = strdup(loaderData->ctcprot); + } + + if (loaderData->portname) { + iface->portname = strdup(loaderData->portname); + } + + if (loaderData->nettype) { + iface->nettype = strdup(loaderData->nettype); + } + + if (loaderData->ethtool) { + parseEthtoolSettings(loaderData); + } + + if (loaderData->noDns) { + iface->flags |= IFACE_FLAGS_NO_WRITE_RESOLV_CONF; + } + + iface->dhcptimeout = loaderData->dhcpTimeout; +} + +int readNetConfig(char * device, iface_t * iface, + char * dhcpclass, int methodNum) { + int err; + int ret; + int i = 0; + struct netconfopts opts; + struct in_addr addr; + struct intfconfig_s ipcomps; + + /* ipcomps contains the user interface components */ + ipcomps.ipv4 = NULL; + ipcomps.ipv6 = NULL; + ipcomps.cidr4 = NULL; + ipcomps.cidr6 = NULL; + ipcomps.gw = NULL; + ipcomps.gw6 = NULL; + ipcomps.ns = NULL; + + /* init opts */ + opts.ipv4Choice = 0; + opts.ipv6Choice = 0; + + /* JKFIXME: we really need a way to override this and be able to change + * our network config */ + if (!FL_TESTING(flags) && IFACE_IS_PRESET(iface->flags)) { + logMessage(INFO, "doing kickstart... setting it up"); + + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return LOADER_BACK; + } + + i = get_connection(iface); + newtPopWindow(); + + if (i > 0) { + newtWinMessage(_("Network Error"), _("Retry"), + _("There was an error configuring your network " + "interface.")); + return LOADER_BACK; + } + + return LOADER_NOOP; + } + + /* dhcp/manual network configuration loop */ + i = 1; + while (i == 1) { + ret = configureTCPIP(device, iface, &opts, methodNum); + + if (ret == LOADER_NOOP) { + /* dhcp selected, proceed */ + i = 0; + } else if (ret == LOADER_OK) { + /* do manual configuration */ + ret = manualNetConfig(device, iface, &ipcomps, &opts); + + if (ret == LOADER_BACK) { + continue; + } else if (ret == LOADER_OK) { + i = 0; + } + } else if (ret == LOADER_BACK) { + return LOADER_BACK; + } + } + +/* + if (ipcomps.gw && *ipcomps.gw) { + if (inet_pton(AF_INET, ipcomps.gw, &iface->gateway) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else if (inet_pton(AF_INET6, ipcomps.gw, &iface->gateway6) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } +*/ + + /* calculate any missing IPv4 pieces */ + if (opts.ipv4Choice == '*') { + memset(&addr, 0, sizeof(addr)); + addr.s_addr = (iface->ipaddr.s_addr) & (iface->netmask.s_addr); + + if (iface->broadcast.s_addr == 0) { + iface->broadcast.s_addr = addr.s_addr | ~(iface->netmask.s_addr); + } + } + + /* dump some network debugging info */ + debugNetworkInfo(iface); + + /* bring up the interface */ + if (!FL_TESTING(flags)) { + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return LOADER_BACK; + } + + i = get_connection(iface); + newtPopWindow(); + + if (i > 0) { + newtWinMessage(_("Network Error"), _("Retry"), + _("There was an error configuring your network " + "interface.")); + return LOADER_BACK; + } + } + + return LOADER_OK; +} + +int configureTCPIP(char * device, iface_t * iface, + struct netconfopts * opts, int methodNum) { + int i = 0, z = 0, skipForm = 0, dret = 0, err; + newtComponent f, okay, back, answer; + newtComponent ipv4Checkbox, ipv6Checkbox, v4Method[2], v6Method[3]; + newtGrid grid, checkgrid, buttons; + + /* UI WINDOW 1: ask for ipv4 choice, ipv6 choice, and conf methods */ + + /* IPv4 checkbox */ + if (!opts->ipv4Choice) { + if (FL_NOIPV4(flags) && !FL_IP_PARAM(flags)) + opts->ipv4Choice = ' '; + else + opts->ipv4Choice = '*'; + } + + ipv4Checkbox = newtCheckbox(-1, -1, _("Enable IPv4 support"), + opts->ipv4Choice, NULL, &(opts->ipv4Choice)); + v4Method[0] = newtRadiobutton(-1, -1, DHCP_METHOD_STR, 1, NULL); + v4Method[1] = newtRadiobutton(-1, -1, MANUAL_METHOD_STR, 0, v4Method[0]); + + /* IPv6 checkbox */ + if (!opts->ipv6Choice) { + if (FL_NOIPV6(flags) && !FL_IPV6_PARAM(flags)) + opts->ipv6Choice = ' '; + else + opts->ipv6Choice = '*'; + } + + ipv6Checkbox = newtCheckbox(-1, -1, _("Enable IPv6 support"), + opts->ipv6Choice, NULL, &(opts->ipv6Choice)); + v6Method[0] = newtRadiobutton(-1, -1, AUTO_METHOD_STR, 1, NULL); + v6Method[1] = newtRadiobutton(-1, -1, DHCPV6_METHOD_STR, 0, v6Method[0]); + v6Method[2] = newtRadiobutton(-1, -1, MANUAL_METHOD_STR, 0, v6Method[1]); + + /* button bar at the bottom of the window */ + buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL); + + /* checkgrid contains the toggle options for net configuration */ + checkgrid = newtCreateGrid(1, 8); + + newtGridSetField(checkgrid, 0, 0, NEWT_GRID_COMPONENT, ipv4Checkbox, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + for (i = 1; i < 3; i++) + newtGridSetField(checkgrid, 0, i, NEWT_GRID_COMPONENT, v4Method[i-1], + 7, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtGridSetField(checkgrid, 0, 4, NEWT_GRID_COMPONENT, ipv6Checkbox, + 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0); + for (i = 5; i < 8; i++) + newtGridSetField(checkgrid, 0, i, NEWT_GRID_COMPONENT, v6Method[i-5], + 7, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + /* main window layout */ + grid = newtCreateGrid(1, 2); + newtGridSetField(grid, 0, 0, NEWT_GRID_SUBGRID, checkgrid, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + f = newtForm(NULL, NULL, 0); + newtGridAddComponentsToForm(grid, f, 1); + newtGridWrappedWindow(grid, _("Configure TCP/IP")); + newtGridFree(grid, 1); + + /* callbacks */ + newtComponentAddCallback(ipv4Checkbox, v4MethodCallback, &v4Method); + newtComponentAddCallback(ipv6Checkbox, v6MethodCallback, &v6Method); + + /* match radio button sensitivity to initial checkbox choices */ + if (opts->ipv4Choice == ' ') + setMethodSensitivity(&v4Method, 2); + + if (opts->ipv6Choice == ' ') + setMethodSensitivity(&v6Method, 3); + + /* If the user provided any of the following boot paramters, skip + * prompting for network configuration information: + * ip=<val> ipv6=<val> + * noipv4 noipv6 + * ip=<val> noipv6 + * ipv6=<val> noipv4 + * we also skip this form for anyone doing a kickstart install + */ + if ((FL_IP_PARAM(flags) && FL_IPV6_PARAM(flags)) || + (FL_IP_PARAM(flags) && FL_NOIPV6(flags)) || + (FL_IPV6_PARAM(flags) && FL_NOIPV4(flags)) || + (FL_NOIPV4(flags) && FL_NOIPV6(flags)) || + (FL_IS_KICKSTART(flags))) { + skipForm = 1; + newtPopWindow(); + } + + /* run the form */ + do { + if (!skipForm) { + answer = newtRunForm(f); + + if (answer == back) { + newtFormDestroy(f); + newtPopWindow(); + return LOADER_BACK; + } + + /* need at least one stack */ + if (opts->ipv4Choice == ' ' && opts->ipv6Choice == ' ') { + newtWinMessage(_("Missing Protocol"), _("Retry"), + _("You must select at least one protocol (IPv4 " + "or IPv6).")); + continue; + } + + /* NFS only works over IPv4 */ + if (opts->ipv4Choice == ' ' && methodNum == METHOD_NFS) { + newtWinMessage(_("IPv4 Needed for NFS"), _("Retry"), + _("NFS installation method requires IPv4 support.")); + continue; + } + } + + /* what TCP/IP stacks do we use? what conf methods? */ + if (opts->ipv4Choice == '*') { + flags &= ~LOADER_FLAGS_NOIPV4; + for (z = IPV4_FIRST_METHOD; z <= IPV4_LAST_METHOD; z++) + if (newtRadioGetCurrent(v4Method[0]) == v4Method[z-1]) + iface->ipv4method = z; + } else { + flags |= LOADER_FLAGS_NOIPV4; + } + + if (opts->ipv6Choice == '*') { + flags &= ~LOADER_FLAGS_NOIPV6; + for (z = IPV6_FIRST_METHOD; z <= IPV6_LAST_METHOD; z++) + if (newtRadioGetCurrent(v6Method[0]) == v6Method[z-1]) + iface->ipv6method = z; + } else { + flags |= LOADER_FLAGS_NOIPV6; + } + + /* do interface configuration (call DHCP here, or return for manual) */ + if ((!FL_NOIPV4(flags) && iface->ipv4method == IPV4_DHCP_METHOD) || + (!FL_NOIPV6(flags) && (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD))) { + /* do DHCP if selected */ + if (!FL_TESTING(flags)) { + err = writeEnabledNetInfo(iface); + if (err) { + logMessage(ERROR, + "failed to write /etc/sysconfig data for %s (%d)", + iface->device, err); + return LOADER_BACK; + } + + dret = get_connection(iface); + newtPopWindow(); + } + + if (!dret) { + iface->flags |= IFACE_FLAGS_IS_DYNAMIC; + +/* XXX: if we don't have working DNS lookups, ask for a nameserver, + * but be friendly to NM. we should ask NM if it knows about a + * nameserver and then ask the user for one if NM isn't in the know. + */ +/* + if (iface->numdns == 0) { + logMessage(WARNING, + "dhcp worked, but did not return a DNS server"); +*/ + /* + * prompt for a nameserver IP address when: + * - DHCP for IPv4, DHCP/AUTO for IPv6 and both enabled + * - IPv4 disabled and DHCP/AUTO for IPv6 + * - IPv6 disabled and DHCP for IPv4 + */ +/* + if ((iface->ipv4method == IPV4_DHCP_METHOD + && (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD)) + || (iface->ipv4method == IPV4_DHCP_METHOD + && FL_NOIPV6(flags)) + || (FL_NOIPV4(flags) + && (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD))) { + i = getDnsServers(iface); + i = i ? 0 : 1; + } else { + i = 1; + } + } else { + i = 1; + } +*/ + i = 1; + } else { + logMessage(DEBUGLVL, "get_connection() failed, returned %d", dret); + i = 0; + } + } else { + /* manual IP configuration for IPv4 and IPv6 */ + newtFormDestroy(f); + newtPopWindow(); + return LOADER_OK; + } + } while (i != 1); + + newtFormDestroy(f); + newtPopWindow(); + + if ((!FL_NOIPV4(flags) && iface->ipv4method == IPV4_MANUAL_METHOD) || + (!FL_NOIPV6(flags) && iface->ipv6method == IPV6_MANUAL_METHOD)) + return LOADER_OK; + else + return LOADER_NOOP; +} + +int manualNetConfig(char * device, iface_t * iface, + struct intfconfig_s * ipcomps, struct netconfopts * opts) { + int i, rows, pos, prefix, cidr, have[2], stack[2]; + char *buf = NULL; + char ret[48]; + struct in_addr addr; + struct in6_addr addr6; + struct in_addr *tmpaddr = NULL; + newtComponent f, okay, back, answer; + newtGrid egrid = NULL; + newtGrid qgrid = NULL; + newtGrid rgrid = NULL; + newtGrid buttons, grid; + newtComponent text = NULL; + + memset(ret, '\0', INET6_ADDRSTRLEN+1); + + /* so we don't perform this test over and over */ + stack[IPV4] = opts->ipv4Choice == '*' && + iface->ipv4method == IPV4_MANUAL_METHOD; + stack[IPV6] = opts->ipv6Choice == '*' && + iface->ipv6method == IPV6_MANUAL_METHOD; + + /* UI WINDOW 2 (optional): manual IP config for non-DHCP installs */ + rows = 2; + for (i = 0; i < 2; i++) { + if (stack[i]) { + rows++; + } + } + egrid = newtCreateGrid(4, rows); + + pos = 0; + + /* IPv4 entry items */ + if (stack[IPV4]) { + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("IPv4 address:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + ipcomps->ipv4Entry = newtEntry(-1, -1, NULL, 16, &ipcomps->ipv4, 0); + ipcomps->cidr4Entry = newtEntry(-1, -1, NULL, 16, &ipcomps->cidr4, 0); + + /* use a nested grid for ipv4 addr & netmask */ + qgrid = newtCreateGrid(3, 1); + + newtGridSetField(qgrid, 0, 0, NEWT_GRID_COMPONENT, + ipcomps->ipv4Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(qgrid, 1, 0, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("/")), + 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(qgrid, 2, 0, NEWT_GRID_COMPONENT, + ipcomps->cidr4Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtGridSetField(egrid, 1, pos, NEWT_GRID_SUBGRID, qgrid, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtComponentAddCallback(ipcomps->ipv4Entry, ipCallback, ipcomps); + newtComponentAddCallback(ipcomps->cidr4Entry, cidrCallback, ipcomps); + + /* populate fields if we have data already */ + if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } else if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (*ret) { + newtEntrySet(ipcomps->ipv4Entry, ret, 1); + } + + if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->netmask, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } else if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->netmask, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (*ret) { + newtEntrySet(ipcomps->cidr4Entry, ret, 1); + } + + pos++; + } + + /* IPv6 entry items */ + if (stack[IPV6]) { + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("IPv6 address:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + ipcomps->ipv6Entry = newtEntry(-1, -1, NULL, 41, &ipcomps->ipv6, 0); + ipcomps->cidr6Entry = newtEntry(-1, -1, NULL, 4, &ipcomps->cidr6, 0); + + /* use a nested grid for ipv6 addr & netmask */ + rgrid = newtCreateGrid(3, 1); + + newtGridSetField(rgrid, 0, 0, NEWT_GRID_COMPONENT, + ipcomps->ipv6Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(rgrid, 1, 0, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("/")), + 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(rgrid, 2, 0, NEWT_GRID_COMPONENT, + ipcomps->cidr6Entry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtGridSetField(egrid, 1, pos, NEWT_GRID_SUBGRID, rgrid, + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + newtComponentAddCallback(ipcomps->ipv6Entry, ipCallback, ipcomps); + newtComponentAddCallback(ipcomps->cidr6Entry, cidrCallback, ipcomps); + + /* populate fields if we have data already */ + if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, ret, + INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } else if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, ret, + INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } + } + + if (*ret) { + newtEntrySet(ipcomps->ipv6Entry, ret, 1); + } + + if (iface->ip6prefix) { + if (asprintf(&buf, "%d", iface->ip6prefix) == -1) { + buf = NULL; + } + } else if (iface->ip6prefix) { + if (asprintf(&buf, "%d", iface->ip6prefix) == -1) { + buf = NULL; + } + } + + if (buf != NULL) { + newtEntrySet(ipcomps->cidr6Entry, buf, 1); + free(buf); + } + + pos++; + } + + /* common entry items */ + ipcomps->gwEntry = newtEntry(-1, -1, NULL, 41, &ipcomps->gw, 0); + ipcomps->nsEntry = newtEntry(-1, -1, NULL, 41, &ipcomps->ns, 0); + + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("Gateway:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(egrid, 1, pos, NEWT_GRID_COMPONENT, + ipcomps->gwEntry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + pos++; + + newtGridSetField(egrid, 0, pos, NEWT_GRID_COMPONENT, + newtLabel(-1, -1, _("Name Server:")), + 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(egrid, 1, pos, NEWT_GRID_COMPONENT, + ipcomps->nsEntry, 1, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); + + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, ret, + INET_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + newtEntrySet(ipcomps->gwEntry, ret, 1); + } + } else if (iface_have_in6_addr(&iface->gateway6)) { + if (inet_ntop(AF_INET6, &iface->gateway6, ret, + INET6_ADDRSTRLEN) == NULL) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + newtEntrySet(ipcomps->gwEntry, ret, 1); + } + } + + if (iface->numdns) { + newtEntrySet(ipcomps->nsEntry, iface->dns[0], 1); + } else if (iface->numdns) { + newtEntrySet(ipcomps->nsEntry, iface->dns[0], 1); + } + + newtComponentAddCallback(ipcomps->gwEntry, ipCallback, ipcomps); + newtComponentAddCallback(ipcomps->nsEntry, ipCallback, ipcomps); + + /* button bar at the bottom of the window */ + buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL); + + /* main window layout */ + grid = newtCreateGrid(1, 3); + + if (asprintf(&buf, + _("Enter the IPv4 and/or the IPv6 address and prefix " + "(address / prefix). For IPv4, the dotted-quad " + "netmask or the CIDR-style prefix are acceptable. " + "The gateway and name server fields must be valid IPv4 " + "or IPv6 addresses.")) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + text = newtTextboxReflowed(-1, -1, buf, 52, 0, 10, 0); + + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 1, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, egrid, + 0, 0, 0, 1, NEWT_ANCHOR_LEFT, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + f = newtForm(NULL, NULL, 0); + newtGridAddComponentsToForm(grid, f, 1); + newtGridWrappedWindow(grid, _("Manual TCP/IP Configuration")); + newtGridFree(grid, 1); + + /* run the form */ + while ((have[IPV4] != 2) || (have[IPV6] != 2)) { + have[IPV4] = 0; + have[IPV6] = 0; + + for (i = 0; i < 2; i++) + if (!stack[i]) have[i] = 2; + + answer = newtRunForm(f); + /* memset(newCfg, 0, sizeof(*newCfg)); */ + + /* collect IPv4 data */ + if (stack[IPV4]) { + if (ipcomps->ipv4) { + if (inet_pton(AF_INET, ipcomps->ipv4, &iface->ipaddr) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + have[IPV4]++; + } + } + + if (ipcomps->cidr4) { + if (inet_pton(AF_INET, ipcomps->cidr4, &iface->netmask)>=1) { + have[IPV4]++; + } else { + errno = 0; + cidr = strtol(ipcomps->cidr4, NULL, 10); + + if ((errno == ERANGE && (cidr == LONG_MIN || + cidr == LONG_MAX)) || + (errno != 0 && cidr == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (cidr >= 1 && cidr <= 32) { + tmpaddr = iface_prefix2netmask(cidr); + if (tmpaddr != NULL) { + memcpy(&iface->netmask, tmpaddr, + sizeof(struct in_addr)); + have[IPV4]++; + } else { + iface->netmask.s_addr = 0; + } + } + } + } + } + + /* collect IPv6 data */ + if (stack[IPV6]) { + if (ipcomps->ipv6) { + if (inet_pton(AF_INET6, ipcomps->ipv6, &iface->ip6addr) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + } else { + have[IPV6]++; + } + } + + if (ipcomps->cidr6) { + errno = 0; + prefix = strtol(ipcomps->cidr6, NULL, 10); + + if ((errno == ERANGE && (prefix == LONG_MIN || + prefix == LONG_MAX)) || + (errno != 0 && prefix == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (prefix > 0 || prefix <= 128) { + iface->ip6prefix = prefix; + have[IPV6]++; + } + } + } + + /* collect common network settings */ + if (ipcomps->gw) { + if (inet_pton(AF_INET, ipcomps->gw, &iface->gateway) <= 0) { + memset(&iface->gateway, 0, sizeof(iface->gateway)); + + if (inet_pton(AF_INET6, ipcomps->gw, &iface->gateway6) <= 0) { + logMessage(ERROR, "%s (%d): %s", __func__, __LINE__, + strerror(errno)); + memset(&iface->gateway6, 0, sizeof(iface->gateway6)); + } + } + } + + /* gather nameservers */ + if (ipcomps->ns) { + if ((inet_pton(AF_INET, ipcomps->ns, &addr) >= 1) || + (inet_pton(AF_INET6, ipcomps->ns, &addr6) >= 1)) { + iface->dns[0] = strdup(ipcomps->ns); + if (iface->numdns < 1) + iface->numdns = 1; + } + } + + /* user selected back, but we've saved what they entered already */ + if (answer == back) { + newtFormDestroy(f); + newtPopWindow(); + free(buf); + return LOADER_BACK; + } + + /* we might be done now */ + if (have[IPV4] != 2) { + newtWinMessage(_("Missing Information"), _("Retry"), + _("You must enter both a valid IPv4 address and a " + "network mask or CIDR prefix.")); + } + + if (have[IPV6] != 2) { + newtWinMessage(_("Missing Information"), _("Retry"), + _("You must enter both a valid IPv6 address and a " + "CIDR prefix.")); + } + + strcpy(iface->device, device); + iface->flags &= ~IFACE_FLAGS_IS_DYNAMIC; + } + + free(buf); + newtFormDestroy(f); + newtPopWindow(); + + return LOADER_OK; +} + +void debugNetworkInfo(iface_t * iface) { + int i; + char buf[INET6_ADDRSTRLEN]; + + logMessage(DEBUGLVL, "device = %s", iface->device); + + if (iface->macaddr != NULL) { + logMessage(DEBUGLVL, "MAC address = %s", iface->macaddr); + } + + if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, INET_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv4 address = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv4 address = %s", buf); + } + } + + if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->netmask, buf, INET_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv4 netmask = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv4 netmask = %s", buf); + } + } + + if (iface_have_in_addr(&iface->broadcast)) { + if (inet_ntop(AF_INET, &iface->broadcast, buf, + INET_ADDRSTRLEN) == NULL ) { + logMessage(DEBUGLVL, "IPv4 broadcast = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv4 broadcast = %s", buf); + } + } + + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, buf, INET_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "Gateway = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "Gateway = %s", buf); + } + } + + if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, buf, + INET6_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv6 address = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv6 address = %s", buf); + } + } + + if (iface->ip6prefix) { + logMessage(DEBUGLVL, "IPv6 prefix = %d", iface->ip6prefix); + } + + if (iface_have_in6_addr(&iface->gateway6)) { + if (inet_ntop(AF_INET6, &iface->gateway6, buf, + INET6_ADDRSTRLEN) == NULL) { + logMessage(DEBUGLVL, "IPv6 Gateway = <unable to convert>"); + } else { + logMessage(DEBUGLVL, "IPv6 Gateway = %s", buf); + } + } + + if (iface->numdns > 0) { + for (i = 0; i < iface->numdns; i++) { + logMessage(DEBUGLVL, "DNS[%d] = %s", i, iface->dns[i]); + } + } + + if (iface->hostname) { + logMessage(DEBUGLVL, "hostname = %s", iface->hostname); + } + + if (iface->domain) { + logMessage(DEBUGLVL, "domain = %s", iface->domain); + } + + if (iface->dhcptimeout) { + logMessage(DEBUGLVL, "DHCP timeout = %d", iface->dhcptimeout); + } + + if (iface->vendorclass) { + logMessage(DEBUGLVL, "DHCP vendor class = %s", iface->vendorclass); + } + + if (iface->ssid) { + logMessage(DEBUGLVL, "SSID = %s", iface->ssid); + } + + if (iface->wepkey) { + logMessage(DEBUGLVL, "WEP key = %s", iface->wepkey); + } + +#if defined(__s390__) || defined(__s390x__) + if (iface->mtu) { + logMessage(DEBUGLVL, "mtu = %d", iface->mtu); + } + + if (iface->subchannels) { + logMessage(DEBUGLVL, "subchannels = %s", iface->subchannels); + } + + if (iface->portname) { + logMessage(DEBUGLVL, "portname = %s", iface->portname); + } + + if (iface->peerid) { + logMessage(DEBUGLVL, "peerid = %s", iface->peerid); + } + + if (iface->nettype) { + logMessage(DEBUGLVL, "nettype = %s", iface->nettype); + } + + if (iface->ctcprot) { + logMessage(DEBUGLVL, "ctcprot = %s", iface->ctcprot); + } +#endif + +/* FIXME: print rest of iface structure */ + + return; +} + +/* + * By default, we disable all network interfaces and then only + * bring up the ones the user wants. + */ +int writeDisabledNetInfo(void) { + int i = 0; + char *ofile = NULL; + FILE *fp = NULL; + struct device **devs = NULL; + + devs = getDevices(DEVICE_NETWORK); + + if (devs == NULL) { + return 1; + } + + for (i = 0; devs[i]; i++) { + if (asprintf(&ofile, "/etc/sysconfig/network-scripts/ifcfg-%s", + devs[i]->device) == -1) { + logMessage(ERROR, "%s (%d): %m", __func__, __LINE__); + abort(); + } + + if ((fp = fopen(ofile, "w")) == NULL) { + free(ofile); + return 2; + } + + fprintf(fp, "DEVICE=%s\n", devs[i]->device); + fprintf(fp, "HWADDR=%s\n", iface_mac2str(devs[i]->device)); + fprintf(fp, "ONBOOT=no\n"); + fprintf(fp, "NM_CONTROLLED=no\n"); + + if (ofile) { + free(ofile); + } + + if (fclose(fp) == EOF) { + return 3; + } + } + + return 0; +} + +/* + * Write out network interface control files: + * /etc/sysconfig/network-scripts/ifcfg-DEVICE + * /etc/sysconfig/network + */ +int writeEnabledNetInfo(iface_t *iface) { + int i = 0; + FILE *fp = NULL; + char buf[INET6_ADDRSTRLEN+1]; + char *ofile = NULL; + + memset(&buf, '\0', sizeof(buf)); + + if (asprintf(&ofile, "/etc/sysconfig/network-scripts/ifcfg-%s", + iface->device) == -1) { + return 1; + } + + if ((fp = fopen(ofile, "w")) == NULL) { + free(ofile); + return 2; + } + + fprintf(fp, "DEVICE=%s\n", iface->device); + fprintf(fp, "HWADDR=%s\n", iface_mac2str(iface->device)); + fprintf(fp, "ONBOOT=yes\n"); + fprintf(fp, "NM_CONTROLLED=yes\n"); + + if (!FL_NOIPV4(flags)) { + if (iface->ipv4method == IPV4_DHCP_METHOD) { + fprintf(fp, "BOOTPROTO=dhcp\n"); + } else if (iface->ipv4method == IPV4_MANUAL_METHOD) { + fprintf(fp, "BOOTPROTO=static\n"); + + if (iface_have_in_addr(&iface->ipaddr)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 3; + } + + fprintf(fp, "IPADDR=%s\n", buf); + } + + if (iface_have_in_addr(&iface->netmask)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 4; + } + + fprintf(fp, "NETMASK=%s\n", buf); + } + + if (iface_have_in_addr(&iface->broadcast)) { + if (inet_ntop(AF_INET, &iface->ipaddr, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 5; + } + + fprintf(fp, "BROADCAST=%s\n", buf); + } + + /* XXX: this should not be here, but ifcfg-fedora + * in NM does not currently read the global + * /etc/sysconfig/network file. + */ + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, buf, + INET_ADDRSTRLEN) == NULL) { + free(ofile); + return 6; + } + + fprintf(fp, "GATEWAY=%s\n", buf); + } + } + } + + if (!FL_NOIPV6(flags)) { + if (iface->ipv6method == IPV6_AUTO_METHOD || + iface->ipv6method == IPV6_DHCP_METHOD || + iface->ipv6method == IPV6_MANUAL_METHOD) { + fprintf(fp, "IPV6INIT=yes\n"); + + if (iface->ipv6method == IPV6_AUTO_METHOD) { + fprintf(fp, "IPV6_AUTOCONF=yes\n"); + } else if (iface->ipv6method == IPV6_DHCP_METHOD) { + fprintf(fp, "DHCPV6C=yes\n"); + } else if (iface->ipv6method == IPV6_MANUAL_METHOD) { + if (iface_have_in6_addr(&iface->ip6addr)) { + if (inet_ntop(AF_INET6, &iface->ip6addr, buf, + INET6_ADDRSTRLEN) == NULL) { + free(ofile); + return 7; + } + + if (iface->ip6prefix) { + fprintf(fp, "IPV6ADDR=%s/%d\n", buf, iface->ip6prefix); + } else { + fprintf(fp, "IPV6ADDR=%s\n", buf); + } + } + } + } + } + + if (iface->numdns > 0) { + for (i = 0; i < iface->numdns; i++) { + fprintf(fp, "DNS%d=%s\n", i+1, iface->dns[i]); + } + } + + if (iface->hostname) { + fprintf(fp, "HOSTNAME=%s\n", iface->hostname); + } + + if (iface->domain) { + fprintf(fp, "DOMAIN=%s\n", iface->domain); + } + + if (iface->mtu) { + fprintf(fp, "MTU=%d\n", iface->mtu); + } + + if (iface->peerid) { + fprintf(fp, "PEERID=%s\n", iface->peerid); + } + + if (iface->subchannels) { + fprintf(fp, "SUBCHANNELS=%s\n", iface->subchannels); + } + + if (iface->portname) { + fprintf(fp, "PORTNAME=%s\n", iface->portname); + } + + if (iface->nettype) { + fprintf(fp, "NETTYPE=%s\n", iface->nettype); + } + + if (iface->ctcprot) { + fprintf(fp, "CTCPROT=%s\n", iface->ctcprot); + } + + if (ofile) { + free(ofile); + } + + if (fclose(fp) == EOF) { + return 8; + } + + /* Global settings */ + if ((fp = fopen("/etc/sysconfig/network", "w")) == NULL) { + return 9; + } + + if (!FL_NOIPV4(flags)) { + fprintf(fp, "NETWORKING=yes\n"); + } + + if (!FL_NOIPV6(flags)) { + fprintf(fp, "NETWORKING_IPV6=yes\n"); + } + + if (iface->hostname != NULL) { + fprintf(fp, "HOSTNAME=%s\n", iface->hostname); + } + + if (iface_have_in_addr(&iface->gateway)) { + if (inet_ntop(AF_INET, &iface->gateway, buf, + INET_ADDRSTRLEN) == NULL) { + return 10; + } + + fprintf(fp, "GATEWAY=%s\n", buf); + } + + if (iface_have_in6_addr(&iface->gateway6)) { + if (inet_ntop(AF_INET6, &iface->gateway6, buf, + INET6_ADDRSTRLEN) == NULL) { + return 11; + } + + fprintf(fp, "IPV6_DEFAULTGW=%s\n", buf); + } + + if (fclose(fp) == EOF) { + return 12; + } + + return 0; +} + +void setKickstartNetwork(struct loaderData_s * loaderData, int argc, + char ** argv) { + char * arg, * bootProto = NULL, * device = NULL, *ethtool = NULL, * class = NULL; + char * essid = NULL, * wepkey = NULL, * onboot = NULL; + int mtu = 1500, noipv4 = 0, noipv6 = 0, dhcpTimeout = -1, noDns = 0, noksdev = 0; + int rc; + poptContext optCon; + iface_t iface; + + struct poptOption ksOptions[] = { + { "bootproto", '\0', POPT_ARG_STRING, &bootProto, 0, NULL, NULL }, + { "device", '\0', POPT_ARG_STRING, &device, 0, NULL, NULL }, + { "dhcpclass", '\0', POPT_ARG_STRING, &class, 0, NULL, NULL }, + { "gateway", '\0', POPT_ARG_STRING, NULL, 'g', NULL, NULL }, + { "ip", '\0', POPT_ARG_STRING, NULL, 'i', NULL, NULL }, + { "mtu", '\0', POPT_ARG_INT, &mtu, 0, NULL, NULL }, + { "nameserver", '\0', POPT_ARG_STRING, NULL, 'n', NULL, NULL }, + { "netmask", '\0', POPT_ARG_STRING, NULL, 'm', NULL, NULL }, + { "noipv4", '\0', POPT_ARG_NONE, &noipv4, 0, NULL, NULL }, + { "noipv6", '\0', POPT_ARG_NONE, &noipv6, 0, NULL, NULL }, + { "nodns", '\0', POPT_ARG_NONE, &noDns, 0, NULL, NULL }, + { "hostname", '\0', POPT_ARG_STRING, NULL, 'h', NULL, NULL}, + { "ethtool", '\0', POPT_ARG_STRING, ðtool, 0, NULL, NULL }, + { "essid", '\0', POPT_ARG_STRING, &essid, 0, NULL, NULL }, + { "wepkey", '\0', POPT_ARG_STRING, &wepkey, 0, NULL, NULL }, + { "onboot", '\0', POPT_ARG_STRING, &onboot, 0, NULL, NULL }, + { "notksdevice", '\0', POPT_ARG_NONE, &noksdev, 0, NULL, NULL }, + { "dhcptimeout", '\0', POPT_ARG_INT, &dhcpTimeout, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + iface_init_iface_t(&iface); + + optCon = poptGetContext(NULL, argc, (const char **) argv, + ksOptions, 0); + while ((rc = poptGetNextOpt(optCon)) >= 0) { + arg = (char *) poptGetOptArg(optCon); + + switch (rc) { + case 'g': + loaderData->gateway = strdup(arg); + break; + case 'i': + loaderData->ipv4 = strdup(arg); + break; + case 'n': + loaderData->dns = strdup(arg); + break; + case 'm': + loaderData->netmask = strdup(arg); + break; + case 'h': + if (loaderData->hostname) + free(loaderData->hostname); + loaderData->hostname = strdup(arg); + break; + } + } + + if (rc < -1) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to kickstart network command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + } else { + poptFreeContext(optCon); + } + + /* if they've specified dhcp/bootp or haven't specified anything, + * use dhcp for the interface */ + if ((bootProto && (!strncmp(bootProto, "dhcp", 4) || + !strncmp(bootProto, "bootp", 4))) || + (!bootProto && !loaderData->ipv4)) { + loaderData->ipv4 = strdup("dhcp"); + loaderData->ipinfo_set = 1; + } else if (loaderData->ipv4) { + /* JKFIXME: this assumes a bit... */ + loaderData->ipinfo_set = 1; + } + + /* now make sure the specified bootproto is valid */ + if (bootProto && strcmp(bootProto, "dhcp") && strcmp(bootProto, "bootp") && + strcmp(bootProto, "static") && strcmp(bootProto, "query")) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad bootproto %s specified in network command"), + bootProto); + } + + if (!noksdev) { + if (device) { + loaderData->netDev = strdup(device); + loaderData->netDev_set = 1; + } + + if (class) { + loaderData->netCls = strdup(class); + loaderData->netCls_set = 1; + } + + if (ethtool) { + if (loaderData->ethtool) + free(loaderData->ethtool); + loaderData->ethtool = strdup(ethtool); + free(ethtool); + } + + if (essid) { + if (loaderData->essid) + free(loaderData->essid); + loaderData->essid = strdup(essid); + free(essid); + } + + if (wepkey) { + if (loaderData->wepkey) + free(loaderData->wepkey); + loaderData->wepkey = strdup(wepkey); + free(wepkey); + } + + if (mtu) { + loaderData->mtu = mtu; + } + + if (noipv4) + flags |= LOADER_FLAGS_NOIPV4; + + if (noipv6) + flags |= LOADER_FLAGS_NOIPV6; + } + + if (noDns) { + loaderData->noDns = 1; + } + + /* Make sure the network is always up if there's a network line in the + * kickstart file, as %post/%pre scripts might require that. + */ + if (loaderData->method != METHOD_NFS && loaderData->method != METHOD_URL) { + if (kickstartNetworkUp(loaderData, &iface)) + logMessage(ERROR, "unable to bring up network"); + } +} + +/* if multiple interfaces get one to use from user. */ +/* NOTE - uses kickstart data available in loaderData */ +int chooseNetworkInterface(struct loaderData_s * loaderData) { + int i, rc, ask, idrc, secs, deviceNums = 0, deviceNum, foundDev = 0; + unsigned int max = 40; + char **devices; + char **deviceNames; + char *ksMacAddr = NULL, *seconds = strdup("10"), *idstr = NULL; + struct device **devs; + struct newtWinEntry entry[] = {{N_("Seconds:"), (char **) &seconds, 0}, + {NULL, NULL, 0 }}; + + devs = getDevices(DEVICE_NETWORK); + if (!devs) { + logMessage(ERROR, "no network devices in choose network device!"); + return LOADER_ERROR; + } + + for (i = 0; devs[i]; i++); + + devices = alloca((i + 1) * sizeof(*devices)); + deviceNames = alloca((i + 1) * sizeof(*devices)); + if (loaderData->netDev && (loaderData->netDev_set) == 1) { + if ((loaderData->bootIf && (loaderData->bootIf_set) == 1) && !strcasecmp(loaderData->netDev, "bootif")) { + ksMacAddr = strdup(loaderData->bootIf); + } else { + ksMacAddr = strdup(loaderData->netDev); + } + + ksMacAddr = str2upper(ksMacAddr); + } + + for (i = 0; devs[i]; i++) { + if (!devs[i]->device) + continue; + + if (devs[i]->description) { + deviceNames[deviceNums] = alloca(strlen(devs[i]->device) + + strlen(devs[i]->description) + 4); + sprintf(deviceNames[deviceNums],"%s - %s", + devs[i]->device, devs[i]->description); + if (strlen(deviceNames[deviceNums]) > max) + max = strlen(deviceNames[deviceNums]); + devices[deviceNums] = devs[i]->device; + } else { + devices[deviceNums] = devs[i]->device; + deviceNames[deviceNums] = devs[i]->device; + } + + deviceNums++; + + /* this device has been set and we don't really need to ask + * about it again... */ + if (loaderData->netDev && (loaderData->netDev_set == 1)) { + if (!strcmp(loaderData->netDev, devs[i]->device)) { + foundDev = 1; + } else if (ksMacAddr != NULL) { + /* maybe it's a mac address */ + char *devmacaddr = NULL; + devmacaddr = iface_mac2str(devs[i]->device); + if ((devmacaddr != NULL) && !strcmp(ksMacAddr, devmacaddr)) { + foundDev = 1; + free(loaderData->netDev); + loaderData->netDev = devs[i]->device; + if (devmacaddr != NULL) + free(devmacaddr); + break; + } + + if (devmacaddr != NULL) + free(devmacaddr); + } + } + } + if (ksMacAddr) + free(ksMacAddr); + if (foundDev == 1) + return LOADER_NOOP; + + devices[deviceNums] = NULL; + deviceNames[deviceNums] = NULL; + qsort(devices, deviceNums, sizeof(*devices), simpleStringCmp); + qsort(deviceNames, deviceNums, sizeof(*devices), simpleStringCmp); + + /* ASSERT: we should *ALWAYS* have a network device when we get here */ + if (!deviceNums) { + logMessage(CRITICAL, "no network device in chooseNetworkInterface"); + return LOADER_ERROR; + } + + /* JKFIXME: if we only have one interface and it doesn't have link, + * do we go ahead? */ + if (deviceNums == 1) { + logMessage(INFO, "only have one network device: %s", devices[0]); + loaderData->netDev = devices[0]; + return LOADER_NOOP; + } + + if ((loaderData->netDev && (loaderData->netDev_set == 1)) && + !strcmp(loaderData->netDev, "link")) { + logMessage(INFO, "looking for first netDev with link"); + for (rc = 0; rc < 5; rc++) { + for (i = 0; i < deviceNums; i++) { + if (get_link_status(devices[i]) == 1) { + loaderData->netDev = devices[i]; + logMessage(INFO, "%s has link, using it", devices[i]); + return LOADER_NOOP; + } + } + sleep(1); + } + logMessage(WARNING, "wanted netdev with link, but none present. prompting"); + } + + startNewt(); + + if (max > 70) + max = 70; + + /* JKFIXME: should display link status */ + deviceNum = 0; + ask = 1; + while (ask) { + rc = newtWinMenu(_("Networking Device"), + _("You have multiple network devices on this system. " + "Which would you like to install through?"), + max, 10, 10, + deviceNums < 6 ? deviceNums : 6, deviceNames, + &deviceNum, _("OK"), _("Identify"), _("Back"), NULL); + + if (rc == 2) { + if (!devices[deviceNum]) { + logMessage(ERROR, "NIC %d contains no device name", deviceNum); + continue; + } + + if (asprintf(&idstr, "%s %s %s", + _("You can identify the physical port for"), + devices[deviceNum], + _("by flashing the LED lights for a number of " + "seconds. Enter a number between 1 and 30 to " + "set the duration to flash the LED port " + "lights.")) == -1) { + logMessage(ERROR, "asprintf() failure in %s: %m", __func__); + abort(); + } + + i = 1; + while (i) { + idrc = newtWinEntries(_("Identify NIC"), idstr, 50, 5, 15, 24, + entry, _("OK"), _("Back"), NULL); + + if (idrc == 0 || idrc == 1) { + errno = 0; + secs = strtol((const char *) seconds, NULL, 10); + if (errno == EINVAL || errno == ERANGE) { + logMessage(ERROR, "strtol() failure in %s: %m", + __func__); + continue; + } + + if (secs <=0 || secs > 30) { + newtWinMessage(_("Invalid Duration"), _("OK"), + _("You must enter the number of " + "seconds as an integer between 1 " + "and 30.")); + continue; + } + + idrc = 41 + strlen(devices[deviceNum]); + if (secs > 9) { + idrc += 1; + } + + winStatus(idrc, 3, NULL, + _("Flashing %s port lights for %d seconds..."), + devices[deviceNum], secs); + + if (identifyNIC(devices[deviceNum], secs)) { + logMessage(ERROR, + "error during physical NIC identification"); + } + + newtPopWindow(); + i = 0; + } else if (idrc == 2) { + i = 0; + } + } + } else if (rc == 3) { + ask = 0; + return LOADER_BACK; + } else { + ask = 0; + } + } + + loaderData->netDev = devices[deviceNum]; + return LOADER_OK; +} + +/* JKFIXME: bad name. this function brings up networking early on a + * kickstart install so that we can do things like grab the ks.cfg from + * the network */ +int kickstartNetworkUp(struct loaderData_s * loaderData, iface_t * iface) { + int rc; + + /* we may have networking already, so return to the caller */ + if ((loaderData->ipinfo_set == 1) || (loaderData->ipv6info_set == 1)) + return 0; + + memset(iface, 0, sizeof(*iface)); + + do { + do { + /* this is smart and does the right thing based on whether or not + * we have ksdevice= specified */ + rc = chooseNetworkInterface(loaderData); + + if (rc == LOADER_ERROR) { + /* JKFIXME: ask for a driver disk? */ + logMessage(ERROR, "no network drivers for doing kickstart"); + return -1; + } else if (rc == LOADER_BACK) { + return -1; + } + + /* insert device into iface structure */ + strcpy(iface->device, loaderData->netDev); + + break; + } while (1); + + /* we don't want to end up asking about interface more than once + * if we're in a kickstart-ish case (#100724) */ + loaderData->netDev_set = 1; + + /* JKFIXME: this is kind of crufty, we depend on the fact that the + * ip is set and then just get the network up. we should probably + * add a way to do asking about static here and not be such a hack */ + if (!loaderData->ipv4) { + loaderData->ipv4 = strdup("dhcp"); + } + loaderData->ipinfo_set = 1; + + setupNetworkDeviceConfig(iface, loaderData); + + rc = readNetConfig(loaderData->netDev, iface, loaderData->netCls, + loaderData->method); + + if (rc == LOADER_ERROR) { + logMessage(ERROR, "unable to setup networking"); + return -1; + } + else if (rc == LOADER_BACK) { + /* Going back to the interface selection screen, so unset anything + * we set before attempting to bring the incorrect interface up. + */ + loaderData->netDev_set = 0; + free(loaderData->ipv4); + loaderData->ipinfo_set = 0; + } + else + break; + } while (1); + + return 0; +} + +void splitHostname (char *str, char **host, char **port) +{ + char *rightbrack = strchr(str, ']'); + + *host = NULL; + *port = NULL; + + if (*str == '[' && rightbrack) { + /* An IPv6 address surrounded by brackets, optionally with a colon and + * port number. + */ + char *colon = strrchr(rightbrack, ':'); + + if (colon) { + *host = strndup(str+1, rightbrack-1-str); + *port = strdup(colon+1); + } + else + *host = strndup(str+1, rightbrack-1-str); + } else if (strcount(str, ':') > 1) { + /* An IPv6 address without brackets. Don't make the user surround the + * address with brackets if there's no port number. + */ + *host = strdup(str); + } else { + /* An IPv4 address, optionally with a colon and port number. */ + char *colon = strrchr(str, ':'); + + if (colon) { + *host = strndup(str, colon-str); + *port = strdup(colon+1); + } + else + *host = strdup(str); + } +} + +/* + * Start NetworkManager and wait for a valid link, return non-zero on error. + */ +int get_connection(iface_t *iface) { + int ret; + DBusConnection *connection = NULL; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + DBusError error; + DBusMessageIter iter, variant_iter; + dbus_uint32_t state = 0; + char *nm_iface = "org.freedesktop.NetworkManager"; + char *property = "State"; + + if (iface == NULL) { + return 1; + } + + /* display status */ + if (FL_CMDLINE(flags)) { + printf(_("Waiting for NetworkManager to configure %s...\n"), + iface->device); + } else { + winStatus(55, 3, NULL, + _("Waiting for NetworkManager to configure %s...\n"), + iface->device, 0); + } + + /* start NetworkManager for configured interface */ + logMessage(INFO, "starting NetworkManager (%d) for %s", __LINE__, + iface->device); + ret = iface_start_NetworkManager(iface); + if (ret > 0) { + return 2; + } + + dbus_error_init(&error); + connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + return 3; + } + + dbus_error_init(&error); + message = dbus_message_new_method_call(NM_DBUS_SERVICE, + NM_DBUS_PATH, + "org.freedesktop.DBus.Properties", + "Get"); + if (!message) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + return 4; + } + + dbus_error_init(&error); + if (!dbus_message_append_args(message, + DBUS_TYPE_STRING, &nm_iface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + dbus_message_unref(message); + return 5; + } + + /* send message and block until a reply or error comes back */ + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(connection, + message, -1, + &error); + dbus_message_unref(message); + if (!reply) { + if (dbus_error_is_set(&error)) { + logMessage(DEBUGLVL, "%s (%d): %s: %s", __func__, + __LINE__, error.name, error.message); + dbus_error_free(&error); + } + + dbus_message_unref(reply); + return 6; + } + + /* extra uint32 'state' property from the returned variant type */ + dbus_message_iter_init(reply, &iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + logMessage(DEBUGLVL, "%s (%d): unexpected reply format", + __func__, __LINE__); + dbus_message_unref(reply); + return 7; + } + + /* open the variant */ + dbus_message_iter_recurse(&iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_UINT32) { + logMessage(DEBUGLVL, "%s (%d): unexpected reply format", + __func__, __LINE__); + dbus_message_unref(reply); + return 8; + } + + dbus_message_iter_get_basic(&variant_iter, &state); + if (state == NM_STATE_CONNECTED) { + logMessage(DEBUGLVL, "%s (%d): NetworkManager connected", + __func__, __LINE__); + dbus_message_unref(reply); + return 0; + } + + /* NM is not in NM_STATE_CONNECTED if we get here */ + dbus_message_unref(reply); + return 9; +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/net.h b/loader/net.h new file mode 100644 index 000000000..c765c9cec --- /dev/null +++ b/loader/net.h @@ -0,0 +1,68 @@ +/* + * net.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_LOADER_NET +#define H_LOADER_NET + +#include <newt.h> +#include "../isys/iface.h" +#include "loader.h" + +#define DHCP_METHOD_STR _("Dynamic IP configuration (DHCP)") +#define DHCPV6_METHOD_STR _("Dynamic IP configuration (DHCPv6)") +#define MANUAL_METHOD_STR _("Manual configuration") +#define AUTO_METHOD_STR _("Automatic neighbor discovery") + +struct intfconfig_s { + newtComponent ipv4Entry, cidr4Entry; + newtComponent ipv6Entry, cidr6Entry; + newtComponent gwEntry, nsEntry; + const char *ipv4, *cidr4; + const char *ipv6, *cidr6; + const char *gw, *gw6, *ns; +}; + +struct netconfopts { + char ipv4Choice; + char ipv6Choice; +}; + +typedef int int32; + +int readNetConfig(char * device, iface_t * iface, + char * dhcpclass, int methodNum); +int configureTCPIP(char * device, iface_t * iface, struct netconfopts * opts, + int methodNum); +int manualNetConfig(char * device, iface_t * iface, + struct intfconfig_s * ipcomps, struct netconfopts * opts); +void debugNetworkInfo(iface_t * iface); +int writeDisabledNetInfo(void); +int writeEnabledNetInfo(iface_t * iface); +int chooseNetworkInterface(struct loaderData_s * loaderData); +void setupNetworkDeviceConfig(iface_t * iface, + struct loaderData_s * loaderData); +int setupWireless(iface_t * iface); +void setKickstartNetwork(struct loaderData_s * loaderData, int argc, + char ** argv); +int kickstartNetworkUp(struct loaderData_s * loaderData, + iface_t * iface); +void splitHostname (char *str, char **host, char **port); +int get_connection(iface_t * iface); + +#endif diff --git a/loader/nfsinstall.c b/loader/nfsinstall.c new file mode 100644 index 000000000..60dec649e --- /dev/null +++ b/loader/nfsinstall.c @@ -0,0 +1,506 @@ +/* + * nfsinstall.c - code to set up nfs installs + * + * 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 <fcntl.h> +#include <newt.h> +#include <popt.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include "copy.h" +#include "loader.h" +#include "lang.h" +#include "loadermisc.h" +#include "kickstart.h" +#include "log.h" +#include "method.h" +#include "nfsinstall.h" +#include "net.h" +#include "cdinstall.h" +#include "windows.h" + +#include "../isys/imount.h" +#include "../isys/iface.h" + +/* boot flags */ +extern uint64_t flags; + +static int nfsGetSetup(char ** hostptr, char ** dirptr) { + struct newtWinEntry entries[3]; + char * buf; + char * newServer = *hostptr ? strdup(*hostptr) : NULL; + char * newDir = *dirptr ? strdup(*dirptr) : NULL; + int rc; + + entries[0].text = _("NFS server name:"); + entries[0].value = &newServer; + entries[0].flags = NEWT_FLAG_SCROLL; + + if (asprintf(&entries[1].text, _("%s directory:"), + getProductName()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + entries[1].value = &newDir; + entries[1].flags = NEWT_FLAG_SCROLL; + entries[2].text = NULL; + entries[2].value = NULL; + + if (asprintf(&buf, _("Please enter the server name and path to your %s " + "installation image."), getProductName()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + do { + rc = newtWinEntries(_("NFS Setup"), buf, 60, 5, 15, + 24, entries, _("OK"), _("Back"), NULL); + } while (!strcmp(newServer, "") || !strcmp(newDir, "")); + + free(buf); + free(entries[1].text); + + if (rc == 2) { + if (newServer) free(newServer); + if (newDir) free(newDir); + return LOADER_BACK; + } + + if (*hostptr) free(*hostptr); + if (*dirptr) free(*dirptr); + *hostptr = newServer; + *dirptr = newDir; + + return 0; +} + +char * mountNfsImage(struct installMethod * method, + char * location, struct loaderData_s * loaderData) { + char * host = NULL; + char * directory = NULL; + char * mountOpts = NULL; + char * fullPath = NULL; + char * url = NULL; + + enum { NFS_STAGE_NFS, NFS_STAGE_MOUNT, NFS_STAGE_DONE, + NFS_STAGE_UPDATES } stage = NFS_STAGE_NFS; + + int rc; + + /* JKFIXME: ASSERT -- we have a network device setup when we get here */ + while (stage != NFS_STAGE_DONE) { + switch (stage) { + case NFS_STAGE_NFS: + logMessage(INFO, "going to do nfsGetSetup"); + if (loaderData->method == METHOD_NFS && loaderData->stage2Data) { + host = ((struct nfsInstallData *)loaderData->stage2Data)->host; + directory = ((struct nfsInstallData *)loaderData->stage2Data)->directory; + + if (((struct nfsInstallData *) + loaderData->stage2Data)->mountOpts == NULL) { + mountOpts = strdup("ro"); + } else { + if (asprintf(&mountOpts, "ro,%s", + ((struct nfsInstallData *) + loaderData->stage2Data)->mountOpts) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + logMessage(INFO, "host is %s, dir is %s, opts are '%s'", host, directory, mountOpts); + + if (!host || !directory) { + logMessage(ERROR, "missing host or directory specification"); + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + + loaderData->method = -1; + break; + } else { + host = strdup(host); + directory = strdup(directory); + } + } else { + char *substr, *tmp; + + if (nfsGetSetup(&host, &directory) == LOADER_BACK) + return NULL; + + /* If the user-provided URL points at a repo instead of a + * stage2 image, fix that up now. + */ + substr = strstr(directory, ".img"); + if (!substr || (substr && *(substr+4) != '\0')) { + if (asprintf(&tmp, "nfs:%s:%s/images/install.img", + host, directory) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + setStage2LocFromCmdline(tmp, loaderData); + free(host); + free(directory); + free(tmp); + continue; + } + + loaderData->invalidRepoParam = 1; + } + + stage = NFS_STAGE_MOUNT; + break; + + case NFS_STAGE_MOUNT: { + char *buf; + struct in_addr ip; + + if (loaderData->noDns && !(inet_pton(AF_INET, host, &ip))) { + newtWinMessage(_("Error"), _("OK"), + _("Hostname specified with no DNS configured")); + if (loaderData->method >= 0) + loaderData->method = -1; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + + break; + } + + if (asprintf(&fullPath, "%s:%.*s", host, + (int) (strrchr(directory, '/')-directory), + directory) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(INFO, "mounting nfs path %s", fullPath); + + if (FL_TESTING(flags)) { + stage = NFS_STAGE_DONE; + break; + } + + stage = NFS_STAGE_NFS; + + if (!doPwMount(fullPath, "/mnt/stage2", "nfs", mountOpts, NULL)) { + if (asprintf(&buf, "/mnt/stage2/%s", + strrchr(directory, '/')) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (!access(buf, R_OK)) { + logMessage(INFO, "can access %s", buf); + rc = mountStage2(buf); + + if (rc == 0) { + stage = NFS_STAGE_UPDATES; + + if (asprintf(&url, "nfs:%s:%s", host, + directory) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, + __LINE__); + abort(); + } + + free(buf); + break; + } else { + logMessage(WARNING, "unable to mount %s", buf); + free(buf); + break; + } + } else { + logMessage(WARNING, "unable to access %s", buf); + free(buf); + umount("/mnt/stage2"); + } + } else { + newtWinMessage(_("Error"), _("OK"), + _("That directory could not be mounted from " + "the server.")); + if (loaderData->method >= 0) + loaderData->method = -1; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + + break; + } + + if (asprintf(&buf, _("That directory does not seem to " + "contain a %s installation image."), + getProductName()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + newtWinMessage(_("Error"), _("OK"), buf); + free(buf); + + if (loaderData->method >= 0) + loaderData->method = -1; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + + break; + } + + case NFS_STAGE_UPDATES: { + char *buf; + + if (asprintf(&buf, "%.*s/RHupdates", + (int) (strrchr(fullPath, '/')-fullPath), + fullPath) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(INFO, "mounting nfs path %s for updates", buf); + + if (!doPwMount(buf, "/tmp/update-disk", "nfs", mountOpts, NULL)) { + logMessage(INFO, "Using RHupdates/ for NFS install"); + copyDirectory("/tmp/update-disk", "/tmp/updates", NULL, NULL); + umount("/tmp/update-disk"); + unlink("/tmp/update-disk"); + } else { + logMessage(INFO, "No RHupdates/ directory found, skipping"); + } + + stage = NFS_STAGE_DONE; + break; + } + + case NFS_STAGE_DONE: + break; + } + } + + free(host); + free(directory); + if (fullPath) + free(fullPath); + + return url; +} + + +void setKickstartNfs(struct loaderData_s * loaderData, int argc, + char ** argv) { + char * host = NULL, * dir = NULL, * mountOpts = NULL; + char *substr = NULL; + poptContext optCon; + int rc; + struct poptOption ksNfsOptions[] = { + { "server", '\0', POPT_ARG_STRING, &host, 0, NULL, NULL }, + { "dir", '\0', POPT_ARG_STRING, &dir, 0, NULL, NULL }, + { "opts", '\0', POPT_ARG_STRING, &mountOpts, 0, NULL, NULL}, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + logMessage(INFO, "kickstartFromNfs"); + optCon = poptGetContext(NULL, argc, (const char **) argv, ksNfsOptions, 0); + if ((rc = poptGetNextOpt(optCon)) < -1) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to NFS kickstart method " + "command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + return; + } + + if (!host || !dir) { + logMessage(ERROR, "host and directory for nfs kickstart not specified"); + return; + } + + loaderData->method = METHOD_NFS; + + substr = strstr(dir, ".img"); + if (!substr || (substr && *(substr+4) != '\0')) { + if (mountOpts) { + if (asprintf(&(loaderData->instRepo), "nfs:%s:%s:%s", host, dir, mountOpts) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } else { + if (asprintf(&(loaderData->instRepo), "nfs:%s:%s", host, dir) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + logMessage(INFO, "results of nfs, host is %s, dir is %s, opts are '%s'", + host, dir, mountOpts); + } else { + loaderData->stage2Data = calloc(sizeof(struct nfsInstallData *), 1); + ((struct nfsInstallData *)loaderData->stage2Data)->host = host; + ((struct nfsInstallData *)loaderData->stage2Data)->directory = dir; + ((struct nfsInstallData *)loaderData->stage2Data)->mountOpts = mountOpts; + + logMessage(INFO, "results of nfs, host is %s, dir is %s, opts are '%s'", + ((struct nfsInstallData *) loaderData->stage2Data)->host, + ((struct nfsInstallData *) loaderData->stage2Data)->directory, + ((struct nfsInstallData *) loaderData->stage2Data)->mountOpts); + } +} + + +int getFileFromNfs(char * url, char * dest, struct loaderData_s * loaderData) { + char * host = NULL, *path = NULL, * file = NULL, * opts = NULL; + char * chk = NULL, *ip = NULL; + int failed = 0; + iface_t iface; + + if (kickstartNetworkUp(loaderData, &iface)) { + logMessage(ERROR, "unable to bring up network"); + return 1; + } + + /* if they just did 'linux ks', they want us to figure it out from + * the dhcp/bootp information + */ +/* XXX: fixme NetworkManager integration */ +/* + if (url == NULL) { + char ret[47]; + ip_addr_t *tip; + + if (!(netCfg.dev.set & PUMP_INTFINFO_HAS_NEXTSERVER)) { + logMessage(ERROR, "no bootserver was found"); + return 1; + } + + tip = &(netCfg.dev.nextServer); + if (!(netCfg.dev.set & PUMP_INTFINFO_HAS_BOOTFILE)) { + inet_ntop(tip->sa_family, IP_ADDR(tip), ret, IP_STRLEN(tip)); + + if (asprintf(&url, "%s:%s", ret, "/kickstart/") == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(ERROR, "bootp: no bootfile received"); + } else { + inet_ntop(tip->sa_family, IP_ADDR(tip), ret, IP_STRLEN(tip)); + + if (asprintf(&url, "%s:%s", ret, netCfg.dev.bootFile) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + logMessage(INFO, "bootp: bootfile is %s", netCfg.dev.bootFile); + } + } +*/ + + /* get the IP of the target system */ + if ((ip = iface_ip2str(loaderData->netDev)) == NULL) { + logMessage(ERROR, "nl_ip2str returned NULL"); + return 1; + } + + logMessage(INFO, "url is %s", url); + getHostandPath(url, &host, &path, ip); + + opts = strchr(host, ':'); + if (opts && (strlen(opts) > 1)) { + char * c = opts; + opts = host; + host = c + 1; + *c = '\0'; + } else { + opts = NULL; + } + + /* nfs has to be a little bit different... split off the last part as + * the file and then concatenate host + dir path */ + file = strrchr(path, '/'); + if (!file) { + file = path; + } else { + *file++ ='\0'; + chk = host + strlen(host)-1; + + if (*chk == '/' || *path == '/') { + if (asprintf(&host, "%s%s", host, path) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } else { + if (asprintf(&host, "%s/%s", host, path) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + } + + logMessage(INFO, "file location: nfs:%s/%s", host, file); + + if (!doPwMount(host, "/tmp/mnt", "nfs", opts, NULL)) { + char * buf; + + if (asprintf(&buf, "/tmp/mnt/%s", file) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (copyFile(buf, dest)) { + logMessage(ERROR, "failed to copy file to %s", dest); + failed = 1; + } + + free(buf); + } else { + logMessage(ERROR, "failed to mount nfs source"); + failed = 1; + } + + free(host); + free(path); + if (ip) free(ip); + + umount("/tmp/mnt"); + unlink("/tmp/mnt"); + + return failed; +} + +int kickstartFromNfs(char * url, struct loaderData_s * loaderData) { + return getFileFromNfs(url, "/tmp/ks.cfg", loaderData); +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/nfsinstall.h b/loader/nfsinstall.h new file mode 100644 index 000000000..95d76d659 --- /dev/null +++ b/loader/nfsinstall.h @@ -0,0 +1,39 @@ +/* + * nfsinstall.h + * + * Copyright (C) 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/>. + */ + +#ifndef NFSINSTALL_H +#define NFSINSTALL_H + +#include "method.h" + +struct nfsInstallData { + char * host; + char * directory; + char * mountOpts; +}; + + +void setKickstartNfs(struct loaderData_s * loaderData, int argc, + char ** argv); +int kickstartFromNfs(char * url, struct loaderData_s * loaderData); +char * mountNfsImage(struct installMethod * method, + char * location, struct loaderData_s * loaderData); +int getFileFromNfs(char * url, char * dest, struct loaderData_s * loaderData); + +#endif diff --git a/loader/selinux.c b/loader/selinux.c new file mode 100644 index 000000000..b565a1207 --- /dev/null +++ b/loader/selinux.c @@ -0,0 +1,56 @@ +/* + * selinux.c - Various SELinux related functionality needed for the loader. + * Portions extracted from libselinux which was released as public domain + * software by the NSA. + * + * Copyright (C) 2004 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): Jeremy Katz <katzj@redhat.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <string.h> + +#include "loader.h" +#include "loadermisc.h" +#include "log.h" + +int loadpolicy() { + int pid, status; + + logMessage(INFO, "Loading SELinux policy"); + + if (!(pid = fork())) { + setenv("LD_LIBRARY_PATH", LIBPATH, 1); + execl("/usr/sbin/load_policy", + "/usr/sbin/load_policy", "-q", NULL); + logMessage(ERROR, "exec of load_policy failed: %m"); + exit(1); + } + + waitpid(pid, &status, 0); + if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) + return 1; + + return 0; +} + diff --git a/loader/selinux.h b/loader/selinux.h new file mode 100644 index 000000000..5877dddc7 --- /dev/null +++ b/loader/selinux.h @@ -0,0 +1,27 @@ +/* + * selinux.h + * + * Copyright (C) 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/>. + */ + +#ifndef SELINUX_H +#define SELINUX_H + +int loadpolicy(); + +#define ANACONDA_CONTEXT "system_u:system_r:anaconda_t:s0" + +#endif diff --git a/loader/shutdown.c b/loader/shutdown.c new file mode 100644 index 000000000..dadfc2a6e --- /dev/null +++ b/loader/shutdown.c @@ -0,0 +1,124 @@ +/* + * shutdown.c + * + * Shutdown a running system. If built with -DAS_SHUTDOWN=1, then + * it builds a standalone shutdown binary. + * + * Copyright (C) 1996, 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/>. + */ + +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/reboot.h> +#include <sys/types.h> +#include <unistd.h> + +#ifdef AS_SHUTDOWN +int testing = 0; +#else +extern int testing; +#endif + +void disableSwap(void); +void unmountFilesystems(void); + +static void rebootHandler(int signum) { + printf("rebooting system\n"); +#if USE_MINILIBC + reboot(0xfee1dead, 672274793, 0x1234567); +#else + reboot(RB_AUTOBOOT); +#endif +} + +void shutDown(int noKill, int doReboot, int doPowerOff) { + sync(); sync(); + + if (!testing && !noKill) { + printf("sending termination signals..."); + kill(-1, 15); + sleep(2); + printf("done\n"); + + printf("sending kill signals..."); + kill(-1, 9); + sleep(2); + printf("done\n"); + } + + printf("disabling swap...\n"); + disableSwap(); + + printf("unmounting filesystems...\n"); + unmountFilesystems(); + + if (doReboot) { + printf("rebooting system\n"); + sleep(2); + +#if USE_MINILIBC + reboot(0xfee1dead, 672274793, 0x1234567); +#else + reboot(RB_AUTOBOOT); +#endif + } else if (doPowerOff) { + printf("powering off system\n"); + reboot(RB_POWER_OFF); + } else { + printf("you may safely reboot your system\n"); + signal(SIGINT, rebootHandler); + while (1) sleep(60); + } + + exit(0); + + return; +} + +#ifdef AS_SHUTDOWN +int main(int argc, char ** argv) { + int fd; + int doReboot = 0; + int i = 1; + + while (i < argc) { + if (!strncmp("-r", argv[i], 2)) + doReboot = 1; + i++; + } + + /* ignore some signals so we don't kill ourself */ + signal(SIGINT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + + /* now change to / */ + i = chdir("/"); + + /* redirect output to the real console */ + fd = open("/dev/console", O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + + shutDown(0, doReboot, 0); + return 0; +} +#endif diff --git a/loader/simplemot b/loader/simplemot new file mode 100755 index 000000000..bebc1cfde --- /dev/null +++ b/loader/simplemot @@ -0,0 +1,81 @@ +#!/usr/bin/perl +# +# simplemot +# +# Copyright (C) 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/>. +# + +$inone = 0; +$intran = 0; +$total = 0; + +binmode(STDIN, ":raw"); +binmode(STDOUT, ":raw"); + +while (<>) { + if (!$inone && /^msgid/) { + chop; + $str = substr($_, 7, length($_) - 8); + $inone = 1; + } elsif ($inone && /^"/) { + chop; + $str .= substr($_, 1, length($_) - 2); + } elsif ($inone) { + $inone = 0; + + $str =~ s/\\n/\n/g; + $str =~ s/\\t/\t/g; + $str =~ s/\\"/"/g; + + # the string is complete -- calculate a hash + $sum = 0; + $xor = 0; + for ($i = 0; $i < length($str); $i++) { + $char = ord(substr($str, $i, 1)); + $sum += $char; + $xor ^= $char; + } + + $total = ($sum << 16) | (($xor & 0xFF) << 8) | (length($str) & 0xFF); + } + + if (!$intran && /^msgstr/) { + chop; + $tran = substr($_, 8, length($_) - 9); + $intran = 1; + } elsif ($intran && /^"/) { + chop; + $tran .= substr($_, 1, length($_) - 2); + } elsif ($intran) { + $intran = 0; + + $tran =~ s/\\n/\n/g; + $tran =~ s/\\t/\t/g; + $tran =~ s/\\"/"/g; + + if (!$total && $str) { + print STDERR "Missing string for $tran"; + exit 1 + } elsif ($str && $tran) { + print pack("Nn", $total, length($tran)); + print $tran; + + #if ($tran < 60) { + #printf STDERR ("0x%x %s\n", $total, $tran); + #} + } + } +} diff --git a/loader/telnet.c b/loader/telnet.c new file mode 100644 index 000000000..a2c0b201f --- /dev/null +++ b/loader/telnet.c @@ -0,0 +1,273 @@ +/* + * telnet.c -- basic telnet protocol handling for ttywatch + * + * Copyright (C) 2001 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): Michael K. Johnson <johnsonm@redhat.com> + */ + +/* Shamelessly stolen from ttywatch -- oot */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "telnet.h" +#include "log.h" + +#define IAC "\xff" +#define DONT "\xfe" +#define WONT "\xfc" +#define WILL "\xfb" +#define DO "\xfd" +#define SB "\xfa" +#define SE "\xf0" +#define ECHO "\x01" +#define SUPPRESS_GO_AHEAD "\x03" +#define TERMINAL_TYPE "\x18" +#define NAWS "\x1f" +#define LINEMODE "\x22" +#define NEWENVIRON "\x27" +#define MODE "\x01" + +/* Make a request. Not intended to be RFC-compatible, just enough + * to convince telnet clients to do what we want... To do this + * right, we would have to honestly negotiate, not speak blind. + * + * For now, assume all responses will be favorable and stripped + * out in telnet_process_input()... Sending it all in a single + * write makes it more efficient because it will all go out in a + * single packet, and the responses are more likely to all come + * back in a single packet (and thus, practically, a single read) + * too. + */ +void +telnet_negotiate(int socket, char ** term_type_ptr, int * heightPtr, + int * widthPtr) { + char ch; + int done = 0; + char * termType = NULL; + int termLength = 0, termAlloced = 0; + enum { ST_NONE, ST_TERMTYPE, ST_WINDOWSIZE } state; + char sizeBuf[4]; + int height = -1, width = -1; + char * sizePtr = sizeBuf; + char request[]= + IAC DONT ECHO + IAC WILL ECHO + IAC WILL NAWS + IAC WILL SUPPRESS_GO_AHEAD + IAC DO SUPPRESS_GO_AHEAD + IAC DONT NEWENVIRON + IAC WONT NEWENVIRON + IAC WONT LINEMODE + IAC DO NAWS + IAC SB TERMINAL_TYPE "\x01" IAC SE + ; + int ret; + + ret = write(socket, request, sizeof(request)-1); + + /* Read from the terminal until we get the terminal type. This will + do bad things if the client doesn't send the terminal type, but + those clients have existed for aeons (right?) */ + + do { + ret = read(socket, &ch, 1); + if (ch != '\xff') { + abort(); + } + + ret = read(socket, &ch, 1); /* command */ + + if (ch != '\xfa') { + ret = read(socket, &ch, 1); /* verb */ + continue; + } + + ret = read(socket, &ch, 1); /* suboption */ + if (ch == '\x18') { + state = ST_TERMTYPE; + ret = read(socket, &ch, 1); /* should be 0x0! */ + done = 1; + } else if (ch == '\x1f') { + state = ST_WINDOWSIZE; + } else { + state = ST_NONE;; + } + + ret = read(socket, &ch, 1); /* data */ + while (ch != '\xff') { + if (state == ST_TERMTYPE) { + if (termAlloced == termLength) { + termAlloced += 10; + termType = realloc(termType, termAlloced + 1); + } + + termType[termLength++] = tolower(ch); + } else if (state == ST_WINDOWSIZE) { + if ((sizePtr - sizeBuf) < (int)sizeof(sizeBuf)) + *sizePtr++ = ch; + } + + ret = read(socket, &ch, 1); /* data */ + } + + ret = read(socket, &ch, 1); /* should be a SE */ + + } while (!done); + + termType[termLength] = '\0'; + + if (sizePtr - sizeBuf == sizeof(sizeBuf)) { + width = (sizeBuf[0] << 8) + sizeBuf[1]; + height = (sizeBuf[2] << 8) + sizeBuf[3]; + } + + if (heightPtr) *heightPtr = height; + if (widthPtr) *widthPtr = width; + + if (term_type_ptr) *term_type_ptr = termType; +} + +int +telnet_process_input(telnet_state * ts, char *data, int len) { + char *s, *d; /* source, destination */ + +# if DEBUG_TELNET + printf("\nprinting packet:"); + for (s=data; s<data+len; s++) { + if (!((s-data)%10)) + printf("\n %03d: ", s-data); + printf("%02x ", *s & 0x000000FF); + } + printf("\n"); +# endif /* DEBUG_TELNET */ + + for (s=data, d=data; s<data+len; s++) { + switch (*ts) { + case TS_DATA: + if (*s == '\xff') { /* IAC */ + *ts = TS_IAC; + continue; + } +#if DEBUG_TELNET + printf("copying data element '%c'\n", *s); +#endif /* DEBUG_TELNET */ + if (s>d) { + *(d++) = *s; + } else { + d++; + } + break; + + case TS_IAC: + if (*s == '\xfa') { /* SB */ + *ts = TS_SB; + continue; + } + /* if not SB, skip IAC verb object */ +# if DEBUG_TELNET + printf("skipping verb/object (offset %d)...\n", s-data-1); +# endif /* DEBUG_TELNET */ + s += 1; + *ts = TS_DATA; + break; + + case TS_SB: +# if DEBUG_TELNET + printf("skipping SB (offset %d)...\n", s-data-1); +# endif /* DEBUG_TELNET */ + while (s < (data+(len-1))) { + if (*s == '\xff') { + break; /* fall through to TS_SB_IAC setting below */ + } else { + s++; + } + } + if (*s == '\xff') { + *ts = TS_SB_IAC; + } + break; + + case TS_SB_IAC: + if (*s == '\xf0') { /* SE */ +# if DEBUG_TELNET + printf("SE ends SB (offset %d)...\n", s-data-1); +# endif /* DEBUG_TELNET */ + *ts = TS_DATA; + } else { +# if DEBUG_TELNET + printf("IAC without SE in SB (offset %d)\n", s-data-1); +# endif /* DEBUG_TELNET */ + *ts = TS_SB; + } + break; + + default: + logMessage(WARNING, "unknown telnet state %d for data element %c", + *ts, *s); + *ts = TS_DATA; + break; + } + } + + /* calculate new length after copying data around */ + len = d - data; +#if DEBUG_TELNET + printf("returning len: %d of packet:", len); + for (s=data; s<data+len; s++) { + if (!((s-data)%10)) + printf("\n %03d: ", s-data); + printf("%02x ", *s & 0x000000FF); + } + printf("\n"); +#endif /* DEBUG_TELNET */ + + return len; +} + +/* The telnet protocol requires CR/NL instead of just NL + * We normally deal with Unix, which just uses NL, so we need to translate. + * + * It would be easy to go through line-by-line and write each line, but + * that would create more packet overhead by sending out one packet + * per line, and over things like slow PPP connections, that is painful. + * Therefore, instead, we create a modified copy of the data and write + * the whole modified copy at once. + */ +void +telnet_send_output(int sock, char *data, int len) { + char *s, *d; /* source, destination */ + char *buf; + int ret; + + buf = alloca((len*2)+1); /* max necessary size */ + + /* just may need to add CR before NL (but do not double existing CRs) */ + for (s=data, d=buf; d-buf<len; s++, d++) { + if ((*s == '\n') && (s == data || (*(s-1) != '\r'))) { + /* NL without preceding CR */ + *(d++) = '\r'; + len++; + } + *d = *s; + } + + /* now send it... */ + ret = write(sock, buf, len); +} diff --git a/loader/telnet.h b/loader/telnet.h new file mode 100644 index 000000000..5c3415477 --- /dev/null +++ b/loader/telnet.h @@ -0,0 +1,40 @@ +/* + * telnet.h -- basic telnet protocol handling for ttywatch + * + * Copyright (C) 2001 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): Michael K. Johnson <johnsonm@redhat.com> + */ + +#ifndef __TELNET_H__ +#define __TELNET_H__ + +typedef enum { + TS_DATA = 0, + TS_IAC, + TS_SB, + TS_SB_IAC, +} telnet_state; + +void +telnet_negotiate(int socket, char ** term_type_ptr, int * heightPtr, + int * widthPtr); +int +telnet_process_input(telnet_state * ts, char *data, int len); +void +telnet_send_output(int sock, char *data, int len); + +#endif /* __TELNET_H__ */ diff --git a/loader/telnetd.c b/loader/telnetd.c new file mode 100644 index 000000000..99c6feb24 --- /dev/null +++ b/loader/telnetd.c @@ -0,0 +1,251 @@ +/* + * telnetd.c - glue to tie telnet.c from ttywatch to the loader + * + * Copyright (C) 2002 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> + * Jeremy Katz <katzj@redhat.com> + */ + +#include <arpa/inet.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <newt.h> +#include <pty.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/signal.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lang.h" +#include "loader.h" +#include "log.h" +#include "modules.h" +#include "net.h" +#include "telnet.h" +#include "windows.h" + +#ifndef IPPORT_TELNET +#define IPPORT_TELNET 23 +#endif + +/* boot flags */ +extern uint64_t flags; + +/* Forks, keeping the loader as our child (so we know when it dies). */ +int beTelnet(void) { + int sock; + int conn; + socklen_t addrLength; + pid_t child; + int i; + int masterFd, ttyFd; + struct sockaddr_in address; + char buf[4096]; + struct pollfd fds[3]; + telnet_state ts = TS_DATA; + char * termType; + int height, width; + struct winsize ws; + + if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + logMessage(ERROR, "socket: %s", strerror(errno)); + return -1; + } + + address.sin_family = AF_INET; + address.sin_port = htons(IPPORT_TELNET); + memset(&address.sin_addr, 0, sizeof(address.sin_addr)); + addrLength = sizeof(address); + + /* Let the kernel reuse the socket address. This lets us run + twice in a row, without waiting for the (ip, port) tuple + to time out. Makes testing much easier*/ + conn = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &conn, sizeof(conn)); + + bind(sock, (struct sockaddr *) &address, sizeof(address)); + listen(sock, 5); + + winStatus(45, 3, _("Telnet"), _("Waiting for telnet connection...")); + + if ((conn = accept(sock, (struct sockaddr *) &address, &addrLength)) < 0) { + newtWinMessage(_("Error"), _("OK"), "accept failed: %s", + strerror(errno)); + close(sock); + return -1; + } + + stopNewt(); + close(sock); + telnet_negotiate(conn, &termType, &height, &width); + +#ifdef DEBUG_TELNET + printf("got term type %s\n", termType); +#endif + + masterFd = open("/dev/ptmx", O_RDWR); + if (masterFd < 0) { + logMessage(CRITICAL, "cannot open /dev/ptmx"); + close(conn); + return -1; + } + + if (height != -1 && width != -1) { +#ifdef DEBUG_TELNET + printf("setting window size to %d x %d\n", width, height); +#endif + ws.ws_row = height; + ws.ws_col = width; + ioctl(masterFd, TIOCSWINSZ, &ws); + } + + + child = fork(); + + if (child) { +#ifndef DEBUG_TELNET + startNewt(); + winStatus(45, 3, _("Telnet"), _("Running anaconda via telnet...")); +#endif + + fds[0].events = POLLIN; + fds[0].fd = masterFd; + + fds[1].events = POLLIN; + fds[1].fd = conn; + + while ((i = poll(fds, 2, -1)) > 0) { + if (fds[0].revents) { + i = read(masterFd, buf, sizeof(buf)); +#ifdef DEBUG_TELNET + { + int j; + int row; + + for (row = 0; row < (i / 12) + 1; row++) { + printf("wrote:"); + + for (j = (row * 12); j < i && j < ((row + 1) * 12); j++) + printf(" 0x%2x", (unsigned char) buf[j]); + + printf("\nwrote:"); + + for (j = (row*12); j < i && j < ((row+1)*12); j++) { + if (isprint(buf[j])) + printf(" %c ", buf[j]); + else + printf(" "); + } + + printf("\n"); + } + } +#endif + /* child died */ + if (i < 0) + break; + + telnet_send_output(conn, buf, i); + } + + if (fds[1].revents) { + int ret; + i = read(conn, buf, sizeof(buf)); + + /* connection went away */ + if (!i) + break; + + i = telnet_process_input(&ts, buf, i); + ret = write(masterFd, buf, i); +#ifdef DEBUG_TELNET + { + int j; + + printf("got:"); + for (j = 0; j < i; j++) + printf(" 0x%x", (unsigned char) buf[j]); + printf("\n"); + } +#endif + } + } + + if (i < 0) { + logMessage(ERROR, "poll: %s", strerror(errno)); + } + +#ifndef DEBUG_TELNET + stopNewt(); +#endif + + kill(child, SIGTERM); + close(conn); + exit(0); + } + + unlockpt(masterFd); + grantpt(masterFd); + ttyFd = open(ptsname(masterFd), O_RDWR); + close(masterFd); + setsid(); + close(0); + close(1); + close(2); + + if (ttyFd != 0) { + dup2(ttyFd, 0); + close(ttyFd); + } + + dup2(0, 1); + dup2(0, 2); + + /* brand new tty! */ + setenv("TERM", termType, 1); + + startNewt(); + + return 0; +} + +void startTelnetd(struct loaderData_s * loaderData) { + char ret[INET_ADDRSTRLEN+1]; + iface_t iface; + + iface_init_iface_t(&iface); + + if (kickstartNetworkUp(loaderData, &iface)) { + logMessage(ERROR, "unable to bring up network"); + return; + } + + if (iface.ipaddr.s_addr) { + inet_ntop(AF_INET, &iface.ipaddr, ret, INET_ADDRSTRLEN); + logMessage(INFO, "going to beTelnet for %s", ret); + if (!beTelnet()) + flags |= LOADER_FLAGS_TEXT | LOADER_FLAGS_NOSHELL; + } + + return; +} diff --git a/loader/telnetd.h b/loader/telnetd.h new file mode 100644 index 000000000..fedb0fae6 --- /dev/null +++ b/loader/telnetd.h @@ -0,0 +1,25 @@ +/* + * telnetd.h + * + * Copyright (C) 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/>. + */ + +#ifndef TELNETD_H +#define TELNETD_H + +void startTelnetd(struct loaderData_s * loaderData); + +#endif diff --git a/loader/tr/.cvsignore b/loader/tr/.cvsignore new file mode 100644 index 000000000..596343c4a --- /dev/null +++ b/loader/tr/.cvsignore @@ -0,0 +1 @@ +*.tr diff --git a/loader/udelay.h b/loader/udelay.h new file mode 100644 index 000000000..5ad0a5675 --- /dev/null +++ b/loader/udelay.h @@ -0,0 +1,205 @@ +/* + * udelay.h -- udelay and other time related functions. + * + * Copyright (C) 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): Peter Jones <pjones@redhat.com> + */ + +#ifndef UDELAY_H +#define UDELAY_H 1 + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <time.h> + +#define USECS_PER_SEC 1000000LL +#define NSECS_PER_USEC 1000LL +#define NSECS_PER_SEC (NSECS_PER_USEC * USECS_PER_SEC) + +static inline void +nsectospec(long long nsecs, struct timespec *ts) +{ + if (nsecs < 0) { + ts->tv_sec = -1; + ts->tv_nsec = -1; + return; + } + ts->tv_sec = nsecs / NSECS_PER_SEC; + ts->tv_nsec = (nsecs % NSECS_PER_SEC); +} + +static inline void +usectospec(long long usecs, struct timespec *ts) +{ + if (usecs > 0 && LLONG_MAX / NSECS_PER_USEC > usecs) + usecs *= NSECS_PER_USEC; + + nsectospec(usecs, ts); +} + +static inline int +speczero(struct timespec *ts) +{ + return (ts->tv_sec == 0 && ts->tv_nsec == 0); +} + +static inline int +specinf(struct timespec *ts) +{ + return (ts->tv_sec < 0 || ts->tv_nsec < 0); +} + +static inline long long +spectonsec(struct timespec *ts) +{ + long long nsecs = 0; + if (specinf(ts)) + return -1; + + nsecs = ts->tv_sec * NSECS_PER_SEC; + nsecs += ts->tv_nsec; + return nsecs; +} + +static inline long long +spectousec(struct timespec *ts) +{ + long long usecs = spectonsec(ts); + + return usecs < 0 ? usecs : usecs / NSECS_PER_USEC; +} + +static inline int +gettimespecofday(struct timespec *ts) +{ + struct timeval tv = {0, 0}; + int rc; + + rc = gettimeofday(&tv, NULL); + if (rc >= 0) { + ts->tv_sec = tv.tv_sec; +#if 0 + ts->tv_nsec = (tv.tv_usec % NSECS_PER_USEC >= NSECS_PER_USEC / 2) ? + tv.tv_usec / NSECS_PER_USEC + 1 : + tv.tv_usec / NSECS_PER_USEC; +#else + ts->tv_nsec = tv.tv_usec / NSECS_PER_USEC; +#endif + } + return rc; +} + +/* minuend minus subtrahend equals difference */ +static inline void +tssub(struct timespec *minuend, struct timespec *subtrahend, + struct timespec *difference) +{ + long long m, s, d; + + m = spectonsec(minuend); + s = spectonsec(subtrahend); + + if (s < 0) { + d = 0; + } else if (m < 0) { + d = -1; + } else { + m -= s; + d = m < 0 ? 0 : m; + } + + nsectospec(d, difference); + return; +} + +static inline void +tsadd(struct timespec *augend, struct timespec *addend, struct timespec *sum) +{ + long long aug, add; + + aug = spectonsec(augend); + add = spectonsec(addend); + +// printf("aug: %Ld add: %Ld\n", aug, add); + + if (aug < 0 || add < 0) + nsectospec(-1, sum); + else if (LLONG_MAX - MAX(add,aug) < MAX(add,aug)) + nsectospec(LLONG_MAX, sum); + else + nsectospec(aug+add, sum); + return; +} + +#define tsGT(x,y) (tscmp((x), (y)) < 0) +#define tsGE(x,y) (tscmp((x), (y)) <= 0) +#define tsET(x,y) (tscmp((x), (y)) == 0) +#define tsNE(x,y) (tscmp((x), (y)) != 0) +#define tsLE(x,y) (tscmp((x), (y)) >= 0) +#define tsLT(x,y) (tscmp((x), (y)) > 0) + +static inline int +tscmp(struct timespec *a, struct timespec *b) +{ + long long m, s; + long long rc; + + m = spectonsec(a); + s = spectonsec(b); + + if (s < 0) { + rc = 1; + if (m < 0) + rc = 0; + } else if (m < 0) { + rc = -1; + } else { + rc = MIN(MAX(s-m, -1), 1); + } + + return rc; +} + +static inline void +udelayspec(struct timespec total) +{ + struct timespec rem; + if (specinf(&total)) { + do { + usectospec(LLONG_MAX, &rem); + } while (nanosleep(&rem, &rem) == -1 && errno == EINTR); + } else { + rem = total; + while (nanosleep(&rem, &rem) == -1 && errno == EINTR) + ; + } +} + +static inline void +udelay(long long usecs) +{ + struct timespec rem = {0,0}; + + usectospec(usecs, &rem); + udelayspec(rem); +} + +#endif /* UDELAY_H */ +/* + * vim:ts=8:sw=4:sts=4:et + */ diff --git a/loader/undomounts.c b/loader/undomounts.c new file mode 100644 index 000000000..a7152b1b9 --- /dev/null +++ b/loader/undomounts.c @@ -0,0 +1,236 @@ +/* + * undomounts.c: Handles some basic unmounting stuff for init + * Broken out so that it can be used on s390 in a shutdown binary + * + * Copyright (C) 1996, 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> + * Jeremy Katz <katzj@redhat.com> + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/swap.h> +#include <unistd.h> + +#include "devt.h" + +struct unmountInfo { + char * name; + int mounted; + int loopDevice; + enum { FS, LOOP } what; +} ; + +extern int testing; + +void undoLoop(struct unmountInfo * fs, int numFs, int this); + +static void printstr(char * string) { + int ret; + + ret = write(1, string, strlen(string)); +} + +void undoMount(struct unmountInfo * fs, int numFs, int this) { + size_t len = strlen(fs[this].name); + int i; + + if (!fs[this].mounted) return; + fs[this].mounted = 0; + + /* unmount everything underneath this */ + for (i = 0; i < numFs; i++) { + if (fs[i].name && (strlen(fs[i].name) >= len) && + (fs[i].name[len] == '/') && + !strncmp(fs[this].name, fs[i].name, len)) { + if (fs[i].what == LOOP) + undoLoop(fs, numFs, i); + else + undoMount(fs, numFs, i); + } + } + + printf("\t%s", fs[this].name); + /* don't need to unmount /tmp. it is busy anyway. */ + if (!testing) { + if (umount2(fs[this].name, 0) < 0) { + printf(" umount failed (%d)", errno); + } else { + printf(" done"); + } + } + printf("\n"); +} + +void undoLoop(struct unmountInfo * fs, int numFs, int this) { + int i; + int fd; + + if (!fs[this].mounted) return; + fs[this].mounted = 0; + + /* find the device mount */ + for (i = 0; i < numFs; i++) { + if (fs[i].what == FS && (fs[i].loopDevice == fs[this].loopDevice)) + break; + } + + if (i < numFs) { + /* the device is mounted, unmount it (and recursively, anything + * underneath) */ + undoMount(fs, numFs, i); + } + + unlink("/tmp/loop"); + mknod("/tmp/loop", 0600 | S_IFBLK, (7 << 8) | fs[this].loopDevice); + printf("\tdisabling /dev/loop%d", fs[this].loopDevice); + if ((fd = open("/tmp/loop", O_RDONLY, 0)) < 0) { + printf(" failed to open device: %d", errno); + } else { + if (!testing && ioctl(fd, LOOP_CLR_FD, 0)) + printf(" LOOP_CLR_FD failed: %d", errno); + close(fd); + } + + printf("\n"); +} + +void unmountFilesystems(void) { + int fd, size; + char buf[65535]; /* this should be big enough */ + char * chptr, * start; + struct unmountInfo filesystems[500]; + int numFilesystems = 0; + int i; + struct loop_info li; + char * device; + struct stat sb; + + fd = open("/proc/mounts", O_RDONLY, 0); + if (fd < 1) { + /* FIXME: was perror */ + printstr("failed to open /proc/mounts"); + sleep(2); + return; + } + + size = read(fd, buf, sizeof(buf) - 1); + buf[size] = '\0'; + + close(fd); + + chptr = buf; + while (*chptr) { + device = chptr; + while (*chptr != ' ') chptr++; + *chptr++ = '\0'; + start = chptr; + while (*chptr != ' ') chptr++; + *chptr++ = '\0'; + + if (strcmp(start, "/") && strcmp(start, "/tmp") && + strcmp(start, "/dev")) { + filesystems[numFilesystems].name = strdup(start); + filesystems[numFilesystems].what = FS; + filesystems[numFilesystems].mounted = 1; + + stat(start, &sb); + if ((sb.st_dev >> 8) == 7) { + filesystems[numFilesystems].loopDevice = sb.st_dev & 0xf; + } else { + filesystems[numFilesystems].loopDevice = -1; + } + + numFilesystems++; + } + + while (*chptr != '\n') chptr++; + chptr++; + } + + for (i = 0; i < 7; i++) { + unlink("/tmp/loop"); + mknod("/tmp/loop", 0600 | S_IFBLK, (7 << 8) | i); + if ((fd = open("/tmp/loop", O_RDONLY, 0)) >= 0) { + if (!ioctl(fd, LOOP_GET_STATUS, &li) && li.lo_name[0]) { + filesystems[numFilesystems].name = strdup(li.lo_name); + filesystems[numFilesystems].what = LOOP; + filesystems[numFilesystems].mounted = 1; + filesystems[numFilesystems].loopDevice = i; + numFilesystems++; + } + + close(fd); + } + } + + for (i = 0; i < numFilesystems; i++) { + if (filesystems[i].what == LOOP) { + undoLoop(filesystems, numFilesystems, i); + } + } + + for (i = 0; i < numFilesystems; i++) { + if ((filesystems[i].mounted) && (filesystems[i].name)) { + undoMount(filesystems, numFilesystems, i); + } + } + + for (i = 0; i < numFilesystems; i++) + free(filesystems[i].name); +} + +void disableSwap(void) { + int fd; + char buf[4096]; + int i; + char * start; + char * chptr; + + if ((fd = open("/proc/swaps", O_RDONLY, 0)) < 0) return; + + i = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (i < 0) return; + buf[i] = '\0'; + + start = buf; + while (*start) { + while (*start != '\n' && *start) start++; + if (!*start) return; + + start++; + if (*start != '/') return; + chptr = start; + while (*chptr && *chptr != ' ') chptr++; + if (!(*chptr)) return; + *chptr = '\0'; + printf("\t%s", start); + if (swapoff(start)) + printf(" failed (%d)", errno); + printf("\n"); + + start = chptr + 1; + } +} diff --git a/loader/unicode-linedraw-chars.txt b/loader/unicode-linedraw-chars.txt new file mode 100644 index 000000000..c1a481421 --- /dev/null +++ b/loader/unicode-linedraw-chars.txt @@ -0,0 +1,22 @@ +─ +│ +┌ +┐ +└ +┘ +┤ +├ +┴ +┬ +┼ +▒ +◆ +° +± +· +← +→ +↓ +↑ +▒ +▮ diff --git a/loader/urlinstall.c b/loader/urlinstall.c new file mode 100644 index 000000000..177716612 --- /dev/null +++ b/loader/urlinstall.c @@ -0,0 +1,452 @@ +/* + * urlinstall.c - code to set up url (ftp/http) 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 <newt.h> +#include <popt.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <unistd.h> +#include <errno.h> + +#include "../isys/iface.h" + +#include "copy.h" +#include "kickstart.h" +#include "loader.h" +#include "loadermisc.h" +#include "lang.h" +#include "log.h" +#include "method.h" +#include "net.h" +#include "method.h" +#include "urlinstall.h" +#include "cdinstall.h" +#include "urls.h" +#include "windows.h" + +/* boot flags */ +extern uint64_t flags; + +static int loadSingleUrlImage(struct iurlinfo * ui, char *path, + char * dest, char * mntpoint, char * device, + int silentErrors) { + int fd; + int rc = 0; + char *ehdrs = NULL; + + if (ui->protocol == URL_METHOD_HTTP) { + char *arch = getProductArch(); + char *name = getProductName(); + + if (asprintf(&ehdrs, "User-Agent: anaconda/%s\r\n" + "X-Anaconda-Architecture: %s\r\n" + "X-Anaconda-System-Release: %s\r\n", + VERSION, arch, name) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + fd = urlinstStartTransfer(ui, path, ehdrs); + + if (fd == -2) { + if (ehdrs) free (ehdrs); + return 2; + } + else if (fd < 0) { + if (!silentErrors) { + newtWinMessage(_("Error"), _("OK"), + _("Unable to retrieve %s://%s%s."), + (ui->protocol == URL_METHOD_FTP ? "ftp" : "http"), + ui->address, path); + } + + if (ehdrs) free (ehdrs); + return 2; + } + + if (dest != NULL) { + rc = copyFileAndLoopbackMount(fd, dest, device, mntpoint); + } + + urlinstFinishTransfer(ui, fd); + return rc; +} + +static void copyWarnFn (char *msg) { + logMessage(WARNING, msg); +} + +static void copyErrorFn (char *msg) { + newtWinMessage(_("Error"), _("OK"), _(msg)); +} + +static int loadUrlImages(struct iurlinfo * ui) { + char *buf, *path, *dest, *slash; + int rc; + + /* Figure out the path where updates.img and product.img files are + * kept. Since ui->prefix points to a stage2 image file, we just need + * to trim off the file name and look in the same directory. + */ + if ((slash = strrchr(ui->prefix, '/')) == NULL) + return 0; + + if ((path = strndup(ui->prefix, slash - ui->prefix)) == NULL) + path = ui->prefix; + + /* grab the updates.img before install.img so that we minimize our + * ramdisk usage */ + if (asprintf(&buf, "%s/%s", path, "updates.img") == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (!loadSingleUrlImage(ui, buf, + "/tmp/updates-disk.img", "/tmp/update-disk", + "/dev/loop7", 1)) { + copyDirectory("/tmp/update-disk", "/tmp/updates", copyWarnFn, + copyErrorFn); + umountLoopback("/tmp/update-disk", "/dev/loop7"); + unlink("/tmp/updates-disk.img"); + unlink("/tmp/update-disk"); + } else if (!access("/tmp/updates-disk.img", R_OK)) { + unpackCpioBall("/tmp/updates-disk.img", "/tmp/updates"); + unlink("/tmp/updates-disk.img"); + } + + free(buf); + + /* grab the product.img before install.img so that we minimize our + * ramdisk usage */ + if (asprintf(&buf, "%s/%s", path, "product.img") == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (!loadSingleUrlImage(ui, buf, + "/tmp/product-disk.img", "/tmp/product-disk", + "/dev/loop7", 1)) { + copyDirectory("/tmp/product-disk", "/tmp/product", copyWarnFn, + copyErrorFn); + umountLoopback("/tmp/product-disk", "/dev/loop7"); + unlink("/tmp/product-disk.img"); + unlink("/tmp/product-disk"); + } + + free(buf); + + if (asprintf(&dest, "/tmp/install.img") == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + rc = loadSingleUrlImage(ui, ui->prefix, dest, "/mnt/runtime", "/dev/loop0", 0); + free(dest); + + if (rc) { + if (rc != 2) + newtWinMessage(_("Error"), _("OK"), + _("Unable to retrieve the install image.")); + return 1; + } + + return 0; +} + +char *mountUrlImage(struct installMethod *method, char *location, + struct loaderData_s *loaderData) { + struct iurlinfo ui; + char *url = NULL; + + enum { URL_STAGE_MAIN, URL_STAGE_FETCH, + URL_STAGE_DONE } stage = URL_STAGE_MAIN; + + memset(&ui, 0, sizeof(ui)); + + while (stage != URL_STAGE_DONE) { + switch(stage) { + case URL_STAGE_MAIN: { + /* If the stage2= parameter was given (or inferred from repo=) + * then use that configuration info to fetch the image. This + * could also have come from kickstart. Else, we need to show + * the UI. + */ + if (loaderData->method == METHOD_URL && loaderData->stage2Data) { + url = ((struct urlInstallData *) loaderData->stage2Data)->url; + logMessage(INFO, "URL_STAGE_MAIN: url is %s", url); + + if (!url) { + logMessage(ERROR, "missing URL specification"); + loaderData->method = -1; + free(loaderData->stage2Data); + loaderData->stage2Data = NULL; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + + break; + } + + /* explode url into ui struct */ + convertURLToUI(url, &ui); + + /* ks info was adequate, lets skip to fetching image */ + stage = URL_STAGE_FETCH; + break; + } else { + char *substr; + + if (urlMainSetupPanel(&ui)) + return NULL; + + /* If the user-provided URL points at a repo instead of + * a stage2 image, fix it up now. + */ + substr = strstr(ui.prefix, ".img"); + if (!substr || (substr && *(substr+4) != '\0')) { + if (asprintf(&ui.prefix, "%s/images/install.img", + ui.prefix) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, + __LINE__); + abort(); + } + } + + loaderData->invalidRepoParam = 1; + } + + stage = URL_STAGE_FETCH; + break; + } + + case URL_STAGE_FETCH: { + if (FL_TESTING(flags)) { + stage = URL_STAGE_DONE; + break; + } + + if (loadUrlImages(&ui)) { + stage = URL_STAGE_MAIN; + + if (loaderData->method >= 0) + loaderData->method = -1; + + if (loaderData->inferredStage2) + loaderData->invalidRepoParam = 1; + } else { + stage = URL_STAGE_DONE; + } + + break; + } + + case URL_STAGE_DONE: + break; + } + } + + url = convertUIToURL(&ui); + return url; +} + +int getFileFromUrl(char * url, char * dest, + struct loaderData_s * loaderData) { + int retval = 0; + struct iurlinfo ui; + enum urlprotocol_t proto = + !strncmp(url, "ftp://", 6) ? URL_METHOD_FTP : URL_METHOD_HTTP; + char * host = NULL, * file = NULL, * chptr = NULL, *login = NULL, *password = NULL; + int fd, rc; + iface_t iface; + char *ehdrs = NULL, *ip = NULL; + + iface_init_iface_t(&iface); + + if (kickstartNetworkUp(loaderData, &iface)) { + logMessage(ERROR, "unable to bring up network"); + return 1; + } + + memset(&ui, 0, sizeof(ui)); + ui.protocol = proto; + + if ((ip = iface_ip2str(loaderData->netDev)) == NULL) { + logMessage(ERROR, "getFileFromUrl: no client IP information"); + return 1; + } + + getHostPathandLogin((proto == URL_METHOD_FTP ? url + 6 : url + 7), + &host, &file, &login, &password, ip); + + logMessage(INFO, "file location: %s://%s%s", + (proto == URL_METHOD_FTP ? "ftp" : "http"), host, file); + + chptr = strchr(host, '/'); + if (chptr == NULL) { + ui.address = strdup(host); + ui.prefix = strdup("/"); + } else { + *chptr = '\0'; + ui.address = strdup(host); + host = chptr; + *host = '/'; + ui.prefix = strdup(host); + } + + if (password[0] != '\0') + ui.password = strdup (password); + if (login[0] != '\0') + ui.login = strdup (login); + + if (proto == URL_METHOD_HTTP) { + char *arch = getProductArch(); + char *name = getProductName(); + + if (asprintf(&ehdrs, "User-Agent: anaconda/%s\r\n" + "X-Anaconda-Architecture: %s\r\n" + "X-Anaconda-System-Release: %s\r\n", + VERSION, arch, name) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + if (proto == URL_METHOD_HTTP && FL_KICKSTART_SEND_MAC(flags)) { + /* find all ethernet devices and make a header entry for each one */ + int i; + char *dev, *mac, *tmpstr; + struct device **devices; + + devices = getDevices(DEVICE_NETWORK); + for (i = 0; devices && devices[i]; i++) { + dev = devices[i]->device; + mac = iface_mac2str(dev); + + if (mac) { + if (asprintf(&tmpstr, "X-RHN-Provisioning-MAC-%d: %s %s\r\n", + i, dev, mac) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + if (!ehdrs) { + ehdrs = strdup(tmpstr); + } else { + ehdrs = (char *) realloc(ehdrs, strlen(ehdrs)+strlen(tmpstr)+1); + strcat(ehdrs, tmpstr); + } + + free(mac); + free(tmpstr); + } + } + } + + fd = urlinstStartTransfer(&ui, file, ehdrs); + if (fd < 0) { + logMessage(ERROR, "failed to retrieve http://%s/%s%s", ui.address, ui.prefix, file); + retval = 1; + goto err; + } + + rc = copyFileFd(fd, dest); + if (rc) { + unlink (dest); + logMessage(ERROR, "failed to copy file to %s", dest); + retval = 1; + goto err; + } + + urlinstFinishTransfer(&ui, fd); + +err: + if (file) free(file); + if (ehdrs) free(ehdrs); + if (host) free(host); + if (login) free(login); + if (password) free(password); + + return retval; +} + +/* pull kickstart configuration file via http */ +int kickstartFromUrl(char * url, struct loaderData_s * loaderData) { + return getFileFromUrl(url, "/tmp/ks.cfg", loaderData); +} + +void setKickstartUrl(struct loaderData_s * loaderData, int argc, + char ** argv) { + + char *url = NULL, *substr = NULL; + poptContext optCon; + int rc; + struct poptOption ksUrlOptions[] = { + { "url", '\0', POPT_ARG_STRING, &url, 0, NULL, NULL }, + { 0, 0, 0, 0, 0, 0, 0 } + }; + + logMessage(INFO, "kickstartFromUrl"); + optCon = poptGetContext(NULL, argc, (const char **) argv, ksUrlOptions, 0); + if ((rc = poptGetNextOpt(optCon)) < -1) { + startNewt(); + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Bad argument to Url kickstart method " + "command %s: %s"), + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); + return; + } + + if (!url) { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Must supply a --url argument to Url kickstart method.")); + return; + } + + /* determine install type */ + if (strstr(url, "http://") || strstr(url, "ftp://")) + loaderData->method = METHOD_URL; + else { + newtWinMessage(_("Kickstart Error"), _("OK"), + _("Unknown Url method %s"), url); + return; + } + + substr = strstr(url, ".img"); + if (!substr || (substr && *(substr+4) != '\0')) { + loaderData->instRepo = strdup(url); + } else { + if ((loaderData->stage2Data = calloc(sizeof(struct urlInstallData *), 1)) == NULL) + return; + + ((struct urlInstallData *)loaderData->stage2Data)->url = url; + } + + logMessage(INFO, "results of url ks, url %s", url); +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/urlinstall.h b/loader/urlinstall.h new file mode 100644 index 000000000..586d462ca --- /dev/null +++ b/loader/urlinstall.h @@ -0,0 +1,38 @@ +/* + * urlinstall.h + * + * Copyright (C) 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/>. + */ + +#ifndef URLINSTALL_H +#define URLINSTALL_H + +#include "method.h" + +struct urlInstallData { + char * url; +}; + + +void setKickstartUrl(struct loaderData_s * loaderData, int argc, + char ** argv); +int kickstartFromUrl(char * url, struct loaderData_s * loaderData); +char * mountUrlImage(struct installMethod * method, + char * location, struct loaderData_s * loaderData); +int getFileFromUrl(char * url, char * dest, struct loaderData_s * loaderData); + + +#endif diff --git a/loader/urls.c b/loader/urls.c new file mode 100644 index 000000000..3cd9a712b --- /dev/null +++ b/loader/urls.c @@ -0,0 +1,367 @@ +/* + * urls.c - url handling code + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 <arpa/inet.h> +#include <ctype.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <newt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <errno.h> + +#include "ftp.h" +#include "lang.h" +#include "loader.h" +#include "loadermisc.h" +#include "urls.h" +#include "log.h" +#include "windows.h" +#include "net.h" + +/* boot flags */ +extern uint64_t flags; + +/* convert a url (ftp or http) to a ui */ +int convertURLToUI(char *url, struct iurlinfo *ui) { + char *chptr; + + memset(ui, 0, sizeof(*ui)); + + if (!strncmp("ftp://", url, 6)) { + ui->protocol = URL_METHOD_FTP; + url += 6; + + /* There could be a username/password on here */ + if ((chptr = strchr(url, '@'))) { + if ((chptr = strchr(url, ':'))) { + *chptr = '\0'; + ui->login = strdup(url); + url = chptr + 1; + + chptr = strchr(url, '@'); + *chptr = '\0'; + ui->password = strdup(url); + url = chptr + 1; + } else { + chptr = strchr(url, '@'); + *chptr = '\0'; + ui->login = strdup(url); + url = chptr + 1; + } + } + } else if (!strncmp("http://", url, 7)) { + ui->protocol = URL_METHOD_HTTP; + url += 7; + } else { + logMessage(ERROR, "unknown url protocol '%s'", url); + return -1; + } + + /* url is left pointing at the hostname */ + chptr = strchr(url, '/'); + if (chptr != NULL) { + *chptr = '\0'; + ui->address = strdup(url); + url = chptr; + *url = '/'; + ui->prefix = strdup(url); + } else { + ui->address = strdup(url); + ui->prefix = strdup("/"); + } + + logMessage(DEBUGLVL, "url address %s", ui->address); + logMessage(DEBUGLVL, "url prefix %s", ui->prefix); + + return 0; +} + +static char * getLoginName(char * login, struct iurlinfo *ui) { + int i; + + i = 0; + /* password w/o login isn't useful */ + if (ui->login && strlen(ui->login)) { + i += strlen(ui->login) + 5; + if (strlen(ui->password)) + i += 3*strlen(ui->password) + 5; + + if (ui->login || ui->password) { + login = malloc(i); + strcpy(login, ui->login); + if (ui->password) { + char * chptr; + char code[4]; + + strcat(login, ":"); + for (chptr = ui->password; *chptr; chptr++) { + sprintf(code, "%%%2x", *chptr); + strcat(login, code); + } + strcat(login, "@"); + } + } + } + + return login; +} + +/* convert a UI to a URL, returns allocated string */ +char *convertUIToURL(struct iurlinfo *ui) { + char *login, *finalPrefix, *url, *p; + + if (!strcmp(ui->prefix, "/")) + finalPrefix = "/."; + else + finalPrefix = ui->prefix; + + login = ""; + login = getLoginName(login, ui); + + url = malloc(strlen(finalPrefix) + 25 + strlen(ui->address) + strlen(login)); + + /* sanitize url so we dont have problems like bug #101265 */ + /* basically avoid duplicate /'s */ + if (ui->protocol == URL_METHOD_HTTP) { + for (p=finalPrefix; *p == '/' && *(p+1) && *(p+1) == '/'; p++); + finalPrefix = p; + } + + sprintf(url, "%s://%s%s%s", + ui->protocol == URL_METHOD_FTP ? "ftp" : "http", + login, ui->address, finalPrefix); + + return url; +} + +/* extraHeaders only applicable for http and used for pulling ks from http */ +/* see ftp.c:httpGetFileDesc() for details */ +/* set to NULL if not needed */ +int urlinstStartTransfer(struct iurlinfo * ui, char *path, + char *extraHeaders) { + int fd, port; + int family = -1; + struct in_addr addr; + struct in6_addr addr6; + char *hostname, *portstr, *fileName; + struct hostent *host; + + logMessage(INFO, "transferring %s://%s%s to a fd", + ui->protocol == URL_METHOD_FTP ? "ftp" : "http", + ui->address, path); + + splitHostname(ui->address, &hostname, &portstr); + if (portstr == NULL) { + port = -1; + } else { + errno = 0; + port = strtol(portstr, NULL, 10); + + if ((errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || + (errno != 0 && port == 0)) { + logMessage(ERROR, "%s: %d: %m", __func__, __LINE__); + abort(); + } + } + + if (inet_pton(AF_INET, hostname, &addr) >= 1) + family = AF_INET; + else if (inet_pton(AF_INET6, hostname, &addr6) >= 1) + family = AF_INET6; + else { + if ((host = gethostbyname(hostname)) == NULL) { + logMessage(ERROR, "cannot determine address family of %s: %s", + hostname, hstrerror(h_errno)); + return -1; + } + else + family = host->h_addrtype; + } + + if (ui->protocol == URL_METHOD_FTP) { + ui->ftpPort = ftpOpen(hostname, family, + ui->login ? ui->login : "anonymous", + ui->password ? ui->password : "rhinstall@", + port); + if (ui->ftpPort < 0) { + if (hostname) free(hostname); + return -2; + } + + fd = ftpGetFileDesc(ui->ftpPort, addr6, family, path); + if (fd < 0) { + close(ui->ftpPort); + if (hostname) free(hostname); + return -1; + } + } else { + fd = httpGetFileDesc(hostname, port, path, extraHeaders); + if (fd < 0) { + if (portstr) free(portstr); + return -1; + } + } + + fileName = strrchr(path, '/'); + + if (FL_CMDLINE(flags)) { + printf("%s %s...\n", _("Retrieving"), fileName+1); + } else { + winStatus(70, 3, _("Retrieving"), "%s %s...", _("Retrieving"), fileName+1); + } + + if (hostname) free(hostname); + return fd; +} + +int urlinstFinishTransfer(struct iurlinfo * ui, int fd) { + if (ui->protocol == URL_METHOD_FTP) + close(ui->ftpPort); + close(fd); + + if (!FL_CMDLINE(flags)) + newtPopWindow(); + + return 0; +} + +char * addrToIp(char * hostname) { + struct in_addr ad; + struct in6_addr ad6; + char *ret; + struct hostent *host; + + if ((ret = malloc(INET6_ADDRSTRLEN+1)) == NULL) + return hostname; + + if (inet_ntop(AF_INET, &ad, ret, INET_ADDRSTRLEN) != NULL) + return ret; + else if (inet_ntop(AF_INET6, &ad6, ret, INET6_ADDRSTRLEN) != NULL) + return ret; + else if ((host = gethostbyname(hostname)) != NULL) + return host->h_name; + else + return NULL; +} + +int urlMainSetupPanel(struct iurlinfo * ui) { + newtComponent form, okay, cancel, urlEntry; + newtComponent answer, text; + char *url = ""; + char * reflowedText = NULL; + int width, height; + newtGrid buttons, grid; + char * chptr; + char * buf = NULL; + + /* Populate the UI with whatever initial value we've got. */ + if (ui && ui->prefix) + url = convertUIToURL(ui); + + buttons = newtButtonBar(_("OK"), &okay, _("Back"), &cancel, NULL); + + if (asprintf(&buf, + _("Please enter the URL containing the %s installation image on your server."), + getProductName()) == -1) { + logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); + abort(); + } + + reflowedText = newtReflowText(buf, 47, 5, 5, &width, &height); + free(buf); + + text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP); + newtTextboxSetText(text, reflowedText); + free(reflowedText); + + urlEntry = newtEntry(22, 8, url, 60, (const char **) &url, + NEWT_ENTRY_SCROLL); + + grid = newtCreateGrid(1, 3); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, urlEntry, + 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons, + 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX); + + form = newtForm(NULL, NULL, 0); + newtGridAddComponentsToForm(grid, form, 1); + newtGridWrappedWindow(grid, _("URL Setup")); + newtGridFree(grid, 1); + + do { + answer = newtRunForm(form); + if (answer != cancel) { + if (!strlen(url)) { + newtWinMessage(_("Error"), _("OK"), + _("You must enter a URL.")); + continue; + } + + if (!strstr(url, "http://") && !strstr(url, "ftp://")) { + newtWinMessage(_("Error"), _("OK"), + _("URL must be either an ftp or http URL")); + continue; + } + + /* Now split up the URL we were given into its components for + * ease of checking. + */ + if (convertURLToUI(url, ui) == -1) + continue; + + if (!addrToIp(ui->address)) { + newtWinMessage(_("Unknown Host"), _("OK"), + _("%s is not a valid hostname."), ui->address); + continue; + } + } + + break; + } while (1); + + if (answer == cancel) { + newtFormDestroy(form); + newtPopWindow(); + + return LOADER_BACK; + } + + /* Get rid of trailing /'s */ + chptr = ui->prefix + strlen(ui->prefix) - 1; + while (chptr > ui->prefix && *chptr == '/') chptr--; + chptr++; + *chptr = '\0'; + + newtFormDestroy(form); + newtPopWindow(); + + return 0; +} diff --git a/loader/urls.h b/loader/urls.h new file mode 100644 index 000000000..75761ac76 --- /dev/null +++ b/loader/urls.h @@ -0,0 +1,46 @@ +/* + * urls.h + * + * Copyright (C) 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/>. + */ + +#ifndef H_LOADER_URLS +#define H_LOADER_URLS + +enum urlprotocol_t { URL_METHOD_FTP, URL_METHOD_HTTP }; +typedef enum urlprotocol_t urlprotocol; + +struct iurlinfo { + urlprotocol protocol; + char * address; + char * login; + char * password; + char * prefix; + char * proxy; + char * proxyPort; + int ftpPort; +}; + +int convertURLToUI(char *url, struct iurlinfo *ui); +char *convertUIToURL(struct iurlinfo *ui); + +int setupRemote(struct iurlinfo * ui); +int urlMainSetupPanel(struct iurlinfo * ui); +int urlSecondarySetupPanel(struct iurlinfo * ui); +int urlinstStartTransfer(struct iurlinfo * ui, char *path, char *extraHeaders); +int urlinstFinishTransfer(struct iurlinfo * ui, int fd); + +#endif diff --git a/loader/windows.c b/loader/windows.c new file mode 100644 index 000000000..424f8a71e --- /dev/null +++ b/loader/windows.c @@ -0,0 +1,67 @@ +/* + * windows.c - simple popup windows used by the loader + * + * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 <errno.h> +#include <newt.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> + +#include "windows.h" + +void winStatus(int width, int height, char * title, char * text, ...) { + newtComponent t, f; + char * buf = NULL; + va_list args; + + va_start(args, text); + + if (vasprintf(&buf, text, args) != -1) { + newtCenteredWindow(width, height, title); + + t = newtTextbox(1, 1, width - 2, height - 2, NEWT_TEXTBOX_WRAP); + newtTextboxSetText(t, buf); + f = newtForm(NULL, NULL, 0); + + free(buf); + + newtFormAddComponent(f, t); + + newtDrawForm(f); + newtRefresh(); + newtFormDestroy(f); + } + + va_end(args); +} + + +void scsiWindow(const char * driver) { + winStatus(40, 3, _("Loading SCSI driver"), + _("Loading %s driver..."), driver); +} + +/* vim:set shiftwidth=4 softtabstop=4: */ diff --git a/loader/windows.h b/loader/windows.h new file mode 100644 index 000000000..2400c9173 --- /dev/null +++ b/loader/windows.h @@ -0,0 +1,31 @@ +/* + * windows.h + * + * Copyright (C) 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/>. + */ + +#ifndef _WINDOWS_H_ +#define _WINDOWS_H_ + +#include "lang.h" + +void winStatus(int width, int height, char * title, char * text, ...); +void scsiWindow(const char * driver); + +#define errorWindow(String) \ + newtWinMessage(_("Error"), _("OK"), String, strerror (errno)); + +#endif /* _WINDOWS_H_ */ |