summaryrefslogtreecommitdiffstats
path: root/loader
diff options
context:
space:
mode:
authorDavid Cantrell <dcantrell@redhat.com>2008-08-25 17:13:37 -1000
committerDavid Cantrell <dcantrell@redhat.com>2008-08-25 17:13:37 -1000
commit80713e3f73e48856221c667f32b94b0a023ebecc (patch)
treeaff4d9170fc24d2f1acc238a2d8908159a71d3dd /loader
parentef5fbf7bc72572f3a6326b12f9187a5438e58e4c (diff)
downloadanaconda-80713e3f73e48856221c667f32b94b0a023ebecc.tar.gz
anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.tar.xz
anaconda-80713e3f73e48856221c667f32b94b0a023ebecc.zip
Renamed loader2 subdirectory to loader (hooray for git)
Diffstat (limited to 'loader')
-rw-r--r--loader/.gitignore12
-rw-r--r--loader/Makefile156
-rw-r--r--loader/cdinstall.c508
-rw-r--r--loader/cdinstall.h34
-rw-r--r--loader/copy.c141
-rw-r--r--loader/copy.h26
-rw-r--r--loader/devices.h103
-rw-r--r--loader/devt.h39
-rw-r--r--loader/dirbrowser.c215
-rw-r--r--loader/dirbrowser.h28
-rw-r--r--loader/driverdisk.c642
-rw-r--r--loader/driverdisk.h40
-rw-r--r--loader/driverselect.c278
-rw-r--r--loader/ftp.c785
-rw-r--r--loader/ftp.h53
-rw-r--r--loader/fwloader.c680
-rw-r--r--loader/fwloader.h35
-rw-r--r--loader/getparts.c176
-rw-r--r--loader/getparts.h27
-rw-r--r--loader/hardware.c201
-rw-r--r--loader/hardware.h30
-rw-r--r--loader/hdinstall.c519
-rw-r--r--loader/hdinstall.h38
-rw-r--r--loader/init.c798
-rw-r--r--loader/kbd.c187
-rw-r--r--loader/kbd.h27
-rw-r--r--loader/keymaps-i386bin0 -> 12131 bytes
-rw-r--r--loader/keymaps-ppcbin0 -> 12714 bytes
-rw-r--r--loader/keymaps-x86_64bin0 -> 12131 bytes
-rw-r--r--loader/kickstart.c566
-rw-r--r--loader/kickstart.h54
-rw-r--r--loader/lang.c404
-rw-r--r--loader/lang.h41
-rw-r--r--loader/linuxrc.s390674
-rw-r--r--loader/loader.c2208
-rw-r--r--loader/loader.h161
-rw-r--r--loader/loadermisc.c247
-rw-r--r--loader/loadermisc.h31
-rw-r--r--loader/log.c154
-rw-r--r--loader/log.h44
-rw-r--r--loader/mediacheck.c126
-rw-r--r--loader/mediacheck.h25
-rw-r--r--loader/method.c540
-rw-r--r--loader/method.h58
-rw-r--r--loader/mkctype.c76
-rw-r--r--loader/moduleinfo.c276
-rw-r--r--loader/moduleinfo.h78
-rw-r--r--loader/modules.c380
-rw-r--r--loader/modules.h30
-rw-r--r--loader/net.c2090
-rw-r--r--loader/net.h68
-rw-r--r--loader/nfsinstall.c506
-rw-r--r--loader/nfsinstall.h39
-rw-r--r--loader/selinux.c56
-rw-r--r--loader/selinux.h27
-rw-r--r--loader/shutdown.c124
-rwxr-xr-xloader/simplemot81
-rw-r--r--loader/telnet.c273
-rw-r--r--loader/telnet.h40
-rw-r--r--loader/telnetd.c251
-rw-r--r--loader/telnetd.h25
-rw-r--r--loader/tr/.cvsignore1
-rw-r--r--loader/udelay.h205
-rw-r--r--loader/undomounts.c236
-rw-r--r--loader/unicode-linedraw-chars.txt22
-rw-r--r--loader/urlinstall.c452
-rw-r--r--loader/urlinstall.h38
-rw-r--r--loader/urls.c367
-rw-r--r--loader/urls.h46
-rw-r--r--loader/windows.c67
-rw-r--r--loader/windows.h31
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
new file mode 100644
index 000000000..bb6dadc8b
--- /dev/null
+++ b/loader/keymaps-i386
Binary files differ
diff --git a/loader/keymaps-ppc b/loader/keymaps-ppc
new file mode 100644
index 000000000..12fa39fae
--- /dev/null
+++ b/loader/keymaps-ppc
Binary files differ
diff --git a/loader/keymaps-x86_64 b/loader/keymaps-x86_64
new file mode 100644
index 000000000..bb6dadc8b
--- /dev/null
+++ b/loader/keymaps-x86_64
Binary files differ
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 OSA­2 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 (&current_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, &ethtool, 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_ */