aboutsummaryrefslogtreecommitdiff
path: root/loader
diff options
context:
space:
mode:
authorwiktor w brodlo <wiktor@brodlo.net>2011-06-15 16:59:54 +0000
committerwiktor w brodlo <wiktor@brodlo.net>2011-06-15 16:59:54 +0000
commit2590d96369d0217e31dc2812690dde61dac417b5 (patch)
tree82276f787b08a28548e342c7921486f1acefab9f /loader
parentfirst commit (diff)
downloadanaconda-2590d96369d0217e31dc2812690dde61dac417b5.tar.gz
anaconda-2590d96369d0217e31dc2812690dde61dac417b5.tar.bz2
anaconda-2590d96369d0217e31dc2812690dde61dac417b5.zip
Initial import from Sabayon (ver 0.9.9.56)
Diffstat (limited to 'loader')
-rw-r--r--loader/.gitignore12
-rw-r--r--loader/Makefile.am118
-rw-r--r--loader/cdinstall.c509
-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.c199
-rw-r--r--loader/dirbrowser.h28
-rw-r--r--loader/driverdisk.c892
-rw-r--r--loader/driverdisk.h54
-rw-r--r--loader/driverselect.c251
-rw-r--r--loader/fwloader.c675
-rw-r--r--loader/fwloader.h35
-rw-r--r--loader/getparts.c180
-rw-r--r--loader/getparts.h27
-rw-r--r--loader/hardware.c150
-rw-r--r--loader/hardware.h28
-rw-r--r--loader/hdinstall.c489
-rw-r--r--loader/hdinstall.h38
-rw-r--r--loader/ibft.c105
-rw-r--r--loader/ibft.h45
-rw-r--r--loader/init.c798
-rw-r--r--loader/init.h31
-rw-r--r--loader/kbd.c164
-rw-r--r--loader/kbd.h27
-rw-r--r--loader/keymaps-i386bin0 -> 12173 bytes
-rw-r--r--loader/keymaps-ppcbin0 -> 12871 bytes
-rw-r--r--loader/keymaps-x86_64bin0 -> 12173 bytes
-rw-r--r--loader/kickstart.c549
-rw-r--r--loader/kickstart.h53
-rw-r--r--loader/lang.c399
-rw-r--r--loader/lang.h41
-rw-r--r--loader/linuxrc.s3903107
-rw-r--r--loader/loader.c2406
-rw-r--r--loader/loader.h192
-rw-r--r--loader/loadermisc.c150
-rw-r--r--loader/loadermisc.h33
-rw-r--r--loader/mediacheck.c115
-rw-r--r--loader/mediacheck.h25
-rw-r--r--loader/method.c545
-rw-r--r--loader/method.h60
-rw-r--r--loader/mkctype.c76
-rw-r--r--loader/moduleinfo.c276
-rw-r--r--loader/moduleinfo.h78
-rw-r--r--loader/modules.c411
-rw-r--r--loader/modules.h43
-rw-r--r--loader/net.c2112
-rw-r--r--loader/net.h77
-rw-r--r--loader/nfsinstall.c556
-rw-r--r--loader/nfsinstall.h40
-rw-r--r--loader/rpmextract.c325
-rw-r--r--loader/rpmextract.h45
-rw-r--r--loader/selinux.c56
-rw-r--r--loader/selinux.h27
-rw-r--r--loader/shutdown.c153
-rwxr-xr-xloader/simplemot81
-rw-r--r--loader/telnet.c273
-rw-r--r--loader/telnet.h40
-rw-r--r--loader/telnetd.c256
-rw-r--r--loader/telnetd.h25
-rw-r--r--loader/udelay.h199
-rw-r--r--loader/undomounts.c239
-rw-r--r--loader/unicode-linedraw-chars.txt22
-rw-r--r--loader/urlinstall.c414
-rw-r--r--loader/urlinstall.h36
-rw-r--r--loader/urls.c370
-rw-r--r--loader/urls.h35
-rw-r--r--loader/windows.c121
-rw-r--r--loader/windows.h43
71 files changed, 19302 insertions, 0 deletions
diff --git a/loader/.gitignore b/loader/.gitignore
new file mode 100644
index 0000000..021a962
--- /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.am b/loader/Makefile.am
new file mode 100644
index 0000000..18f1fbe
--- /dev/null
+++ b/loader/Makefile.am
@@ -0,0 +1,118 @@
+# loader/Makefile.am for anaconda
+#
+# Copyright (C) 2009 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: David Cantrell <dcantrell@redhat.com>
+
+bootdir = $(libdir)/$(PACKAGE_NAME)
+shareddir = $(datadir)/$(PACKAGE_NAME)
+
+boot_PROGRAMS = loader
+shared_DATA = loader.tr
+dist_shared_DATA = unicode-linedraw-chars.txt
+noinst_PROGRAMS = mkctype dirbrowser
+noinst_DATA = ctype.c
+noinst_HEADERS = *.h
+
+if IS_S390
+boot_PROGRAMS += shutdown
+dist_shared_SCRIPTS = linuxrc.s390
+else
+boot_PROGRAMS += init
+endif
+
+if IS_KEYMAPS_OVERRIDE_ARCH
+keymapsdir = $(datadir)/$(PACKAGE_NAME)
+keymaps_DATA = keymaps-override-$(ARCH)
+endif
+
+COMMON_CFLAGS = -DUSE_LOGDEV -DVERSION='"$(PACKAGE_VERSION)"'
+
+loader_CFLAGS = $(COMMON_CFLAGS) $(GLIB_CFLAGS) $(LIBNM_GLIB_CFLAGS) \
+ $(LIBCURL_CFLAGS) $(IPV6_CFLAGS) $(LIBARCHIVE_CFLAGS) \
+ $(RPM_CFLAGS) -DINCLUDE_LOCAL -DINCLUDE_NETWORK
+loader_LDADD = $(NEWT_LIBS) $(GLIB_LIBS) $(LIBNL_LIBS) \
+ $(LIBNM_GLIB_LIBS) $(CHECKISOMD5_LIBS) \
+ $(LIBCURL_LIBS) $(LIBARCHIVE_LIBS) $(RPM_LIBS) \
+ $(ISCSI_LIBS) $(top_srcdir)/isys/libisys.la -lm
+loader_SOURCES = loader.c copy.c moduleinfo.c loadermisc.c \
+ modules.c windows.c lang.c kbd.c driverdisk.c \
+ selinux.c mediacheck.c kickstart.c driverselect.c \
+ getparts.c dirbrowser.c fwloader.c ibft.c hardware.c \
+ method.c cdinstall.c hdinstall.c nfsinstall.c \
+ urlinstall.c net.c urls.c telnet.c telnetd.c \
+ rpmextract.c
+
+init_CFLAGS = $(COMMON_CFLAGS) $(GLIB_CFLAGS)
+init_LDADD = $(GLIB_LIBS)
+init_SOURCES = init.c undomounts.c shutdown.c copy.c
+
+shutdown_CFLAGS = $(COMMON_CFLAGS) -DAS_SHUTDOWN=1
+shutdown_SOURCES = shutdown.c undomounts.c
+
+mkctype_CFLAGS = $(COMMON_CFLAGS)
+mkctype_SOURCES = mkctype.c
+
+dirbrowser_CFLAGS = $(COMMON_CFLAGS) -DSTANDALONE
+dirbrowser_LDADD = $(NEWT_LIBS)
+dirbrowser_SOURCES = dirbrowser.c
+
+EXTRA_DIST = simplemot keymaps-*
+
+CLEANFILES = keymaps-override-$(ARCH) ctype.c tr/*.tr
+
+MAINTAINERCLEANFILES = Makefile.in
+
+sed_verbose = $(sed_verbose_$(V))
+sed_verbose_ = $(sed_verbose_$(AM_DEFAULT_VERBOSITY))
+sed_verbose_0 = @echo " SED "$@;
+
+cp_verbose = $(cp_verbose_$(V))
+cp_verbose_ = $(cp_verbose_$(AM_DEFAULT_VERBOSITY))
+cp_verbose_0 = @echo " CP "$@;
+
+mkctype_verbose = $(mkctype_verbose_$(V))
+mkctype_verbose_ = $(mkctype_verbose_$(AM_DEFAULT_VERBOSITY))
+mkctype_verbose_0 = @echo " MAKE "$@;
+
+msgmerge_verbose = $(msgmerge_verbose_$(V))
+msgmerge_verbose_ = $(msgmerge_verbose_$(AM_DEFAULT_VERBOSITY))
+msgmerge_verbose_0 = echo " MERGE "$${lang}.po;
+
+xgettext_verbose = $(xgettext_verbose_$(V))
+xgettext_verbose_ = $(xgettext_verbose_$(AM_DEFAULT_VERBOSITY))
+xgettext_verbose_0 = @echo " GETTXT "$@;
+
+keymaps-override-$(ARCH): keymaps-$(ARCH)
+ $(cp_verbose)cp -p $< $@
+
+ctype.c: mkctype
+ $(mkctype_verbose)./mkctype > ctype.c
+
+loader.tr: $(top_srcdir)/lang-table loader.po
+ @LANGS="`cut -f 2 $(top_srcdir)/lang-table | egrep -v '(^en$$)'`" ; \
+ if [ ! -d tr ]; then \
+ mkdir -p tr ; \
+ fi ; \
+ for lang in $$LANGS ; do \
+ $(msgmerge_verbose)msgmerge -q $(top_srcdir)/po/$$lang.po loader.po | msgconv -t utf-8 | ./simplemot > tr/$$lang.tr ; \
+ done ; \
+ (cd tr ; ls -1 *.tr | cpio --quiet -Hcrc -o | gzip -9) > $@
+
+loader.po:
+ $(xgettext_verbose)xgettext --default-domain=loader --add-comments \
+ --keyword=_ --keyword=N_ *.c
+ $(sed_verbose)sed -i 's/charset=CHARSET/charset=UTF-8/' $@
diff --git a/loader/cdinstall.c b/loader/cdinstall.c
new file mode 100644
index 0000000..9d5cee1
--- /dev/null
+++ b/loader/cdinstall.c
@@ -0,0 +1,509 @@
+/*
+ * cdinstall.c - code to set up cdrom installs
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Erik Troan <ewt@redhat.com>
+ * Matt Wilson <msw@redhat.com>
+ * Michael Fulbright <msf@redhat.com>
+ * Jeremy Katz <katzj@redhat.com>
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <newt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+/* FIXME Remove hack when: https://bugzilla.redhat.com/show_bug.cgi?id=478663
+ is resolved */
+/* Hack both __BIG_ENDIAN and __LITTLE_ENDIAN get defined by glibc, the
+ kernel headers we need do not like this! */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#undef __BIG_ENDIAN
+#else
+#undef __LITTLE_ENDIAN
+#endif
+#include <asm/types.h>
+#include <limits.h>
+#include <linux/cdrom.h>
+
+#include "kickstart.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "lang.h"
+#include "modules.h"
+#include "method.h"
+#include "cdinstall.h"
+#include "mediacheck.h"
+#include "windows.h"
+
+#include "../isys/imount.h"
+#include "../isys/isys.h"
+#include "../isys/log.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+/* ejects the CD device the device node points at */
+static void ejectCdrom(char *device) {
+ int ejectfd;
+
+ if (!device) return;
+ logMessage(INFO, "ejecting %s...",device);
+ if ((ejectfd = open(device, O_RDONLY | O_NONBLOCK, 0)) >= 0) {
+ ioctl(ejectfd, CDROM_LOCKDOOR, 0);
+ if (ioctl(ejectfd, CDROMEJECT, 0))
+ logMessage(ERROR, "eject failed on device %s: %m", device);
+ close(ejectfd);
+ } else {
+ logMessage(ERROR, "could not open device %s: %m", device);
+ }
+}
+
+static char *cdrom_drive_status(int rc) {
+ struct {
+ int code;
+ char *str;
+ } status_codes[] =
+ {
+ { CDS_NO_INFO, "CDS_NO_INFO" },
+ { CDS_NO_DISC, "CDS_NO_DISC" },
+ { CDS_TRAY_OPEN, "CDS_TRAY_OPEN" },
+ { CDS_DRIVE_NOT_READY, "CDS_DRIVE_NOT_READY" },
+ { CDS_DISC_OK, "CDS_DISC_OK" },
+ { CDS_AUDIO, "CDS_AUDIO" },
+ { CDS_DATA_1, "CDS_DATA_1" },
+ { CDS_DATA_2, "CDS_DATA_2" },
+ { CDS_XA_2_1, "CDS_XA_2_1" },
+ { CDS_XA_2_2, "CDS_XA_2_2" },
+ { CDS_MIXED, "CDS_MIXED" },
+ { INT_MAX, NULL },
+ };
+ int i;
+
+ if (rc < 0)
+ return strerror(-rc);
+
+ for (i = 0; status_codes[i].code != INT_MAX; i++) {
+ if (status_codes[i].code == rc)
+ return status_codes[i].str;
+ }
+ return NULL;
+}
+
+static int waitForCdromTrayClose(int fd) {
+ int rc;
+ int prev = INT_MAX;
+
+ do {
+ char *status = NULL;
+ rc = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ if (rc < 0)
+ rc = -errno;
+
+ /* only bother to print the status if it changes */
+ if (prev == INT_MAX || prev != rc) {
+ status = cdrom_drive_status(rc);
+ if (status != NULL) {
+ logMessage(INFO, "drive status is %s", status);
+ } else {
+ logMessage(INFO, "drive status is unknown status code %d", rc);
+ }
+ }
+ prev = rc;
+ if (rc == CDS_DRIVE_NOT_READY)
+ usleep(100000);
+ } while (rc == CDS_DRIVE_NOT_READY);
+ return rc;
+}
+
+static void closeCdromTray(char *device) {
+ int fd;
+
+ if (!device || !*device)
+ return;
+
+ logMessage(INFO, "closing CD tray on %s .", device);
+ if ((fd = open(device, O_RDONLY | O_NONBLOCK, 0)) >= 0) {
+ if (ioctl(fd, CDROMCLOSETRAY, 0)) {
+ logMessage(ERROR, "closetray failed on device %s: %m", device);
+ } else {
+ waitForCdromTrayClose(fd);
+ ioctl(fd, CDROM_LOCKDOOR, 1);
+ }
+ close(fd);
+ } else {
+ logMessage(ERROR, "could not open device %s: %m", device);
+ }
+}
+
+/* Given cd device cddriver, this function will attempt to check its internal
+ * checksum.
+ */
+static void mediaCheckCdrom(char *cddriver) {
+ int rc;
+ int first;
+
+ first = 1;
+ do {
+ char *descr;
+ char *tstamp;
+ int ejectcd;
+
+ /* init every pass */
+ ejectcd = 0;
+ descr = NULL;
+
+ closeCdromTray(cddriver);
+
+ /* if first time through, see if they want to eject the CD */
+ /* currently in the drive (most likely the CD they booted from) */
+ /* and test a different disk. Otherwise just test the disk in */
+ /* the drive since it was inserted in the previous pass through */
+ /* this loop, so they want it tested. */
+ if (first) {
+ first = 0;
+ rc = newtWinChoice(_("Media Check"), _("Test"), _("Eject Disc"),
+ _("Choose \"%s\" to test the disc currently in "
+ "the drive, or \"%s\" to eject the disc and "
+ "insert another for testing."), _("Test"),
+ _("Eject Disc"));
+
+ if (rc == 2)
+ ejectcd = 1;
+ }
+
+ if (!ejectcd) {
+ /* XXX MSFFIXME: should check return code for error */
+ readStampFileFromIso(cddriver, &tstamp, &descr);
+ doMediaCheck(cddriver, descr);
+
+ if (descr)
+ free(descr);
+ }
+
+ ejectCdrom(cddriver);
+
+ rc = newtWinChoice(_("Media Check"), _("Test"), _("Continue"),
+ _("If you would like to test additional media, "
+ "insert the next disc and press \"%s\". "
+ "Testing each disc is not strictly required, however "
+ "it is highly recommended. Minimally, the discs should "
+ "be tested prior to using them for the first time. "
+ "After they have been successfully tested, it is not "
+ "required to retest each disc prior to using it again."),
+ _("Test"), _("Continue"));
+
+ if (rc == 2) {
+ closeCdromTray(cddriver);
+ return;
+ } else {
+ continue;
+ }
+ } while (1);
+}
+
+/* output an error message when CD in drive is not the correct one */
+/* Used by mountCdromStage2() */
+static void wrongCDMessage(void) {
+ newtWinMessage(_("Error"), _("OK"),
+ _("The %s disc was not found "
+ "in any of your drives. Please insert "
+ "the %s disc and press %s to retry."),
+ getProductName(), getProductName(), _("OK"));
+}
+
+/* ask about doing media check */
+static void queryCDMediaCheck(char *dev, char *location) {
+ int rc;
+ char *stage2loc;
+
+ /* dont bother to test in automated installs */
+ if (FL_KICKSTART(flags) && !FL_MEDIACHECK(flags))
+ return;
+
+ /* see if we should check image(s) */
+ /* in rescue mode only test if they explicitly asked to */
+ if (!FL_RESCUE(flags) || FL_MEDIACHECK(flags)) {
+ startNewt();
+ rc = newtWinChoice(_("Disc Found"), _("OK"), _("Skip"),
+ _("To begin testing the media before installation press %s.\n\n"
+ "Choose %s to skip the media test and start the installation."),
+ _("OK"), _("Skip"));
+
+ if (rc != 2) {
+ /* We already mounted the CD earlier to verify there's at least a
+ * stage2 image. Now we need to unmount to perform the check, then
+ * remount to pretend nothing ever happened.
+ */
+ umount(location);
+ mediaCheckCdrom(dev);
+
+ do {
+ if (doPwMount(dev, location, "iso9660", "ro", NULL)) {
+ ejectCdrom(dev);
+ wrongCDMessage();
+ continue;
+ }
+
+ checked_asprintf(&stage2loc, "%s/images/install.img",
+ location);
+
+ if (access(stage2loc, R_OK)) {
+ free(stage2loc);
+ umount(location);
+ ejectCdrom(dev);
+ wrongCDMessage();
+ continue;
+ }
+
+ free(stage2loc);
+ break;
+ } while (1);
+ }
+ }
+}
+
+/* Set up a CD/DVD drive to mount the stage2 image from. If successful, the
+ * stage2 image will be left mounted on /mnt/runtime.
+ *
+ * location: Where to mount the media at (usually /mnt/stage2)
+ * loaderData: The usual, can be NULL if no info
+ * interactive: Whether or not to prompt about questions/errors
+ * mediaCheck: Do we run media check or not?
+ */
+static char *setupCdrom(char *location, struct loaderData_s *loaderData,
+ int interactive, int mediaCheck) {
+ int i, rc;
+ int stage2inram = 0;
+ char *retbuf = NULL, *stage2loc, *stage2img;
+ struct device ** devices;
+ char *cddev = NULL;
+
+ devices = getDevices(DEVICE_CDROM);
+ if (!devices) {
+ logMessage(ERROR, "got to setupCdrom without a CD device");
+ return NULL;
+ }
+
+ checked_asprintf(&stage2loc, "%s/images/install.img", location);
+
+ /* JKFIXME: ASSERT -- we have a cdrom device when we get here */
+ do {
+ for (i = 0; devices[i]; i++) {
+ char *tmp = NULL;
+ int fd;
+
+ if (!devices[i]->device)
+ continue;
+
+ if (strncmp("/dev/", devices[i]->device, 5)) {
+ checked_asprintf(&tmp, "/dev/%s", devices[i]->device);
+
+ free(devices[i]->device);
+ devices[i]->device = tmp;
+ }
+
+ logMessage(INFO, "trying to mount CD device %s on %s",
+ devices[i]->device, location);
+
+ if (!FL_CMDLINE(flags))
+ winStatus(60, 3, _("Scanning"), _("Looking for installation images on CD device %s\n"), devices[i]->device);
+ else
+ printf(_("Looking for installation images on CD device %s"), devices[i]->device);
+
+ fd = open(devices[i]->device, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ logMessage(ERROR, "Couldn't open %s: %m", devices[i]->device);
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+ continue;
+ }
+
+ rc = waitForCdromTrayClose(fd);
+ close(fd);
+ switch (rc) {
+ case CDS_NO_INFO:
+ logMessage(ERROR, "Drive tray reports CDS_NO_INFO");
+ break;
+ case CDS_NO_DISC:
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+ continue;
+ case CDS_TRAY_OPEN:
+ logMessage(ERROR, "Drive tray reports open when it should be closed");
+ break;
+ default:
+ break;
+ }
+
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+
+ if (!(rc=doPwMount(devices[i]->device, location, "iso9660", "ro", NULL))) {
+ cddev = devices[i]->device;
+ if (!access(stage2loc, R_OK)) {
+ char *updpath;
+
+ if (mediaCheck)
+ queryCDMediaCheck(devices[i]->device, location);
+
+ /* if in rescue mode lets copy stage 2 into RAM so we can */
+ /* free up the CD drive and user can have it avaiable to */
+ /* aid system recovery. */
+ if (FL_RESCUE(flags) && !FL_TEXT(flags) &&
+ totalMemory() > MIN_GUI_RAM ) {
+ rc = copyFile(stage2loc, "/tmp/install.img");
+ stage2img = strdup("/tmp/install.img");
+ stage2inram = 1;
+ } else {
+ stage2img = strdup(stage2loc);
+ stage2inram = 0;
+ }
+
+ rc = mountStage2(stage2img);
+ free(stage2img);
+
+ if (rc) {
+ logMessage(INFO, "mounting stage2 failed");
+ umount(location);
+ continue;
+ }
+
+ checked_asprintf(&updpath, "%s/images/updates.img", location);
+
+ logMessage(INFO, "Looking for updates in %s", updpath);
+ copyUpdatesImg(updpath);
+ free(updpath);
+
+ checked_asprintf(&updpath, "%s/images/product.img", location);
+
+ logMessage(INFO, "Looking for product in %s", updpath);
+ copyProductImg(updpath);
+ free(updpath);
+
+ /* if in rescue mode and we copied stage2 to RAM */
+ /* we can now unmount the CD */
+ if (FL_RESCUE(flags) && stage2inram) {
+ umount(location);
+ }
+
+ checked_asprintf(&retbuf, "cdrom://%s:%s",
+ devices[i]->device, location);
+ } else {
+ /* this wasnt the CD we were looking for, clean up and */
+ /* try the next CD drive */
+ umount(location);
+ }
+ }
+ }
+
+ if (!retbuf) {
+ if (interactive) {
+ char * buf;
+
+ checked_asprintf(&buf, _("The %s disc was not found in any of your "
+ "CDROM drives. Please insert the %s disc "
+ "and press %s to retry."),
+ getProductName(), getProductName(), _("OK"));
+
+ ejectCdrom(cddev);
+ rc = newtWinChoice(_("Disc Not Found"),
+ _("OK"), _("Back"), buf, _("OK"));
+ free(buf);
+ if (rc == 2)
+ goto err;
+ } else {
+ /* we can't ask them about it, so just return not found */
+ goto err;
+ }
+ }
+ } while (!retbuf);
+
+err:
+ free(stage2loc);
+ return retbuf;
+}
+
+/* try to find a install CD non-interactively */
+char * findAnacondaCD(char *location) {
+ return setupCdrom(location, NULL, 0, 1);
+}
+
+/* look for a CD and mount it. if we have problems, ask */
+char * mountCdromImage(struct installMethod * method,
+ char * location, struct loaderData_s * loaderData) {
+ return setupCdrom(location, loaderData, 1, 1);
+}
+
+void setKickstartCD(struct loaderData_s * loaderData, int argc, char ** argv) {
+
+ logMessage(INFO, "kickstartFromCD");
+ loaderData->method = METHOD_CDROM;
+}
+
+int kickstartFromCD(char *kssrc) {
+ int rc, i;
+ char *p, *kspath;
+ struct device ** devices;
+
+ logMessage(INFO, "getting kickstart file from first CDROM");
+
+ devices = getDevices(DEVICE_CDROM);
+ /* usb can take some time to settle, even with the various hacks we
+ * have in place. some systems use portable USB CD-ROM drives, try to
+ * make sure there really isn't one before bailing */
+ for (i = 0; !devices && i < 10; ++i) {
+ logMessage(INFO, "sleeping to wait for a USB CD-ROM");
+ sleep(2);
+ devices = getDevices(DEVICE_CDROM);
+ }
+ if (!devices) {
+ logMessage(ERROR, "No CDROM devices found!");
+ return 1;
+ }
+
+ /* format is cdrom:[/path/to/ks.cfg] */
+ kspath = "";
+ p = strchr(kssrc, ':');
+ if (p)
+ kspath = p + 1;
+
+ if (!p || strlen(kspath) < 1)
+ kspath = "/ks.cfg";
+
+ for (i=0; devices[i]; i++) {
+ if (!devices[i]->device)
+ continue;
+
+ rc = getKickstartFromBlockDevice(devices[i]->device, kspath);
+ if (rc == 0)
+ return 0;
+ }
+
+ startNewt();
+ newtWinMessage(_("Error"), _("OK"),
+ _("Cannot find kickstart file on CDROM."));
+ return 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 et */
diff --git a/loader/cdinstall.h b/loader/cdinstall.h
new file mode 100644
index 0000000..a0ecbc3
--- /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 0000000..1c61233
--- /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 0000000..1153bf8
--- /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 0000000..974e792
--- /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 0000000..364a134
--- /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 0000000..9199850
--- /dev/null
+++ b/loader/dirbrowser.c
@@ -0,0 +1,199 @@
+/*
+ * 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 "../isys/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
+
+#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 0000000..7fb22f2
--- /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 0000000..22543b0
--- /dev/null
+++ b/loader/driverdisk.c
@@ -0,0 +1,892 @@
+/*
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include <blkid/blkid.h>
+
+#include <glob.h>
+#include <rpm/rpmlib.h>
+#include <sys/utsname.h>
+
+#include "copy.h"
+#include "loader.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 "rpmextract.h"
+
+#include "../isys/isys.h"
+#include "../isys/imount.h"
+#include "../isys/eddsupport.h"
+#include "../isys/log.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+/* modprobe DD mode */
+int modprobeDDmode(void)
+{
+ FILE *f = fopen("/etc/depmod.d/ddmode.conf", "w");
+ if (f) {
+ struct utsname unamedata;
+
+ if (uname(&unamedata))
+ fprintf(f, " pblacklist /lib/modules\n");
+ else
+ fprintf(f, " pblacklist /lib/modules/%s\n", unamedata.release);
+ fclose(f);
+ }
+
+ return f==NULL;
+}
+
+int modprobeNormalmode(void)
+{
+ /* remove depmod overrides */
+ if (unlink("/etc/depmod.d/ddmode.conf")) {
+ logMessage(ERROR, "removing ddmode.conf failed");
+ return -1;
+ }
+
+ /* run depmod to refresh modules db */
+ if (system("depmod -a")) {
+ logMessage(ERROR, "depmod -a failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * check if the RPM in question provides
+ * Provides: userptr
+ * we use it to check kernel-modules-<kernelversion>
+ */
+int dlabelProvides(const char* dep, void *userptr)
+{
+ char *kernelver = (char*)userptr;
+
+ logMessage(DEBUGLVL, "Provides: %s\n", dep);
+
+ return strcmp(dep, kernelver);
+}
+
+/*
+ * during cpio extraction, only extract files we need
+ * eg. module .ko files and firmware directory
+ */
+int dlabelFilter(const char* name, const struct stat *fstat, void *userptr)
+{
+ int l = strlen(name);
+
+ logMessage(DEBUGLVL, "Unpacking %s\n", name);
+
+ /* we want firmware files */
+ if (!strncmp("lib/firmware/", name, 13)) return 0;
+
+ if (l<3)
+ return 1;
+ l-=3;
+
+ /* and we want only .ko files */
+ if (strcmp(".ko", name+l))
+ return 1;
+
+ /* TODO we are unpacking kernel module, read it's description */
+
+ return 0;
+}
+
+char* moduleDescription(const char* modulePath)
+{
+ char *command = NULL;
+ FILE *f = NULL;
+ char *description = NULL;
+ int size;
+
+ checked_asprintf(&command, "modinfo --description '%s'", modulePath);
+ f = popen(command, "r");
+ free(command);
+
+ if (f==NULL)
+ return NULL;
+
+ description = malloc(sizeof(char)*256);
+ if (!description)
+ return NULL;
+
+ size = fread(description, 1, 255, f);
+ if (size == 0) {
+ free(description);
+ return NULL;
+ }
+
+ description[size-1]=0; /* strip the trailing newline */
+ pclose(f);
+
+ return description;
+}
+
+int globErrFunc(const char *epath, int eerrno)
+{
+ /* TODO check fatal errors */
+
+ return 0;
+}
+
+int dlabelUnpackRPMDir(char* rpmdir, char* destination)
+{
+ char *kernelver;
+ struct utsname unamedata;
+ char *oldcwd;
+ char *globpattern;
+ int rc;
+
+ /* get current working directory */
+ oldcwd = getcwd(NULL, 0);
+ if (!oldcwd) {
+ logMessage(ERROR, "getcwd() failed: %m");
+ return 1;
+ }
+
+ /* set the cwd to destination */
+ if (chdir(destination)) {
+ logMessage(ERROR, "We weren't able to CWD to \"%s\": %m", destination);
+ free(oldcwd);
+ return 1;
+ }
+
+ /* get running kernel version */
+ rc = uname(&unamedata);
+ checked_asprintf(&kernelver, "kernel-modules-%s",
+ rc ? "unknown" : unamedata.release);
+ logMessage(DEBUGLVL, "Kernel version: %s\n", kernelver);
+
+ checked_asprintf(&globpattern, "%s/*.rpm", rpmdir);
+ glob_t globres;
+ char** globitem;
+ if (!glob(globpattern, GLOB_NOSORT|GLOB_NOESCAPE, globErrFunc, &globres)) {
+ /* iterate over all rpm files */
+ globitem = globres.gl_pathv;
+ while (globres.gl_pathc>0 && globitem != NULL) {
+ explodeRPM(*globitem, dlabelFilter, dlabelProvides, NULL, kernelver);
+ }
+ globfree(&globres);
+ /* end of iteration */
+ }
+ free(globpattern);
+
+ /* restore CWD */
+ if (chdir(oldcwd)) {
+ logMessage(WARNING, "We weren't able to restore CWD to \"%s\": %m", oldcwd);
+ }
+
+ /* cleanup */
+ free(kernelver);
+ free(oldcwd);
+ return rc;
+}
+
+
+static char * driverDiskFiles[] = { "repodata", NULL };
+
+static int verifyDriverDisk(char *mntpt) {
+ char ** fnPtr;
+ char file[200];
+ struct stat sb;
+
+ /* check for dd descriptor */
+ sprintf(file, "%s/rhdd3", mntpt);
+ if (access(file, R_OK)) {
+ logMessage(ERROR, "can't find driver disk identifier, bad "
+ "driver disk");
+ return LOADER_BACK;
+ }
+
+ /* side effect: file is still mntpt/ddident */
+ stat(file, &sb);
+ if (!sb.st_size)
+ return LOADER_BACK;
+ for (fnPtr = driverDiskFiles; *fnPtr; fnPtr++) {
+ snprintf(file, 200, "%s/%s/%s", mntpt, getProductArch(), *fnPtr);
+ if (access(file, R_OK)) {
+ logMessage(ERROR, "cannot find %s, bad driver disk", file);
+ 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) {
+ /* FIXME moduleInfoSet modInfo = loaderData->modInfo; */
+ char file[200], dest[200], src[200];
+ char *title;
+ char *fwdir = NULL;
+ struct moduleBallLocation * location;
+ struct stat sb;
+ static int disknum = 0;
+ int fd, ret;
+
+ /* check for new version */
+ sprintf(file, "%s/rhdd3", 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, DD_RPMDIR_TEMPLATE, disknum);
+ mkdirChain(file);
+ mkdirChain(DD_MODULES);
+ mkdirChain(DD_FIRMWARE);
+
+ if (!FL_CMDLINE(flags)) {
+ startNewt();
+ winStatus(40, 3, _("Loading"), _("Reading driver disk"));
+ }
+
+ location = malloc(sizeof(struct moduleBallLocation));
+ location->title = strdup(title);
+ checked_asprintf(&location->path, DD_MODULES);
+
+ sprintf(dest, DD_RPMDIR_TEMPLATE, disknum);
+ sprintf(src, "%s/rpms/%s", mntpt, getProductArch());
+ copyDirectory(src, dest, copyWarnFn, copyErrorFn);
+
+ /* unpack packages from dest into location->path */
+ if (dlabelUnpackRPMDir(dest, DD_EXTRACTED)) {
+ /* fatal error, log this and jump to exception handler */
+ logMessage(ERROR, "Error unpacking RPMs from driver disc no.%d",
+ disknum);
+ goto loadDriverDiscException;
+ }
+
+ /* run depmod to refresh modules db */
+ if (system("depmod -a")) {
+ /* this is not really fatal error, it might still work, log it */
+ logMessage(ERROR, "Error running depmod -a for driverdisc no.%d", disknum);
+ }
+
+ checked_asprintf(&fwdir, DD_FIRMWARE);
+ if (!access(fwdir, R_OK|X_OK)) {
+ add_fw_search_dir(loaderData, fwdir);
+ stop_fw_loader(loaderData);
+ start_fw_loader(loaderData);
+ }
+ free(fwdir);
+
+ /* TODO generate and read module info
+ *
+ * sprintf(file, "%s/modinfo", mntpt);
+ * readModuleInfo(file, modInfo, location, 1);
+ */
+
+loadDriverDiscException:
+
+ 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);
+
+ if (!devs)
+ return numDevices;
+
+ 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;
+
+ /* Do not crash if the device disappeared */
+ if (!part_list) {
+ stage = DEV_DEVICE;
+ break;
+ }
+
+ 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", "auto", "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;
+
+ checked_asprintf(&buf,
+ _("Insert your driver disk into /dev/%s "
+ "and press \"OK\" to continue."), device);
+
+ 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", "auto", "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", 4)) {
+ 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);
+
+void useKickstartDD(struct loaderData_s * loaderData,
+ int argc, char ** argv) {
+ char * dev = NULL;
+ char * biospart = NULL, * p = NULL;
+ gchar *fstype = NULL, *src = NULL;
+ gint usebiosdev = 0;
+ gchar **remaining = NULL;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksDDOptions[] = {
+ /* The --type option is deprecated and now has no effect. */
+ { "type", 0, 0, G_OPTION_ARG_STRING, &fstype, NULL, NULL },
+ { "source", 0, 0, G_OPTION_ARG_STRING, &src, NULL, NULL },
+ { "biospart", 0, 0, G_OPTION_ARG_INT, &usebiosdev, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining,
+ NULL, NULL },
+ { NULL },
+ };
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksDDOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("The following invalid argument was specified for "
+ "the kickstart driver disk command: %s"),
+ optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ g_strfreev(remaining);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ if ((remaining != NULL) && (g_strv_length(remaining) == 1)) {
+ dev = remaining[0];
+ }
+
+ 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) {
+ getDDFromDev(loaderData, dev);
+ } else {
+ getDDFromSource(loaderData, src);
+ }
+
+ g_strfreev(remaining);
+ return;
+}
+
+static void getDDFromDev(struct loaderData_s * loaderData, char * dev) {
+ if (doPwMount(dev, "/tmp/drivers", "auto", "ro", NULL)) {
+ logMessage(ERROR, "unable to mount driver disk %s", dev);
+ return;
+ }
+
+ loadFromLocation(loaderData, "/tmp/drivers");
+ umount("/tmp/drivers");
+ unlink("/tmp/drivers");
+}
+
+
+/*
+ * Look for partition with specific label (part of #316481)
+ */
+GSList* findDriverDiskByLabel(void)
+{
+ char *ddLabel = "OEMDRV";
+ GSList *ddDevice = NULL;
+ blkid_cache bCache;
+
+ int res;
+ blkid_dev_iterate bIter;
+ blkid_dev bDev;
+
+ if (blkid_get_cache(&bCache, NULL)<0) {
+ logMessage(ERROR, "Cannot initialize cache instance for blkid");
+ return NULL;
+ }
+ if ((res = blkid_probe_all(bCache))<0) {
+ logMessage(ERROR, "Cannot probe devices in blkid: %d", res);
+ return NULL;
+ }
+ if ((res = blkid_probe_all_removable(bCache))<0) {
+ logMessage(ERROR, "Cannot probe removable devices in blkid: %d", res);
+ }
+
+ bIter = blkid_dev_iterate_begin(bCache);
+ blkid_dev_set_search(bIter, "LABEL", ddLabel);
+ while ((res = blkid_dev_next(bIter, &bDev)) == 0) {
+ bDev = blkid_verify(bCache, bDev);
+ if (!bDev)
+ continue;
+
+ char *devname = strdup(blkid_dev_devname(bDev));
+ logMessage(DEBUGLVL, "Adding driver disc %s to the list "
+ "of available DDs.", devname);
+ ddDevice = g_slist_prepend(ddDevice, (gpointer)devname);
+ /* Freeing bDev is taken care of by the put cache call */
+ }
+ blkid_dev_iterate_end(bIter);
+
+ blkid_put_cache(bCache);
+
+ return ddDevice;
+}
+
+int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device)
+{
+ int rc;
+
+ logMessage(INFO, "trying to mount %s", device);
+ if (doPwMount(device, "/tmp/drivers", "auto", "ro", NULL)) {
+ logMessage(ERROR, "Failed to mount driver disk.");
+ return -1;
+ }
+
+ rc = verifyDriverDisk("/tmp/drivers");
+ if (rc == LOADER_BACK) {
+ logMessage(ERROR, "Driver disk is invalid for this "
+ "release of %s.", getProductName());
+ umount("/tmp/drivers");
+ return -2;
+ }
+
+ rc = loadDriverDisk(loaderData, "/tmp/drivers");
+ umount("/tmp/drivers");
+ if (rc == LOADER_BACK) {
+ return -3;
+ }
+
+ return 0;
+}
+
diff --git a/loader/driverdisk.h b/loader/driverdisk.h
new file mode 100644
index 0000000..98bfd4a
--- /dev/null
+++ b/loader/driverdisk.h
@@ -0,0 +1,54 @@
+/*
+ * 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"
+
+#define DD_RPMDIR_TEMPLATE "/tmp/DD-%d"
+#define DD_EXTRACTED "/tmp/DD"
+#define DD_MODULES "/tmp/DD/lib/modules"
+#define DD_FIRMWARE "/tmp/DD/lib/firmware"
+
+extern char *ddFsTypes[];
+
+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);
+
+int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device);
+
+GSList* findDriverDiskByLabel(void);
+
+int modprobeNormalmode();
+int modprobeDDmode();
+
+#endif
diff --git a/loader/driverselect.c b/loader/driverselect.c
new file mode 100644
index 0000000..19aa357
--- /dev/null
+++ b/loader/driverselect.c
@@ -0,0 +1,251 @@
+/*
+ * 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 "../isys/log.h"
+
+#include "modules.h"
+#include "moduleinfo.h"
+#include "loader.h"
+#include "loadermisc.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, gchar *** 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);
+ checked_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);
+
+ 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) {
+ *moduleArgs = g_strsplit(argsEntry, " ", 0);
+ }
+
+ 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 = ' ';
+ gchar **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;
+
+ checked_asprintf(&buf, "%s (%s)",
+ modInfo->moduleList[sortedOrder[i].index].description,
+ modInfo->moduleList[sortedOrder[i].index].moduleName);
+
+ 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);
+
+ if (moduleArgs) {
+ g_strfreev(moduleArgs);
+ }
+
+ return LOADER_OK;
+}
diff --git a/loader/fwloader.c b/loader/fwloader.c
new file mode 100644
index 0000000..5ad1d8e
--- /dev/null
+++ b/loader/fwloader.c
@@ -0,0 +1,675 @@
+/*
+ * 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/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include "../isys/log.h"
+
+#include "loader.h"
+#include "fwloader.h"
+#include "udelay.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;
+
+ 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 0000000..e0b0fe8
--- /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 0000000..c60dc83
--- /dev/null
+++ b/loader/getparts.c
@@ -0,0 +1,180 @@
+/*
+ * 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 "../isys/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);
+ *part = NULL;
+ }
+ }
+
+ free(list);
+ list = NULL;
+}
diff --git a/loader/getparts.h b/loader/getparts.h
new file mode 100644
index 0000000..b672a77
--- /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 0000000..ad20ed5
--- /dev/null
+++ b/loader/hardware.c
@@ -0,0 +1,150 @@
+/*
+ * 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 <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <glib.h>
+
+#include "loader.h"
+#include "hardware.h"
+
+/* FIXME: for turning off dma */
+#include <sys/ioctl.h>
+#include <linux/hdreg.h>
+#include "../isys/isys.h"
+#include "../isys/log.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/udevadm", "settle", NULL, NULL };
+ int fd = open("/dev/tty3", O_RDWR);
+
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+
+ if (timeout) {
+ checked_asprintf(&args[2], "--timeout=%d", timeout);
+ }
+
+ rc = execv("/sbin/udevadm", 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;
+ gint argc = 0;
+ gchar **argv = NULL;
+ GError *optErr = NULL;
+
+ /* 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 (!g_shell_parse_argv(cmdLine, &argc, &argv, &optErr)) {
+ g_error_free(optErr);
+ 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();
+}
diff --git a/loader/hardware.h b/loader/hardware.h
new file mode 100644
index 0000000..47c34d2
--- /dev/null
+++ b/loader/hardware.h
@@ -0,0 +1,28 @@
+/*
+ * 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);
+
+#endif
diff --git a/loader/hdinstall.c b/loader/hdinstall.c
new file mode 100644
index 0000000..42f1a61
--- /dev/null
+++ b/loader/hdinstall.c
@@ -0,0 +1,489 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "driverdisk.h"
+#include "hdinstall.h"
+#include "getparts.h"
+#include "kickstart.h"
+#include "loader.h"
+#include "loadermisc.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"
+#include "../isys/log.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;
+
+ logMessage(INFO, "mounting device %s for hard drive install", device);
+
+ if (doPwMount(device, "/mnt/isodir", "auto", "ro", NULL))
+ return NULL;
+
+ checked_asprintf(&dirspec, "/mnt/isodir%.*s",
+ (int) (strrchr(dirName, '/') - dirName), dirName);
+ checked_asprintf(&path, "/mnt/isodir%s", dirName);
+
+ 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;
+ }
+
+ checked_asprintf(&updpath, "%s/updates.img", dirspec);
+
+ logMessage(INFO, "Looking for updates for HD in %s", updpath);
+ copyUpdatesImg(updpath);
+ free(updpath);
+
+ checked_asprintf(&updpath, "%s/product.img", dirspec);
+
+ logMessage(INFO, "Looking for product for HD in %s", updpath);
+ copyProductImg(updpath);
+
+ free(updpath);
+ free(dirspec);
+ umount("/mnt/isodir");
+
+ checked_asprintf(&url, "hd:%s:/%s", device,
+ dirName ? dirName : ".");
+
+ return url;
+ } else {
+ free(dirspec);
+ free(path);
+
+ if (rc) {
+ umount("/mnt/isodir");
+ return NULL;
+ }
+ }
+
+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 = NULL, *ksdirectory = NULL;
+
+ /* 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);
+ } else {
+ ksdirectory = strdup("/images/install.img");
+ }
+
+ 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) {
+ loaderData->stage2Data = NULL;
+ return NULL;
+ }
+
+ rc = loadDriverFromMedia(DEVICE_DISK, loaderData, 0, 0);
+ continue;
+ }
+
+ /* now find out which partition has the stage2 image */
+ checked_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());
+
+ 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) {
+ loaderData->stage2Data = NULL;
+ return NULL;
+ } else if (es.reason == NEWT_EXIT_HOTKEY && es.u.key == NEWT_KEY_F2) {
+ rc = loadDriverFromMedia(DEVICE_DISK, loaderData, 0, 0);
+ 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')) {
+ checked_asprintf(&dir, "%s/images/install.img", dir);
+ }
+
+ 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 *p;
+ gchar *biospart = NULL, *partition = NULL, *dir = NULL;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksHDOptions[] = {
+ { "biospart", 0, 0, G_OPTION_ARG_STRING, &biospart, NULL, NULL },
+ { "partition", 0, 0, G_OPTION_ARG_STRING, &partition, NULL, NULL },
+ { "dir", 0, 0, G_OPTION_ARG_STRING, &dir, NULL, NULL },
+ { NULL },
+ };
+
+ logMessage(INFO, "kickstartFromHD");
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksHDOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to HD kickstart method "
+ "command: %s"), optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ 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 0000000..44351a3
--- /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/ibft.c b/loader/ibft.c
new file mode 100644
index 0000000..b3a3827
--- /dev/null
+++ b/loader/ibft.c
@@ -0,0 +1,105 @@
+/*
+ File name: ibft.c
+ Date: 2008/09/02
+ Author: Martin Sivak <msivak@redhat.com>
+
+ Copyright (C) 2008 Red Hat
+
+ 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
+ in a file called COPYING along with this program; if not, write to
+ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ 02139, USA.
+*/
+
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libiscsi.h>
+#include "ibft.h"
+
+struct libiscsi_network_config ibft_context;
+int ibft_ispresent = 0;
+int ibft_initialized = 0;
+
+int ibft_init(void)
+{
+ int ret;
+
+ memset(&ibft_context, 0, sizeof(ibft_context));
+
+ ret = libiscsi_get_firmware_network_config(&ibft_context);
+
+ /* ret == 0 -> OK */
+ ibft_ispresent = !ret;
+ ibft_initialized = 1;
+
+ return ibft_initialized;
+}
+
+/* Is iBFT available on this system */
+int ibft_present()
+{
+ if(!ibft_initialized)
+ ibft_init();
+
+ return ibft_ispresent;
+}
+
+/* Is the iBFT network configured to use DHCP */
+int ibft_iface_dhcp()
+{
+ if(!ibft_initialized)
+ ibft_init();
+
+ if(!ibft_present())
+ return -1;
+
+ return ibft_context.dhcp;
+}
+
+#define ibft_iface_charfunc(name, var) char* ibft_iface_##name()\
+{\
+ if(!ibft_initialized)\
+ ibft_init();\
+\
+ if(!ibft_present())\
+ return NULL;\
+\
+ if(!strlen(ibft_context.var))\
+ return NULL;\
+\
+ return ibft_context.var;\
+}
+
+
+/* Get the iBFT MAC address */
+ibft_iface_charfunc(mac, mac_address)
+
+/* Get the iBFT ip address */
+ibft_iface_charfunc(ip, ip_address)
+
+/* Get the iBFT subnet mask */
+ibft_iface_charfunc(mask, netmask)
+
+/* Get the iBFT gateway */
+ibft_iface_charfunc(gw, gateway)
+
+/* Get the iBFT iface name */
+ibft_iface_charfunc(iface, iface_name)
+
+/* Get the iBFT dns servers */
+ibft_iface_charfunc(dns1, primary_dns)
+ibft_iface_charfunc(dns2, secondary_dns)
+
diff --git a/loader/ibft.h b/loader/ibft.h
new file mode 100644
index 0000000..a922c91
--- /dev/null
+++ b/loader/ibft.h
@@ -0,0 +1,45 @@
+/*
+ File name: ibft.h
+ Date: 2008/09/02
+ Author: Martin Sivak
+
+ Copyright (C) 2008 Red Hat
+
+ 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
+ in a file called COPYING along with this program; if not, write to
+ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ 02139, USA.
+*/
+
+
+#ifndef __IBFT_H__
+#define __IBFT_H__
+
+
+int ibft_init();
+int ibft_present();
+
+int ibft_iface_dhcp();
+
+char* ibft_iface_mac();
+char* ibft_iface_ip();
+char* ibft_iface_mask();
+char* ibft_iface_gw();
+char* ibft_iface_iface();
+char* ibft_iface_dns1();
+char* ibft_iface_dns2();
+
+
+#endif
+
+/* end of ibft.h */
diff --git a/loader/init.c b/loader/init.c
new file mode 100644
index 0000000..a2095de
--- /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
+#define klogctl syslog
+#else
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <execinfo.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 <glib.h>
+
+#include "init.h"
+#include "copy.h"
+#include "devt.h"
+#include "devices.h"
+
+#endif
+
+#include <asm/types.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(__powerpc64__) || (defined(__sparc__) && defined(__arch64__))
+ "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
+ *
+ */
+
+void shutDown(int doKill, reboot_action rebootAction);
+static int getKillPolicy(void);
+static void getSyslog(char*);
+struct termios ts;
+
+static int expected_exit = 0;
+
+static void doExit(int) __attribute__ ((noreturn));
+static void doExit(int result)
+{
+ expected_exit = 1;
+ exit(result);
+}
+
+static void printstr(char * string) {
+ int ret;
+ ret = write(1, string, strlen(string));
+}
+
+static void fatal_error(int usePerror) {
+ printf("failed.\n");
+
+ printf("\nI can't recover from this.\n");
+#if !defined(__s390__) && !defined(__s390x__)
+ while (1) ;
+#endif
+}
+
+/* sets up and launches syslog */
+static void startSyslog(void) {
+ int conf_fd;
+ int ret;
+ char addr[128];
+ char forwardtcp[] = "*.* @@";
+
+ /* update the config file with command line arguments first */
+ getSyslog(addr);
+ if (strlen(addr) > 0) {
+ conf_fd = open("/etc/rsyslog.conf", O_WRONLY|O_APPEND);
+ if (conf_fd < 0) {
+ printf("error opening /etc/rsyslog.conf: %d\n", errno);
+ printf("syslog forwarding will not be enabled\n");
+ sleep(5);
+ } else {
+ ret = write(conf_fd, forwardtcp, strlen(forwardtcp));
+ ret = write(conf_fd, addr, strlen(addr));
+ ret = write(conf_fd, "\n", 1);
+ close(conf_fd);
+ }
+ }
+
+ /* rsyslog is going to take care of things, so disable console logging */
+ klogctl(8, NULL, 1);
+ /* now we really start the daemon. */
+ int status;
+ status = system("/sbin/rsyslogd -c 4");
+ if (status < 0 ||
+ !WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0) {
+ printf("Unable to start syslog daemon.\n");
+ fatal_error(1);
+ }
+}
+
+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);
+ }
+
+ if (!strcmp(ttyname(fd), "/dev/hvc0")) {
+ /* using an HMC on a POWER system, use vt320 */
+ env[ENV_TERM] = "TERM=vt320";
+ } else {
+ /* 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(__sparc__)
+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)
+ return 1;
+ return memcmp(a->c_cc, b->c_cc, sizeof(a->c_cc));
+}
+#endif
+
+#if !defined(__s390__) && !defined(__s390x__) && !defined(__sparc__)
+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;
+
+ /* unset the umask so devices are created with correct perms
+ and not complemented by the previous umask call */
+
+ mode_t previous_umask = umask(0);
+
+ 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);
+ }
+
+ /* Hurray for hacks, this stops /lib/udev/rules.d/65-md-incremental.rules
+ from medling with mdraid sets. */
+ i = creat("/dev/.in_sysinit", 0644);
+ close(i);
+
+ /* Restore umask for minimal side affects */
+ umask(previous_umask);
+}
+
+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(getKillPolicy(), REBOOT);
+}
+
+/* halt handler */
+static void sigUsr1Handler(int signum) {
+ termReset();
+ shutDown(getKillPolicy(), HALT);
+}
+
+/* poweroff handler */
+static void sigUsr2Handler(int signum) {
+ termReset();
+ shutDown(getKillPolicy(), POWEROFF);
+}
+
+static int getKillPolicy(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 0;
+ }
+ return 1;
+}
+
+/* Looks through /proc/cmdline for remote syslog paramters. */
+static void getSyslog(char *addr) {
+ int fd;
+ int len;
+ char buf[1024];
+
+ /* assume nothing gets found */
+ addr[0] = '\0';
+ if ((fd = open("/proc/cmdline", O_RDONLY,0)) <= 0) {
+ return;
+ }
+ len = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ buf[len] = '\0';
+
+ /* Parse the command line into argument vector using glib */
+ int i;
+ int argc;
+ char** argv;
+ GError* err = NULL;
+ if (!g_shell_parse_argv(buf, &argc, &argv, &err )) {
+ g_error_free(err);
+ return;
+ }
+ for (i = 0; i < argc; ++i) {
+ /* find what we are looking for */
+ if (!strncmp(argv[i], "syslog=", 7)) {
+ strncpy(addr, argv[i] + 7, 127);
+ addr[127] = '\0';
+ break;
+ }
+ }
+ g_strfreev(argv);
+
+ /* address can be either a hostname or IPv4 or IPv6, with or without port;
+ thus we only allow the following characters in the address: letters and
+ digits, dots, colons, slashes, dashes and square brackets */
+ if (!g_regex_match_simple("^[\\w.:/\\-\\[\\]]*$", addr, 0, 0)) {
+ /* the parameter is malformed, disable its use */
+ addr[0] = '\0';
+ printf("The syslog= command line parameter is malformed and will be\n");
+ printf("ignored by the installer.\n");
+ sleep(5);
+ }
+
+}
+
+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);
+}
+
+void initSegvHandler(int signum) {
+ void *array[30];
+ size_t i;
+ const char const * const errmsgs[] = {
+ "init received SIG",
+ "! Backtrace:\n",
+ "init exited unexpectedly! 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 */
+
+ if (signum == 0) {
+ i = write(STDERR_FILENO, errmsgs[2], strlen(errmsgs[2]));
+ } else {
+ 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);
+}
+
+void initExitHandler(void)
+{
+ if (expected_exit)
+ return;
+
+ initSegvHandler(0);
+}
+
+static void setupBacktrace(void)
+{
+ void *array;
+
+ signal(SIGSEGV, initSegvHandler);
+ signal(SIGABRT, initSegvHandler);
+ atexit(initExitHandler);
+
+ /* Turns out, there's an initializer at the top of backtrace() that
+ * (on some arches) calls dlopen(). dlopen(), unsurprisingly, calls
+ * malloc(). So, call backtrace() early in signal handler setup so
+ * we can later safely call it from the signal handler itself. */
+ backtrace(&array, 1);
+}
+
+int main(int argc, char **argv) {
+ pid_t installpid, childpid;
+ int waitStatus;
+ int fd = -1;
+ int doShutdown =0;
+ reboot_action shutdown_method = HALT;
+ int isSerial = 0;
+ char * console = NULL;
+ int doKill = 1;
+ 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);
+ doExit(0);
+ } else if (!strncmp(basename(argv[0]), "halt", 4)) {
+ printf("Running halt...\n");
+ fd = getInitPid();
+ if (fd > 0)
+ kill(fd, SIGUSR1);
+ doExit(0);
+ } else if (!strncmp(basename(argv[0]), "reboot", 6)) {
+ printf("Running reboot...\n");
+ fd = getInitPid();
+ if (fd > 0)
+ kill(fd, SIGINT);
+ doExit(0);
+ }
+
+ /* turn off screen blanking */
+ printstr("\033[9;0]");
+ printstr("\033[8]");
+
+ umask(022);
+
+ /* set up signal handler */
+ setupBacktrace();
+
+ printstr("\nGreetings.\n");
+
+ printf("anaconda installer init version %s starting\n", VERSION);
+
+ printf("mounting /proc filesystem... ");
+ if (mount("/proc", "/proc", "proc", 0, NULL))
+ fatal_error(1);
+ printf("done\n");
+
+ printf("creating /dev filesystem... ");
+ if (mount("/dev", "/dev", "tmpfs", 0, NULL))
+ fatal_error(1);
+ createDevices();
+ printf("done\n");
+ printf("starting udev...");
+ if ((childpid = fork()) == 0) {
+ execl("/sbin/udevd", "/sbin/udevd", "--daemon", NULL);
+ exit(1);
+ }
+
+ /* wait at least until the udevd process that we forked exits */
+ do {
+ pid_t retpid;
+ int waitstatus;
+
+ retpid = waitpid(childpid, &waitstatus, 0);
+ if (retpid == -1) {
+ if (errno == EINTR)
+ continue;
+ /* if the child exited before we called waitpid, we can get
+ * ECHILD without anything really being wrong; we just lost
+ * the race.*/
+ if (errno == ECHILD)
+ break;
+ printf("init: error waiting on udevd: %m\n");
+ exit(1);
+ } else if (WIFEXITED(waitstatus)) {
+ break;
+ }
+ } while (1);
+
+ if (fork() == 0) {
+ execl("/sbin/udevadm", "udevadm", "control", "--env=ANACONDA=1", NULL);
+ exit(1);
+ }
+ printf("done\n");
+
+ printf("mounting /dev/pts (unix98 pty) filesystem... ");
+ if (mount("/dev/pts", "/dev/pts", "devpts", 0, NULL))
+ fatal_error(1);
+ printf("done\n");
+
+ printf("mounting /sys filesystem... ");
+ 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;
+ }
+ }
+
+ doKill = getKillPolicy();
+
+#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);
+ }
+ }
+
+ 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);
+ }
+
+ 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 tmpfs, 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 */
+ rename("/tmp", "/oldtmp");
+ mkdir("/tmp", 0755);
+
+ printf("mounting /tmp as tmpfs... ");
+ if (mount("none", "/tmp", "tmpfs", 0, "size=250m"))
+ 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. */
+ startSyslog();
+
+ /* 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 (fork() == 0) {
+ execl("/sbin/dbus-uuidgen", "/sbin/dbus-uuidgen", "--ensure", NULL);
+ doExit(1);
+ }
+
+ if (fork() == 0) {
+ execl("/sbin/dbus-daemon", "/sbin/dbus-daemon", "--system", NULL);
+ doExit(1);
+ }
+
+ sleep(2);
+
+ /* 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, HALT);
+ }
+
+ /* 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) {
+ pid_t childpid;
+ childpid = waitpid(-1, &waitStatus, 0);
+
+ if (childpid == installpid) {
+ doShutdown = 1;
+ ioctl(0, VT_ACTIVATE, 1);
+ }
+ }
+
+ if (!WIFEXITED(waitStatus) ||
+ (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus))) {
+ shutdown_method = DELAYED_REBOOT;
+ printf("install exited abnormally [%d/%d] ", WIFEXITED(waitStatus),
+ WEXITSTATUS(waitStatus));
+ if (WIFSIGNALED(waitStatus)) {
+ printf("-- received signal %d", WTERMSIG(waitStatus));
+ }
+ printf("\n");
+ } else {
+ shutdown_method = REBOOT;
+ }
+
+ shutDown(doKill, shutdown_method);
+
+ return 0;
+}
+
+/* vim:tw=78:ts=4:et:sw=4
+ */
diff --git a/loader/init.h b/loader/init.h
new file mode 100644
index 0000000..e1e5b70
--- /dev/null
+++ b/loader/init.h
@@ -0,0 +1,31 @@
+/*
+ * init.h
+ *
+ * Copyright (C) 2009 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 INIT_H
+#define INIT_H
+
+typedef enum {
+ REBOOT,
+ POWEROFF,
+ HALT,
+ /* gives user a chance to read the trace before scrolling the text out
+ with disk unmounting and termination info */
+ DELAYED_REBOOT
+} reboot_action;
+
+#endif /* INIT_H */
diff --git a/loader/kbd.c b/loader/kbd.c
new file mode 100644
index 0000000..b94f920
--- /dev/null
+++ b/loader/kbd.c
@@ -0,0 +1,164 @@
+/*
+ * 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 "lang.h"
+#include "windows.h"
+
+#include "../isys/stubs.h"
+#include "../isys/lang.h"
+#include "../isys/log.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;
+
+ 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)
+ 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);
+
+ 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 0000000..26c7111
--- /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 0000000..93f2e58
--- /dev/null
+++ b/loader/keymaps-i386
Binary files differ
diff --git a/loader/keymaps-ppc b/loader/keymaps-ppc
new file mode 100644
index 0000000..914f4bf
--- /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 0000000..93f2e58
--- /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 0000000..4ff800f
--- /dev/null
+++ b/loader/kickstart.c
@@ -0,0 +1,549 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "loader.h"
+#include "loadermisc.h"
+#include "lang.h"
+#include "kickstart.h"
+#include "modules.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"
+#include "../isys/log.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);
+
+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;
+ gint argc = 0;
+ gchar **argv = NULL;
+ GError *optErr = NULL;
+ 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 (!g_shell_parse_argv(start, &argc, &argv, &optErr) && argc) {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Error in %s on line %d of kickstart "
+ "file %s."), argv[0], line, cmdFile);
+ g_error_free(optErr);
+ } else if (!argc) {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Missing options on line %d of kickstart "
+ "file %s."), 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);
+ /* usb can take some time to settle, even with the various hacks we
+ * have in place. some systems use portable USB CD-ROM drives, try to
+ * make sure there really isn't one before bailing. */
+ for (i = 0; !devices && i < 10; ++i) {
+ logMessage(INFO, "sleeping to wait for a USB disk");
+ sleep(2);
+ 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");
+}
+
+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;
+}
+
+int isKickstartFileRemote(char *ksFile) {
+ char *location = NULL;
+
+ if (ksFile == NULL) {
+ return 0;
+ }
+
+ if (!strcmp(ksFile, "ks")) {
+ return 1;
+ } else if (!strncmp(ksFile, "ks=", 3)) {
+ location = ksFile + 3;
+ }
+
+ if (!strncmp(location, "http", 4) ||
+ !strncmp(location, "ftp://", 6) ||
+ !strncmp(location, "nfs:", 4)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+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", 4) || !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) {
+ if (!FL_NOKILL(flags))
+ flags |= LOADER_FLAGS_POWEROFF;
+ return;
+}
+
+static void setHalt(struct loaderData_s * loaderData, int argc,
+ char ** argv) {
+ if (!FL_NOKILL(flags))
+ flags |= LOADER_FLAGS_HALT;
+ return;
+}
+
+static void setShutdown(struct loaderData_s * loaderData, int argc,
+ char ** argv) {
+ gint eject = 0, reboot = 0, halt = 0, poweroff = 0;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksOptions[] = {
+ { "eject", 'e', 0, G_OPTION_ARG_INT, &eject, NULL, NULL },
+ { "reboot", 'r', 0, G_OPTION_ARG_INT, &reboot, NULL, NULL },
+ { "halt", 'h', 0, G_OPTION_ARG_INT, &halt, NULL, NULL },
+ { "poweroff", 'p', 0, G_OPTION_ARG_INT, &poweroff, NULL, NULL },
+ { NULL },
+ };
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to shutdown kickstart method "
+ "command: %s"), optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ if (FL_NOKILL(flags)) {
+ flags |= LOADER_FLAGS_HALT;
+ } else {
+ if (poweroff)
+ flags |= LOADER_FLAGS_POWEROFF;
+ if ((!poweroff && !reboot) || (halt))
+ flags |= LOADER_FLAGS_HALT;
+ }
+}
+
+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);
+ }
+ }
+}
+
+/* vim:set sw=4 sts=4 et: */
diff --git a/loader/kickstart.h b/loader/kickstart.h
new file mode 100644
index 0000000..3418cac
--- /dev/null
+++ b/loader/kickstart.h
@@ -0,0 +1,53 @@
+/*
+ * 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);
+
+int isKickstartFileRemote(char *ksFile);
+void getKickstartFile(struct loaderData_s * loaderData);
+void runKickstart(struct loaderData_s * loaderData);
+int getKickstartFromBlockDevice(char *device, char *path);
+
+#endif
diff --git a/loader/lang.c b/loader/lang.c
new file mode 100644
index 0000000..31749fc
--- /dev/null
+++ b/loader/lang.c
@@ -0,0 +1,399 @@
+/*
+ * 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 "loadermisc.h"
+#include "windows.h"
+
+#include "../isys/stubs.h"
+#include "../isys/cpio.h"
+#include "../isys/lang.h"
+#include "../isys/isys.h"
+#include "../isys/log.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 = "/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;
+ 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);
+ isysLoadFont();
+
+ /* clear out top line */
+ buf = alloca(81); /* reserve one byte for \0 */
+ for (i=0; i < 80; i++)
+ buf[i] = ' ';
+ buf[80] = 0; /* and set the \0 */
+ newtDrawRootText(0, 0, buf);
+
+ char *fmt = FL_RESCUE(flags) ? _(topLineWelcomeRescue) : _(topLineWelcome);
+ checked_asprintf(&buf, fmt, getProductName(), getProductArch());
+
+ 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 0000000..965f5a0
--- /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 0000000..2f0beba
--- /dev/null
+++ b/loader/linuxrc.s390
@@ -0,0 +1,3107 @@
+#! /bin/bash
+
+# linuxrc.s390: init process of Red Hat's installer initrd for s390(x)
+# 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>
+# Copyright (C) IBM Corp. 2008,2009
+# Author: Steffen Maier <maier@de.ibm.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
+#
+
+# prerequisites of this script to run inside the installer initrd:
+# - udevadm and udevd need to be there
+# - have /etc/udev/udev.conf with at least one comment line as content
+# - if necessary, have udev rules
+# - lsznet.raw and znetcontrolunits from s390utils-base in /lib/s390-tools
+# - pack kernel modules and module-init-tools (no longer use busybox for that)
+# - "multi on" in /etc/host.conf [RH bugs 486457,486461,483244]
+
+# TODOs:
+# - make sure driver modules get loaded automatically
+# - udev rule for lcs/ctcm vs. cu3088
+
+# debug: set -x
+
+VERSION=1.2
+
+export TEXTDOMAIN=s390installer
+export TEXTDOMAINDIR=/usr/lib/locale
+
+# helper function to execute command in arguments and print command on stdout
+function debug() {
+ # uncomment the following echo "$*" to enable debug output
+ #echo "$*"
+ $*
+}
+
+# FIXME: maybe change to "$$" for production use, in case it wouldn't be init
+declare -r INITPID="1"
+
+unset testing
+[ "$$" != "$INITPID" ] && testing="1"
+# uncomment the following test="1" to never execute sensitive commands
+#testing="1"
+
+if [ "$RUNKS" = "0" ]; then
+ RUNKS=""
+fi
+
+# helper function to disable commands while running outside the initrd
+function tv() {
+ if [ -z "$testing" ]; then
+ $*
+ else
+ return 0
+ fi
+}
+
+function checkipv6()
+{
+ local ip=$1
+ [ -z "$ip" ] && return 1
+ /sbin/ipcalc -c -6 "$ip" >/dev/null 2>&1
+ return $?
+}
+
+function checkipv4()
+{
+ local ip=$1
+ [ -z "$ip" ] && return 1
+ /sbin/ipcalc -c -4 "$ip" >/dev/null 2>&1
+ return $?
+}
+
+function doshutdown()
+{
+ echo $"about to exec shutdown"
+ exec /sbin/shutdown
+ exit 0
+}
+
+function doreboot()
+{
+ if [ -e "/sys/firmware/reipl" ]; then
+ read REIPL_TYPE < /sys/firmware/reipl/reipl_type
+ echo "reipl_type=$REIPL_TYPE"
+ pushd /sys/firmware/reipl/$REIPL_TYPE >/dev/null 2>&1
+ for i in *; do
+ echo "$i=`cat $i`"
+ done
+ popd >/dev/null 2>&1
+ fi
+
+ echo $"about to exec shutdown -r"
+ exec /sbin/shutdown -r
+ exit 0
+}
+
+function sysecho () {
+ file=$1
+ shift
+ local i=1
+ while [ $i -le 10 ] ; do
+ if [ ! -f "$file" ]; then
+ sleep 1
+ i=$((i+1))
+ else
+ break
+ fi
+ done
+ [ -f "$file" ] && echo $* > $file
+}
+
+function dasd_settle() {
+ local dasd_status=/sys/bus/ccw/devices/$1/status
+ if [ ! -f $dasd_status ]; then
+ return 1
+ fi
+ local i=1
+ while [ $i -le 30 ] ; do
+ local status
+ read status < $dasd_status
+ case $status in
+ online|unformatted)
+ return 0 ;;
+ *)
+ sleep 0.1
+ i=$((i+1)) ;;
+ esac
+ done
+ return 1
+}
+
+function dasd_settle_all() {
+ for dasdccw in $(cut -d '(' -f 1 /proc/dasd/devices) ; do
+ if ! dasd_settle $dasdccw ; then
+ echo $"Could not access DASD $dasdccw in time"
+ return 1
+ fi
+ done
+ return 0
+}
+
+function startinetd()
+{
+ echo
+ echo $"Starting sshd to allow login over the network."
+ if [ -z "$testing" ]; then
+ 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
+ fi # testing
+
+ /sbin/sshd -f /etc/ssh/sshd_config.anaconda
+ if [ -z "$RUNKS" ]; then
+ echo
+ echo $"Connect now to $IPADDR and log in as user install to start the installation."
+ echo $"E.g. using: ssh -x install@$IPADDR"
+ echo $"You may log in as the root user to start an interactive shell."
+ read
+ while : ; do
+ /bin/sh --login
+ [ $? = 0 ] || break
+ done
+ fi
+}
+
+# prints a canonocalized device bus ID for a given devno of any format
+function canonicalize_devno()
+{
+ case ${#1} in
+ 3) echo "0.0.0${1}" ;;
+ 4) echo "0.0.${1}" ;;
+ *) echo "${1}" ;;
+ esac
+ return 0
+}
+
+# read file from CMS and write it to /tmp
+function readcmsfile() # $1=dasdport $2=filename
+{
+ local dev
+ if [ $# -ne 2 ]; then return; fi
+ # precondition: udevd created dasda block device node
+ if ! sysecho /proc/cio_ignore "free $1"; then
+ echo $"DASD $1 could not be cleared from device blacklist"
+ return 1
+ fi
+ # /proc/cio_ignore won't block on freeing devices until resensing
+ # has been completed, so wait until the udev event queue depletes
+ # (without udevadm settle we could wait 2 seconds unconditionally)
+ #debug ls -laF /dev/.udev
+ udevadm settle
+ # even though the device might now be online, some of its
+ # sysfs attributes might not yet be available
+ sleep 1
+ # precondition: dasd_eckd_mod driver incl. dependencies loaded,
+ # dasd_mod must be loaded without setting any DASD online
+ dev=$(canonicalize_devno $1)
+ if ! sysecho /sys/bus/ccw/devices/$dev/online 1; then
+ echo $"DASD $dev could not be set online"
+ return 1
+ fi
+ udevadm settle
+ if ! dasd_settle $dev ; then
+ echo $"Could not access DASD $dev in time"
+ return 1
+ fi
+ udevadm settle
+ if ! cmsfscat -d /dev/dasda -a $2 > /tmp/$2; then
+ echo $"Could not read conf file $2 on CMS DASD $1."
+ fi
+ if ! sysecho /sys/bus/ccw/devices/$dev/online 0; then
+ echo $"DASD $dev could not be set offline again"
+ return 1
+ fi
+ udevadm settle
+ # consequences of no more module unload: loader can no longer
+ # use DASD module option to online DASDs and set other DASD parameters!
+}
+
+# adaption of the same function in init.c (udevd gets started later)
+function 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
+ # tty handling is different from init.c since s390 does not have all
+ for i in 2 3 4 5 6 7 8 9 ; do
+ ln -s console /dev/tty$i
+ done
+ mkdir /dev/pts
+ ln -s /proc/self/fd /dev/fd
+}
+
+# approximately the main() function of init.c
+function init_main() {
+ S390ARCH=$(uname -m)
+ if [ "$S390ARCH" = "s390" ]; then
+ export S390ARCH="S/390"
+ else
+ export S390ARCH="zSeries"
+ fi
+
+ echo
+ 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
+
+ if [ -z "$testing" ]; then
+
+ mount -t proc none /proc
+
+ mount -t tmpfs none /dev
+ createDevices
+ # udevd req'd by udevadm settle (/dev/.udev/queue)
+ # in readcmsfile, dialog_network_table, semantic_check_subchannels.
+ # (important: start udevd at the right time, e.g. after setup of /dev)
+ echo $"Starting udev..."
+ udevd --daemon
+ # debug: udevadm control --log-priority=debug
+
+ udevadm control --env=ANACONDA=1
+
+ 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 3270 console
+ # (console_loglevel of 4 is just right to not get driver info,
+ # e.g. from qeth, since that would mix up with the user dialog)
+ echo "4 4 1 7" > /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
+
+ echo -e "127.0.0.1\tlocalhost.localdomain localhost localhost4 localhost4.localdomain4" > /etc/hosts
+ echo -e "::1\t\tlocalhost.localdomain localhost localhost6 localhost6.localdomain6" >> /etc/hosts
+
+ /sbin/dbus-uuidgen --ensure &
+ [ $? != 0 ] && echo "error on calling /sbin/dbus-uuidgen --ensure"
+ /sbin/dbus-daemon --system &
+ [ $? != 0 ] && echo "error on calling /sbin/dbus-daemon --system"
+
+ fi # testing
+}
+
+# trigger udev to automatically load device drivers
+function udev_setup() {
+ if [ -z "$testing" ]; then
+ # debug: udevadm monitor &
+ udevadm trigger
+ udevadm settle
+ fi # testing
+}
+
+# from here on accesses to sysfs try to follow
+# linux/Documentation/sysfs-rules.txt
+
+### lsznet.raw integration
+
+declare -a nettable
+
+function read_lsznet_output() {
+ count=0
+ local line
+ while read line; do
+ nettable[$count]="$line"
+ count=$((count + 1))
+ # using the more sophisticated process substitution instead of temp file
+ # requires the symlink /dev/fd -> /proc/self/fd => createDevices
+ done < <(/lib/s390-tools/lsznet.raw)
+}
+
+function print_nettable() {
+ local fmtstring="%3s %-14s %-7s %-5s %-4s %-6s %-7s %s\n"
+ printf "$fmtstring" \
+ "NUM" "CARD" "CU" "CHPID" "TYPE" "DRIVER" "IF" "DEVICES"
+ local i
+ for ((i=0; i < count; i++)); do
+ local item cutype chp chpidtype devdrv devname chlist cardtype
+ read item cutype chp chpidtype devdrv devname chlist cardtype <<< ${nettable[$i]}
+ printf "$fmtstring" \
+ $item "$cardtype" $cutype $chp "$chpidtype" $devdrv $devname $chlist
+ done
+}
+
+function clear_screen() {
+ # FIXME: find a way to clear screen despite 3215 line mode terminal
+ echo
+}
+
+function dialog_network_table() {
+ while : ; do
+ echo $"Scanning for available network devices..."
+ # This may take a long time so we show "progress":
+ #( while true; do echo -n "."; sleep 1; done ) &
+ #local childpid=$!
+ read_lsznet_output
+ #kill $childpid
+ #echo
+ echo $"Autodetection found ${count} devices."
+ # count==0: there might still be a blacklist the user wants to clear.
+ # do not flood user with long list if there are many devices
+ if [ "$count" -le 15 ]; then
+ # Show list
+ answer=s
+ else # [ $count -gt 15 ]
+ echo
+ while : ; do
+ echo $"s) show all, m) manual config:"
+ local answer
+ read answer
+ case $answer in
+ s|m) break ;;
+ esac
+ done
+ fi
+ [ "$answer" = "m" ] && break
+ # show network table to select network hardware configuration from
+ if [ "$count" -gt 0 ]; then
+ clear_screen
+ print_nettable
+ echo
+ fi
+ # account for possibly ignored common I/O devices
+ # cio_wc_bytes is NOT local so it can be re-used outside this function
+ cio_wc_bytes=0
+ local cio_wc_filename cio_wc_foo
+ if [ -f /proc/cio_ignore ]; then
+ local cio_wc=$(wc -c /proc/cio_ignore)
+ read cio_wc_bytes cio_wc_filename cio_wc_foo <<< "$cio_wc"
+ if [ "$cio_wc_bytes" != "0" ]; then
+ echo $"Note: There is a device blacklist active! (Clearing might take long)"
+ #cat /proc/cio_ignore | tr '\n' ','
+ #echo
+ else
+ if [ "$count" -eq 0 ]; then
+ # count==0 AND no device blacklist => manual mode
+ echo $"Entering manual configuration mode."
+ break
+ fi
+ fi
+ fi
+ # selection dialog
+ while : ; do
+ [ "$count" -gt 0 ] && echo -n $"<num>) use config, "
+ [ "$cio_wc_bytes" != "0" ] && echo -n $"c) clear blacklist, "
+ echo $"m) manual config, r) rescan, s) shell:"
+ local choice
+ read choice
+ [ -z "$choice" ] && continue
+ if [ "$choice" = "s" ]; then
+ echo $"Enter 'exit' at the shell prompt to get back to the installation dialog."
+ /bin/bash
+ continue 2
+ fi
+ [ "$choice" = "m" ] && break
+ [ "$choice" = "r" ] && continue 2
+ [ "$cio_wc_bytes" != "0" -a "$choice" = "c" ] && break
+ [[ "$choice" =~ ^[[:digit:]]+$ ]]
+ case $? in
+ 0)
+ # string matched the pattern
+ [ "$choice" -ge 1 -a "$choice" -le "$count" ] && break
+ ;;
+ 1)
+ # string did not match the pattern
+ continue
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ done
+ if [ "$choice" = "c" ]; then
+ echo $"Clearing device blacklist..."
+ if sysecho /proc/cio_ignore "free all"; then
+ cio_wc_bytes=0
+ # /proc/cio_ignore won't block on freeing devices
+ # until resensing has been completed, so wait until
+ # the udev event queue depletes.
+ # This may take a long time so we show "progress":
+ #( while true; do echo -n "."; sleep 3; done ) &
+ #local childpid=$!
+ #debug ls -laF /dev/.udev
+ udevadm settle
+ # (virtual) CTC/A takes some more time to appear in sysfs
+ # FIXME: how long to wait? 3 seconds seems to be enough.
+ sleep 3
+ #kill $childpid
+ #echo
+ continue
+ else
+ echo $"Device blacklist could not be cleared"
+ fi
+ fi
+ [ "$choice" = "m" ] && break
+ # finally extract config info from selected item
+ # array nettable starts at index zero, user input starts at index one
+ choice=$((choice - 1))
+ local item cutype chp chpidtype devdrv devname chlist cardtype
+ read item cutype chp chpidtype devdrv devname chlist cardtype <<< ${nettable[$choice]}
+ # $NETTYPE happens to be exactly the network driver name
+ if [ "$devdrv" = "ctcm" ]; then
+ NETTYPE="ctc"
+ else
+ NETTYPE=$devdrv
+ fi
+ SUBCHANNELS=$chlist
+ break
+ done
+ echo
+}
+
+declare -r PREFIXFORMAT=[[:xdigit:]]*
+declare -r SSIDFORMAT=[0-3]
+declare -r BUSIDFORMAT=[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]
+declare -r IDFORMAT=$PREFIXFORMAT.$SSIDFORMAT.$BUSIDFORMAT
+declare -r SUBCHANNEL_TYPE_IO=0
+
+. /lib/s390-tools/znetcontrolunits
+
+function cardtype2cleartext() {
+ local cardtype=$1
+ case $cardtype in
+ OSD_10GIG) echo "OSA card in OSD mode, 10 Gigabit Ethernet" ;;
+ OSD_1000) echo "OSA card in OSD mode, Gigabit Ethernet" ;;
+ OSD_100) echo "OSA card in OSD mode, Fast Ethernet" ;;
+ OSD_GbE_LANE) echo "OSA card in OSD mode, Gigabit Ethernet, LAN Emulation" ;;
+ OSD_FE_LANE) echo "OSA card in OSD mode, Fast Ethernet, LAN Emulation" ;;
+ OSD_TR_LANE) echo "OSA card in OSD mode, Token Ring, LAN Emulation" ;;
+ OSD_ATM_LANE) echo "OSA card in OSD mode, ATM, LAN Emulation" ;;
+ OSD_Express) echo "OSA card in OSD mode, unknown link type" ;;
+ HSTR) echo "OSA card in OSD mode, High Speed Token Ring" ;;
+ OSN) echo "OSA for NCP, ESCON/CDLC bridge" ;;
+ HiperSockets) echo "HiperSockets with CHPID type IQD" ;;
+ "GuestLAN QDIO") echo "GuestLAN based on OSA (QDIO)" ;;
+ "GuestLAN Hiper") echo "GuestLAN based on HiperSockets" ;;
+ unknown) echo "other" ;;
+ *) echo "unknown"
+ echo "l.$LINENO: found unknown card_type, code needs to be fixed" 1>&2
+ ;;
+ esac
+}
+
+# returns true iff running under z/VM
+function isVM() {
+ local cpu_version=$(cat /proc/cpuinfo |grep "^processor " | head -n1 | sed 's/.*version = \([[:xdigit:]][[:xdigit:]]\).*/\1/' | tr '[:lower:]' '[:upper:]')
+ if [ "$cpu_version" = "FF" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# watch out: potential error message as side effect
+function isLayer2Default() {
+ # Read default from sysfs because according to device
+ # drivers book there are differences in the default between
+ # OSA (l2), hipersockets (l3).
+ # This only works here in installer where nobody has overwritten
+ # the default setting with another custom value already!
+ if [ ! -f /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/layer2 ]; then
+ echo $"Could not read layer mode from sysfs"
+ return 1
+ fi
+ local layer2
+ read layer2 < /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/layer2
+ if [ "$layer2" = "1" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# returns true iff either LAYER2 has been set to 1 or is the default
+# watch out: potential error message as side effect
+function isLayer2() {
+ case "x$LAYER2" in
+ x0) return 1 ;; # layer 3
+ x1) return 0 ;; # layer 2
+ x) # LAYER2 is unset or empty => qeth driver default applies.
+ isLayer2Default
+ return $?
+ ;;
+ *) echo "l.$LINENO: unknown value \"$LAYER2\" for LAYER2, code needs to be fixed" 1>&2
+ return 2 ;;
+ esac
+}
+
+# returns true iff qeth device $SCH_R_DEVBUSID
+# is capable of supporting IPv6
+# watch out: potential error message as side effect
+function ipv6_capable() {
+ [ "$NETTYPE" = "qeth" ] || return 1
+ case $cardtype in
+ OSD_10GIG|OSD_1000|OSD_100|OSD_Express|HiperSockets|"GuestLAN QDIO")
+ return 0 ;;
+ OSD_GbE_LANE|OSD_FE_LANE|OSD_TR_LANE|OSD_ATM_LANE) return 1 ;;
+ HSTR|OSN|unknown) return 1 ;;
+ "GuestLAN Hiper") return 1 ;;
+ *) echo $"Unknown card_type to determine IPv6 support"
+ return 1 ;;
+ esac
+}
+
+# sets device online _and_ retrieves DEVICE at the same time
+function set_device_online() {
+ echo $"Activating network device..."
+ local sysnettype
+ case "${NETTYPE}" in
+ qeth|lcs) sysnettype=${NETTYPE} ;;
+ ctc) sysnettype=ctcm ;;
+ esac
+ if ! [ -f /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online ]; then
+ echo $"Sysfs path to set device online does not exist."
+ return 1
+ fi
+ if ! sysecho /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online "1"; then
+ echo $"Could not set device ($SUBCHANNELS) online"
+ return 1
+ fi
+ udevadm settle
+ local i=1
+ while : ; do
+ local online
+ read online < /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online
+ [ "$online" == "1" ] && break
+ sleep 1
+ i=$((i+1))
+ if [ "$i" -gt 10 ]; then
+ echo $"Could not set device ($SUBCHANNELS) online within timeout"
+ return 1
+ fi
+ done
+ if [ "$NETTYPE" = "lcs" -o "$NETTYPE" = "ctc" ]; then
+ # KH FIXME: Workaround for missing sysfs interface
+ # DEVICE=$(cat /sys/devices/lcs/${SUBCHANNELS//,*/}/if_name)
+ # replaced with flexible solution:
+ # https://bugzilla.redhat.com/show_bug.cgi?id=204803#c9
+ # "sys/bus/ccwgroup/devices/${SUBCHANNEL}/net\:*
+ # for lcs after setting online"
+ if [ ! -h /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/net:* ]; then
+ echo $"Device $SUBCHANNELS does not have required sysfs attribute 'net:*'"
+ return 1
+ fi
+ DEVICE=$(echo /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/net:*)
+ DEVICE=${DEVICE//*:/}
+ if [ "$DEVICE" = "" ]; then
+ echo $"Could not get device name for $SUBCHANNELS"
+ return 1
+ fi
+ else # qeth
+ if [ ! -f /sys/devices/qeth/$SCH_R_DEVBUSID/if_name ]; then
+ echo $"Device $SUBCHANNELS does not have required sysfs attribute 'if_name'"
+ return 1
+ fi
+ # (device needs to be online to read if_name from sysfs attribute!)
+ read DEVICE < /sys/devices/qeth/$SCH_R_DEVBUSID/if_name
+ if [ "$DEVICE" = "" ]; then
+ echo $"Could not get device name for $SUBCHANNELS"
+ return 1
+ fi
+ if [ -f /sys/devices/qeth/$SCH_R_DEVBUSID/card_type ]; then
+ read cardtype < /sys/devices/qeth/$SCH_R_DEVBUSID/card_type
+ #debug echo "$cardtype"
+ # device is now online and link type will be known
+ echo -n $"Detected: "
+ cardtype2cleartext "$cardtype"
+ else
+ echo $"Could not read qeth network card type from sysfs."
+ fi
+ fi
+}
+
+# sets device up and blocks until device appears to be up
+function set_device_up() {
+ if [ -z "$DEVICE" ]; then
+ echo $"Could not determine interface name to bring up device $SUBCHANNELS"
+ return 1
+ fi
+ # Device does not come up fast enough to use "ip" to configure, so block.
+ # While OSA come up themselves after setting online,
+ # e.g. HiperSockets won't => set them up explicitly for the following check
+ debug ip link set up $DEVICE
+ local i=1
+ while : ; do
+ local tst=$(ip -o link show up dev $DEVICE)
+ [ -n "$tst" ] && break
+ sleep 1
+ i=$((i+1))
+ if [ "$i" -gt 10 ]; then
+ echo $"Could not bring up device $DEVICE within timeout"
+ return 1
+ fi
+ done
+ return 0
+}
+
+function syntax_check_domainname() {
+ # - match against regex adopted from RFC1035,sec.2.3.1 or RFC1034,sec.3.5
+ # (Internationalized Domain Names in Applications (IDNA) [RFC4690]
+ # have to be entered after encoding by punycode [RFC3492])
+ [[ "$1" =~ ^[[:alpha:]]([[:alnum:]-]{0,61}[[:alnum:]])?(\.[[:alpha:]]([[:alnum:]-]{0,61}[[:alnum:]])?)*$ ]]
+ case $? in
+ 0)
+ # string matched the pattern
+ return 0
+ ;;
+ 1)
+ # string did not match the pattern
+ echo "$2"
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ return 1
+}
+
+function modprobe_alias() {
+ if [ ":$NETTYPE" = ":ctc" ]; then
+ echo "alias $DEVICE ctcm" >> /tmp/modprobe.conf
+ else
+ echo "alias $DEVICE $NETTYPE" >> /tmp/modprobe.conf
+ fi
+ if [ $? -ne 0 ]; then
+ echo $"Could not append alias for network device $DEVICE to modprobe.conf"
+ return 1
+ fi
+ return 0
+}
+
+function disable_ipv6_autoconf() {
+ sysctl -w net.ipv6.conf.all.accept_ra=0 > /dev/null
+ sysctl -w net.ipv6.conf.all.accept_redirects=0 > /dev/null
+ sysctl -w net.ipv6.conf.all.autoconf=0 > /dev/null
+ sysctl -w net.ipv6.conf.default.accept_ra=0 > /dev/null
+ sysctl -w net.ipv6.conf.default.accept_redirects=0 > /dev/null
+ sysctl -w net.ipv6.conf.default.autoconf=0 > /dev/null
+}
+
+function configure_ipv6_address() {
+ # device needs to be online
+ # arp flag needs to be on for ipv6 over osa because of ndisc.
+ # happens automatically by the driver. do NOT mess with default setting.
+ #NO#debug ip link set dev $DEVICE arp on
+ if ! debug ip -6 address add $IPADDR/$NETMASK dev $DEVICE; then
+ echo $"Could net set IPv6 address $IPADDR/$NETMASK for device $DEVICE"
+ return 1
+ fi
+ # network route has been set by above "ip address add" already
+ # take care of MTU, which is bundled with ifconfig in the other IPv4 cases
+ if [ -n "$MMTU" ]; then
+ if ! debug ip link set $DEVICE $MMTU; then
+ echo $"Could net set maximum transfer unit ($MMTU) for device $DEVICE"
+ return 1
+ fi
+ fi
+ return 0
+}
+
+function configure_ipv4_address() {
+ # it's IPv4 and we can make use of ipcalc for better usability
+ if ipcalc -bmnp $ipcalc_arg > /tmp/ipcalc.$$.out 2> /dev/null; then
+ . /tmp/ipcalc.$$.out
+ else
+ echo $"Could not calculate network address and broadcast address from"
+ echo $" IPv4 address $IPADDR and netmask $NETMASK"
+ return 1
+ fi
+ rm /tmp/ipcalc.$$.out
+ # device needs to be online
+ if ! debug ifconfig $DEVICE $IPADDR $MMTU netmask $NETMASK broadcast $BROADCAST; then
+ echo $"Could not set IPv4 address $IPADDR for device $DEVICE"
+ echo $" with network mask $NETMASK and broadcast address $BROADCAST"
+ [ -n "$MMTU" ] && echo $" and maximum transfer unit: $MMTU"
+ return 1
+ fi
+ # This network route is already there after ifconfig!
+ #if ! debug route add -net $NETWORK netmask $NETMASK dev $DEVICE; then
+ # echo $"Could not add network route to $NETWORK/$NETMASK on device $DEVICE"
+ # return 1
+ #fi
+ return 0
+}
+
+function handle_mtu() {
+ # 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
+ [ -n "$MTU" -a -z "$MMTU" ] && MMTU="mtu $MTU"
+}
+
+function rollback_config() {
+ # each transaction to roll back may fail, if previous setup has not
+ # made progress that far to reach a certain transation
+ # => error output is misleading and should be avoided
+ [ -n "$DEVICE" ] && tv ip -4 route flush default dev $DEVICE
+ [ -n "$DEVICE" ] && tv ip -6 route flush default dev $DEVICE
+ # address flush seems to be effective for all address families
+ [ -n "$DEVICE" ] && ip address flush dev $DEVICE
+ if [ -n "$NETTYPE" ]; then
+ if [ -n "$SCH_R_DEVBUSID" ]; then
+ local sysnettype
+ case "${NETTYPE}" in
+ qeth|lcs) sysnettype=${NETTYPE} ;;
+ ctcm) sysnettype=ctcm ;;
+ esac
+ [ -f /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online ] && \
+ sysecho /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/online "0"
+ udevadm settle
+ [ -f /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/ungroup ] && \
+ sysecho /sys/devices/${sysnettype}/$SCH_R_DEVBUSID/ungroup "1"
+ udevadm settle
+ fi
+ fi
+ [ -z "$mtu_was_set" ] && unset MTU
+ [ -z "$mmtu_was_set" ] && unset MMTU
+ [ -z "$vswitch_was_set" ] && unset VSWITCH
+ # prevent possible reuse of an old DEVICE on restarting dialog
+ unset DEVICE
+ # set activated DASDs offline again
+ local dasd
+ while read dasd < /proc/dasd/devices; do
+ dasd=${dasd%%(*}
+ sysecho /sys/bus/ccw/devices/$dasd/online 0
+ done
+ udevadm settle
+}
+
+### workflow helper functions
+
+# workflow ideas:
+# - setting/applying single configuration steps right away save us explicit
+# syntactical & semantic checks PLUS we get direct feedback on error
+# - check error level of forked external programs and react on errors
+
+unset reenter
+unset redoitem
+unset interaction_happened
+
+function reenter() {
+ [ -z "$reenter" ] && return 1
+ # reenter menu should only be shown if NOT redoing item
+ if [ -n "$redoitem" ]; then
+ # unset redoitem # wrong => do NOT do this here
+ return 1
+ fi
+ return 0
+}
+
+function reenter_menu() {
+ local oldvalue=$1
+ interaction_happened="yes"
+ # unsetting input here is not sufficient, since reenter_menu
+ # is not called for predefined parameters
+ # which then might get assigned a previous old input of another parameter!
+ #unset input
+ reenter || return 0
+ # don't present reenter menu for empty parameters
+ # (currently ignoring parameters that are allowed to be empty!)
+ # this could be improved by checking if variable has been set/defined
+ #[ -z "$1" ] && return 0
+ while : ; do
+ if [ -n "$helptext" ]; then
+ echo $"0) default is previous \"$oldvalue\", 1) new value, ?) help"
+ else
+ echo $"0) default is previous \"$oldvalue\", 1) new value"
+ fi
+ # uncoded alternative: 2) skip parameter
+ local answer
+ read answer
+ [ -z "$answer" ] && return 1
+ case $answer in
+ 0) return 1 ;;
+ 1) # Deciding to enter new value gets user out of reenter-mode
+ # temporarily for this parameter.
+ # To put it differently: redoing does NOT present old values.
+ redoitem="yes"
+ echo -n $"new value: "
+ return 0
+ ;;
+ "?") input="?"
+ return 1
+ ;;
+ esac
+ done
+}
+
+function workflow_item_menu() {
+ local noredo=$1
+ # default is to continue if running kickstart to prevent interaction
+ [ -n "$RUNKS" ] && return 0
+ interaction_happened="yes"
+ while : ; do
+ unset redoitem
+ if [ "$noredo" = "noredo" ]; then
+ echo $"1) continue, 2) restart dialog, 3) halt, 4) shell"
+ else
+ echo $"0) redo this parameter, 1) continue, 2) restart dialog, 3) halt, 4) shell"
+ fi
+ local answer
+ read answer
+ case $answer in
+ 0) [ "$noredo" = "noredo" ] && continue
+ redoitem="yes"
+ continue 2
+ ;;
+ 1) return 0 ;; # can be used to break at caller on ignore
+ 2) reenter="yes"
+ rollback_config
+ continue 3
+ ;;
+ 3) tv doshutdown
+ exit 0
+ ;;
+ 4) echo $"Enter 'exit' at the shell prompt to get back to the installation dialog."
+ /bin/bash
+ if [ "$noredo" != "noredo" ] && [ -n "$question_prefix" ]; then
+ $question_prefix
+ echo
+ fi
+ ;; # stay in workflow item menu
+ esac
+ done
+}
+
+# input variables: PARMNAME, question_prefix, question_choices,
+# "options" ...
+# output variables: $question_prefix, $helptext
+# modifies: the variable named $PARMNAME, $OPTIND
+function ask() {
+ [ $# -lt 3 ] && echo "l.$LINENO: too few arguments (<3), please fix calling code." 1>&2
+ local PARMNAME=$1
+ shift
+ question_prefix=$1
+ shift
+ local question_choices=$1
+ shift
+ local exception
+ local syntax_check
+ unset helptext
+ local handle
+ local finish
+ local optname
+ OPTIND=1
+ while getopts ":e:s:h:c:f:" optname; do
+ case $optname in
+ e) exception=$OPTARG ;;
+ s) syntax_check=$OPTARG ;;
+ h) helptext=$OPTARG ;;
+ c) handle=$OPTARG ;;
+ f) finish=$OPTARG ;;
+ "?") ;; # ignore invalid option
+ :) echo "l.$LINENO: Missing parameter to option -$OPTARG" 1>&2 ;;
+ esac
+ done
+ while : ; do
+ unset input
+ local input
+ # actually ask question if one of the following is true:
+ # - $PARMNAME parameter has not been set yet, e.g. not in parm file
+ # - on 2nd and further attempts, i.e. redoing the parameter
+ # - on having restarted the whole dialog
+ # describing the same from another viewpoint:
+ # - if $PARMNAME has been set, try to check syntax and apply
+ # - on redo, $PARMNAME has been set and reenter is false,
+ # but still ask question again
+ # - on reenter, $PARMNAME might have been set, but still ask question
+ if [ -z "${!PARMNAME}" -o -n "$redoitem" -o -n "$reenter" ]; then
+ # one empty line to separate parameter questions from each other
+ echo
+ $question_prefix
+ if reenter; then
+ echo
+ else
+ $question_choices
+ fi
+ # on reenter, give choice between old value and entering new one
+ reenter_menu ${!PARMNAME} && read input \
+ && [ "$input" != "?" ] && eval ${PARMNAME}=\$input
+ # escaping the $ in the RHS of the eval statement makes it safe
+ fi
+ if [ -n "$helptext" ] && [ "$input" = "?" ]; then
+ $helptext
+ continue
+ fi
+ # optional: default or exceptional handling
+ [ -n "$exception" ] && $exception
+ if [ -n "$syntax_check" -a -z "$handle" ]; then
+ # some parameters have only syntax check (and deferred config):
+ if $syntax_check; then
+ break
+ else
+ workflow_item_menu && break
+ fi
+ elif [ -n "$syntax_check" -a -n "$handle" ]; then
+ # most common parameters have syntax and configuration:
+ # user might still continue on syntax error
+ $syntax_check || workflow_item_menu
+ # optional: actual configuration
+ if $handle; then
+ # parmname has been configured successfully
+ break
+ else
+ # user might still continue on configuration failure
+ workflow_item_menu && break
+ fi
+ elif [ -n "$finish" ]; then
+ # few parameters need special handling done by their own function:
+ $finish
+ else
+ echo $"Unsupported calling of ask function, please fix calling code"
+ fi
+ done # PARMNAME
+ # disable potential temporary redoing-mode during reenter-mode
+ unset redoitem
+}
+
+### NETTYPE
+
+function syntax_check_nettype() {
+ # - NETTYPE \in {qeth,lcs,ctc}
+ [[ "$NETTYPE" =~ (^qeth$)|(^lcs$)|(^ctc$) ]]
+ case $? in
+ 0)
+ # string matched the pattern
+ return 0
+ ;;
+ 1)
+ # string did not match the pattern
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ echo $"Incorrect format or value for network type (NETTYPE): $NETTYPE"
+ return 1
+}
+
+function question_prefix_nettype() {
+ echo -n $"Network type"
+}
+
+function question_choices_nettype() {
+ echo $" (qeth, lcs, ctc, ? for help). Default is qeth:"
+}
+
+function helptext_nettype() {
+ echo $" Help text for network type:"
+ echo $" qeth: OSA-Express Fast Ethernet, Gigabit Ethernet (including 1000Base-T),"
+ echo $" High Speed Token Ring, Hipersockets, and ATM (running Ethernet LAN emulation)"
+ echo $" features in QDIO mode."
+ echo $" [default]"
+ echo $" lcs: OSA-2 Ethernet/Token Ring, OSA-Express Fast Ethernet in non-QDIO mode,"
+ echo $" OSA-Express High Speed Token Ring in non-QDIO mode and Gigabit Ethernet"
+ echo $" in non-QDIO mode."
+ echo $" ctc: Deprecated, useful for migration."
+}
+
+function exception_nettype() {
+ # - default is qeth since it should be common
+ if [ -z "$NETTYPE" ]; then
+ NETTYPE=qeth
+ break
+ fi
+}
+
+function finish_nettype() {
+ if syntax_check_nettype; then
+ break
+ else
+ # necessary parts which would otherwise be done by workflow_item_menu
+ interaction_happened="yes"
+ redoitem="yes"
+ fi
+}
+
+function do_nettype() {
+ ask NETTYPE \
+ question_prefix_nettype question_choices_nettype \
+ -h helptext_nettype -e exception_nettype -f finish_nettype
+}
+
+### CHANDEV
+
+function do_chandev() {
+ echo
+ echo $"The CHANDEV variable isn't used anymore, please update your "
+ echo $".parm or the .conf file to use NETTYPE, SUBCHANNELS, etc. instead."
+ echo
+}
+
+### SUBCHANNELS
+
+function syntax_check_subchannels() {
+ SUBCHANNELS=$(echo $SUBCHANNELS | tr ABCDEF abcdef)
+ # - make subchannel question dependent on NETTYPE (2 vs. 3 subchannels)
+ if [ "$NETTYPE" = "qeth" ]; then
+ # - match against regex, depending on qeth
+ [[ "$SUBCHANNELS" =~ ^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4},[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4},[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$ ]]
+ else
+ # - match against regex, depending on lcs/ctc
+ [[ "$SUBCHANNELS" =~ ^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4},[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$ ]]
+ fi
+ case $? in
+ 0)
+ # string matched the pattern
+ return 0
+ ;;
+ 1)
+ # string did not match the pattern
+ echo $"Incorrect format for channels (SUBCHANNELS): $SUBCHANNELS"
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ return 1
+}
+
+function semantic_check_subchannels() {
+ local subch_count
+ if [ "$NETTYPE" = "qeth" ]; then
+ subch_count=3
+ else
+ subch_count=2
+ fi
+ # done: make subchannel handling more robust by not relying on REMATCH
+ local -a subch_array
+ IFS=,
+ read -a subch_array <<< "indexzero,$SUBCHANNELS"
+ unset IFS
+ local i
+ local all_subch_good=0
+ for ((i=1; i <= $subch_count; i++)); do
+ local devbusid=${subch_array[$i]}
+ # remember first subchannel for potential undo of ccwgroup
+ # (via /sys/devices/qeth/$SCH_R_DEVBUSID/ungroup)
+ [ "$i" -eq 1 ] && SCH_R_DEVBUSID=$devbusid
+ local prefix ssid devno foo
+ IFS=.
+ read prefix ssid devno foo <<< "$devbusid"
+ unset IFS
+ local dev_p=$(echo /sys/devices/css$prefix/$IDFORMAT/$devbusid)
+ # - check for existence of devnos in sysfs
+ if [ ! -d "$dev_p" -a "$cio_wc_bytes" != "0" ]; then
+ # - try to free from /proc/cio_ignore if they don't exist
+ echo $"Device $devbusid not present, trying to clear from blacklist and resense..."
+ if sysecho /proc/cio_ignore "free $devbusid"; then
+ # /proc/cio_ignore won't block on freeing devices
+ # until resensing has been completed, so wait until
+ # the udev event queue depletes (without udevadm settle we
+ # could wait 2 seconds unconditionally)
+ #debug ls -laF /dev/.udev
+ udevadm settle
+ # even though the device might now be online, some of its
+ # sysfs attributes (e.g. cutype) might not yet be available
+ sleep 1
+ else
+ echo $"Device $devbusid could not be cleared from device blacklist"
+ fi
+ fi
+ # reevaluate since globbing might not have worked before device existed
+ dev_p=$(echo /sys/devices/css$prefix/$IDFORMAT/$devbusid)
+ if [ ! -d "$dev_p" ]; then
+ echo $"Device $devbusid does not exist"
+ all_subch_good=1
+ continue
+ fi
+ # devno does exist now
+ local subch_p=${dev_p%/*}
+ local subch=${subch_p##*/}
+ # filter definitely unusable subchannels ...
+ # - check for subchannel type I/O
+ if [ -f $subch_p/type ]; then
+ local type
+ read type < $subch_p/type
+ if [ "$type" != "$SUBCHANNEL_TYPE_IO" ]; then
+ echo $"Channel $subch (device $devbusid) is not of type I/O"
+ all_subch_good=1
+ continue
+ fi
+ fi
+ # - check for correct CU type/model, depending on qeth/lcs/ctc
+ if [ ! -f $dev_p/cutype ]; then
+ echo $"Device $devbusid does not have required sysfs attribute 'cutype'"
+ all_subch_good=1
+ continue
+ fi
+ local cutype
+ read cutype < $dev_p/cutype
+ if search_cu $cutype; then
+ local driver
+ if [ "$NETTYPE" = "ctc" ]; then
+ driver="ctcm"
+ else
+ driver=$NETTYPE
+ fi
+ if [ "${CU_DEVDRV[$cu_idx]}" != "$driver" ]; then
+ echo $"Device $devbusid has control unit type $cutype,"
+ echo $" which does not match your selected network type $NETTYPE"
+ all_subch_good=1
+ continue
+ fi
+ else
+ echo $"Device $devbusid has control unit type $cutype which is unknown"
+ all_subch_good=1
+ continue
+ fi
+ # read CHPIDs information about subchannels
+ if [ ! -f $subch_p/chpids ]; then
+ echo $"Channel $subch (device $devbusid) does not have required sysfs attribute 'chpids'"
+ all_subch_good=1
+ continue
+ fi
+ local chpid_list
+ read chpid_list < $subch_p/chpids
+ local -a chpids
+ read -a chpids <<< "$chpid_list"
+ if [ ${#chpids[@]} -ne 8 ]; then
+ echo $"sysfs reported ${#chpids[@]} CHPIDs instead of expected 8, code needs fix"
+ fi
+ if [ ! -f $subch_p/pimpampom ]; then
+ echo $"Channel $subch (device $devbusid) does not have required sysfs attribute 'pimpampom'"
+ all_subch_good=1
+ continue
+ fi
+ local pim pam pom foo
+ read pim pam pom foo < $subch_p/pimpampom
+ local pimchpidZ=""
+ for ((chp=0; chp < 8; chp++)); do
+ local mask=$((0x80 >> chp))
+ if (( 0x$pim & $mask )); then
+ pimchpidZ=${pimchpidZ}${chpids[chp]}
+ else
+ pimchpidZ=${pimchpidZ}"ZZ"
+ fi
+ done
+ local pimchpids=${pimchpidZ//ZZ/}
+ if [ "x$pimchpids" == "x" ]; then
+ echo $"Channel $subch (device $devbusid) does not have any installed channel path"
+ all_subch_good=1
+ continue
+ fi
+ # compare parts of different subchannels for required matches
+ if [ "$i" -eq 1 ]; then
+ # remember parts of first subchannel for comparison
+ local sch_r_prefix=$prefix
+ local sch_r_ssid=$ssid
+ local sch_r_devno=$devno
+ local sch_r_pimchipidZ=$pimchpidZ
+ local sch_r_cutype=$cutype
+ else
+ local comparison=0
+ # $sch_r_... might be empty if first channel was wrong
+ # => be sure to quote all variable accesses in test statements.
+ # - all subchannels must be of same CU type/model
+ if [ "$cutype" != "$sch_r_cutype" ]; then
+ echo $"Device $devbusid does not have the same control unit type as device $SCH_R_DEVBUSID"
+ comparison=1
+ fi
+ # - all subchannels must have same CHPIDs
+ if [ "$pimchpidZ" != "$sch_r_pimchipidZ" ]; then
+ echo $"Device $devbusid does not have the same CHPIDs as device $SCH_R_DEVBUSID"
+ comparison=1
+ fi
+ # - all subchannels should have same prefix & ssid ?
+ if [ "$prefix" != "$sch_r_prefix" \
+ -o "$ssid" != "$sch_r_ssid" ]; then
+ echo $"Device $devbusid does not have the same prefix and subchannel set ID as device $SCH_R_DEVBUSID"
+ comparison=1
+ fi
+ if [ "$i" -eq 2 ]; then
+ local sch_w_devbusid=$devbusid
+ local sch_w_devno=$devno
+ # TODO: not true for CTCM => relax
+ # - write_devbusid == read_devbusid+1
+ if [ $((0x$devno)) -ne $((0x$sch_r_devno + 1)) ]; then
+ echo $"Device bus ID of write channel (dev $devbusid) must be one larger than"
+ echo $" that of read channel (dev $SCH_R_DEVBUSID)"
+ comparison=1
+ fi
+ elif [ "$i" -eq 3 ]; then
+ # check data subchannel unequal to read/write subchannel
+ # (also seems to be handled by ccwgroup kernel subsystem)
+ if [ "$devbusid" = "$sch_w_devbusid" \
+ -o "$devbusid" = "$SCH_R_DEVBUSID" ]; then
+ echo $"Device bus ID of data channel (dev $devbusid) must be different to that of"
+ echo $" read channel ($SCH_R_DEVBUSID) and write channel ($sch_w_devbusid)"
+ comparison=1
+ fi
+ fi
+ if [ "$comparison" != 0 ]; then
+ all_subch_good=1
+ continue
+ fi
+ fi
+ # filter potentially good subchannels ...
+ if [ -h $dev_p/group_device ]; then
+ echo $"Device $devbusid is already in a ccwgroup and thus unavailable"
+ all_subch_good=1
+ continue
+ fi
+ if [ ! -f $dev_p/online ]; then
+ echo $"Device $devbusid does not have required sysfs attribute 'online'"
+ all_subch_good=1
+ continue
+ fi
+ local online
+ read online < $dev_p/online
+ if [ "$online" = "1" ]; then
+ echo $"Device $devbusid is already in use and thus unavailable"
+ all_subch_good=1
+ continue
+ fi
+ # - check availability
+ if [ ! -f $dev_p/availability ]; then
+ echo $"Device $devbusid does not have required sysfs attribute 'availability'"
+ all_subch_good=1
+ continue
+ fi
+ local availability
+ read availability < $dev_p/availability
+ if [ "$availability" != "good" ]; then
+ echo $"Device $devbusid is not available but '$availiability'"
+ all_subch_good=1
+ continue
+ fi
+
+ done # for ((i=1; i <= $subch_count; i++))
+ if [ "$all_subch_good" = "0" ]; then
+ return 0
+ fi
+ return 1
+}
+
+function handle_subchannels() {
+ # - try to establish ccwgroup right here and fail out on error
+ local driver
+ if [ "$NETTYPE" = "ctc" ]; then
+ driver="ctcm"
+ else
+ driver=$NETTYPE
+ fi
+ if sysecho /sys/bus/ccwgroup/drivers/${driver}/group "$SUBCHANNELS"; then
+ udevadm settle
+ case "$NETTYPE" in
+ qeth)
+ # Just preliminary card_type info until device goes online!
+ # In fact it seems enough to separate OSA from HiperSockets.
+ if [ -f /sys/devices/qeth/$SCH_R_DEVBUSID/card_type ]; then
+ read cardtype < /sys/devices/qeth/$SCH_R_DEVBUSID/card_type
+ else
+ echo $"Could not read qeth network card type from sysfs."
+ fi
+ ;;
+ ctc|lcs)
+ if [ -f /sys/devices/$driver/$SCH_R_DEVBUSID/type ]; then
+ local type
+ read type < /sys/devices/$driver/$SCH_R_DEVBUSID/type
+ [ "$type" = "CTC/A" ] && \
+ type="channel-to-channel adapter (CTC/A)"
+ echo $"Detected: $type"
+ else
+ echo $"Could not read ctc network card type from sysfs."
+ fi
+ ;;
+ esac
+ return 0
+ else
+ echo $"Channels $SUBCHANNELS could not be grouped"
+ fi
+ return 1
+}
+
+function question_prefix_subchannels() {
+ if [ "$NETTYPE" = "qeth" ]; then
+ echo -n $"Read,write,data channel"
+ else
+ echo -n $"Read,write channel"
+ fi
+}
+
+function question_choices_subchannels() {
+ if [ "$NETTYPE" = "qeth" ]; then
+ echo $" (e.g. 0.0.0300,0.0.0301,0.0.0302 or ? for help)."
+ else
+ echo $" (e.g. 0.0.0600,0.0.0601 or ? for help)"
+ fi
+}
+
+function helptext_subchannels() {
+ if [ "$NETTYPE" = "qeth" ]; then
+ echo $" Help text for qeth channels:"
+ echo $" Enter the device bus ID of your CCW devices."
+ echo $" QETH needs three channels for read, write, and data,"
+ echo $" e.g. 0.0.0300,0.0.0301,0.0.0302"
+ else
+ echo $" Help text for lcs/ctc channels:"
+ echo $" Enter the device bus ID of your CCW devices."
+ echo $" CTC/ESCON and LCS need two channels for read and write,"
+ echo $" e.g. 0.0.0600,0.0.0601 will configure the CTC or ESCON interface"
+ echo $" with the channels 0x600 and 0x601"
+ fi
+}
+
+function finish_subchannels() {
+ syntax_check_subchannels || workflow_item_menu
+ # continuing on syntax error is doomed to fail,
+ # since handle_subchannels relies on the regex-based strict parsing
+ # in syntax_check_subchannels which does not match anything then
+ # news: relaxed by splitting semantic check and actual handling
+ semantic_check_subchannels || workflow_item_menu
+ if handle_subchannels; then
+ break
+ else
+ workflow_item_menu && break
+ fi
+}
+
+function do_subchannels() {
+ ask SUBCHANNELS \
+ question_prefix_subchannels question_choices_subchannels \
+ -h helptext_subchannels -f finish_subchannels
+}
+
+### PORTNAME (qeth)
+
+function syntax_check_portname() {
+ # - 1-8 characters, we convert it to upper case
+ PORTNAME=$(echo $PORTNAME | tr '[:lower:]' '[:upper:]')
+ local portname_len=${#PORTNAME}
+ if [ "$portname_len" -ge 1 -a "$portname_len" -le 8 ]; then
+ return 0
+ fi
+ echo $"Incorrect string length [1..8] for portname (PORTNAME): $PORTNAME"
+ return 1
+}
+
+function handle_portname() {
+ [ -n "$PORTNAME" ] || return 0
+ # - try to set portname right here w/ error handling
+ if sysecho /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/portname "$PORTNAME"; then
+ return 0
+ else
+ echo $"Portname '$PORTNAME' could not be configured for $SUBCHANNELS"
+ fi
+ return 1
+}
+
+function hint_portname() {
+ if [ -f /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/portname ]; then
+ local pname_hint
+ read pname_hint < /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/portname
+ if [ "$pname_hint" = "no portname required" ]; then
+ echo $" * Your configuration does not require a portname. *"
+ fi
+ fi
+}
+
+function question_prefix_portname(){
+ echo -n $"Portname"
+}
+
+function question_choices_portname(){
+ echo $" (1..8 characters, or ? for help). Default is no portname:"
+}
+
+function helptext_portname(){
+ echo $" Help text for portname:"
+ # updated text describing when portname is obsolete;
+ # taken from:
+ # SA22-7935-09, Open Systems Adapter-Express Customer's
+ # Guide and Reference, 10th ed. May 2008, IBM, p.17f.
+ # SC33-8411-00, Device Drivers, Features, and Commands,
+ # 1st ed. May 2008, IBM, p.116.
+ echo $" Portname of the OSA-Express feature in QDIO mode and z/VM Guest LAN."
+ echo $" This parameter is optional with:"
+ echo $" - z/VM 4.4.0 or z/VM 4.3.0 with APARs VM63308 and PQ73878"
+ echo $" - z800, z900 with >= Driver 3G - EC stream J11204, MCL032 (OSA level 3.33)"
+ echo $" - z890, z990, z9, z10 mainframes"
+ hint_portname
+ echo $" If portname is used, all operating systems sharing port must use same name."
+ echo $" Input empty string if you don't want to enter a portname. [default]"
+}
+
+function exception_portname(){
+ [ -z "$PORTNAME" ] && break
+}
+
+function do_portname() {
+ ask PORTNAME \
+ question_prefix_portname question_choices_portname \
+ -h helptext_portname \
+ -e exception_portname -s syntax_check_portname -c handle_portname
+}
+
+### PORTNO (qeth)
+
+function syntax_check_qeth_portno() {
+ case $PORTNO in
+ 0|1)
+ return 0
+ ;;
+ esac
+ echo $"Incorrect format or value for relative port number (PORTNO): $PORTNO"
+ return 1
+}
+
+function handle_qeth_portno() {
+ if sysecho /sys/devices/qeth/$SCH_R_DEVBUSID/portno "$PORTNO"; then
+ return 0
+ fi
+ echo $"Could not configure relative port number $PORTNO for $SUBCHANNELS"
+ return 1
+}
+
+function question_prefix_portno() {
+ echo -n $"Relative port number for OSA"
+}
+
+function question_choices_portno() {
+ echo $" (0, 1, or ? for help). Default is 0:"
+}
+
+function helptext_portno() {
+ echo $" Help text for relative port number for OSA with 2 ports per CHPID:"
+ echo $" This applies to:"
+ echo $" - OSA-Express3 Gigabit Ethernet on z10 systems"
+ echo $" - OSA-Express ATM on zSeries 800 and 900 systems"
+ echo $" 0 for relative port number 0 [default]"
+ echo $" 1 for relative port number 1"
+ echo $" Input empty string to not modify the default configuration."
+}
+
+function exception_portno() {
+ # Writing portno of e.g. hipersockets device fails.
+ # Therefore, do not configure on empty default value.
+ [ -z "$PORTNO" ] && break
+}
+
+function do_portno() {
+ ask PORTNO \
+ question_prefix_portno question_choices_portno \
+ -h helptext_portno -e exception_portno \
+ -s syntax_check_qeth_portno -c handle_qeth_portno
+}
+
+### LAYER2
+
+function syntax_check_layer2() {
+ # - $LAYER2 \in {0,1}
+ case $LAYER2 in
+ 0|1)
+ return 0
+ ;;
+ esac
+ echo $"Incorrect format or value for layer2 mode (LAYER2): $LAYER2"
+ return 1
+}
+
+function handle_layer2() {
+ [ "$NETTYPE" == "qeth" ] || return 0
+ [ -n "$LAYER2" ] || return 0
+ # - try to set layer2 mode right here w/ error handling
+ if sysecho /sys/devices/${NETTYPE}/$SCH_R_DEVBUSID/layer2 "$LAYER2"; then
+ return 0
+ else
+ echo $"Layer2 mode '$LAYER2' could not be configured for $SUBCHANNELS"
+ fi
+ return 1
+}
+
+function question_prefix_layer2() {
+ echo -n $"Layer mode"
+}
+
+function question_choices_layer2() {
+ echo -n $" (0 for layer3, 1 for layer2, or ? for help)."
+ if [ "$isLayer2Default" = "yes" ]; then
+ echo $" Default is 1:"
+ else
+ echo $" Default is 0:"
+ fi
+}
+
+function helptext_layer2() {
+ echo $" Help text for OSA mode of operation: layer 2 vs. layer 3"
+ if [ "$isLayer2Default" = "yes" ]; then
+ echo $" 0 for layer 3 mode (may not work with dhcp, tcpdump, etc.)"
+ echo $" 1 for layer 2 mode [default]"
+ else
+ echo $" 0 for layer 3 mode [default] (may not work with dhcp, tcpdump, etc.)"
+ echo $" 1 for layer 2 mode"
+ fi
+}
+
+function exception_layer2() {
+ if [ -z "$LAYER2" ]; then
+ isLayer2Default && LAYER2=1 || LAYER2=0
+ # do not break, always apply, default may differ from online layer mode
+ #break
+ fi
+}
+
+function do_layer2() {
+ isLayer2Default && isLayer2Default=yes || isLayer2Default=no
+ ask LAYER2 \
+ question_prefix_layer2 question_choices_layer2 \
+ -h helptext_layer2 -e exception_layer2 \
+ -s syntax_check_layer2 -c handle_layer2
+}
+
+### MACADDR
+
+function syntax_check_macaddr() {
+ # - match against regex
+ [[ "$MACADDR" =~ ^[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]$ ]]
+ case $? in
+ 0)
+ # string matched the pattern
+ return 0
+ ;;
+ 1)
+ # string did not match the pattern
+ echo $"Incorrect format for mac address (MACADDR): $MACADDR"
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ return 1
+}
+
+function handle_macaddr() {
+ # - try to set macaddr right here w/ error handlg.
+ # device needs to be online
+ if debug ifconfig $DEVICE hw ether $MACADDR; then
+ return 0
+ fi
+ echo $"MAC address $MACADDR could not be configured for"
+ echo $" $SUBCHANNELS (network device $DEVICE)"
+ return 1
+}
+
+function question_prefix_macaddr() {
+ echo -n $"Unique MAC address"
+}
+
+function question_choices_macaddr() {
+ macaddr_default=$(ifconfig $DEVICE | grep 'HWaddr' | sed 's/.*HWaddr \([[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]:[[:xdigit:]][[:xdigit:]]\).*/\1/')
+ echo $" (e.g. 02:00:00:00:00:00, ? for help). Default is $macaddr_default:"
+}
+
+function helptext_macaddr() {
+ echo $" Help text for MAC address:"
+ if [ -z "${cardtype//OSD_*/}" ]; then
+ echo $" For real OSA in layer 2 mode, a random MAC address is automatically assigned."
+ else
+ echo $" If connecting to a layer 2 VSWITCH, a MAC address is automatically assigned."
+ fi
+ echo $" You may accept the automatic MAC address with an empty input. [default]"
+ echo $" If the automatic address is not unique, please provide a MAC address."
+ [ -z "${cardtype//OSD_*/}" ] && \
+ echo $" For real OSA, the provided address must be different from that of the OSA."
+ echo $" You may override the automatic MAC address with non-empty input."
+ echo $" An example MAC address would be: 02:00:00:00:00:00"
+}
+
+function exception_macaddr() {
+ if [ -z "$MACADDR" ]; then
+ if [ -z "${cardtype//OSD_*/}" ]; then
+ # keep random default MAC address of real OSA,
+ # so the OSA comes up with the same MAC each time in the future
+ MACADDR=$macaddr_default
+ else
+ # virtual OSA in layer2 is GuestLAN or VSWITCH
+ VSWITCH=1
+ fi
+ break
+ fi
+}
+
+function do_macaddr() {
+ ask MACADDR \
+ question_prefix_macaddr question_choices_macaddr \
+ -h helptext_macaddr -e exception_macaddr \
+ -s syntax_check_macaddr -c handle_macaddr
+}
+
+### CTCPROT
+
+function syntax_check_ctcprot() {
+ case "x$CTCPROT" in
+ x|x0)
+ unset CTCPROT
+ return 0
+ ;;
+ x1|x3)
+ return 0
+ ;;
+ x2)
+ echo $"CTC tty's are not usable for this installation (CTCPROT)"
+ ;;
+ *)
+ echo $"Incorrect format or value for CTC protocol (CTCPROT): $CTCPROT"
+ ;;
+ esac
+ return 1
+}
+
+function handle_ctcprot() {
+ [ -n "$CTCPROT" ] || return 0
+ if sysecho /sys/devices/ctcm/${SCH_R_DEVBUSID}/protocol "$CTCPROT"; then
+ return 0
+ fi
+ echo $"Could not configure CTC protocol $CTCPROT for $SUBCHANNELS"
+ return 1
+}
+
+function question_prefix_ctcprot() {
+ echo -n $"CTC protocol"
+}
+
+function question_choices_ctcprot() {
+ echo $" (0, 1, 3, or ? for help). Default is 0:"
+}
+
+function helptext_ctcprot() {
+ echo $" Help text for CTC protocol:"
+ echo $" Protocol which 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"
+}
+
+function do_ctcprot() {
+ ask CTCPROT \
+ question_prefix_ctcprot question_choices_ctcprot \
+ -h helptext_ctcprot -s syntax_check_ctcprot -c handle_ctcprot
+}
+
+### PORTNAME (LCS portno)
+
+function syntax_check_lcs_portno() {
+ [[ "$PORTNAME" =~ ^[[:digit:]]+$ ]]
+ case $? in
+ 0)
+ # string matched the pattern
+ return 0
+ ;;
+ 1)
+ # string did not match the pattern
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ echo $"Incorrect format for LCS port number (PORTNAME): $PORTNAME"
+ return 1
+}
+
+function handle_lcs_portno() {
+ [ -n "$PORTNAME" ] || return 0
+ if sysecho /sys/devices/lcs/$SCH_R_DEVBUSID/portno "$PORTNAME"; then
+ return 0
+ fi
+ echo $"Could not configure relative port number $PORTNAME for $SUBCHANNELS"
+ return 1
+}
+
+function question_prefix_lcs_portno() {
+ echo -n $"Relative port number of your LCS device"
+}
+
+function question_choices_lcs_portno() {
+ echo $" (number or ? for help). Default is 0:"
+}
+
+function helptext_lcs_portno() {
+ echo $" Help text for relative port number of LCS device:"
+ echo $" Required for OSA-Express ATM cards only."
+}
+
+function exception_lcs_portno() {
+ [ -z "$PORTNAME" ] && break
+}
+
+function do_lcs_portno() {
+ # LCS portno and QETH portname share the parameter variable PORTNAME.
+ # For compatibility with existing parm files we keep this scheme.
+ ask PORTNAME \
+ question_prefix_lcs_portno question_choices_lcs_portno \
+ -e exception_lcs_portno \
+ -h helptext_lcs_portno -s syntax_check_lcs_portno -c handle_lcs_portno
+}
+
+### HOSTNAME
+
+function syntax_check_hostname() {
+ syntax_check_domainname "$HOSTNAME" "Incorrect format for hostname (HOSTNAME): $HOSTNAME"
+}
+
+function handle_hostname() {
+ if ! hostname $HOSTNAME; then
+ echo $"Could not configure hostname $HOSTNAME"
+ return 1
+ fi
+ return 0
+}
+
+function question_prefix_hostname() {
+ echo -n $"Hostname of your new Linux guest"
+}
+
+function question_choices_hostname() {
+ echo $" (FQDN e.g. s390.redhat.com or ? for help):"
+}
+
+function helptext_hostname() {
+ echo $" Help text for hostname:"
+ echo $" Enter the full qualified domain name of your host."
+}
+
+function do_hostname() {
+ ask HOSTNAME \
+ question_prefix_hostname question_choices_hostname \
+ -h helptext_hostname -s syntax_check_hostname -c handle_hostname
+}
+
+### IPADDR
+
+function syntax_check_ipaddr() {
+ unset ipv4
+ unset ipv6
+ if checkipv4 $IPADDR; then
+ ipv4="yes"
+ return 0
+ elif [ "$ipv6_capable" = "yes" ] && checkipv6 $IPADDR; then
+ ipv6="yes"
+ return 0
+ fi
+ echo $"Incorrect format for IP address (IPADDR): $IPADDR"
+ return 1
+}
+
+function question_prefix_ipaddr() {
+ echo -n $"IPv4 address"
+ [ "$ipv6_capable" = "yes" ] && echo -n $" / IPv6 addr."
+}
+
+function question_choices_ipaddr() {
+ echo -n $" (e.g. 10.0.0.2"
+ [ "$ipv6_capable" = "yes" ] && echo -n $" / 2001:0DB8::"
+ echo $" or ? for help)"
+}
+
+function helptext_ipaddr() {
+ echo $" Help text for IP address:"
+ echo $" Enter a valid IPv4 address of your new Linux guest (e.g. 10.0.0.2)"
+ if [ "$ipv6_capable" = "yes" ]; then
+ echo $" or alternatively a valid IPv6 address without CIDR prefix (e.g. 2001:0DB8::)"
+ echo $" IPv6 is supported on:"
+ echo $" - Ethernet interfaces of the OSA-Express adapter running in QDIO mode."
+ echo $" - HiperSockets interfaces"
+ echo $" - z/VM guest LAN interfaces running in QDIO mode."
+ echo $" IPv6 is not supported on HiperSockets guest LAN, OSA-Express Token Ring, ATM."
+ fi
+}
+
+function do_ipaddr() {
+ ipv6_capable && ipv6_capable=yes || ipv6_capable=no
+ ask IPADDR \
+ question_prefix_ipaddr question_choices_ipaddr \
+ -h helptext_ipaddr -s syntax_check_ipaddr
+ if [ "$ipv6" ]; then
+ # qeth_l3 would load ipv6 automatically but not qeth_l2
+ modprobe ipv6
+ tv disable_ipv6_autoconf
+ fi
+
+ # no handling/configuring of IPADDR yet, since more parameters needed
+}
+
+### NETMASK (IPv4)
+
+function syntax_check_netmask_v4() {
+ # also support CIDR prefix
+ if [[ "$NETMASK" =~ ^[[:digit:]]+$ ]]; then
+ if [ "$NETMASK" -ge 1 -a "$NETMASK" -le 32 ]; then
+ ipcalc_arg="$IPADDR/$NETMASK"
+ return 0
+ fi
+ echo $"Incorrect value for network prefix [1..32] (NETMASK): $NETMASK"
+ return 1
+ elif checkipv4 $NETMASK; then
+ ipcalc_arg="$IPADDR $NETMASK"
+ return 0
+ fi
+ echo $"Incorrect format or value for network mask (NETMASK): $NETMASK"
+ return 1
+}
+
+function question_prefix_netmask() {
+ echo -n $"IPv4 netmask or CIDR prefix"
+}
+
+function hint_netmask_v4() {
+ # default based on class a/b/c address
+ local a b c d
+ IFS=.
+ read a b c d <<< "$IPADDR"
+ unset IFS
+ local ip=$(( ( a << 24 ) + ( b << 16 ) + ( c << 8 ) + ( d ) ))
+ # <<EOF convince syntax highlighter that above shifts are no here documents
+ if [ $(( ip & 0x80000000 )) -eq $(( 0x00000000 )) ]; then
+ # class a
+ echo "255.0.0.0"
+ elif [ $(( ip & 0xC0000000 )) -eq $(( 0x80000000 )) ]; then
+ # class b
+ echo "255.255.0.0"
+ elif [ $(( ip & 0xE0000000 )) -eq $(( 0xC0000000 )) ]; then
+ # class c
+ echo "255.255.255.0"
+ else
+ # some other class that should not be used as host address
+ return 1
+ fi
+ return 0
+}
+
+function question_choices_netmask() {
+ echo -n $" (e.g. 255.255.255.0 or 1..32 or ? for help)"
+ local default=$(hint_netmask_v4)
+ if [ -n "$default" ]; then
+ echo $". Default is $default:"
+ else
+ echo $":"
+ echo $"The IP address you entered previously should probably not be used for a host."
+ fi
+}
+
+function helptext_netmask() {
+ echo $" Help text for IPv4 netmask or CIDR prefix:"
+ echo $" Enter a valid IPv4 netmask or CIDR prefix (e.g. 255.255.255.0 or 1..32)"
+ local default=$(hint_netmask_v4)
+ if [ -n "$default" ]; then
+ echo $" Default is $default"
+ else
+ echo $"The IP address you entered previously should probably not be used for a host."
+ fi
+}
+
+function exception_netmask() {
+ if [ -z "$NETMASK" ]; then
+ NETMASK=$(hint_netmask_v4)
+ fi
+}
+
+function do_netmask() {
+ ask NETMASK \
+ question_prefix_netmask question_choices_netmask \
+ -h helptext_netmask \
+ -s syntax_check_netmask_v4 -e exception_netmask
+ # no handling/configuring of NETMASK yet, since more parameters needed
+}
+
+### NETWORK
+
+function do_network() {
+ echo
+ echo $"The NETWORK parameter isn't used anymore and will be ignored."
+ echo $" It is sufficient to specify IPADDR and NETMASK."
+ echo
+}
+
+### BROADCAST
+
+function do_broadcast() {
+ echo
+ echo $"The BROADCAST parameter isn't used anymore and will be ignored."
+ echo $" It is sufficient to specify IPADDR and NETMASK."
+ echo
+}
+
+### NETMASK (IPv6)
+
+function syntax_check_prefix_v6() {
+ if [[ "$NETMASK" =~ ^[[:digit:]]+$ ]]; then
+ if [ "$NETMASK" -ge 1 -a "$NETMASK" -le 128 ]; then
+ return 0
+ fi
+ fi
+ echo $"Incorrect value for network prefix [1..128] (NETMASK): $NETMASK"
+ return 1
+}
+
+function question_prefix_netmask_v6() {
+ echo -n $"CIDR prefix for the IPv6 address"
+}
+
+function question_choices_netmask_v6() {
+ echo $" (1..128):"
+}
+
+function do_netmask_v6() {
+ ask NETMASK \
+ question_prefix_netmask_v6 question_choices_netmask_v6 \
+ -s syntax_check_prefix_v6
+ # no handling/configuring of NETMASK yet, since more parameters needed
+}
+
+### GATEWAY (IPv4)
+
+function configure_ipv4_gateway() {
+ # FIXME:
+ # - Strictly speaking we should first check reachability of gateway
+ # and then configure the gateway route.
+ # This would require a new intermediate workflow_item step
+ # so that the user might continue despite unreachable gateway.
+ # done: Only adding default route might add multiple undesired default
+ # routes on redoing the parameter item, so delete default route
+ # before adding a new one.
+ ip -4 route del default dev $DEVICE >& /dev/null
+ [ -z "$GATEWAY" ] && return 0
+ if ! tv route add default gw $GATEWAY dev $DEVICE; then
+ echo $"Could net set default route on device $DEVICE via gateway $GATEWAY"
+ return 1
+ fi
+ # BH FIXME: Workaround for manual MACADDR, need ping to update arp table
+ echo $"Trying to reach gateway $GATEWAY..."
+ if [ "$NETTYPE" = "ctc" ]; then
+ # (virtual) CTC(/A) seems to need some time to get functional
+ local i=1
+ while : ; do
+ ping -c 1 -w 5 $GATEWAY >& /dev/null && break
+ i=$((i+1))
+ if [ "$i" -gt 3 ]; then
+ echo $"Could not reach gateway $GATEWAY within timeout"
+ return 1
+ fi
+ done
+ else
+ if ! ping -c 1 -w 5 $GATEWAY >& /dev/null; then
+ echo $"Could not reach your default gateway $GATEWAY"
+ return 1
+ fi
+ fi
+ return 0
+}
+
+function hint_ipv4_gateway() {
+ # - provide default suggestion based on network,
+ # for a class C network this would be either .1 or .254 at the end
+ local a b c d
+ IFS=.
+ read a b c d <<< "$NETWORK"
+ unset IFS
+ local ip=$(( ( a << 24 ) + ( b << 16 ) + ( c << 8 ) + ( d ) ))
+ # <<EOF convince syntax highlighter that above shifts are no here documents
+ local lo=$(( ip | 1 ))
+ local lo_a=$(( (lo & 0xFF000000) >> 24 ))
+ local lo_b=$(( (lo & 0x00FF0000) >> 16 ))
+ local lo_c=$(( (lo & 0x0000FF00) >> 8 ))
+ local lo_d=$(( (lo & 0x000000FF) ))
+ local hi=$(( ip | ( (2**(32 - PREFIX)) - 1 ) ))
+ local hi_a=$(( (hi & 0xFF000000) >> 24 ))
+ local hi_b=$(( (hi & 0x00FF0000) >> 16 ))
+ local hi_c=$(( (hi & 0x0000FF00) >> 8 ))
+ local hi_d=$(( (hi & 0x000000FE) ))
+ echo $" Depending on your network design patterns, the default gateway"
+ echo $" might be $lo_a.$lo_b.$lo_c.$lo_d or $hi_a.$hi_b.$hi_c.$hi_d"
+}
+
+function question_prefix_gateway() {
+ echo -n $"IPv4 address of your default gateway"
+}
+
+function question_choices_gateway() {
+ echo $" or ? for help:"
+}
+
+function helptext_gateway() {
+ echo $" Help text for IPv4 default gateway:"
+ echo $" For HiperSockets with internal traffic only you may want to leave this empty"
+ echo $" and choose continue afterwards to go on without gateway."
+ hint_ipv4_gateway
+}
+
+function finish_gateway() {
+ if ! checkipv4 $GATEWAY; then
+ # above checkipv4 is silent, so make up for syntax error
+ echo $"Incorrect format for IPv4 address of gateway (GATEWAY): $GATEWAY"
+ workflow_item_menu
+ fi
+ if configure_ipv4_gateway; then
+ break
+ else
+ workflow_item_menu && break
+ fi
+}
+
+# FIXME: allow empty/no gateway?
+
+function do_gateway() {
+ ask GATEWAY \
+ question_prefix_gateway question_choices_gateway \
+ -h helptext_gateway -f finish_gateway
+}
+
+### GATEWAY (IPv6)
+
+function configure_ipv6_gateway() {
+ # FIXME:
+ # - Strictly speaking we should first check reachability of gateway
+ # and then configure the gateway route.
+ # This would require a new intermediate workflow_item step
+ # so that the user might continue despite unreachable gateway.
+ # done: Only adding default route might add multiple undesired default
+ # routes on redoing the parameter item, so delete default route
+ # before adding a new one.
+ ip -6 route del default dev $DEVICE >& /dev/null
+ [ -z "$GATEWAY" ] && return 0
+ # IPv6 http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html_single/Linux+IPv6-HOWTO.html#AEN1147
+ # ip -6 route add ::/0 dev $DEVICE via $GATEWAY
+ # (Could also be learned by autoconfiguration on the link:
+ # after IP address setup and device up,
+ # see if default route has been learned
+ # ip -6 route show | grep ^default
+ # However, we currently use manual IPv6 configuration only.)
+ if ! debug ip -6 route add ::/0 dev $DEVICE via $GATEWAY; then
+ echo $"Could net set default route on device $DEVICE"
+ echo $" via gateway $GATEWAY"
+ return 1
+ fi
+ # BH FIXME: Workaround for manual MACADDR, need ping to update arp table
+ echo $"Trying to reach gateway $GATEWAY..."
+ if ! ping6 -c 1 $GATEWAY >& /dev/null; then
+ echo $"Could not reach your default gateway $GATEWAY"
+ return 1
+ fi
+ return 0
+}
+
+function question_prefix_gateway_v6() {
+ echo -n $"IPv6 address of your default gateway"
+}
+
+function question_choices_gateway_v6() {
+ echo $":"
+}
+
+function helptext_gateway_v6() {
+ echo $" Help text for IPv6 default gateway:"
+ echo $" For HiperSockets with internal traffic only you may want to leave this empty"
+ echo $" and choose continue afterwards to go on without gateway."
+}
+
+function finish_gateway_v6() {
+ if ! checkipv6 $GATEWAY; then
+ # above checkipv6 is silent, so make up for syntax error
+ echo $"Incorrect format for IPv6 address of gateway (GATEWAY): $GATEWAY"
+ workflow_item_menu
+ fi
+ if configure_ipv6_gateway; then
+ break
+ else
+ workflow_item_menu && break
+ fi
+}
+
+# FIXME: allow empty/no gateway?
+
+function do_gateway_v6() {
+ ask GATEWAY \
+ question_prefix_gateway_v6 question_choices_gateway_v6 \
+ -h helptext_gateway_v6 -f finish_gateway_v6
+}
+
+### GATEWAY (IPv4, point-to-point)
+
+function configure_ipv4_ptp() {
+ # device needs to be online
+ if debug ifconfig $DEVICE $IPADDR $MMTU pointopoint $GATEWAY; then
+ configure_ipv4_gateway
+ return $?
+ fi
+ echo $"Could not set IPv4 address $IPADDR for device $DEVICE"
+ echo $" to peer $GATEWAY"
+ [ -n "$MMTU" ] && echo $" and maximum transfer unit: $MMTU"
+ return 1
+}
+
+function question_prefix_ptp_gateway() {
+ echo -n $"IPv4 address of your point-to-point partner"
+}
+
+function question_choices_ptp_gateway() {
+ echo $" or ? for help:"
+ # no hinting possible here
+}
+
+function helptext_ptp_gateway() {
+ echo $" Help text for point-to-point partner:"
+ echo $" IPv4 address of your CTC or ESCON point-to-point partner."
+}
+
+function finish_ptp_gateway() {
+ if checkipv4 $GATEWAY; then
+ if [ "$GATEWAY" = "$IPADDR" ]; then
+ echo $"IPv4 address of partner should probably be different from the guest's address"
+ workflow_item_menu && break
+ else
+ break
+ fi
+ else
+ # above checkipv4 is silent, so make up for syntax error
+ echo $"Incorrect format for IPv4 address of partner (GATEWAY): $GATEWAY"
+ workflow_item_menu && break
+ fi
+ # too early to actually configure gateway
+}
+
+function do_ptp_gateway() {
+ ask GATEWAY \
+ question_prefix_ptp_gateway question_choices_ptp_gateway \
+ -h helptext_ptp_gateway -f finish_ptp_gateway
+}
+
+### DNS
+
+function syntax_check_dns() {
+ if [ -z "$DNS" ]; then
+ echo $"You might encounter problems without a nameserver, especially with FTP installs"
+ return 1
+ fi
+ local dnsitem
+ local allgood="yes"
+ if [ "$ipv6" ]; then
+ while read dnsitem; do
+ if ! checkipv6 $dnsitem; then
+ echo $"Not a valid IPv6 address for DNS server: $dnsitem"
+ allgood="no"
+ fi
+ done < <(echo $DNS | sed 's/,/\n/g')
+ else
+ while read dnsitem; do
+ if ! checkipv4 $dnsitem; then
+ echo $"Not a valid IPv4 address for DNS server: $dnsitem"
+ allgood="no"
+ fi
+ done < <(echo $DNS | sed 's/:/\n/g')
+ fi
+ if [ "$allgood" = "yes" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function handle_dns() {
+ # - foreach DNS try if server is reachable by one ping
+ [ -z "$DNS" ] && return 0
+ local dnsitem
+ local allgood="yes"
+ echo $"Trying to reach DNS servers..."
+ if [ "$ipv6" ]; then
+ while read dnsitem; do
+ if ! ping6 -c 1 $dnsitem >& /dev/null; then
+ echo $"Could not ping DNS server (might still serve DNS requests): $dnsitem"
+ allgood="no"
+ # this should not be a hard failure since some network
+ # environments may prevent pings to DNS servers
+ # => prevent workflow_item_menu in kickstart mode
+ fi
+ done < <(echo $DNS | sed 's/,/\n/g')
+ else
+ while read dnsitem; do
+ # Some network environment may prevent a DNS server from being
+ # reachable by ping, so it would make sense to use nslookup.
+ # However, nslookup fails with "Resolver Error 0 (no error)"
+ # at this stage of the setup progress => not useful
+ if ! ping -c 1 -w 5 $dnsitem >& /dev/null; then
+ echo $"Could not ping DNS server: $dnsitem"
+# if nslookup $dnsitem $dnsitem >& /dev/null; then
+# echo $" but could resolve DNS server with itself: $dnsitem"
+# else
+# echo $"Could not resolve DNS server with itself: $dnsitem"
+# allgood="no"
+# fi
+# elif ! nslookup $dnsitem $dnsitem >& /dev/null; then
+# echo $"Could not resolve DNS server with itself: $dnsitem"
+ allgood="no"
+ fi
+ done < <(echo $DNS | sed 's/:/\n/g')
+ fi
+ if [ "$allgood" = "yes" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function question_prefix_dns() {
+ if [ "$ipv6" ]; then
+ echo -n $"IPv6 addresses of DNS servers"
+ else
+ echo -n $"IPv4 addresses of DNS servers"
+ fi
+}
+
+function question_choices_dns() {
+ if [ "$ipv6" ]; then
+ echo $" (separated by commas ',' or ? for help):"
+ else
+ echo $" (separated by colons ':' or ? for help):"
+ fi
+}
+
+function helptext_dns() {
+ echo $" Help text for DNS servers:"
+ if [ "$ipv6" ]; then
+ echo $" Enter IPv6 addresses of DNS servers separated by commas ','"
+ else
+ echo $" Enter IPv4 addresses of DNS servers separated by colons ':'"
+ fi
+ echo $" Default are no DNS servers at all."
+ echo $" However, you might encounter problems without a nameserver,"
+ echo $" especially with FTP installs."
+ if [ "$ipv6" ]; then
+ echo $" An example with 2 servers would be: 2001:0DB8::42,2001:0DB8::BE:AF"
+ else
+ echo $" An example with 2 servers would be: 10.0.0.250:10.1.1.1"
+ fi
+}
+
+function do_dns() {
+ ask DNS \
+ question_prefix_dns question_choices_dns \
+ -h helptext_dns -s syntax_check_dns -c handle_dns
+}
+
+### SEARCHDNS
+
+function syntax_check_searchdns() {
+ [ -z "$SEARCHDNS" ] && return 0
+ local dnsitem
+ local allgood="yes"
+ while read dnsitem; do
+ syntax_check_domainname "$dnsitem" $"Not a valid DNS search domain: $dnsitem" || allgood="no"
+ done < <(echo $SEARCHDNS | sed 's/:/\n/g')
+ if [ "$allgood" = "yes" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function question_prefix_searchdns() {
+ echo -n $"DNS search domains"
+}
+
+function question_choices_searchdns() {
+ echo $" (separated by colons ':' or ? for help):"
+}
+
+function helptext_searchdns() {
+ echo $" Help text for DNS search domains:"
+ echo $" Enter search domains according to hostname syntax separated by colons."
+ echo $" Default are no DNS search domains at all."
+ echo $" An example would be: subdomain.domain.com:domain.com"
+}
+
+function do_searchdns() {
+ ask SEARCHDNS \
+ question_prefix_searchdns question_choices_searchdns \
+ -h helptext_searchdns -s syntax_check_searchdns
+}
+
+### DASD
+
+function parse_dasd() {
+ local handle
+ [ "$1" = "-h" ] && handle=yes || unset handle
+ local dasditem
+ local allgood="yes"
+ local cio_wc=$(wc -c /proc/cio_ignore)
+ read cio_wc_bytes cio_wc_filename cio_wc_foo <<< "$cio_wc"
+ if [ "$handle" = "yes" -a "$cio_wc_bytes" != "0" ]; then
+ echo $"Trying to clear specified DASDs from device blacklist..."
+ mkdir -p /etc/modprobe.d
+ echo "options dasd_mod dasd=$DASD" > /etc/modprobe.d/dasd_mod.conf
+ if ! dasd_cio_free; then
+ echo $"Not all specified DASDs could be detected within timeout."
+ allgood="no"
+ fi
+ fi
+ while read dasditem; do
+ unset range features range lo hi rangegood \
+ attrs devno lodevno hidevno devbusid sys
+ case $dasditem in
+ autodetect)
+ [ -z "$handle" ] && continue
+ cio_wc=$(wc -c /proc/cio_ignore)
+ read cio_wc_bytes cio_wc_filename cio_wc_foo <<< "$cio_wc"
+ # above we only freed the devices specified in $DASD,
+ # so there might still be other DASDs in the blacklist
+ if [ "$cio_wc_bytes" != "0" ]; then
+ echo $"Note: There is a device blacklist active! Only activating visible DASDs."
+ fi
+ local sys
+ while read sys; do
+ if ! sysecho $sys/online 1; then
+ echo $"Could not set DASD ${sys##*/} online"
+ fi
+ done < <(find /sys/bus/ccw/drivers/dasd-eckd/ -name "*.?.????" 2>/dev/null;\
+ find /sys/bus/ccw/drivers/dasd-fba/ -name "*.?.????" 2>/dev/null)
+ ;;
+ probeonly|nopav|nofcx)
+ if [ -z "$handle" ]; then
+ echo $"DASD option $dasditem not supported by installer"
+ fi
+ ;;
+ "") continue ;; # empty range
+ *) local range features rangegood="yes"
+ IFS='('
+ read range features <<< "$dasditem"
+ unset IFS
+ # parse: dev OR dev'-'dev
+ local lo=${range%%-*}
+ [[ "$lo" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]]
+ case $? in
+ 0) # string matched the pattern
+ lo=$(canonicalize_devno $lo) ;;
+ 1) # string did not match the pattern
+ rangegood="no"
+ if [ -z "$handle" ]; then
+ echo $"Incorrect format for lower bound of DASD range $range: $lo"
+ allgood="no"
+ fi
+ ;;
+ 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;;
+ *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;;
+ esac
+ if [ "${range//*-*/}" = "" ]; then
+ local hi=${range##*-}
+ [[ "$hi" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]]
+ case $? in
+ 0) # string matched the pattern
+ hi=$(canonicalize_devno $hi)
+ if [ "${lo%.*}" != "${hi%.*}" ]; then
+ echo $"Prefixes of DASD range $range do not match: ${lo%.*} != ${hi%.*}"
+ rangegood="no"
+ allgood="no"
+ fi
+ ;;
+ 1) # string did not match the pattern
+ rangegood="no"
+ if [ -z "$handle" ]; then
+ echo $"Incorrect format for upper bound of DASD range $range: $hi"
+ allgood="no"
+ fi
+ ;;
+ 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;;
+ *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;;
+ esac
+ fi
+ if [ "${features//*)/}" != "" ]; then
+ if [ -z "$handle" ]; then
+ echo $"Missing closing parenthesis at features of DASD range $range: ($features"
+ allgood="no"
+ fi
+ fi
+ local attrs=""
+ if [ -n "$features" ]; then
+ features="${features%)}"
+ while read feature; do
+ case $feature in
+ ro) attrs=$attrs" readonly" ;;
+ diag) attrs=$attrs" use_diag" ;;
+ erplog|failfast) attrs=$attrs" "$feature ;;
+ *) if [ -z "$handle" ]; then
+ echo $"Unknown DASD feature for device range $range: $feature"
+ allgood="no"
+ fi
+ ;;
+ esac
+ done < <(echo $features | sed 's/:/\n/g')
+ fi
+ [ "$rangegood" = "yes" ] || continue
+ [ "$handle" = "yes" ] || continue
+ # now apply $attrs and set DASDs $lo to $hi online
+ [ -z "$hi" ] && hi=$lo
+ local devno lodevno=$((0x${lo##*.})) hidevno=$((0x${hi##*.}))
+ for ((devno=$lodevno; $devno <= $hidevno; ++devno)); do
+ local devbusid=$(printf "%s.%04x" ${lo%.*} $devno)
+ local sys="/sys/bus/ccw/devices/"$devbusid
+ for attr in $attrs; do
+ if [ "$attr" = "use_diag" ]; then
+ # diag discipline cannot be auto-loaded
+ modprobe dasd_diag_mod
+ fi
+ if [ ! -f $sys/$attr ]; then
+ echo $"DASD $devbusid does not provide attribute $attr"
+ continue
+ fi
+ if ! sysecho $sys/$attr 1; then
+ echo $"Could not set attribute $attr for DASD $devbusid"
+ fi
+ done
+ if [ ! -f $sys/online ]; then
+ echo $"DASD $devbusid not found"
+ continue
+ fi
+ if ! sysecho $sys/online 1; then
+ echo $"Could not set DASD $devbusid online"
+ fi
+ done
+ ;;
+ esac
+ done < <(echo $DASD | sed 's/,/\n/g')
+ if [ "$handle" = "yes" ]; then
+ udevadm settle
+ dasd_settle_all || return 1
+ echo $"Activated DASDs:"
+ cat /proc/dasd/devices | sed -e 's/ at ([^)]*) is//' -e 's/ at/,/'
+ fi
+ if [ "$allgood" = "yes" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+function syntax_check_dasd() {
+ parse_dasd
+ return $?
+}
+
+function handle_dasd() {
+ parse_dasd -h
+ return $?
+}
+
+function question_prefix_dasd() {
+ echo -n $"DASD range"
+}
+
+function question_choices_dasd() {
+ echo $" (e.g. 200-203,205 or ? for help). Default is autoprobing:"
+}
+
+function helptext_dasd() {
+ echo $" Help text for DASD range:"
+ echo $" Comma separated list of ranges of device bus IDs."
+ echo $" Default is autoprobing (not recommended)."
+ echo $" Examples would be: 200-203 or 200,201,202,203 or 0.0.0200-0.0.0203,0.0.0205"
+}
+
+function exception_dasd() {
+ [ -z "$DASD" ] && DASD="autodetect"
+}
+
+function do_dasd() {
+ ask DASD \
+ question_prefix_dasd question_choices_dasd \
+ -h helptext_dasd -e exception_dasd -s syntax_check_dasd -c handle_dasd
+}
+
+### FCP
+
+function syntax_check_fcp() {
+ local allgood="yes"
+ local i
+ for i in ${!FCP_*}; do
+ local -a fcp
+ local devno wwpn lun
+ read -a fcp <<< "${!i}"
+ case ${#fcp[@]} in
+ 3)
+ devno=${fcp[0]}
+ wwpn=${fcp[1]}
+ lun=${fcp[2]}
+ ;;
+ 5)
+ devno=${fcp[0]}
+ wwpn=${fcp[2]}
+ lun=${fcp[4]}
+ echo $"Deprecated number of FCP arguments (5 instead of 3): "
+ echo $" $i=\"${!i}\""
+ echo $" should instead be: "
+ echo $" $i=\"$devno $wwpn $lun\""
+ ;;
+ *)
+ echo $"Unsupported number of FCP arguments (${#fcp[@]} instead of 3) in:"
+ echo $" $i=\"${!i}\""
+ allgood="no"
+ continue
+ ;;
+ esac
+ [[ "$devno" =~ (^[[:xdigit:]]+\.[0-3]\.[[:xdigit:]]{4}$)|(^[[:xdigit:]]{3,4}$) ]]
+ case $? in
+ 0) ;; # string matched the pattern
+ 1) # string did not match the pattern
+ echo $"Incorrect format for FCP device $devno in:"
+ echo $" $i=\"${!i}\""
+ allgood="no"
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ # zfcp.py:class ZFCPDevice would also accept WWPN without leading 0x
+ [[ "$wwpn" =~ ^0x[[:xdigit:]]{16}$ ]]
+ case $? in
+ 0) ;; # string matched the pattern
+ 1) # string did not match the pattern
+ echo $"Incorrect format for FCP WWPN $wwpn in:"
+ echo $" $i=\"${!i}\""
+ allgood="no"
+ ;;
+ 2) echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2 ;;
+ *) echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2 ;;
+ esac
+ # zfcp.py:class ZFCPDevice would also accept LUN without leading 0x
+ # zfcp.py:class ZFCPDevice would also accept 16 bit LUN and pads with 0
+ [[ "$lun" =~ ^0x[[:xdigit:]]{8}0{8}$ ]]
+ case $? in
+ 0) ;; # string matched the pattern
+ 1) # string did not match the pattern
+ echo $"Incorrect format for FCP LUN $lun in:"
+ echo $" $i=\"${!i}\""
+ allgood="no"
+ ;;
+ 2)
+ echo "l.$LINENO: syntax error in regex of match operator =~, code needs to be fixed" 1>&2
+ ;;
+ *)
+ echo "l.$LINENO: unexpected return code of regex match operator =~, code needs to be fixed" 1>&2
+ ;;
+ esac
+ done
+ if [ "$allgood" = "yes" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+###
+
+function show_parms() {
+ # The only issue with this stateless approach to showing parameters based
+ # on their content being non-empty is, that parameters with defaults
+ # such as LAYER2, (PORTNAME,) CTCPROT, PORTNO (,MACADDR) won't be shown
+ # if the user just hit enter, so the parm file would be "incomplete".
+ # However this is not easy to fix in here, since it would require the
+ # inter-parameter dependenies coded below in the main part, e.g. an
+ # empty LAYER2 should only be printed with default value if $NETTYPE=qeth.
+ # For the time being, at least the parameters LAYER2, PORTNAME, and CTCPROT
+ # only get asked on being empty if not running in kickstart mode.
+ cat << EOF
+NETTYPE=$NETTYPE
+IPADDR=$IPADDR
+NETMASK=$NETMASK
+GATEWAY=$GATEWAY
+HOSTNAME=$HOSTNAME
+EOF
+ [ "$SUBCHANNELS" ] && echo "SUBCHANNELS=$SUBCHANNELS"
+ [ "$LAYER2" ] && echo "LAYER2=$LAYER2"
+ [ "$VSWITCH" ] && echo "VSWITCH=$VSWITCH"
+ [ "$MACADDR" ] && echo "MACADDR=$MACADDR"
+ [ "$PORTNAME" ] && echo "PORTNAME=$PORTNAME"
+ [ "$PORTNO" ] && echo "PORTNO=$PORTNO"
+ [ "$PEERID" ] && echo "PEERID=$PEERID"
+ [ "$CTCPROT" ] && echo "CTCPROT=$CTCPROT"
+ if [ -n "$mmtu_was_set" ]; then
+ echo "MMTU=\"$MMTU\""
+ elif [ -n "$mtu_was_set" ]; then
+ echo "MTU=$MTU"
+ fi
+ [ "$DNS" ] && echo "DNS=$DNS"
+ [ "$SEARCHDNS" ] && echo "SEARCHDNS=$SEARCHDNS"
+ [ "$DASD" ] && echo "DASD=$DASD"
+}
+
+function final_check() {
+ # final check && break
+ if [ -z "$interaction_happened" ]; then
+ # if parm file was good enough just continue without interaction
+ break
+ return 0
+ fi
+ while : ; do
+ # optionally consider "continue" as default
+ # but then again the user may inadvertently continue
+ echo
+ echo $"c) continue, p) parm file/configuration, n) network state, r) restart, s) shell"
+ local answer
+ read answer
+ case $answer in
+ c) return 0 ;;
+ p) echo
+ show_parms ;;
+ n) # show interfaces and routing table
+ ifconfig -a
+ if [ "$ipv6" ]; then
+ #route -n -A inet6
+ # the following produces more compact output for 80 columns
+ ip -6 route show | grep -v "^unreachable "
+ else
+ route -n
+ fi
+ ;;
+ d) # show active DASDs with some useful details
+ echo $"Activated DASDs:"
+ cat /proc/dasd/devices|sed -e 's/ at ([^)]*) is//' -e 's/ at/,/'
+ ;;
+ r) break ;;
+ s) echo $"Enter 'exit' at the shell prompt to get back to the installation dialog."
+ /bin/bash
+ ;;
+ esac
+ done
+ return 1
+}
+
+### MAIN ###
+
+init_main
+udev_setup
+
+# Parse configuration
+if [ -n "$CMSDASD" -a -n "$CMSCONFFILE" ]; then
+ readcmsfile $CMSDASD $CMSCONFFILE
+ source /tmp/$CMSCONFFILE #2>/dev/null
+fi
+
+if [ -r /sys/firmware/ipl/ipl_type ]; then
+ #local ipl_type
+ read ipl_type < /sys/firmware/ipl/ipl_type
+ if [ "$ipl_type" = "fcp" ]; then
+ while : ; do
+ echo $"Your IPL device is set to FCP."
+ echo $"Would you like to perform a CD-ROM/DVD-ROM installation? (y/n)"
+ #local do_cd_install
+ read do_cd_install
+ case $do_cd_install in
+ y|Y|[Yy][Ee][Ss])
+ # precondition: zfcp driver incl. dependencies loaded
+ #local CD_DEVICE WWPN LUN
+ read CD_DEVICE < /sys/firmware/ipl/device
+ read WWPN < /sys/firmware/ipl/wwpn
+ read LUN < /sys/firmware/ipl/lun
+ if sysecho /proc/cio_ignore "free $CD_DEVICE"; then
+ udevadm settle
+ # even though device might now be online, some of its
+ # sysfs attributes might not yet be available
+ sleep 1
+ else
+ echo $"Device $CD_DEVICE could not be cleared from device blacklist"
+ fi
+ sysecho /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/online 1 \
+ || echo $"Could not set FCP device $CD_DEVICE online"
+ udevadm settle
+ # port (WWPN) appears automatically
+ sysecho /sys/bus/ccw/drivers/zfcp/$CD_DEVICE/$WWPN/unit_add $LUN \
+ || echo $"Could not add LUN $LUN at WWPN $WWPN on FCP device $CD_DEVICE"
+ udevadm settle
+ break
+ ;;
+ n|N|[Nn][Oo])
+ break
+ ;;
+ *)
+ echo
+ echo $"*** INVALID ANSWER: $do_cd_install"
+ echo
+ unset do_cd_install
+ ;;
+ esac
+ done
+ fi
+fi
+
+# Perform a network installation
+
+[ -n "$MTU" ] && mtu_was_set=$MTU
+[ -n "$MMTU" ] && mmtu_was_set=$MMTU
+[ -n "$VSWITCH" ] && vswitch_was_set=$VSWITCH
+
+[ -n "$CHANDEV" ] && do_chandev
+[ -n "$NETWORK" ] && do_network
+[ -n "$BROADCAST" ] && do_broadcast
+
+# [ -z "${cardtype//OSD_*/}" ] can be used to check for real OSA
+
+# Check for missing parameters, prompt for them if necessary
+while : ; do
+
+ # do not show list of possible network device configurations, if:
+ # - running unattended install with kickstart
+ # - relevant parameters have already been specified in parm file
+ # (a possible optimization would be matching those parms to table entry)
+ # - dialog has not been restarted
+ [ -n "$reenter" \
+ -o -z "$RUNKS" -a \( -z "$NETTYPE" -o -z "$SUBCHANNELS" \) ] && \
+ dialog_network_table
+ if isVM; then
+ echo $"* NOTE: To enter default or empty values press enter twice. *"
+ fi
+ do_nettype
+
+ # precondition: driver (qeth/lcs/ctcm) loaded incl. dependencies
+ do_subchannels
+ if [ "$NETTYPE" = "qeth" ]; then
+ [ -z "$reenter" -a -n "$RUNKS" -a -z "$PORTNAME" ] || \
+ [ -n "${cardtype//OSD_*/}" ] || do_portname
+ # See also https://bugzilla.redhat.com/show_bug.cgi?id=439461
+ #
+ # If running in kickstart mode (unattended), we assume no
+ # interaction and the user won't get asked for PORTNO.
+ # Otherwise the user will be asked for PORTNO.
+ # If the user specified PORTNO in parm/conf file, PORTNO gets
+ # respected (or the user will be asked if it was wrong).
+ if [ -f /sys/devices/qeth/$SCH_R_DEVBUSID/portno ]; then
+ # driver support exists since RHEL5.2
+ [ -z "$reenter" -a -n "$RUNKS" -a -z "$PORTNO" ] || \
+ [ -n "${cardtype//OSD_*/}" ] || do_portno
+ fi
+ do_layer2
+ # set device online to know the device name
+ # and to know if it's OSD/HiperSockets/GuestLAN BUT do not
+ # try to ifconfig the device up since that requires
+ # setting the mac address before (if applicable).
+ set_device_online || workflow_item_menu noredo
+ # MAC address handling is not part of
+ # https://bugzilla.redhat.com/show_bug.cgi?id=233376
+ # Instead the additions come from
+ # https://bugzilla.redhat.com/show_bug.cgi?id=248049
+ # The new parms VSWITCH and MACADDR are described in
+ # the RHEL 5.1 release notes.
+ if isLayer2; then
+ if [ -z "$VSWITCH" -o "$VSWITCH" == 0 ]; then
+ do_macaddr
+ fi
+ fi
+ elif [ "$NETTYPE" = "ctc" ]; then
+ [ -z "$reenter" -a -n "$RUNKS" -a -z "$CTCPROT" ] || do_ctcprot
+ set_device_online || workflow_item_menu noredo
+ elif [ "$NETTYPE" = "lcs" ]; then
+ [ -n "$RUNKS" -a -z "$PORTNAME" ] && PORTNAME=0
+ do_lcs_portno
+ set_device_online || workflow_item_menu noredo
+ fi
+
+ # device needs to be up before configuring with ifconfig/ip in
+ # configure_ipv6_address/configure_ipv4_address/configure_ipv4_address
+ set_device_up || workflow_item_menu noredo
+
+ [ "$HOSTNAME" = "(none)" ] && unset HOSTNAME
+ do_hostname
+
+ # Note: The workflow_item_menu does a rollback_config on restart
+ # dialog, i.e. hardware config has been reset and it is impossible to
+ # only restart halfway at IPADDR.
+ do_ipaddr
+ if [ "$ipv6" ]; then
+ # this branch is all IPv6 and at the same time also NETTYPE==qeth
+ do_netmask_v6
+ handle_mtu
+ configure_ipv6_address || workflow_item_menu noredo
+ do_gateway_v6
+ else
+ # Consider IPv4 as default, even for unknown IP versions
+ # due to invalid input for IPADDR ignored by the user previously
+ # (neither ipv6 nor ipv4 is set).
+ # Otherwise we would skip things like NETMASK or GATEWAY
+ # and jump forward to DNS which is probably not what we want.
+ if [ "$NETTYPE" = "qeth" ] || [ "$NETTYPE" = "lcs" ]; then
+ do_netmask
+ handle_mtu
+ configure_ipv4_address || workflow_item_menu noredo
+ do_gateway
+ else # ctc0
+ if [ -z "$NETMASK" ]; then
+ # If the user did not supply netmask, we add the right one.
+ # Netmask MUST be present,
+ # or pumpSetupInterface() blows routes.
+ NETMASK="255.255.255.255"
+ fi
+ # don't ask for MTU, but use it if set in the parm file
+ # don't overwrite MMTU if it has been set for CTC
+ [ "$NETTYPE" = "ctc" -a -z "$MTU" -a -z "$MMTU" ] && \
+ MMTU="mtu 1500"
+ do_ptp_gateway
+ configure_ipv4_ptp || workflow_item_menu noredo
+ fi
+ fi
+
+ modprobe_alias
+ do_dns
+ [ -n "$DNS" ] && do_searchdns
+
+ do_dasd
+
+ echo $"Initial configuration completed."
+ final_check && break
+ rollback_config
+ reenter="yes"
+
+done # outer dialog loop
+
+if [ -z "$testing" ]; then
+
+ # 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
+ if [ "$ipv6" ]; then
+ RESOLVDNS=$(echo $DNS |sed -e 's/,/ /g')
+ else
+ RESOLVDNS=$(echo $DNS |sed -e 's/:/ /g')
+ fi
+ for i in $RESOLVDNS; do echo "nameserver $i"; done >> /etc/resolv.conf
+ fi
+
+ # make sure we have an /etc/hosts file (originally 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 # testing
+
+# syntax check to give user early feedback on parameters provided in parm file
+# (he probably won't notice the logs written by anaconda later on)
+syntax_check_fcp
+# currently we ignore failed syntax checks since FCP parms are non-interactive
+for i in ${!FCP_*}; do
+ echo "${!i}" >> /tmp/fcpconfig
+done
+# cio_ignore handling for FCP should happen when the content of /tmp/fcpconfig
+# will actually be processed which is in anaconda's zfcp.py ZFCP::readConfig()
+
+# TODO/FIXME: also need to pass IPv6 decision to loader/anaconda
+# [ "$ipv6" ] && echo "IPV6=yes"
+
+# 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"
+SEARCHDNS="$SEARCHDNS"
+PEERID="$PEERID"
+SUBCHANNELS="$SUBCHANNELS"
+ONBOOT="yes"
+CTCPROT="$CTCPROT"
+EOF
+if [ "$ipv6" ]; then
+ DNS1=$(echo $DNS | cut -d ',' -f 1)
+ echo DNS=\"$DNS1\" >> /tmp/install.cfg
+ echo DNS1=\"$DNS1\" >> /tmp/install.cfg
+ echo DNS2=\"$(echo $DNS | cut -d ',' -f 2)\" >> /tmp/install.cfg
+else
+ DNS1=$(echo $DNS | cut -d ':' -f 1)
+ echo DNS=\"$DNS1\" >> /tmp/install.cfg
+ echo DNS1=\"$DNS1\" >> /tmp/install.cfg
+ echo DNS2=\"$(echo $DNS | cut -d ':' -f 2)\" >> /tmp/install.cfg
+fi
+cat >> /tmp/install.cfg << EOF
+export LANG PORTNAME S390ARCH TEXTDOMAIN TEXTDOMAINDIR
+export HOSTNAME DEVICE NETTYPE IPADDR GATEWAY MTU
+export NETWORK NETMASK BROADCAST DNS DNS1 DNS2 SEARCHDNS
+export PEERID ONBOOT SUBCHANNELS CTCPROT
+EOF
+# immediately read it in again to export these into the shell below
+. /tmp/install.cfg
+if [ -z "$testing" ]; then
+ cat /tmp/install.cfg >> /etc/profile
+fi # testing
+
+NETSCRIPTS="/etc/sysconfig/network-scripts"
+IFCFGFILE="$NETSCRIPTS/ifcfg-$DEVICE"
+if [ ! -d "$NETSCRIPTS" ]; then
+ mkdir -p $NETSCRIPTS
+fi
+
+# to please NetworkManager on startup in loader before loader reconfigures net
+cat > /etc/sysconfig/network << EOF
+HOSTNAME=$HOSTNAME
+EOF
+
+cat > $IFCFGFILE << EOF
+DEVICE=$DEVICE
+ONBOOT=yes
+BOOTPROTO=static
+GATEWAY=$GATEWAY
+BROADCAST=$BROADCAST
+MTU=$MTU
+SUBCHANNELS=$SUBCHANNELS
+EOF
+if [ "$ipv6" ]; then
+ cat >> $IFCFGFILE << EOF
+IPV6INIT=yes
+IPV6_AUTOCONF=no
+IPV6ADDR=$IPADDR/$NETMASK
+IPV6_DEFAULTGW=$GATEWAY
+EOF
+ # FIXME: /etc/sysconfig/network:IPV6_DEFAULTGW=$GATEWAY
+ # /etc/sysconfig/network:NETWORKING_IPV6=yes
+else
+ cat >> $IFCFGFILE << EOF
+IPADDR=$IPADDR
+NETMASK=$NETMASK
+EOF
+fi
+[ "$DNS1" != "" ] && echo "DNS1=$DNS1" >> $IFCFGFILE
+[ "$DNS2" != "" ] && echo "DNS2=$DNS2" >> $IFCFGFILE
+# colons in SEARCHDNS already replaced with spaces above for /etc/resolv.conf
+[ "$SEARCHDNS" != "" ] && echo "DOMAIN=\"$SEARCHDNS\"" >> $IFCFGFILE
+[ "$NETTYPE" != "" ] && echo "NETTYPE=$NETTYPE" >> $IFCFGFILE
+[ "$PEERID" != "" ] && echo "PEERID=$PEERID" >> $IFCFGFILE
+[ "$PORTNAME" != "" ] && echo "PORTNAME=$PORTNAME" >> $IFCFGFILE
+[ "$CTCPROT" != "" ] && echo "CTCPROT=$CTCPROT" >> $IFCFGFILE
+[ "$MACADDR" != "" ] && echo "MACADDR=$MACADDR" >> $IFCFGFILE
+optstr=""
+for option in LAYER2 PORTNO; do
+ [ -z "${!option}" ] && continue
+ [ -n "$optstr" ] && optstr=${optstr}" "
+ optstr=${optstr}$(echo ${option} | tr [[:upper:]] [[:lower:]])"="${!option}
+done
+# write single quotes since network.py removes double quotes but we need quotes
+echo "OPTIONS='$optstr'" >> $IFCFGFILE
+unset option
+unset optstr
+
+if [ -z "$testing" ]; then
+
+ # 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
+
+ doshutdown
+
+fi # testing
+
+# ;;; Local Variables: ***
+# ;;; mode: sh ***
+# ;;; end: ***
diff --git a/loader/loader.c b/loader/loader.c
new file mode 100644
index 0000000..b96923b
--- /dev/null
+++ b/loader/loader.c
@@ -0,0 +1,2406 @@
+/*
+ * 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 <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <dirent.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 <sys/utsname.h>
+
+#include <linux/fb.h>
+#include <linux/serial.h>
+#include <linux/vt.h>
+
+#include <glib.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 "lang.h"
+#include "fwloader.h"
+#include "kbd.h"
+#include "kickstart.h"
+#include "windows.h"
+
+/* module stuff */
+#include "modules.h"
+#include "moduleinfo.h"
+
+#include "driverdisk.h"
+
+/* hardware stuff */
+#include "hardware.h"
+
+/* install method stuff */
+#include "method.h"
+#include "cdinstall.h"
+#include "nfsinstall.h"
+#include "hdinstall.h"
+#include "urls.h"
+#include "urlinstall.h"
+
+#include "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/log.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 pid_t init_pid = 1;
+static int init_sig = SIGUSR1; /* default to shutdown=halt */
+static const char *LANG_DEFAULT = "en_US.UTF-8";
+
+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);
+
+static int expected_exit = 0;
+
+static void doExit(int) __attribute__ ((noreturn));
+static void doExit(int result)
+{
+ expected_exit = 1;
+ exit(result);
+}
+
+void doSuspend(void) {
+ newtFinished();
+ doExit(1);
+}
+
+void doShell(void) {
+ pid_t child;
+ int status;
+
+ newtSuspend();
+ child = fork();
+
+ if (child == 0) {
+ if (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();
+ iface_init_iface_t(&iface);
+
+ if (kickstartNetworkUp(loaderData, &iface)) {
+ logMessage(ERROR, "can't run gdbserver due to no network");
+ return;
+ }
+
+ checked_asprintf(&pid, "%d", loaderPid);
+
+ if (!(child = fork())) {
+ logMessage(INFO, "starting gdbserver: %s %s %s %s",
+ "/usr/bin/gdbserver", "--attach", loaderData->gdbServer,
+ pid);
+
+ fd = open("/dev/null", O_RDONLY);
+ close(STDIN_FILENO);
+ dup2(fd, STDIN_FILENO);
+ close(fd);
+ fd = open("/dev/null", O_WRONLY);
+ close(STDOUT_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ close(STDERR_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+
+ if (execl("/usr/bin/gdbserver", "/usr/bin/gdbserver", "--attach",
+ loaderData->gdbServer, pid, NULL) == -1)
+ logMessage(ERROR, "error running gdbserver: %m");
+
+ _exit(1);
+ }
+ }
+}
+
+void startNewt(void) {
+ if (!newtRunning) {
+ char *buf;
+ char *arch = getProductArch();
+ checked_asprintf(&buf, _("Welcome to %s for %s"), getProductName(), arch);
+
+ /*
+ * Because currently initrd.img only has got the default English locale
+ * support, pretend for newtInit() it is actually the used LANG so Newt
+ * knows how to compute character widths etc.
+ */
+ char *lang = getenv("LANG");
+ if (lang) {
+ lang = strdup(lang);
+ }
+ setenv("LANG", LANG_DEFAULT, 1);
+ newtInit();
+ unsetenv("LANG");
+ /* restore the original LANG value */
+ if (lang) {
+ setenv("LANG", lang, 1);
+ free(lang);
+ }
+
+ newtCls();
+ newtDrawRootText(0, 0, buf);
+ free(buf);
+
+ newtPushHelpLine(_(" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen "));
+
+ newtRunning = 1;
+ if (!access("/bin/sh", X_OK))
+ newtSetSuspendCallback((void *) doShell, NULL);
+ }
+}
+
+void stopNewt(void) {
+ if (newtRunning) newtFinished();
+ newtRunning = 0;
+}
+
+static gchar *productName = NULL;
+static gchar *productPath = NULL;
+static gchar *productArch = NULL;
+
+static void initProductInfo(void) {
+ gchar *contents = NULL;
+ gchar **lines = NULL, **stamp = NULL;
+ GError *fileErr = NULL;
+
+ if (!g_file_get_contents("/.buildstamp", &contents, NULL, &fileErr)) {
+ logMessage(ERROR, "error reading .buildstamp: %s", fileErr->message);
+ g_error_free(fileErr);
+ productName = g_strdup("anaconda");
+ productArch = g_strdup("unknown architecture");
+ productPath = g_strdup("anaconda");
+ return;
+ }
+
+ /* .buildstamp uses the first 3 lines in this format:
+ * STAMP.productArch
+ * productName
+ * productPath
+ */
+ lines = g_strsplit(contents, "\n", 0);
+ g_free(contents);
+
+ if ((lines != NULL) && (g_strv_length(lines) >= 3)) {
+ /* STAMP.productArch */
+ stamp = g_strsplit(lines[0], ".", 0);
+
+ if ((stamp != NULL) && (g_strv_length(stamp) == 2)) {
+ productArch = g_strdup(stamp[1]);
+ } else {
+ productArch = g_strdup("unknown architecture");
+ }
+
+ if (stamp) {
+ g_strfreev(stamp);
+ }
+
+ productName = g_strdup(lines[1]);
+ productPath = g_strdup(lines[2]);
+ } else {
+ productName = g_strdup("anaconda");
+ productArch = g_strdup("unknown architecture");
+ productPath = g_strdup("anaconda");
+ }
+
+ if (lines) {
+ g_strfreev(lines);
+ }
+
+ return;
+}
+
+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 */
+ setenv("LANG", LANG_DEFAULT, 1);
+ printf("\033%%G");
+ fflush(stdout);
+
+ isysLoadFont();
+ 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/share/anaconda/pyrc.py", R_OK|X_OK))
+ setenv("PYTHONSTARTUP", "/usr/share/anaconda/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);
+ devNames = NULL;
+ 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);
+ devNames = NULL;
+ return;
+ }
+
+ device = strdup(devNames[num]);
+ free(devNames);
+ devNames = NULL;
+ stage = UPD_PART;
+ }
+
+ case UPD_PART: {
+ char ** part_list = getPartitionsList(device);
+ int nump = 0, num = 0;
+
+ if (part != NULL) {
+ free(part);
+ part = NULL;
+ }
+
+ if ((nump = lenPartitionsList(part_list)) == 0) {
+ if (dir == -1) {
+ stage = UPD_DEVICE;
+ } else {
+ checked_asprintf(&part, "/dev/%s", device);
+ 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:
+ checked_asprintf(&buf, _("Insert your updates disk into %s and "
+ "press \"OK\" to continue."), part);
+
+ rc = newtWinChoice(_("Updates Disk"), _("OK"), _("Back"), buf);
+ free(buf);
+ buf = NULL;
+
+ 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", "auto", "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);
+}
+
+/* XXX: read information from /etc/sysconfig/network-scripts/ifcfg-$INTERFACE
+ * (written by linuxrc), the linuxrc mess should be firing up NM too
+ */
+static void readNetInfo(struct loaderData_s ** ld) {
+ int i;
+ struct loaderData_s * loaderData = *ld;
+ DIR *dp = NULL;
+ FILE *f = NULL;
+ struct dirent *ent = NULL;
+ char *cfgfile = NULL;
+ int bufsiz = 100;
+ char buf[bufsiz];
+ char *vname = NULL;
+ char *vparm = NULL;
+
+ /* when this function is called, we can assume only one network device
+ * config file has been written to /etc/sysconfig/network-scripts, so
+ * find it and read it
+ */
+ dp = opendir("/etc/sysconfig/network-scripts");
+ if (dp == NULL) {
+ return;
+ }
+
+ while ((ent = readdir(dp)) != NULL) {
+ if (!strncmp(ent->d_name, "ifcfg-", 6)) {
+ checked_asprintf(&cfgfile, "/etc/sysconfig/network-scripts/%s",
+ ent->d_name);
+
+ break;
+ }
+ }
+
+ if (dp != NULL) {
+ if (closedir(dp) == -1) {
+ logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+ }
+
+ if (cfgfile == NULL) {
+ logMessage(DEBUGLVL, "no ifcfg files found in /etc/sysconfig/network-scripts");
+ return;
+ }
+
+
+ if ((f = fopen(cfgfile, "r")) == NULL) {
+ logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__);
+ free(cfgfile);
+ return;
+ }
+
+ if ((vname = (char *) malloc(sizeof(char) * 15)) == NULL) {
+ logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+
+ if ((vparm = (char *) malloc(sizeof(char) * 85)) == NULL) {
+ logMessage(DEBUGLVL, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+
+ /* 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->portno = NULL;
+ loaderData->macaddr = NULL;
+#ifdef ENABLE_IPV6
+ loaderData->ipv6 = NULL;
+ loaderData->gateway6 = NULL;
+#endif
+
+ /*
+ * 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, "PORTNO", 6))
+ loaderData->portno = 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;
+ }
+
+ if (fclose(f) == -1) {
+ logMessage(ERROR, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ if (cfgfile != NULL) {
+ free(cfgfile);
+ }
+
+ return;
+}
+
+/* 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 = 1;
+
+ /* 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 = 1;
+ }
+
+ if (loaderData->ipinfo_set)
+ flags |= LOADER_FLAGS_IP_PARAM;
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * 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 (!strncasecmp(argv, "ipv6=dhcp", 9)) {
+ loaderData->ipv6 = strdup("dhcp");
+ } else if (!strncasecmp(argv, "ipv6=auto", 9)) {
+ loaderData->ipv6 = strdup("auto");
+ }
+
+ if (loaderData->ipv6 != NULL) {
+ loaderData->ipv6info_set = 1;
+ flags |= LOADER_FLAGS_IPV6_PARAM;
+ }
+
+ return;
+}
+#endif
+
+static long argToLong(char *arg, int offset) {
+ long retval;
+
+ errno = 0;
+
+ retval = strtol(arg+offset, NULL, 10);
+ if ((errno == ERANGE && (retval == LONG_MIN || retval == LONG_MAX)) ||
+ (errno != 0 && retval == 0)) {
+ logMessage(ERROR, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ return retval;
+}
+
+/* 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;
+ gint argc = 0;
+ gchar **argv = NULL;
+ GError *optErr = NULL;
+ int numExtraArgs = 0;
+ int i;
+ char *front;
+
+ /* 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 (!g_shell_parse_argv(cmdLine, &argc, &argv, &optErr)) {
+ g_error_free(optErr);
+ 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], "nokill"))
+ flags |= LOADER_FLAGS_NOKILL;
+ 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], "dlabel=on"))
+ flags |= LOADER_FLAGS_AUTOMODDISK;
+ else if (!strcasecmp(argv[i], "dlabel=off"))
+ flags &= ~LOADER_FLAGS_AUTOMODDISK;
+ 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;
+#ifdef ENABLE_IPV6
+ else if (!strcasecmp(argv[i], "noipv6"))
+ flags |= LOADER_FLAGS_NOIPV6;
+#endif
+ else if (!strcasecmp(argv[i], "kssendmac"))
+ flags |= LOADER_FLAGS_KICKSTART_SEND_MAC;
+ else if (!strcasecmp(argv[i], "kssendsn"))
+ flags |= LOADER_FLAGS_KICKSTART_SEND_SERIAL;
+ /* 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))
+ 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]);
+#ifdef ENABLE_IPV6
+ else if (!strncasecmp(argv[i], "ipv6=", 5))
+ parseCmdLineIpv6(loaderData, argv[i]);
+#endif
+ 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))
+ loaderData->mtu = argToLong(argv[i], 4);
+ else if (!strncasecmp(argv[i], "wepkey=", 7))
+ loaderData->wepkey = strdup(argv[i] + 7);
+ else if (!strncasecmp(argv[i], "linksleep=", 10))
+ num_link_checks = argToLong(argv[i], 10);
+ else if (!strncasecmp(argv[i], "nicdelay=", 9))
+ post_link_sleep = argToLong(argv[i], 9);
+ else if (!strncasecmp(argv[i], "dhcptimeout=", 12))
+ loaderData->dhcpTimeout = argToLong(argv[i], 12);
+ 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 (!strncasecmp(argv[i], "proxy=", 6))
+ splitProxyParam(argv[i]+6, &loaderData->proxyUser,
+ &loaderData->proxyPassword, &loaderData->proxy);
+ 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)) {
+ writeVNCPasswordFile("/tmp/vncpassword.dat", argv[i]+12);
+ }
+ else if (!strncasecmp(argv[i], "resolution=", 11) ||
+ !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 graphical mode from cmdline");
+ flags |= LOADER_FLAGS_GRAPHICAL;
+ }
+
+ /* the following things require networking to be configured
+ * by loader, so an active connection is ready once we get
+ * to anaconda
+ */
+ if (!strncasecmp(argv[i], "syslog", 6) ||
+ !strncasecmp(argv[i], "vnc", 3)) {
+ logMessage(INFO, "early networking required for %s",
+ argv[i]);
+ flags |= LOADER_FLAGS_EARLY_NETWORKING;
+ }
+ if (isKickstartFileRemote(loaderData->ksFile)) {
+ logMessage(INFO, "early networking required for remote kickstart configuration");
+ flags |= LOADER_FLAGS_EARLY_NETWORKING;
+ }
+
+ if (!strncasecmp(argv[i], "vesa", 4)) {
+ checked_asprintf(&extraArgs[numExtraArgs],
+ "--xdriver=vesa");
+
+ logMessage(WARNING, "\"vesa\" command line argument is deprecated. use \"xdriver=vesa\".");
+ } else {
+ checked_asprintf(&extraArgs[numExtraArgs],"--%s",
+ argv[i]);
+ }
+
+ 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;
+}
+
+/* make sure they have enough ram */
+static void checkForRam(void) {
+ if (totalMemory() < MIN_RAM) {
+ char *buf;
+
+ checked_asprintf(&buf, _("You do not have enough RAM to install %s "
+ "on this machine."), getProductName());
+
+ startNewt();
+ newtWinMessage(_("Error"), _("OK"), buf);
+ free(buf);
+ stopNewt();
+ doExit(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 = LOADER_NOOP, 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;
+
+ /* 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)) {
+ 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"),
+ _("Found local installation media"), 0);
+ sleep(3);
+ newtPopWindow();
+
+ skipLangKbd = 1;
+ flags |= LOADER_FLAGS_NOPASS;
+ } else if (!loaderData->stage2Data && 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;
+
+ checked_asprintf(&tmp, "%s/images/install.img",
+ loaderData->instRepo);
+
+ 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;
+ skipMethodDialog = 1;
+ } else if (loaderData->stage2Data) {
+ skipMethodDialog = 1;
+ }
+ } else {
+ /* Needed because they have already been set when parsing cmdline.
+ * (Leaks a little.)
+ */
+ loaderData->method = -1;
+ loaderData->stage2Data = NULL;
+ }
+
+ 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 (FL_RESCUE(flags) || !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 (FL_RESCUE(flags) || !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");
+ doExit(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 == 2) {
+ loaderData->method = -1;
+ }
+ }
+
+ 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 ((FL_EARLY_NETWORKING(flags) && haveDeviceOfType(DEVICE_NETWORK)) ||
+ (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;
+ loaderData->method = -1;
+ 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) &&
+ !FL_EARLY_NETWORKING(flags)) ||
+ (is_nm_connected())) {
+ 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;
+#ifdef ENABLE_IPV6
+ loaderData->ipv6info_set = 1;
+#endif
+ }
+
+ rc = chooseNetworkInterface(loaderData);
+ if ((rc == LOADER_BACK) || (rc == LOADER_ERROR) ||
+ ((dir == -1) && (rc == LOADER_NOOP))) {
+ /* don't skip method dialog iff we don't have url from ks or boot params */
+ if (!loaderData->stage2Data) {
+ loaderData->method = -1;
+ }
+ 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");
+ doExit(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;
+#ifdef ENABLE_IPV6
+ loaderData->ipv6info_set = 1;
+#endif
+ }
+
+ /* populate netDev based on any kickstart data */
+ setupIfaceStruct(&iface, loaderData);
+ rc = readNetConfig(devName, &iface, loaderData->netCls, loaderData->method);
+
+ /* 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) ||
+ ((dir == -1) && (rc == LOADER_NOOP))) {
+ needsNetwork = 1;
+ step = STEP_IFACE;
+ dir = -1;
+ break;
+ }
+ /* retry */
+ if (rc == LOADER_ERROR) {
+ needsNetwork = 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;
+#ifdef ENABLE_IPV6
+ loaderData->ipv6info_set = 0;
+#endif
+ loaderData->method = -1;
+ 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;
+
+ checked_asprintf(&newInstRepo, "%.*s",
+ (int) (strstr(url, "/images")-url), url);
+
+ 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;
+
+ checked_asprintf(&runtimedir, "/mnt/runtime%s", dirname);
+
+ if (!access(runtimedir, X_OK)) {
+ if (unlink(dirname) == -1) {
+ char * olddir;
+
+ checked_asprintf(&olddir, "%s_old", dirname);
+
+ 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",
+ "Loader exited unexpectedly! 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();
+ if (signum == 0) {
+ i = write(STDERR_FILENO, errmsgs[2], strlen(errmsgs[2]));
+ } else {
+ 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);
+}
+
+void loaderExitHandler(void)
+{
+ if (expected_exit)
+ return;
+
+ loaderSegvHandler(0);
+}
+
+static void setupBacktrace(void)
+{
+ void *array;
+
+ signal(SIGSEGV, loaderSegvHandler);
+ signal(SIGABRT, loaderSegvHandler);
+ atexit(loaderExitHandler);
+
+ /* Turns out, there's an initializer at the top of backtrace() that
+ * (on some arches) calls dlopen(). dlopen(), unsurprisingly, calls
+ * malloc(). So, call backtrace() early in signal handler setup so
+ * we can later safely call it from the signal handler itself. */
+ backtrace(&array, 1);
+}
+
+void loaderUsrXHandler(int signum) {
+ logMessage(INFO, "Remembering signal %d\n", signum);
+ init_sig = signum;
+}
+
+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();
+
+ /* set up signal handler */
+ setupBacktrace();
+
+ return 0;
+}
+
+static void add_to_path_env(const char *env, const char *val)
+{
+ char *oldenv, *newenv;
+
+ oldenv = getenv(env);
+ if (oldenv) {
+ checked_asprintf(&newenv, "%s:%s", val, oldenv);
+
+ oldenv = strdupa(newenv);
+ free(newenv);
+ newenv = oldenv;
+ } else {
+ newenv = strdupa(val);
+ }
+
+ setenv(env, newenv, 1);
+}
+
+static void loadScsiDhModules(void)
+{
+ struct utsname utsname;
+ char *modules = NULL;
+ char *tmp = NULL;
+ struct dirent *ent = NULL;
+
+ uname(&utsname);
+ checked_asprintf(&tmp,
+ "/lib/modules/%s/kernel/drivers/scsi/device_handler", utsname.release);
+
+ DIR *dir = opendir(tmp);
+ free(tmp);
+ if (!dir)
+ return;
+
+ int fd = dirfd(dir);
+ while ((ent = readdir(dir)) != NULL) {
+ struct stat sb;
+
+ if (fstatat(fd, ent->d_name, &sb, 0) < 0)
+ continue;
+
+ size_t len = strlen(ent->d_name) - 3;
+ if (strcmp(ent->d_name+len, ".ko"))
+ continue;
+
+ if (S_ISREG(sb.st_mode)) {
+ char modname[len+1];
+ strncpy(modname, ent->d_name, len);
+ modname[len] = '\0';
+
+ if (modules && modules[0]) {
+ checked_asprintf(&tmp, "%s:%s", modules, modname);
+ } else {
+ checked_asprintf(&tmp, "%s", modname);
+ }
+
+ free(modules);
+ modules = tmp;
+ }
+ }
+ closedir(dir);
+
+ mlLoadModuleSet(modules);
+ free(modules);
+}
+
+int main(int argc, char ** argv) {
+ int rc, ret, pid, status;
+
+ 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, *fmt;
+ GSList *dd, *dditer;
+
+ gchar *cmdLine = NULL, *ksFile = NULL, *virtpcon = NULL;
+ gboolean mediacheck = FALSE;
+ gchar **remaining = NULL;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry optionTable[] = {
+ { "cmdline", 0, 0, G_OPTION_ARG_STRING, &cmdLine, NULL, NULL },
+ { "ksfile", 0, 0, G_OPTION_ARG_STRING, &ksFile, NULL, NULL },
+ { "mediacheck", 0, 0, G_OPTION_ARG_NONE, &mediacheck, NULL, NULL },
+ { "virtpconsole", 0, 0, G_OPTION_ARG_STRING, &virtpcon, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining,
+ NULL, NULL },
+ { NULL },
+ };
+
+ /* get init PID if we have it */
+ if ((f = fopen("/var/run/init.pid", "r")) != NULL) {
+ char linebuf[256];
+
+ while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
+ errno = 0;
+ init_pid = strtol(linebuf, NULL, 10);
+ if (errno == EINVAL || errno == ERANGE) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ init_pid = 1;
+ }
+ }
+
+ fclose(f);
+ }
+
+ signal(SIGUSR1, loaderUsrXHandler);
+ signal(SIGUSR2, loaderUsrXHandler);
+
+ /* 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 */
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, optionTable, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ fprintf(stderr, "bad option: %s\n", optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ doExit(1);
+ }
+
+ g_option_context_free(optCon);
+
+ if (remaining) {
+ fprintf(stderr, "unexpected argument: %s\n", remaining[0]);
+ g_strfreev(remaining);
+ doExit(1);
+ }
+
+ g_strfreev(remaining);
+
+ if (!access("/var/run/loader.run", R_OK)) {
+ printf(_("loader has already been run. Starting shell.\n"));
+ execl("/bin/sh", "-/bin/sh", NULL);
+ doExit(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 (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;
+#endif
+
+ openLog();
+
+ /* XXX if RHEL, enable the AUTODD feature by default,
+ * but we should come with more general way how to control this */
+ if (!strncmp(getProductName(), "Red Hat", 7)) {
+ flags |= LOADER_FLAGS_AUTOMODDISK;
+ }
+
+ 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 = "/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);
+ doExit(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:squashfs:iscsi_tcp");
+
+ loadScsiDhModules();
+
+#if !defined(__s390__) && !defined(__s390x__)
+ mlLoadModuleSet("floppy:edd:pcspkr:iscsi_ibft");
+#endif
+
+#ifdef ENABLE_IPV6
+ if (!FL_NOIPV6(flags))
+ mlLoadModule("ipv6", NULL);
+#endif
+
+ /* now let's do some initial hardware-type setup */
+#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;
+
+ /* Setup depmod & modprobe so we can load multiple DDs */
+ modprobeDDmode();
+
+ /* If there is /.rundepmod file present, rerun depmod */
+ if (!access("/.rundepmod", R_OK)){
+ if (system("depmod -a")) {
+ /* this is not really fatal error, it might still work, log it */
+ logMessage(ERROR, "Error running depmod -a for initrd overlay");
+ }
+ }
+
+ if (FL_AUTOMODDISK(flags)) {
+ /* Load all autodetected DDs */
+ logMessage(INFO, "Trying to detect vendor driver discs");
+ dd = findDriverDiskByLabel();
+ dditer = dd;
+ while(dditer) {
+ /* load the DD */
+ if (loadDriverDiskFromPartition(&loaderData, (char*)(dditer->data))) {
+ logMessage(ERROR, "Automatic driver disk loader failed for %s.", (char*)(dditer->data));
+ }
+ else {
+ logMessage(INFO, "Automatic driver disk loader succeeded for %s.", (char*)(dditer->data));
+ }
+
+ /* clean the device record */
+ free((char*)(dditer->data));
+ dditer->data = NULL;
+
+ /* next DD */
+ dditer = g_slist_next(dditer);
+ }
+ g_slist_free(dd);
+ }
+
+ 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");
+ }
+
+ /* Reset depmod & modprobe to normal mode and get the rest of drivers*/
+ modprobeNormalmode();
+
+ /* 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.
+ */
+ if (!mlInitModuleConfig()) {
+ logMessage(ERROR, "unable to initialize kernel module loading");
+ abort();
+ }
+
+ earlyModuleLoad(0);
+
+ busProbe(FL_NOPROBE(flags));
+
+ /* Disable all network interfaces in NetworkManager by default */
+#if !defined(__s390__) && !defined(__s390x__)
+ int i;
+
+ if ((i = writeDisabledNetInfo()) != 0) {
+ logMessage(ERROR, "writeDisabledNetInfo failure: %d", i);
+ }
+#endif
+
+ /* Start NetworkManager now so it's always available to talk to. */
+ if (iface_start_NetworkManager())
+ logMessage(INFO, "failed to start NetworkManager");
+
+ if (!FL_CMDLINE(flags))
+ startNewt();
+
+ /* 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);
+
+ /* 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/updates/iw");
+ add_to_path_env("PYTHONPATH", "/tmp/updates/textw");
+ 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("raid0:raid1:raid5:raid6:raid456:raid10:linear:dm-mod:dm-zero:dm-mirror:dm-snapshot:dm-multipath:dm-round-robin:dm-crypt:cbc:sha256:lrw:xts");
+
+ 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';
+ checked_asprintf(&binpath, "%s/anaconda", path);
+ 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/ftp-stage2", O_CREAT | O_TRUNC | O_RDWR, 0600);
+ ret = write(fd, url, strlen(url));
+ ret = write(fd, "\r", 1);
+ close(fd);
+ *argptr++ = "@/tmp/ftp-stage2";
+ }
+
+ /* 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_AUTOMODDISK(flags))
+ *argptr++ = "--dlabel";
+
+ if (FL_NOIPV4(flags))
+ *argptr++ = "--noipv4";
+
+#ifdef ENABLE_IPV6
+ if (FL_NOIPV6(flags))
+ *argptr++ = "--noipv6";
+#endif
+
+#if defined(__s390__) || defined(__s390x__)
+ *argptr++ = "--headless";
+#endif
+
+ 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";
+ if (strncmp(loaderData.instRepo, "ftp:", 4)) {
+ *argptr++ = loaderData.instRepo;
+ } else {
+ int fd, ret;
+
+ fd = open("/tmp/ftp-repo", O_CREAT | O_TRUNC | O_RDWR, 0600);
+ ret = write(fd, loaderData.instRepo, strlen(loaderData.instRepo));
+ ret = write(fd, "\r", 1);
+ close(fd);
+ *argptr++ = "@/tmp/ftp-repo";
+ }
+ }
+
+ if (loaderData.proxy && strcmp("", loaderData.proxy)) {
+ *argptr++ = "--proxy";
+
+ *argptr++ = strdup(loaderData.proxy);
+
+ if (loaderData.proxyUser && strcmp(loaderData.proxyUser, "")) {
+ int fd, ret;
+
+ fd = open("/tmp/proxy", O_CREAT|O_TRUNC|O_RDWR, 0600);
+ ret = write(fd, loaderData.proxyUser, strlen(loaderData.proxyUser));
+ ret = write(fd, "\r\n", 2);
+
+ if (loaderData.proxyPassword && strcmp(loaderData.proxyPassword, "")) {
+ ret = write(fd, loaderData.proxyPassword, strlen(loaderData.proxyPassword));
+ ret = write(fd, "\r\n", 2);
+ }
+
+ close(fd);
+
+ *argptr++ = "--proxyAuth";
+ *argptr++ = "/tmp/proxy";
+ }
+ }
+ }
+
+ *argptr = NULL;
+
+ stopNewt();
+ closeLog();
+
+ 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())) {
+ if (execv(anacondaArgs[0], anacondaArgs) == -1) {
+ fprintf(stderr,"exec of anaconda failed: %m\n");
+ doExit(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");
+ doExit(1);
+ }
+ }
+ waitpid(pid, &status, 0);
+ }
+
+ stop_fw_loader(&loaderData);
+#if defined(__s390__) || defined(__s390x__)
+ /* at the latest possibility signal init=linuxrc.s390 to reboot/halt */
+ logMessage(INFO, "Sending signal %d to process %d\n",
+ init_sig, init_pid);
+ kill(init_pid, init_sig);
+#endif
+ doExit(rc);
+
+ doExit(1);
+}
+
+/* vim:set sw=4 sts=4 et: */
diff --git a/loader/loader.h b/loader/loader.h
new file mode 100644
index 0000000..c88457f
--- /dev/null
+++ b/loader/loader.h
@@ -0,0 +1,192 @@
+/*
+ * 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
+
+/* #0 unused */
+/* #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)
+#define LOADER_FLAGS_EARLY_NETWORKING (((uint64_t) 1) << 9)
+#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)
+/* #18 unused */
+#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)
+#ifdef ENABLE_IPV6
+#define LOADER_FLAGS_NOIPV6 (((uint64_t) 1) << 32)
+#endif
+#define LOADER_FLAGS_IP_PARAM (((uint64_t) 1) << 33)
+#ifdef ENABLE_IPV6
+#define LOADER_FLAGS_IPV6_PARAM (((uint64_t) 1) << 34)
+#endif
+#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 LOADER_FLAGS_NOKILL (((uint64_t) 1) << 38)
+#define LOADER_FLAGS_KICKSTART_SEND_SERIAL (((uint64_t) 1) << 39)
+#define LOADER_FLAGS_AUTOMODDISK (((uint64_t) 1) << 40)
+
+#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_EARLY_NETWORKING(a) ((a) & LOADER_FLAGS_EARLY_NETWORKING)
+#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)
+#ifdef ENABLE_IPV6
+#define FL_NOIPV6(a) ((a) & LOADER_FLAGS_NOIPV6)
+#endif
+#define FL_IP_PARAM(a) ((a) & LOADER_FLAGS_IP_PARAM)
+#ifdef ENABLE_IPV6
+#define FL_IPV6_PARAM(a) ((a) & LOADER_FLAGS_IPV6_PARAM)
+#endif
+#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)
+#define FL_NOKILL(a) ((a) & LOADER_FLAGS_NOKILL)
+#define FL_KICKSTART_SEND_SERIAL(a) ((a) & LOADER_FLAGS_KICKSTART_SEND_SERIAL)
+#define FL_AUTOMODDISK(a) ((a) & LOADER_FLAGS_AUTOMODDISK)
+
+void startNewt(void);
+void stopNewt(void);
+char * getProductName(void);
+char * getProductPath(void);
+char * getProductArch(void);
+
+#include "moduleinfo.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, *netmask, *gateway, *dns, *hostname, *peerid, *ethtool, *subchannels, *portname, *essid, *wepkey, *nettype, *ctcprot, *layer2, *portno, *macaddr;
+#ifdef ENABLE_IPV6
+ char *ipv6;
+ int ipv6info_set;
+ char *gateway6;
+#endif
+ int mtu;
+ int noDns;
+ int dhcpTimeout;
+ int ipinfo_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;
+
+ /* Proxy info needs to be in the loaderData so we can get these
+ * settings off the command line, too.
+ */
+ char *proxy;
+ char *proxyUser;
+ char *proxyPassword;
+};
+
+/* 64 bit platforms, definitions courtesy of glib */
+#if defined (__x86_64__) || defined(__ia64__) || defined(__alpha__) || defined(__powerpc64__) || defined(__s390x__) || (defined(__sparc__) && defined(__arch64__))
+#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(__powerpc64__)
+#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
+
+#define checked_asprintf(...) \
+ if (asprintf( __VA_ARGS__ ) == -1) { \
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__); \
+ abort(); \
+ }
+
+#endif
diff --git a/loader/loadermisc.c b/loader/loadermisc.c
new file mode 100644
index 0000000..64e80a2
--- /dev/null
+++ b/loader/loadermisc.c
@@ -0,0 +1,150 @@
+/*
+ * 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 "../isys/log.h"
+
+#include "windows.h"
+
+int copyFileFd(int infd, char * dest, progressCB pbcb,
+ struct progressCBdata *data, long long total) {
+ int outfd;
+ char buf[4096];
+ int i;
+ int rc = 0;
+ long long count = 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;
+ }
+
+ count += i;
+
+ if (pbcb && data && total) {
+ pbcb(data, count, total);
+ }
+ }
+
+ 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, NULL, NULL, 0);
+
+ close(infd);
+
+ return rc;
+}
+
+int simpleStringCmp(const void * a, const void * b) {
+ const char * first = *((const char **) a);
+ const char * second = *((const char **) b);
+
+ return strverscmp(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 0000000..23ebf4a
--- /dev/null
+++ b/loader/loadermisc.h
@@ -0,0 +1,33 @@
+/*
+ * 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>
+
+#include "windows.h"
+
+int copyFile(char * source, char * dest);
+int copyFileFd(int infd, char * dest, progressCB pbcb,
+ struct progressCBdata *data, long long total);
+int simpleStringCmp(const void * a, const void * b);
+int totalMemory(void);
+
+#endif
diff --git a/loader/mediacheck.c b/loader/mediacheck.c
new file mode 100644
index 0000000..4bec1c8
--- /dev/null
+++ b/loader/mediacheck.c
@@ -0,0 +1,115 @@
+/*
+ * 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 "../isys/log.h"
+
+#include "lang.h"
+#include "windows.h"
+
+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."));
+
+ 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, progressCallback, &data);
+
+ newtFormDestroy(f);
+ newtPopWindow();
+
+ if (rc == ISOMD5SUM_CHECK_NOT_FOUND) {
+ logMessage(WARNING, "mediacheck: %s (%s) has no checksum info", file, descr);
+ newtWinMessage(_("Error"), _("OK"),
+ _("Unable to find the checksum in the "
+ "image. This probably "
+ "means the disc was created without adding the "
+ "checksum."));
+ } else if (rc == ISOMD5SUM_FILE_NOT_FOUND) {
+ logMessage(WARNING, "mediacheck: %s (%s) open failed", file, descr);
+ newtWinMessage(_("Error"), _("OK"),
+ _("Unable to open the image."));
+ } else if (rc == ISOMD5SUM_CHECK_FAILED) {
+ 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 == ISOMD5SUM_CHECK_PASSED) {
+ 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 0000000..ab2f887
--- /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 0000000..d6f83e6
--- /dev/null
+++ b/loader/method.c
@@ -0,0 +1,545 @@
+/*
+ * 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/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include "copy.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "lang.h"
+#include "mediacheck.h"
+#include "method.h"
+
+#include "../isys/imount.h"
+#include "../isys/isys.h"
+#include "../isys/cpio.h"
+#include "../isys/log.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, *err = NULL;
+
+ 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;
+ }
+
+ checked_asprintf(&opts, "ro,loop=%s", device);
+
+ if (doPwMount(fsystem, mntpoint, "auto", opts, &err)) {
+ logMessage(ERROR, "failed to mount loopback device %s on %s as %s: %s",
+ device, mntpoint, fsystem, err);
+ 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,
+ progressCB pbcb, struct progressCBdata *data,
+ long long total) {
+ int rc;
+ struct stat sb;
+
+ rc = copyFileFd(fd, dest, pbcb, data, total);
+ 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, i;
+ char file[4096];
+
+ logMessage(INFO, "getFileFromBlockDevice(%s, %s)", device, path);
+
+ /* some USB thumb drives and hard drives are slow to initialize */
+ /* retry up to 5 times or 31 seconds */
+ rc = doPwMount(device, "/tmp/mnt", "auto", "ro", NULL);
+ for (i = 0; mountMightSucceedLater(rc) && i < 5; ++i) {
+ logMessage(INFO, "sleeping to wait for USB storage devices");
+ sleep(1 << i);
+ rc = doPwMount(device, "/tmp/mnt", "auto", "ro", NULL);
+ logMessage(ERROR, "error code: %d", rc);
+ }
+ if (rc) {
+ 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) {
+ if (!strncmp(arg, "nfs:", 4)) {
+ ld->method = METHOD_NFS;
+ ld->stage2Data = calloc(sizeof(struct nfsInstallData *), 1);
+
+ parseNfsHostPathOpts(arg + 4,
+ &(((struct nfsInstallData *)ld->stage2Data)->host),
+ &(((struct nfsInstallData *)ld->stage2Data)->directory),
+ &(((struct nfsInstallData *)ld->stage2Data)->mountOpts));
+ } else if (!strncmp(arg, "nfsiso:", 7)) {
+ ld->method = METHOD_NFS;
+ ld->stage2Data = calloc(sizeof(struct nfsInstallData *), 1);
+
+ parseNfsHostPathOpts(arg + 7,
+ &(((struct nfsInstallData *)ld->stage2Data)->host),
+ &(((struct nfsInstallData *)ld->stage2Data)->directory),
+ &(((struct nfsInstallData *)ld->stage2Data)->mountOpts));
+ } else if (!strncmp(arg, "ftp:", 4) ||
+ !strncmp(arg, "http", 4)) {
+ ld->method = METHOD_URL;
+ ld->stage2Data = calloc(sizeof(struct urlInstallData *), 1);
+ ((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)) {
+ size_t offset;
+
+ arg += strcspn(arg, ":");
+ if (!*arg || !*(arg+1))
+ return;
+ arg += 1;
+ offset = strcspn(arg, ":");
+
+ ld->method = METHOD_HD;
+ ld->stage2Data = calloc(sizeof(struct hdInstallData *), 1);
+ ((struct hdInstallData *)ld->stage2Data)->partition = strndup(arg, offset);
+ arg += offset;
+ if (*arg && *(arg+1))
+ ((struct hdInstallData *)ld->stage2Data)->directory = strdup(arg+1);
+ else
+ ((struct hdInstallData *)ld->stage2Data)->directory = NULL;
+ }
+}
diff --git a/loader/method.h b/loader/method.h
new file mode 100644
index 0000000..1b5e2d3
--- /dev/null
+++ b/loader/method.h
@@ -0,0 +1,60 @@
+/*
+ * 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"
+#include "windows.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,
+ progressCB pbcb, struct progressCBdata *data, long long total);
+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 0000000..12eaba0
--- /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 0000000..2e0ab77
--- /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 0000000..72f6d71
--- /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 0000000..0944b97
--- /dev/null
+++ b/loader/modules.c
@@ -0,0 +1,411 @@
+/*
+ * modules.c - module loading functionality
+ *
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ * 2008, 2009 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>
+ * David Cantrell <dcantrell@redhat.com>
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <newt.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 <glib.h>
+
+#include "../isys/log.h"
+
+#include "loader.h"
+#include "modules.h"
+#include "windows.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+static GSList *modopts = NULL;
+static GSList *blacklist = NULL;
+
+static gboolean _isValidModule(gchar *module) {
+ gint fd = -1, i = 0;
+ gchar *path = NULL, *buf = NULL, *modname = NULL;
+ gchar *ends[] = { ".ko.gz:", ".ko:", NULL };
+ struct utsname utsbuf;
+ struct stat sbuf;
+
+ if (uname(&utsbuf) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ return FALSE;
+ }
+
+ if (asprintf(&path, "/lib/modules/%s/modules.dep", utsbuf.release) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ return FALSE;
+ }
+
+ if (stat(path, &sbuf) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ free(path);
+ return FALSE;
+ }
+
+ if ((fd = open(path, O_RDONLY)) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ free(path);
+ return FALSE;
+ } else {
+ free(path);
+ }
+
+ buf = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!buf || buf == MAP_FAILED) {
+ close(fd);
+ return FALSE;
+ }
+
+ close(fd);
+
+ while (ends[i] != NULL) {
+ if (asprintf(&modname, "/%s%s", module, ends[i]) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ return FALSE;
+ }
+
+ if (g_strstr_len(buf, -1, modname) != NULL) {
+ munmap(buf, sbuf.st_size);
+ free(modname);
+ return TRUE;
+ }
+
+ free(modname);
+ modname = NULL;
+ i++;
+ }
+
+ munmap(buf, sbuf.st_size);
+ return FALSE;
+}
+
+static void _addOption(const gchar *module, const gchar *option) {
+ gboolean found = FALSE;
+ GSList *iterator = modopts;
+ module_t *modopt = NULL;
+ gchar *tmpopt = g_strdup(option);
+
+ while (iterator != NULL) {
+ modopt = (module_t *) iterator->data;
+
+ if (!strncmp(modopt->name, module, strlen(modopt->name))) {
+ found = TRUE;
+ break;
+ }
+
+ iterator = g_slist_next(iterator);
+ }
+
+ if (found) {
+ modopt->options = g_slist_append(modopt->options, tmpopt);
+ } else {
+ if ((modopt = g_malloc0(sizeof(module_t))) == NULL) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+
+ modopt->name = g_strdup(module);
+ modopt->options = NULL;
+ modopt->options = g_slist_append(modopt->options, tmpopt);
+ modopts = g_slist_append(modopts, modopt);
+ }
+
+ return;
+}
+
+static gboolean _writeModulesConf(gchar *conf) {
+ gint fd = -1, rc = 0, len = 0;
+ GSList *iterator = modopts;
+ GString *buf = g_string_new("# Module options and blacklists written by anaconda\n");
+
+ if (conf == NULL) {
+ /* XXX: should this use mkstemp() ? */
+ conf = "/tmp/modprobe.conf";
+ }
+
+ if ((fd = open(conf, O_WRONLY | O_CREAT, 0644)) == -1) {
+ logMessage(ERROR, "error opening to %s: %m", conf);
+ return FALSE;
+ }
+
+ while (iterator != NULL) {
+ module_t *modopt = iterator->data;
+ GSList *optiterator = modopt->options;
+ g_string_append_printf(buf, "options %s", modopt->name);
+
+ while (optiterator != NULL) {
+ gchar *option = (gchar *) optiterator->data;
+ g_string_append_printf(buf, " %s", option);
+ optiterator = g_slist_next(optiterator);
+ }
+
+ g_string_append(buf, "\n");
+ iterator = g_slist_next(iterator);
+ }
+
+ iterator = blacklist;
+
+ while (iterator != NULL) {
+ gchar *module = (gchar *) iterator->data;
+ g_string_append_printf(buf, "blacklist %s\n", module);
+ iterator = g_slist_next(iterator);
+ }
+
+ len = buf->len;
+ rc = write(fd, buf->str, len);
+ close(fd);
+ g_string_free(buf, TRUE);
+
+ return (rc == len);
+}
+
+static gboolean _doLoadModule(const gchar *module, gchar **args) {
+ gint child;
+ gint status;
+
+ if (!(child = fork())) {
+ gint i, rc;
+ gchar **argv = NULL;
+ gint fd = -1;
+
+ if ((argv = g_malloc0(3 * sizeof(*argv))) == NULL) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ abort();
+ }
+
+ if ((fd = open("/dev/tty3", O_RDWR)) == -1) {
+ logMessage(ERROR, "%s (%d): %m", __func__, __LINE__);
+ } else {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+ }
+
+ argv[0] = "/sbin/modprobe";
+ argv[1] = g_strdup(module);
+ argv[2] = NULL;
+
+ if (args) {
+ for (i = 0; args[i] ; i++) {
+ _addOption(module, args[i]);
+ }
+ _writeModulesConf(MODULES_CONF);
+ }
+
+ rc = execv("/sbin/modprobe", argv);
+ g_strfreev(argv);
+ _exit(rc);
+ }
+
+ waitpid(child, &status, 0);
+
+ if (!WIFEXITED(status) || (WIFEXITED(status) && WEXITSTATUS(status))) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+gboolean mlInitModuleConfig(void) {
+ gint i = 0;
+ gchar *cmdline = NULL;
+ gchar **options = NULL;
+ GError *readErr = NULL;
+
+ /* read module options out of /proc/cmdline and into a structure */
+ if (!g_file_get_contents("/proc/cmdline", &cmdline, NULL, &readErr)) {
+ logMessage(ERROR, "unable to read /proc/cmdline: %s", readErr->message);
+ g_error_free(readErr);
+ return _writeModulesConf(MODULES_CONF);
+ }
+
+ cmdline = g_strchomp(cmdline);
+ options = g_strsplit(cmdline, " ", 0);
+ g_free(cmdline);
+
+ if (options == NULL) {
+ return _writeModulesConf(MODULES_CONF);
+ }
+
+ while (options[i] != NULL) {
+ gchar *tmpmod = NULL;
+ gchar **fields = NULL;
+
+ if (g_strstr_len(options[i], -1, "=") == NULL) {
+ i++;
+ continue;
+ }
+
+ if (!strncmp(options[i], "blacklist=", 10)) {
+ if ((fields = g_strsplit(options[i], "=", 0)) != NULL) {
+ if (g_strv_length(fields) == 2) {
+ tmpmod = g_strdup(fields[1]);
+ blacklist = g_slist_append(blacklist, tmpmod);
+ }
+ }
+ } else if ((fields = g_strsplit(options[i], ".", 0)) != NULL) {
+ if (g_strv_length(fields) == 2) {
+ if (_isValidModule(fields[0])) {
+ _addOption(fields[0], fields[1]);
+ }
+ }
+ }
+
+ if (fields != NULL) {
+ g_strfreev(fields);
+ }
+
+ i++;
+ }
+
+ if (options != NULL) {
+ g_strfreev(options);
+ }
+
+ return _writeModulesConf(MODULES_CONF);
+}
+
+/* load a module with a given list of arguments */
+gboolean mlLoadModule(const gchar *module, gchar **args) {
+ return _doLoadModule(module, args);
+}
+
+/* loads a : separated list of modules */
+gboolean mlLoadModuleSet(const gchar *modNames) {
+ gchar **mods = NULL, **iterator = NULL;
+ gboolean rc = FALSE;
+
+ if (modNames == NULL) {
+ return FALSE;
+ }
+
+ if ((mods = g_strsplit(modNames, ":", 0)) != NULL) {
+ iterator = mods;
+
+ while (*iterator != NULL) {
+ rc |= _doLoadModule(*iterator, NULL);
+ iterator++;
+ }
+ } else {
+ return FALSE;
+ }
+
+ g_strfreev(mods);
+ return rc;
+}
+
+gboolean mlAddBlacklist(gchar *module) {
+ gchar *tmpmod = NULL;
+
+ if (module == NULL) {
+ return FALSE;
+ }
+
+ tmpmod = g_strdup(module);
+ blacklist = g_slist_append(blacklist, tmpmod);
+ return _writeModulesConf(MODULES_CONF);
+}
+
+gboolean mlRemoveBlacklist(gchar *module) {
+ GSList *iterator = blacklist;
+
+ if (module == NULL) {
+ return FALSE;
+ }
+
+ while (iterator != NULL) {
+ if (!strcmp((gchar *) iterator->data, module)) {
+ iterator = g_slist_delete_link(blacklist, iterator);
+ continue;
+ } else {
+ iterator = g_slist_next(iterator);
+ }
+ }
+
+ return TRUE;
+}
+
+void loadKickstartModule(struct loaderData_s * loaderData,
+ int argc, char **argv) {
+ gchar *opts = NULL;
+ gchar *module = NULL;
+ gchar **args = NULL, **remaining = NULL;
+ gboolean rc;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksDeviceOptions[] = {
+ { "opts", 0, 0, G_OPTION_ARG_STRING, &opts, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining,
+ NULL, NULL },
+ { NULL },
+ };
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksDeviceOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to device kickstart method "
+ "command: %s"), optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ if ((remaining != NULL) && (g_strv_length(remaining) == 1)) {
+ module = remaining[0];
+ }
+
+ if (!module) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("A module name must be specified for "
+ "the kickstart device command."));
+ return;
+ }
+
+ if (opts) {
+ args = g_strsplit(opts, " ", 0);
+ }
+
+ rc = mlLoadModule(module, args);
+ g_strfreev(args);
+ return;
+}
diff --git a/loader/modules.h b/loader/modules.h
new file mode 100644
index 0000000..88fa25f
--- /dev/null
+++ b/loader/modules.h
@@ -0,0 +1,43 @@
+/*
+ * modules.h
+ *
+ * Copyright (C) 2007, 2009 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): David Cantrell <dcantrell@redhat.com>
+ */
+
+#ifndef H_MODULES
+#define H_MODULES
+
+#include <glib.h>
+#include "loader.h"
+#include "moduleinfo.h"
+
+#define MODULES_CONF "/etc/modprobe.d/anaconda.conf"
+
+typedef struct _module_t {
+ gchar *name;
+ GSList *options;
+} module_t;
+
+gboolean mlInitModuleConfig(void);
+gboolean mlLoadModule(const gchar *, gchar **);
+gboolean mlLoadModuleSet(const gchar *);
+gboolean mlAddBlacklist(gchar *);
+gboolean mlRemoveBlacklist(gchar *);
+void loadKickstartModule(struct loaderData_s *, int, char **);
+
+#endif
diff --git a/loader/net.c b/loader/net.c
new file mode 100644
index 0000000..eff4782
--- /dev/null
+++ b/loader/net.c
@@ -0,0 +1,2112 @@
+/*
+ * net.c
+ *
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+ * 2006, 2007, 2008, 2009
+ *
+ * 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/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <resolv.h>
+#include <net/if.h>
+#include <newt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <NetworkManager.h>
+#include <nm-client.h>
+
+#include "../isys/isys.h"
+#include "../isys/ethtool.h"
+#include "../isys/iface.h"
+#include "../isys/log.h"
+
+#include "lang.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "method.h"
+#include "net.h"
+#include "windows.h"
+#include "ibft.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;
+#ifdef ENABLE_IPV6
+ } 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;
+#endif
+ }
+
+ 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;
+#ifdef ENABLE_IPV6
+ } else if (co == data->ipv6Entry) {
+ /* users must provide a mask, we can't guess for ipv6 */
+ return;
+#endif
+ }
+}
+
+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;
+}
+
+#ifdef ENABLE_IPV6
+static void v6MethodCallback(newtComponent co, void *dptr) {
+ setMethodSensitivity(dptr, 3);
+ return;
+}
+#endif
+
+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);
+}
+
+/* given loader data from kickstart, populate network configuration struct */
+void setupIfaceStruct(iface_t * iface, struct loaderData_s * loaderData) {
+ struct in_addr addr;
+ struct in6_addr addr6;
+ char * c;
+
+ memset(&addr, 0, sizeof(addr));
+ memset(&addr6, 0, sizeof(addr6));
+
+ if (loaderData->ethtool) {
+ parseEthtoolSettings(loaderData);
+ }
+
+ if (loaderData->netCls_set) {
+ iface->vendorclass = loaderData->netCls;
+ } else {
+ iface->vendorclass = NULL;
+ }
+
+ if (loaderData->ipinfo_set && loaderData->ipv4 != NULL) {
+ /* this is iBFT configured device */
+ if (!strncmp(loaderData->ipv4, "ibft", 4)) {
+ char *devmacaddr = iface_mac2str(loaderData->netDev);
+ iface->ipv4method = IPV4_IBFT_METHOD;
+ iface->isiBFT = 1;
+
+ /* Problems with getting the info from iBFT or iBFT uses dhcp*/
+ if(!devmacaddr || !ibft_present()){
+ iface->ipv4method = IPV4_DHCP_METHOD;
+ logMessage(INFO, "iBFT is not present");
+ }
+ /* MAC address doesn't match */
+ else if(strcasecmp(ibft_iface_mac(), devmacaddr)){
+ iface->ipv4method = IPV4_DHCP_METHOD;
+ logMessage(INFO, "iBFT doesn't know what NIC to use - falling back to DHCP");
+ }
+ else if(ibft_iface_dhcp()){
+ iface->ipv4method = IPV4_IBFT_DHCP_METHOD;
+ logMessage(INFO, "iBFT is configured to use DHCP");
+ }
+ if(devmacaddr) free(devmacaddr);
+ }
+ /* this is how we specify dhcp */
+ else if (!strncmp(loaderData->ipv4, "dhcp", 4)) {
+ iface->dhcptimeout = loaderData->dhcpTimeout;
+ iface->ipv4method = IPV4_DHCP_METHOD;
+ } else if (inet_pton(AF_INET, loaderData->ipv4, &addr) >= 1) {
+ iface->ipaddr.s_addr = addr.s_addr;
+ iface->ipv4method = IPV4_MANUAL_METHOD;
+ } else { /* invalid ip information, disable the setting of ip info */
+ loaderData->ipinfo_set = 0;
+ iface->ipv4method = 0;
+ loaderData->ipv4 = NULL;
+ }
+ }
+
+ if (loaderData->netmask != NULL) {
+ if (inet_pton(AF_INET, loaderData->netmask, &iface->netmask) <= 0) {
+ logMessage(ERROR, "%s (%d): %s", __func__, __LINE__,
+ strerror(errno));
+ }
+ }
+
+ if (loaderData->gateway != NULL) {
+ if (inet_pton(AF_INET, loaderData->gateway, &iface->gateway) <= 0) {
+ logMessage(ERROR, "%s (%d): %s", __func__, __LINE__,
+ strerror(errno));
+ }
+ }
+
+#ifdef ENABLE_IPV6
+ if (loaderData->ipv6info_set && loaderData->ipv6 != NULL) {
+ if (!strncmp(loaderData->ipv6, "dhcp", 4)) {
+ iface->ipv6method = IPV6_DHCP_METHOD;
+ } else if (!strncmp(loaderData->ipv6, "auto", 4)) {
+ iface->ipv6method = IPV6_AUTO_METHOD;
+ } else if (inet_pton(AF_INET6, loaderData->ipv6, &addr6) >= 1) {
+ memcpy(&iface->ip6addr, &addr6, sizeof(struct in6_addr));
+ iface->ipv6method = IPV6_MANUAL_METHOD;
+ } else {
+ iface->ipv6method = 0;
+ loaderData->ipv6info_set = 0;
+ loaderData->ipv6 = NULL;
+ }
+ }
+
+ if (loaderData->gateway6 != NULL) {
+ if (inet_pton(AF_INET6, loaderData->gateway6, &iface->gateway6) <= 0) {
+ logMessage(ERROR, "%s (%d): %s", __func__, __LINE__,
+ strerror(errno));
+ }
+ }
+#endif
+
+ /* iBFT configured DNS */
+ if(iface->ipv4method == IPV4_IBFT_METHOD){
+ if(iface->numdns<MAXNS){
+ if(ibft_iface_dns1() && inet_pton(AF_INET, ibft_iface_dns1(), &addr)>=1){
+ iface->dns[iface->numdns] = strdup(ibft_iface_dns1());
+ iface->numdns++;
+ logMessage(INFO, "adding iBFT dns server %s", ibft_iface_dns1());
+ }
+ }
+ if(iface->numdns<MAXNS){
+ if(ibft_iface_dns2() && inet_pton(AF_INET, ibft_iface_dns2(), &addr)>=1){
+ iface->dns[iface->numdns] = strdup(ibft_iface_dns2());
+ iface->numdns++;
+ logMessage(INFO, "adding iBFT dns server %s", ibft_iface_dns2());
+ }
+ }
+ }
+
+ 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->layer2) {
+ iface->layer2 = strdup(loaderData->layer2);
+ }
+
+ if (loaderData->portno) {
+ iface->portno = strdup(loaderData->portno);
+ }
+
+ if (loaderData->noDns) {
+ iface->flags |= IFACE_FLAGS_NO_WRITE_RESOLV_CONF;
+ }
+
+ iface->dhcptimeout = loaderData->dhcpTimeout;
+
+ return;
+}
+
+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.cidr4 = NULL;
+ ipcomps.gw = NULL;
+#ifdef ENABLE_IPV6
+ ipcomps.ipv6 = NULL;
+ ipcomps.cidr6 = NULL;
+ ipcomps.gw6 = NULL;
+#endif
+ ipcomps.ns = NULL;
+
+ /* init opts */
+ opts.ipv4Choice = 0;
+#ifdef ENABLE_IPV6
+ opts.ipv6Choice = 0;
+#endif
+
+ /* JKFIXME: we really need a way to override this and be able to change
+ * our network config */
+ if (!FL_ASKNETWORK(flags) &&
+ ((iface->ipv4method > IPV4_UNUSED_METHOD) ||
+ (iface->ipv6method > IPV4_UNUSED_METHOD))) {
+ logMessage(INFO, "doing kickstart... setting it up");
+
+ err = writeEnabledNetInfo(iface);
+ if (err) {
+ logMessage(ERROR, "failed to write %s data for %s (%d)",
+ SYSCONFIG_PATH, iface->device, err);
+ return LOADER_BACK;
+ }
+
+ i = get_connection(iface);
+ newtPopWindow();
+
+ if (i > 0) {
+ if (FL_CMDLINE(flags)) {
+ fprintf(stderr, _("There was an error configuring your network "
+ "interface."));
+ fprintf(stderr, _("\nThis cannot be corrected in cmdline mode.\n"
+ "Halting.\n"));
+ exit(1);
+ }
+
+ 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;
+ }
+ }
+
+ /* 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);
+ }
+ }
+
+ /* bring up the interface */
+ err = writeEnabledNetInfo(iface);
+ if (err) {
+ logMessage(ERROR, "failed to write %s data for %s (%d)",
+ SYSCONFIG_PATH, iface->device, err);
+ iface->ipv4method = IPV4_UNUSED_METHOD;
+ iface->ipv6method = IPV6_UNUSED_METHOD;
+ return LOADER_BACK;
+ }
+
+ i = get_connection(iface);
+ newtPopWindow();
+
+ if (i > 0) {
+ newtWinMessage(_("Network Error"), _("Retry"),
+ _("There was an error configuring your network "
+ "interface."));
+ iface->ipv4method = IPV4_UNUSED_METHOD;
+ iface->ipv6method = IPV6_UNUSED_METHOD;
+ return LOADER_ERROR;
+ }
+
+ return LOADER_OK;
+}
+
+int configureTCPIP(char * device, iface_t * iface,
+ struct netconfopts * opts, int methodNum) {
+ int i = 0, z = 0, skipForm = 0, ret;
+ newtComponent f, okay, back, answer;
+ newtComponent ipv4Checkbox, v4Method[2];
+#ifdef ENABLE_IPV6
+ newtComponent ipv6Checkbox, v6Method[3];
+#endif
+ 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]);
+
+#ifdef ENABLE_IPV6
+ /* 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]);
+#endif
+
+ /* button bar at the bottom of the window */
+ buttons = newtButtonBar(_("OK"), &okay, _("Back"), &back, NULL);
+
+ /* checkgrid contains the toggle options for net configuration */
+#ifdef ENABLE_IPV6
+ checkgrid = newtCreateGrid(1, 8);
+#else
+ checkgrid = newtCreateGrid(1, 3);
+#endif
+
+ 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);
+
+#ifdef ENABLE_IPV6
+ 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);
+#endif
+
+ /* 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);
+#ifdef ENABLE_IPV6
+ newtComponentAddCallback(ipv6Checkbox, v6MethodCallback, &v6Method);
+#endif
+
+ /* match radio button sensitivity to initial checkbox choices */
+ if (opts->ipv4Choice == ' ')
+ setMethodSensitivity(&v4Method, 2);
+
+#ifdef ENABLE_IPV6
+ if (opts->ipv6Choice == ' ')
+ setMethodSensitivity(&v6Method, 3);
+#endif
+
+#ifdef ENABLE_IPV6
+ /* 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
+ */
+ 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))) {
+ skipForm = 1;
+ newtPopWindow();
+ }
+#else
+ if (FL_IP_PARAM(flags) || FL_NOIPV4(flags)) {
+ skipForm = 1;
+ newtPopWindow();
+ }
+#endif
+
+ /* run the form */
+ do {
+ if (!skipForm) {
+ answer = newtRunForm(f);
+
+ if (answer == back) {
+ newtFormDestroy(f);
+ newtPopWindow();
+ return LOADER_BACK;
+ }
+
+ /* need at least one stack */
+#ifdef ENABLE_IPV6
+ if (opts->ipv4Choice == ' ' && opts->ipv6Choice == ' ') {
+#else
+ if (opts->ipv4Choice == ' ') {
+#endif
+ 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;
+ }
+
+#ifdef ENABLE_IPV6
+ 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;
+ }
+#endif
+
+ /* do interface configuration (call DHCP here, or return for manual) */
+#ifdef ENABLE_IPV6
+ if ((!FL_NOIPV4(flags) && iface->ipv4method == IPV4_DHCP_METHOD) ||
+ (!FL_NOIPV6(flags) && (iface->ipv6method == IPV6_AUTO_METHOD ||
+ iface->ipv6method == IPV6_DHCP_METHOD))) {
+#else
+ if (!FL_NOIPV4(flags) && iface->ipv4method == IPV4_DHCP_METHOD) {
+#endif
+ /* DHCP selected, exit the loop */
+ ret = LOADER_NOOP;
+ i = 1;
+#ifdef ENABLE_IPV6
+ } else if ((!FL_NOIPV4(flags) && iface->ipv4method == IPV4_MANUAL_METHOD) ||
+ (!FL_NOIPV6(flags) && iface->ipv6method == IPV6_MANUAL_METHOD)) {
+#else
+ } else if (!FL_NOIPV4(flags) && iface->ipv4method == IPV4_MANUAL_METHOD) {
+#endif
+
+ /* manual IP configuration selected */
+ ret = LOADER_OK;
+ i = 1;
+ }
+ } while (i != 1);
+
+ newtFormDestroy(f);
+ newtPopWindow();
+ return ret;
+}
+
+int manualNetConfig(char * device, iface_t * iface,
+ struct intfconfig_s * ipcomps, struct netconfopts * opts) {
+ int i, rows, pos, cidr, have[2], stack[2];
+ char *buf = NULL;
+ char ret[48];
+ struct in_addr addr;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+ int prefix;
+#endif
+ struct in_addr *tmpaddr = NULL;
+ newtComponent f, okay, back, answer;
+ newtGrid egrid = NULL;
+ newtGrid qgrid = NULL;
+#ifdef ENABLE_IPV6
+ newtGrid rgrid = NULL;
+#endif
+ 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;
+#ifdef ENABLE_IPV6
+ stack[IPV6] = opts->ipv6Choice == '*' &&
+ iface->ipv6method == IPV6_MANUAL_METHOD;
+#endif
+
+ /* 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++;
+ }
+
+#ifdef ENABLE_IPV6
+ /* 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++;
+ }
+#endif
+
+ /* 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);
+
+ checked_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."));
+
+ 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);
+
+ have[IPV4] = 0;
+ have[IPV6] = 0;
+
+ for (i = IPV4; i <= IPV6; i++) {
+ if (!stack[i]) {
+ have[i] = 2;
+ }
+ }
+
+ /* run the form */
+ while ((have[IPV4] != 2) || (have[IPV6] != 2)) {
+ answer = newtRunForm(f);
+
+ /* 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;
+ }
+ }
+ }
+ }
+ }
+
+#ifdef ENABLE_IPV6
+ /* 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]++;
+ }
+ }
+ }
+#endif
+
+ /* 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) {
+#ifdef ENABLE_IPV6
+ if ((inet_pton(AF_INET, ipcomps->ns, &addr) >= 1) ||
+ (inet_pton(AF_INET6, ipcomps->ns, &addr6) >= 1)) {
+#else
+ if (inet_pton(AF_INET, ipcomps->ns, &addr) >= 1) {
+#endif
+ 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 (stack[IPV4] && have[IPV4] != 2) {
+ have[IPV4] = 0;
+ newtWinMessage(_("Missing Information"), _("Retry"),
+ _("You must enter both a valid IPv4 address and a "
+ "network mask or CIDR prefix."));
+ }
+
+#ifdef ENABLE_IPV6
+ if (stack[IPV6] && have[IPV6] != 2) {
+ have[IPV6] = 0;
+ newtWinMessage(_("Missing Information"), _("Retry"),
+ _("You must enter both a valid IPv6 address and a "
+ "CIDR prefix."));
+ }
+#endif
+
+ strcpy(iface->device, device);
+ }
+
+ free(buf);
+ newtFormDestroy(f);
+ newtPopWindow();
+
+ return LOADER_OK;
+}
+
+/*
+ * 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;
+ char *nfile = NULL;
+ FILE *fp = NULL;
+ struct device **devs = NULL;
+
+ devs = getDevices(DEVICE_NETWORK);
+
+ if (devs == NULL) {
+ return 1;
+ }
+
+ for (i = 0; devs[i]; i++) {
+ /* remove dhclient-DEVICE.conf if we have it */
+ if (asprintf(&ofile, "/etc/dhcp/dhclient-%s.conf", devs[i]->device) == -1) {
+ return 5;
+ }
+
+ if (!access(ofile, R_OK|W_OK)) {
+ if (unlink(ofile)) {
+ logMessage(ERROR, "error removing %s", ofile);
+ }
+ }
+
+ if (ofile) {
+ free(ofile);
+ ofile = NULL;
+ }
+
+ /* write disabled ifcfg-DEVICE file */
+
+ checked_asprintf(&ofile, "%s/.ifcfg-%s",
+ NETWORK_SCRIPTS_PATH,
+ devs[i]->device);
+ checked_asprintf(&nfile, "%s/ifcfg-%s",
+ NETWORK_SCRIPTS_PATH,
+ devs[i]->device);
+
+ 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 (fclose(fp) == EOF) {
+ return 3;
+ }
+
+ if (rename(ofile, nfile) == -1) {
+ free(ofile);
+ free(nfile);
+ return 4;
+ }
+
+ if (ofile) {
+ free(ofile);
+ ofile = NULL;
+ }
+
+ if (nfile) {
+ free(nfile);
+ nfile = NULL;
+ }
+ }
+
+ 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, osa_layer2 = 0, osa_portno = 0;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ FILE *fp = NULL;
+ char buf[INET6_ADDRSTRLEN+1];
+ char *ofile = NULL;
+ char *nfile = NULL;
+ struct utsname kv;
+
+ memset(&buf, '\0', sizeof(buf));
+
+ if ((mkdir(NETWORK_SCRIPTS_PATH, mode) == -1) && (errno != EEXIST)) {
+ return 16;
+ }
+
+ /* write vendor class */
+ if (iface->vendorclass == NULL) {
+ if (uname(&kv) == -1) {
+ iface->vendorclass = "anaconda";
+ } else {
+ if (asprintf(&iface->vendorclass, "anaconda-%s %s %s",
+ kv.sysname, kv.release, kv.machine) == -1 ) {
+ return 20;
+ }
+ }
+ }
+
+ if (asprintf(&ofile, "/etc/dhcp/dhclient-%s.conf", iface->device) == -1) {
+ return 17;
+ }
+
+ if ((fp = fopen(ofile, "w")) == NULL) {
+ free(ofile);
+ return 18;
+ }
+
+ fprintf(fp, "send vendor-class-identifier \"%s\";\n",
+ iface->vendorclass);
+
+ if (fclose(fp) == EOF) {
+ free(ofile);
+ return 19;
+ }
+
+ if (ofile) {
+ free(ofile);
+ ofile = NULL;
+ }
+
+ /* write out new ifcfg-DEVICE file */
+ if (asprintf(&ofile, "%s/.ifcfg-%s",
+ NETWORK_SCRIPTS_PATH, iface->device) == -1) {
+ return 1;
+ }
+
+ if (asprintf(&nfile, "%s/ifcfg-%s",
+ NETWORK_SCRIPTS_PATH, iface->device) == -1) {
+ return 13;
+ }
+
+ 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");
+
+ if (!FL_NOIPV4(flags)) {
+ if (iface->ipv4method == IPV4_IBFT_METHOD) {
+ /* When initrd and NM support iBFT, we should just write
+ * BOOTPROTO=ibft and let NM deal with it. Until than,
+ * just use static and do it ourselves. */
+ fprintf(fp, "BOOTPROTO=static\n");
+ if(ibft_iface_ip()) fprintf(fp, "IPADDR=%s\n", ibft_iface_ip());
+ if(ibft_iface_mask()) fprintf(fp, "NETMASK=%s\n", ibft_iface_mask());
+ if(ibft_iface_gw()) fprintf(fp, "GATEWAY=%s\n", ibft_iface_gw());
+ } else if (iface->ipv4method == IPV4_IBFT_DHCP_METHOD) {
+ fprintf(fp, "BOOTPROTO=dhcp\n");
+ } else 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);
+ fclose(fp);
+ return 3;
+ }
+
+ fprintf(fp, "IPADDR=%s\n", buf);
+ }
+
+ if (iface_have_in_addr(&iface->netmask)) {
+ if (inet_ntop(AF_INET, &iface->netmask, buf,
+ INET_ADDRSTRLEN) == NULL) {
+ free(ofile);
+ fclose(fp);
+ return 4;
+ }
+
+ fprintf(fp, "NETMASK=%s\n", buf);
+ }
+
+ if (iface_have_in_addr(&iface->broadcast)) {
+ if (inet_ntop(AF_INET, &iface->broadcast, buf,
+ INET_ADDRSTRLEN) == NULL) {
+ free(ofile);
+ fclose(fp);
+ return 5;
+ }
+
+ fprintf(fp, "BROADCAST=%s\n", buf);
+ }
+
+ if (iface_have_in_addr(&iface->gateway)) {
+ if (inet_ntop(AF_INET, &iface->gateway, buf,
+ INET_ADDRSTRLEN) == NULL) {
+ free(ofile);
+ fclose(fp);
+ return 6;
+ }
+
+ fprintf(fp, "GATEWAY=%s\n", buf);
+ }
+ }
+ }
+
+#ifdef ENABLE_IPV6
+ 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);
+ fclose(fp);
+ return 7;
+ }
+
+ if (iface->ip6prefix) {
+ fprintf(fp, "IPV6ADDR=%s/%d\n", buf, iface->ip6prefix);
+ } else {
+ fprintf(fp, "IPV6ADDR=%s\n", buf);
+ }
+ }
+ }
+
+ if (iface_have_in6_addr(&iface->gateway6)) {
+ if (inet_ntop(AF_INET6, &iface->gateway6, buf,
+ INET6_ADDRSTRLEN) == NULL) {
+ free(ofile);
+ fclose(fp);
+ return 8;
+ }
+
+ fprintf(fp, "IPV6_DEFAULTGW=%s\n", buf);
+ }
+ }
+ }
+#endif
+
+ 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 (iface->layer2 && !strcmp(iface->layer2, "1")) {
+ osa_layer2 = 1;
+ }
+
+ if (iface->portno && !strcmp(iface->portno, "1")) {
+ osa_portno = 1;
+ }
+
+ if (osa_layer2 || osa_portno) {
+ fprintf(fp, "OPTIONS=\"");
+
+ if (osa_layer2) {
+ fprintf(fp, "layer2=1");
+ }
+
+ if (osa_layer2 && osa_portno) {
+ fprintf(fp, " ");
+ }
+
+ if (osa_portno) {
+ fprintf(fp, "portno=1");
+ }
+
+ fprintf(fp, "\"\n");
+ }
+
+ if (iface->macaddr) {
+ fprintf(fp, "MACADDR=%s\n", iface->macaddr);
+ }
+
+ if (fclose(fp) == EOF) {
+ free(ofile);
+ free(nfile);
+ return 8;
+ }
+
+ if (rename(ofile, nfile) == -1) {
+ free(ofile);
+ free(nfile);
+ return 14;
+ }
+
+ if (ofile) {
+ free(ofile);
+ }
+
+ if (nfile) {
+ free(nfile);
+ }
+
+ /* Global settings */
+ if ((fp = fopen(SYSCONFIG_PATH"/.network", "w")) == NULL) {
+ return 9;
+ }
+
+ if (!FL_NOIPV4(flags)) {
+ fprintf(fp, "NETWORKING=yes\n");
+ }
+
+#ifdef ENABLE_IPV6
+ if (!FL_NOIPV6(flags)) {
+ fprintf(fp, "NETWORKING_IPV6=yes\n");
+ }
+#endif
+
+ 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) {
+ fclose(fp);
+ return 10;
+ }
+
+ fprintf(fp, "GATEWAY=%s\n", buf);
+ }
+
+#ifdef ENABLE_IPV6
+ if (iface_have_in6_addr(&iface->gateway6)) {
+ if (inet_ntop(AF_INET6, &iface->gateway6, buf,
+ INET6_ADDRSTRLEN) == NULL) {
+ fclose(fp);
+ return 11;
+ }
+
+ fprintf(fp, "IPV6_DEFAULTGW=%s\n", buf);
+ }
+#endif
+
+ if (fclose(fp) == EOF) {
+ return 12;
+ }
+
+ if (rename(SYSCONFIG_PATH"/.network",
+ SYSCONFIG_PATH"/network") == -1) {
+ return 15;
+ }
+
+ return 0;
+}
+
+void setKickstartNetwork(struct loaderData_s * loaderData, int argc,
+ char ** argv) {
+ iface_t iface;
+ gchar *bootProto = NULL, *device = NULL, *class = NULL, *ethtool = NULL;
+ gchar *essid = NULL, *wepkey = NULL, *onboot = NULL;
+ gint mtu = 1500, dhcpTimeout = -1;
+ gboolean noipv4 = FALSE, noipv6 = FALSE, noDns = FALSE, noksdev = FALSE;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksOptions[] = {
+ { "bootproto", 0, 0, G_OPTION_ARG_STRING, &bootProto, NULL, NULL },
+ { "device", 0, 0, G_OPTION_ARG_STRING, &device, NULL, NULL },
+ { "dhcpclass", 0, 0, G_OPTION_ARG_STRING, &class, NULL, NULL },
+ { "gateway", 'g', 0, G_OPTION_ARG_STRING, &loaderData->gateway,
+ NULL, NULL },
+ { "ip", 'i', 0, G_OPTION_ARG_STRING, &loaderData->ipv4, NULL, NULL },
+ { "mtu", 0, 0, G_OPTION_ARG_INT, &mtu, NULL, NULL },
+ { "nameserver", 'n', 0, G_OPTION_ARG_STRING, &loaderData->dns,
+ NULL, NULL },
+ { "netmask", 'm', 0, G_OPTION_ARG_STRING, &loaderData->netmask,
+ NULL, NULL },
+ { "noipv4", 0, 0, G_OPTION_ARG_NONE, &noipv4, NULL, NULL },
+ { "noipv6", 0, 0, G_OPTION_ARG_NONE, &noipv6, NULL, NULL },
+ { "nodns", 0, 0, G_OPTION_ARG_NONE, &noDns, NULL, NULL },
+ { "hostname", 'h', 0, G_OPTION_ARG_STRING, &loaderData->hostname,
+ NULL, NULL },
+ { "ethtool", 0, 0, G_OPTION_ARG_STRING, &ethtool, NULL, NULL },
+ { "essid", 0, 0, G_OPTION_ARG_STRING, &essid, NULL, NULL },
+ { "wepkey", 0, 0, G_OPTION_ARG_STRING, &wepkey, NULL, NULL },
+ { "onboot", 0, 0, G_OPTION_ARG_STRING, &onboot, NULL, NULL },
+ { "notksdevice", 0, 0, G_OPTION_ARG_NONE, &noksdev, NULL, NULL },
+ { "dhcptimeout", 0, 0, G_OPTION_ARG_INT, &dhcpTimeout, NULL, NULL },
+ { NULL },
+ };
+
+ iface_init_iface_t(&iface);
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to kickstart network command: %s"),
+ optErr->message);
+ g_error_free(optErr);
+ }
+
+ g_option_context_free(optCon);
+
+ /* if they've specified dhcp/bootp use dhcp for the interface */
+ if (bootProto && (!strncmp(bootProto, "dhcp", 4) ||
+ !strncmp(bootProto, "bootp", 4))) {
+ 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) {
+ /* If --device=MAC was given, translate into a device name now. */
+ if (index(device, ':') != NULL)
+ loaderData->netDev = iface_mac2device(device);
+ else
+ 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;
+
+#ifdef ENABLE_IPV6
+ if (noipv6)
+ flags |= LOADER_FLAGS_NOIPV6;
+#endif
+ }
+
+ 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;
+ int lookForLink = 0;
+ 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 = g_ascii_strup(loaderData->bootIf, -1);
+ } else {
+ ksMacAddr = g_ascii_strup(loaderData->netDev, -1);
+ }
+ }
+
+ 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 - %.50s",
+ 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 = 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;
+ }
+
+ while ((loaderData->netDev && (loaderData->netDev_set == 1)) &&
+ !strcmp(loaderData->netDev, "ibft")) {
+ char *devmacaddr = NULL;
+ char *ibftmacaddr = "";
+
+ /* get MAC from the iBFT table */
+ if (!(ibftmacaddr = ibft_iface_mac())) { /* iBFT not present or error */
+ lookForLink = 0;
+ break;
+ }
+
+ logMessage(INFO, "looking for iBFT configured device %s with link",
+ ibftmacaddr);
+ lookForLink = 0;
+
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->device)
+ continue;
+
+ devmacaddr = iface_mac2str(devs[i]->device);
+
+ if(!strcasecmp(devmacaddr, ibftmacaddr)){
+ logMessage(INFO,
+ "%s has the right MAC (%s), checking for link",
+ devmacaddr, devices[i]);
+ free(devmacaddr);
+
+ if (get_link_status(devices[i]) == 1) {
+ lookForLink = 0;
+ loaderData->netDev = devices[i];
+ logMessage(INFO, "%s has link, using it", devices[i]);
+
+ /* set the IP method to ibft if not requested differently */
+ if (loaderData->ipv4 == NULL) {
+ loaderData->ipv4 = strdup("ibft");
+ logMessage(INFO,
+ "%s will be configured using iBFT values",
+ devices[i]);
+ }
+
+ return LOADER_NOOP;
+ } else {
+ logMessage(INFO, "%s has no link, skipping it", devices[i]);
+ }
+
+ break;
+ } else {
+ free(devmacaddr);
+ }
+ }
+
+ break;
+ }
+
+ if ((loaderData->netDev && (loaderData->netDev_set == 1)) &&
+ !strcmp(loaderData->netDev, "link")) {
+ lookForLink = 1;
+ }
+
+ if (lookForLink) {
+ 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");
+ }
+
+ if (FL_CMDLINE(flags)) {
+ fprintf(stderr, "No way to determine which NIC to use, and cannot "
+ "prompt in cmdline\nmode. Halting.\n");
+ fprintf(stderr, "Please use the ksdevice= parameter to specify the "
+ "device name (e.g., eth0)\n or the MAC address of "
+ "the NIC to use for installation.\n");
+ exit(1);
+ }
+
+ 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;
+ }
+
+ checked_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."));
+
+ 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 > 300) {
+ 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, err;
+
+ if (is_nm_connected() == TRUE)
+ 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;
+
+ /* default to DHCP for IPv4 if nothing is provided */
+ if (loaderData->ipv4 == NULL) {
+ loaderData->ipv4 = strdup("dhcp");
+ loaderData->ipinfo_set = 1;
+ }
+
+ setupIfaceStruct(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.
+ */
+ if ((rc = writeDisabledNetInfo()) != 0) {
+ logMessage(ERROR, "writeDisabledNetInfo failure (%s): %d",
+ __func__, rc);
+ }
+
+ loaderData->netDev_set = 0;
+ loaderData->ipinfo_set = 0;
+ free(loaderData->ipv4);
+ loaderData->ipv4 = NULL;
+ break;
+ } else {
+ break;
+ }
+
+ err = writeEnabledNetInfo(iface);
+ if (err) {
+ logMessage(ERROR,
+ "failed to write %s data for %s (%d)",
+ SYSCONFIG_PATH, iface->device, err);
+ return -1;
+ }
+
+ err = get_connection(iface);
+ newtPopWindow();
+
+ if (err) {
+ logMessage(ERROR, "failed to start NetworkManager (%d)", err);
+ return -1;
+ }
+ } while (1);
+
+ return 0;
+}
+
+void splitHostname (char *str, char **host, char **port)
+{
+ char *rightbrack = strchr(str, ']');
+ char *firstcolon = strchr(str, ':');
+ char *secondcolon = strrchr(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 (firstcolon && secondcolon && firstcolon != secondcolon) {
+ /* 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 count = 0;
+ NMClient *client = NULL;
+ NMState state;
+ GMainLoop *loop;
+ GMainContext *ctx;
+
+ if (iface == NULL) {
+ return 1;
+ }
+
+ logMessage(DEBUGLVL, "configuring device %s", iface->device);
+
+ /* 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);
+ }
+
+ g_type_init();
+
+ client = nm_client_new();
+ if (!client) {
+ logMessage(ERROR, "%s (%d): could not connect to system bus",
+ __func__, __LINE__);
+ return 2;
+ }
+
+ /* Create a loop for processing dbus signals */
+ loop = g_main_loop_new(NULL, FALSE);
+ ctx = g_main_loop_get_context(loop);
+
+ /* pump the loop until all the messages are clear */
+ while (g_main_context_pending (ctx)) {
+ g_main_context_iteration (ctx, FALSE);
+ }
+
+ /* send message and block until a reply or error comes back */
+ while (count < 45) {
+ /* pump the loop again to clear the messages */
+ while (g_main_context_pending (ctx)) {
+ g_main_context_iteration (ctx, FALSE);
+ }
+ state = nm_client_get_state(client);
+
+ if (state == NM_STATE_CONNECTED) {
+ logMessage(INFO, "%s (%d): NetworkManager connected",
+ __func__, __LINE__);
+ res_init();
+ g_object_unref(client);
+ return 0;
+ }
+
+ sleep(1);
+ count++;
+ }
+
+ g_main_loop_unref(loop);
+ g_object_unref(client);
+ return 3;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4: */
diff --git a/loader/net.h b/loader/net.h
new file mode 100644
index 0000000..8245084
--- /dev/null
+++ b/loader/net.h
@@ -0,0 +1,77 @@
+/*
+ * 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 MANUAL_METHOD_STR _("Manual configuration")
+#ifdef ENABLE_IPV6
+#define DHCPV6_METHOD_STR _("Dynamic IP configuration (DHCPv6)")
+#define AUTO_METHOD_STR _("Automatic neighbor discovery")
+#endif
+
+#define SYSCONFIG_PATH "/etc/sysconfig"
+#define NETWORK_SCRIPTS_PATH "/etc/sysconfig/network-scripts"
+
+struct intfconfig_s {
+ newtComponent ipv4Entry, cidr4Entry;
+ newtComponent gwEntry, nsEntry;
+ const char *ipv4, *cidr4;
+#ifdef ENABLE_IPV6
+ newtComponent ipv6Entry, cidr6Entry;
+ const char *ipv6, *cidr6;
+ const char *gw6;
+#endif
+ const char *gw, *ns;
+};
+
+struct netconfopts {
+ char ipv4Choice;
+#ifdef ENABLE_IPV6
+ char ipv6Choice;
+#endif
+};
+
+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 setupIfaceStruct(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 0000000..ae13874
--- /dev/null
+++ b/loader/nfsinstall.c
@@ -0,0 +1,556 @@
+/*
+ * 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 <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 <netdb.h>
+#include <glib.h>
+#include <nm-client.h>
+#include <nm-device.h>
+#include <nm-dhcp4-config.h>
+
+#include "copy.h"
+#include "loader.h"
+#include "lang.h"
+#include "loadermisc.h"
+#include "kickstart.h"
+#include "method.h"
+#include "nfsinstall.h"
+#include "net.h"
+#include "cdinstall.h"
+#include "windows.h"
+
+#include "../isys/imount.h"
+#include "../isys/iface.h"
+#include "../isys/log.h"
+
+/* boot flags */
+extern uint64_t flags;
+
+static int nfsGetSetup(char ** hostptr, char ** dirptr, char ** optsptr) {
+ struct newtWinEntry entries[4];
+ char * buf;
+ char * newServer = *hostptr ? strdup(*hostptr) : NULL;
+ char * newDir = *dirptr ? strdup(*dirptr) : NULL;
+ char * newMountOpts = *optsptr ? strdup(*optsptr) : NULL;
+ int rc;
+
+ entries[0].text = _("NFS server name:");
+ entries[0].value = &newServer;
+ entries[0].flags = NEWT_FLAG_SCROLL;
+
+ checked_asprintf(&entries[1].text, _("%s directory:"), getProductName());
+
+ entries[1].value = &newDir;
+ entries[1].flags = NEWT_FLAG_SCROLL;
+ entries[2].text = _("NFS mount options (optional):");
+ entries[2].value = &newMountOpts;
+ entries[2].flags = NEWT_FLAG_SCROLL;
+ entries[3].text = NULL;
+ entries[3].value = NULL;
+
+ if (asprintf(&buf, _("Please enter the server and path to your %s "
+ "installation image and optionally additional "
+ "NFS mount options."), 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, "")) && rc != 2);
+
+ free(buf);
+ free(entries[1].text);
+
+ if (rc == 2) {
+ if (newServer) free(newServer);
+ if (newDir) free(newDir);
+ if (newMountOpts) free(newMountOpts);
+ return LOADER_BACK;
+ }
+
+ if (*hostptr) free(*hostptr);
+ if (*dirptr) free(*dirptr);
+ if (*optsptr) free(*optsptr);
+ *hostptr = newServer;
+ *dirptr = newDir;
+ *optsptr = newMountOpts;
+
+ return 0;
+}
+
+void parseNfsHostPathOpts(char *url, char **host, char **path, char **opts) {
+ char *tmp;
+ char *hostsrc;
+
+ logMessage(DEBUGLVL, "parseNfsHostPathOpts url: |%s|", url);
+
+ hostsrc = strdup(url);
+ *host = hostsrc;
+ tmp = strchr(*host, ':');
+
+ if (tmp) {
+ *path = strdup(tmp + 1);
+ *tmp = '\0';
+ }
+ else {
+ *path = malloc(sizeof(char *));
+ **path = '\0';
+ }
+
+ tmp = strchr(*path, ':');
+ if (tmp && (strlen(tmp) > 1)) {
+ char * c = tmp;
+ *opts = *host;
+ *host = *path;
+ *path = strdup(c + 1);
+ *c = '\0';
+ } else {
+ *opts = NULL;
+ }
+
+ logMessage(DEBUGLVL, "parseNfsHostPathOpts host: |%s|", *host);
+ logMessage(DEBUGLVL, "parseNfsHostPathOpts path: |%s|", *path);
+ logMessage(DEBUGLVL, "parseNfsHostPathOpts opts: |%s|", *opts);
+}
+
+static void addDefaultKickstartFile(char **file, char *ip) {
+ /* 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'))) {
+ checked_asprintf(file, "%s%s-kickstart", *file, ip);
+ logMessage(DEBUGLVL, "addDefaultKickstartFile file: |%s|", *file);
+ }
+}
+
+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:
+ 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 {
+ checked_asprintf(&mountOpts, "ro,%s",
+ ((struct nfsInstallData *)
+ loaderData->stage2Data)->mountOpts);
+ }
+
+ 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 *colonopts, *substr, *tmp;
+
+ logMessage(INFO, "going to do nfsGetSetup");
+ if (nfsGetSetup(&host, &directory, &mountOpts) == LOADER_BACK) {
+ loaderData->stage2Data = NULL;
+ 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 (mountOpts && strlen(mountOpts)) {
+ checked_asprintf(&colonopts, ":%s", mountOpts);
+ } else {
+ colonopts = strdup("");
+ }
+
+ checked_asprintf(&(loaderData->instRepo), "nfs%s:%s:%s",
+ colonopts, host, directory);
+ checked_asprintf(&tmp, "nfs%s:%s:%s/images/install.img",
+ colonopts, host, directory);
+
+ setStage2LocFromCmdline(tmp, loaderData);
+ free(host);
+ free(directory);
+ free(mountOpts);
+ free(colonopts);
+ free(tmp);
+ continue;
+ }
+
+ loaderData->invalidRepoParam = 1;
+ }
+
+ stage = NFS_STAGE_MOUNT;
+ break;
+
+ case NFS_STAGE_MOUNT: {
+ char *buf;
+
+ checked_asprintf(&fullPath, "%s:%.*s", host,
+ (int) (strrchr(directory, '/')-directory),
+ directory);
+ logMessage(INFO, "mounting nfs path %s", fullPath);
+
+ stage = NFS_STAGE_NFS;
+
+ if (!doPwMount(fullPath, "/mnt/stage2", "nfs", mountOpts, NULL)) {
+ checked_asprintf(&buf, "/mnt/stage2/%s",
+ strrchr(directory, '/'));
+
+ if (!access(buf, R_OK)) {
+ logMessage(INFO, "can access %s", buf);
+ rc = mountStage2(buf);
+
+ if (rc == 0) {
+ stage = NFS_STAGE_UPDATES;
+ checked_asprintf(&url, "nfs:%s:%s", host,
+ directory);
+ 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;
+ }
+
+ checked_asprintf(&buf,
+ _("That directory does not seem to "
+ "contain a %s installation image."),
+ getProductName());
+
+ 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;
+
+ checked_asprintf(&buf, "%.*s/RHupdates",
+ (int) (strrchr(fullPath, '/')-fullPath),
+ fullPath);
+
+ 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 (mountOpts)
+ free(mountOpts);
+ if (fullPath)
+ free(fullPath);
+
+ return url;
+}
+
+
+void setKickstartNfs(struct loaderData_s * loaderData, int argc,
+ char ** argv) {
+ char *substr = NULL;
+ gchar *host = NULL, *dir = NULL, *mountOpts = NULL;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksNfsOptions[] = {
+ { "server", 0, 0, G_OPTION_ARG_STRING, &host, NULL, NULL },
+ { "dir", 0, 0, G_OPTION_ARG_STRING, &dir, NULL, NULL },
+ { "opts", 0, 0, G_OPTION_ARG_STRING, &mountOpts, NULL, NULL },
+ { NULL },
+ };
+
+ logMessage(INFO, "kickstartFromNfs");
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksNfsOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to NFS kickstart method "
+ "command: %s"), optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ if (!host || !dir) {
+ logMessage(ERROR, "host and directory for nfs kickstart not specified");
+ return;
+ }
+
+ loaderData->method = METHOD_NFS;
+ loaderData->stage2Data = NULL;
+
+ substr = strstr(dir, ".img");
+ if (!substr || (substr && *(substr+4) != '\0')) {
+ checked_asprintf(&(loaderData->instRepo), "nfs:%s:%s", host, dir);
+
+ 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, i = 0;
+ iface_t iface;
+ NMClient *client = NULL;
+ NMState state;
+ const GPtrArray *devices;
+
+ 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
+ */
+ if (!url) {
+ g_type_init();
+
+ client = nm_client_new();
+ if (!client) {
+ logMessage(CRITICAL, "%s (%d): failure creating NM proxy",
+ __func__, __LINE__);
+ return 1;
+ }
+
+ state = nm_client_get_state(client);
+ if (state != NM_STATE_CONNECTED) {
+ logMessage(ERROR, "%s (%d): no active network devices",
+ __func__, __LINE__);
+ g_object_unref(client);
+ return 1;
+ }
+
+ devices = nm_client_get_devices(client);
+ for (i = 0; i < devices->len; i++) {
+ NMDevice *candidate = g_ptr_array_index(devices, i);
+ const char *devname = nm_device_get_iface(candidate);
+ NMDHCP4Config *dhcp = NULL;
+ const char *server_name = NULL;
+ const char *filename = NULL;
+ struct in_addr addr;
+ char nextserver[INET_ADDRSTRLEN+1];
+
+ if (nm_device_get_state(candidate) != NM_DEVICE_STATE_ACTIVATED)
+ continue;
+
+ if (strcmp(iface.device, devname))
+ continue;
+
+ dhcp = nm_device_get_dhcp4_config(candidate);
+ if (!dhcp) {
+ logMessage(ERROR, "no boot options received by DHCP");
+ continue;
+ }
+
+ server_name = nm_dhcp4_config_get_one_option(dhcp, "server_name");
+ if (!server_name) {
+ logMessage(ERROR, "no bootserver was found");
+ g_object_unref(client);
+ return 1;
+ }
+
+ /* 'server_name' may be a hostname or an IPv4 address */
+ memset(&nextserver, '\0', sizeof(nextserver));
+ if (inet_pton(AF_INET, server_name, &addr) >= 1) {
+ strcpy(nextserver, server_name);
+ } else {
+ struct hostent *he = gethostbyname(server_name);
+ if (he != NULL) {
+ if (inet_ntop(AF_INET, he->h_addr_list[0],
+ nextserver, INET_ADDRSTRLEN) == NULL) {
+ memset(&nextserver, '\0', sizeof(nextserver));
+ }
+ }
+ }
+
+ filename = nm_dhcp4_config_get_one_option(dhcp, "filename");
+ if (filename == NULL) {
+ checked_asprintf(&url, "%s:/kickstart/", nextserver);
+ logMessage(ERROR, "bootp: no bootfile received");
+ } else {
+ checked_asprintf(&url, "%s:%s", nextserver, filename);
+ logMessage(INFO, "bootp: bootfile is %s", filename);
+ }
+
+ break;
+ }
+
+ g_object_unref(client);
+ }
+
+ /* get the IP of the target system */
+ if ((ip = iface_ip2str(loaderData->netDev, AF_INET)) == NULL) {
+ logMessage(ERROR, "iface_ip2str returned NULL");
+ return 1;
+ }
+
+ logMessage(INFO, "url is %s", url);
+
+ parseNfsHostPathOpts(url, &host, &path, &opts);
+ addDefaultKickstartFile(&path, ip);
+
+ /* 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 == '/') {
+ checked_asprintf(&host, "%s:%s", host, path);
+ } else {
+ checked_asprintf(&host, "%s:/%s", host, path);
+ }
+ }
+
+ logMessage(INFO, "file location: nfs:%s/%s", host, file);
+
+ if (!doPwMount(host, "/tmp/mnt", "nfs", opts, NULL)) {
+ char * buf;
+
+ checked_asprintf(&buf, "/tmp/mnt/%s", file);
+
+ 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 0000000..99a8b06
--- /dev/null
+++ b/loader/nfsinstall.h
@@ -0,0 +1,40 @@
+/*
+ * 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);
+void parseNfsHostPathOpts(char * url, char ** host, char ** path, char ** opts);
+
+#endif
diff --git a/loader/rpmextract.c b/loader/rpmextract.c
new file mode 100644
index 0000000..d1549b8
--- /dev/null
+++ b/loader/rpmextract.c
@@ -0,0 +1,325 @@
+/* unpack the payload of RPM package to the current directory
+ *
+ * File name: rpmextract.c
+ * Date: 2009/12/18
+ * Author: Martin Sivak <msivak at redhat dot com>
+ *
+ * Copyright (C) 2009 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rpm/rpmlib.h> /* rpmReadPackageFile .. */
+#include <rpm/rpmtag.h>
+#include <rpm/rpmio.h>
+#include <rpm/rpmpgp.h>
+
+#include <rpm/rpmts.h>
+
+#include <stdio.h>
+#include <archive.h>
+#include <archive_entry.h>
+
+#include "loader.h"
+#include "rpmextract.h"
+
+#include "../isys/log.h"
+
+/*
+ * internal structure to pass to libarchive callbacks
+ */
+
+struct cpio_mydata {
+ FD_t gzdi;
+ char *buffer;
+};
+
+/*
+ * libarchive callbacks
+ */
+
+ssize_t rpm_myread(struct archive *a, void *client_data, const void **buff)
+{
+ struct cpio_mydata *mydata = client_data;
+ *buff = mydata->buffer;
+ return Fread(mydata->buffer, BUFFERSIZE, 1, mydata->gzdi);
+}
+
+int rpm_myclose(struct archive *a, void *client_data)
+{
+ struct cpio_mydata *mydata = client_data;
+ if (mydata->gzdi > 0)
+ Fclose(mydata->gzdi);
+ return ARCHIVE_OK;
+}
+
+/* read data from RPM header */
+
+const char * headerGetString(Header h, rpmTag tag)
+{
+ const char *res = NULL;
+ struct rpmtd_s td;
+
+ if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
+ if (rpmtdCount(&td) == 1) {
+ res = rpmtdGetString(&td);
+ }
+ rpmtdFreeData(&td);
+ }
+ return res;
+}
+
+/*
+ * explode source RPM into the current directory
+ * use filters to skip packages and files we do not need
+ */
+int explodeRPM(const char *source,
+ filterfunc filter,
+ dependencyfunc provides,
+ dependencyfunc deps,
+ void* userptr)
+{
+ char buffer[BUFFERSIZE+1]; /* make space for trailing \0 */
+ FD_t fdi;
+ Header h;
+ char * rpmio_flags = NULL;
+ rpmRC rc;
+ FD_t gzdi;
+ struct archive *cpio;
+ struct archive_entry *cpio_entry;
+ struct cpio_mydata cpio_mydata;
+
+ rpmts ts;
+ rpmVSFlags vsflags;
+ const char *compr;
+
+ if (strcmp(source, "-") == 0)
+ fdi = fdDup(STDIN_FILENO);
+ else
+ fdi = Fopen(source, "r.ufdio");
+
+ if (Ferror(fdi)) {
+ const char *srcname = (strcmp(source, "-") == 0) ? "<stdin>" : source;
+ logMessage(ERROR, "%s: %s\n", srcname, Fstrerror(fdi));
+ return EXIT_FAILURE;
+ }
+ rpmReadConfigFiles(NULL, NULL);
+
+ /* Initialize RPM transaction */
+ ts = rpmtsCreate();
+ vsflags = 0;
+
+ /* Do not check digests, signatures or headers */
+ vsflags |= _RPMVSF_NODIGESTS;
+ vsflags |= _RPMVSF_NOSIGNATURES;
+ vsflags |= RPMVSF_NOHDRCHK;
+ (void) rpmtsSetVSFlags(ts, vsflags);
+
+ rc = rpmReadPackageFile(ts, fdi, "rpm2dir", &h);
+
+ ts = rpmtsFree(ts);
+
+ switch (rc) {
+ case RPMRC_OK:
+ case RPMRC_NOKEY:
+ case RPMRC_NOTTRUSTED:
+ break;
+ case RPMRC_NOTFOUND:
+ logMessage(ERROR, "%s is not an RPM package", source);
+ return EXIT_FAILURE;
+ break;
+ case RPMRC_FAIL:
+ default:
+ logMessage(ERROR, "error reading header from %s package\n", source);
+ return EXIT_FAILURE;
+ break;
+ }
+
+ /* Retrieve all dependencies and run them through deps function */
+ while (deps) {
+ struct rpmtd_s td;
+ const char *depname;
+
+ if (!headerGet(h, RPMTAG_REQUIRENAME, &td, HEADERGET_MINMEM))
+ break;
+
+ /* iterator */
+ while ((depname = rpmtdNextString(&td))) {
+ if (deps(depname, userptr)) {
+ Fclose(fdi);
+ return EXIT_BADDEPS;
+ }
+ }
+ rpmtdFreeData(&td);
+ break;
+ }
+
+ /* Retrieve all provides and run them through provides function */
+ while (provides) {
+ struct rpmtd_s td;
+ const char *depname;
+ int found = 0;
+
+ if (!headerGet(h, RPMTAG_PROVIDES, &td, HEADERGET_MINMEM))
+ break;
+
+ /* iterator */
+ while ((depname = rpmtdNextString(&td))) {
+ if (!provides(depname, userptr)) {
+ found++;
+ }
+ }
+ rpmtdFreeData(&td);
+ if (found<=0)
+ return EXIT_BADDEPS;
+ break;
+ }
+
+ /* Retrieve type of payload compression. */
+ compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
+ if (compr && strcmp(compr, "gzip")) {
+ checked_asprintf(&rpmio_flags, "r.%sdio", compr);
+ }
+ else {
+ checked_asprintf(&rpmio_flags, "r.gzdio");
+ }
+
+ /* Open uncompressed cpio stream */
+ gzdi = Fdopen(fdi, rpmio_flags);
+ free(rpmio_flags);
+
+ if (gzdi == NULL) {
+ logMessage(ERROR, "cannot re-open payload: %s\n", Fstrerror(gzdi));
+ return EXIT_FAILURE;
+ }
+
+ /* initialize cpio decompressor */
+ cpio = archive_read_new();
+ if (cpio==NULL) {
+ Fclose(gzdi);
+ return -1;
+ }
+
+ cpio_mydata.gzdi = gzdi;
+ cpio_mydata.buffer = buffer;
+ archive_read_support_compression_all(cpio);
+ archive_read_support_format_all(cpio);
+ rc = archive_read_open(cpio, &cpio_mydata, NULL, rpm_myread, rpm_myclose);
+
+ /* check the status of archive_open */
+ if (rc != ARCHIVE_OK){
+ Fclose(gzdi);
+ return -1;
+ }
+
+ /* read all files in cpio archive */
+ while ((rc = archive_read_next_header(cpio, &cpio_entry)) == ARCHIVE_OK){
+ const struct stat *fstat;
+ int64_t fsize;
+ const char* filename;
+ int needskip = 1; /* do we need to read the data to get to the next header? */
+ int offset = 0;
+ int towrite = 0;
+
+ filename = archive_entry_pathname(cpio_entry);
+ fstat = archive_entry_stat(cpio_entry);
+ fsize = archive_entry_size(cpio_entry);
+
+ /* Strip leading slashes */
+ while (filename[offset] == '/')
+ offset+=1;
+
+ /* Strip leading ./ */
+ while (filename[offset] == '.' && filename[offset+1] == '/')
+ offset+=2;
+
+ /* Other file type - we do not care except special cases */
+ if (!S_ISREG(fstat->st_mode))
+ towrite = 1;
+ else
+ towrite = 2;
+
+ if (filter && filter(filename+offset, fstat, userptr)) {
+ /* filter this file */
+ towrite = 0;
+ }
+
+ /* Create directories */
+ char* dirname = strdup(filename+offset);
+
+ /* If the dup fails, let's hope the dirs already exist */
+ if (dirname){
+ char* dirptr = dirname;
+ while (dirptr && *dirptr) {
+ dirptr = strchr(dirptr, '/');
+ if (dirptr) {
+ *dirptr = 0;
+ mkdir(dirname, 0700);
+ *dirptr = '/';
+ dirptr++;
+ }
+ }
+ free(dirname);
+ }
+
+ /* Regular file */
+ if (towrite>=2) {
+ FILE *fdout = fopen(filename+offset, "w");
+
+ if (fdout==NULL){
+ rc = 33;
+ break;
+ }
+
+ rc = archive_read_data_into_fd(cpio, fileno(fdout));
+ if (rc!=ARCHIVE_OK) {
+ /* XXX We didn't get the file.. well.. */
+ needskip = 0;
+ } else {
+ needskip = 0;
+ fclose(fdout);
+ }
+ }
+
+ /* symlink, we assume that the path contained in symlink
+ * is shorter than BUFFERSIZE */
+ while (towrite && S_ISLNK(fstat->st_mode)) {
+ char symlinkbuffer[BUFFERSIZE-1];
+
+ needskip = 0;
+ if ((rc = archive_read_data(cpio, symlinkbuffer, fsize))!=ARCHIVE_OK) {
+ /* XXX We didn't get the file.. well.. */
+ break;
+ }
+
+ if (symlink(buffer, filename+offset)) {
+ logMessage(ERROR, "Failed to create symlink %s -> %s", filename+offset, buffer);
+ }
+
+ break;
+ }
+
+ if(needskip)
+ archive_read_data_skip(cpio);
+ }
+
+ archive_read_finish(cpio);
+
+ return rc != ARCHIVE_OK;
+}
diff --git a/loader/rpmextract.h b/loader/rpmextract.h
new file mode 100644
index 0000000..53a90cf
--- /dev/null
+++ b/loader/rpmextract.h
@@ -0,0 +1,45 @@
+/*
+ File name: rpmextract.h
+ Date: 2009/09/16
+ Author: msivak
+
+ Copyright (C) 2009 Red Hat, Inc.
+
+ 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 __RPMEXTRACT_H__
+#define __RPMEXTRACT_H__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define EXIT_BADDEPS 4
+#define BUFFERSIZE 1024
+
+/* both filter functions return 0 - match, 1 - match not found */
+typedef int (*filterfunc)(const char* name, const struct stat *fstat, void *userptr);
+typedef int (*dependencyfunc)(const char* depends, void *userptr);
+
+int explodeRPM(const char* file,
+ filterfunc filter,
+ dependencyfunc provides,
+ dependencyfunc deps,
+ void* userptr);
+
+#endif
+
+/* end of rpmextract.h */
diff --git a/loader/selinux.c b/loader/selinux.c
new file mode 100644
index 0000000..66bfe4d
--- /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 "../isys/log.h"
+
+int loadpolicy() {
+ int pid, status;
+
+ logMessage(INFO, "Loading SELinux policy");
+
+ if (!(pid = fork())) {
+ setenv("LD_LIBRARY_PATH", LIBPATH, 1);
+ execl("/sbin/load_policy",
+ "/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 0000000..5877ddd
--- /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 0000000..3b481f2
--- /dev/null
+++ b/loader/shutdown.c
@@ -0,0 +1,153 @@
+/*
+ * 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>
+
+#include "init.h"
+
+void disableSwap(void);
+void unmountFilesystems(void);
+
+static void performTerminations(void) {
+ sync();
+ printf("sending termination signals...");
+ kill(-1, 15);
+ sleep(2);
+ printf("done\n");
+
+ printf("sending kill signals...");
+ kill(-1, 9);
+ sleep(2);
+ printf("done\n");
+}
+
+static void performUnmounts(void) {
+ int ignore;
+
+ printf("disabling swap...\n");
+ disableSwap();
+
+ printf("unmounting filesystems...\n");
+ unmountFilesystems();
+
+ printf("waiting for mdraid sets to become clean...\n");
+ ignore = system("/sbin/mdadm --wait-clean --scan");
+}
+
+static void performReboot(reboot_action rebootAction) {
+ switch (rebootAction) {
+ case POWEROFF:
+ printf("powering off system\n");
+ sleep(2);
+ reboot(RB_POWER_OFF);
+ break;
+ case REBOOT:
+ printf("rebooting system\n");
+ sleep(2);
+#if USE_MINILIBC
+ reboot(0xfee1dead, 672274793, 0x1234567);
+#else
+ reboot(RB_AUTOBOOT);
+#endif
+ break;
+ case HALT:
+ printf("halting system\n");
+ reboot(RB_HALT_SYSTEM);
+ break;
+ default:
+ break;
+ }
+}
+
+static void performDelayedReboot()
+{
+ printf("The system will be rebooted when you press Ctrl-C or Ctrl-Alt-Delete.\n");
+ while (1) {
+ sleep(1);
+ }
+}
+
+void shutDown(int doKill, reboot_action rebootAction)
+{
+ static int reentered = 0;
+
+ if (reentered) {
+ performUnmounts();
+ performTerminations();
+ performReboot(rebootAction);
+ }
+ reentered = 1;
+ if (rebootAction != DELAYED_REBOOT && doKill) {
+ performUnmounts();
+ performTerminations();
+ performReboot(rebootAction);
+ } else {
+ performDelayedReboot();
+ }
+ exit(0);
+}
+
+#ifdef AS_SHUTDOWN
+int main(int argc, char ** argv) {
+ int fd;
+ reboot_action rebootAction = HALT;
+ int doKill = 1;
+ int i = 1;
+
+ while (i < argc) {
+ if (!strncmp("-r", argv[i], 2))
+ rebootAction = REBOOT;
+ else if (!strncmp("--nokill", argv[i], 8))
+ doKill = 0;
+ else if (!strncmp("-P", argv[i], 2))
+ rebootAction = POWEROFF;
+ 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(doKill, rebootAction);
+ return 0;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 ts=4: */
diff --git a/loader/simplemot b/loader/simplemot
new file mode 100755
index 0000000..bebc1cf
--- /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 0000000..3c123ae
--- /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 "../isys/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 0000000..5c34154
--- /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 0000000..e3a021d
--- /dev/null
+++ b/loader/telnetd.c
@@ -0,0 +1,256 @@
+/*
+ * 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 "../isys/log.h"
+
+#include "lang.h"
+#include "loader.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 *ipaddr = NULL;
+ iface_t iface;
+
+ iface_init_iface_t(&iface);
+
+ if (kickstartNetworkUp(loaderData, &iface)) {
+ logMessage(ERROR, "unable to bring up network");
+ return;
+ }
+
+ ipaddr = iface_ip2str(iface.device, AF_INET);
+ if (ipaddr == NULL) {
+ logMessage(ERROR, "%s (%d): no IP address found for %s",
+ __func__, __LINE__, iface.device);
+ return;
+ }
+
+ logMessage(INFO, "going to beTelnet for %s", ipaddr);
+ 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 0000000..fedb0fa
--- /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/udelay.h b/loader/udelay.h
new file mode 100644
index 0000000..5315074
--- /dev/null
+++ b/loader/udelay.h
@@ -0,0 +1,199 @@
+/*
+ * 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;
+ ts->tv_nsec = tv.tv_usec * NSECS_PER_USEC;
+ }
+ 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 0000000..af5caaa
--- /dev/null
+++ b/loader/undomounts.c
@@ -0,0 +1,239 @@
+/*
+ * 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"
+
+/* Defined in linux/fs.h, but inside __KERNEL__. */
+#ifdef MNT_DETACH
+#undef MNT_DETACH
+#endif
+
+#define MNT_DETACH 0x00000002
+
+struct unmountInfo {
+ char * name;
+ int mounted;
+ int loopDevice;
+ enum { FS, LOOP } what;
+} ;
+
+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 (umount2(fs[this].name, MNT_DETACH) < 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 (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 0000000..c1a4814
--- /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 0000000..54d1398
--- /dev/null
+++ b/loader/urlinstall.c
@@ -0,0 +1,414 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "../isys/iface.h"
+#include "../isys/log.h"
+
+#include "copy.h"
+#include "kickstart.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "lang.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;
+
+char **extraHeaders = NULL;
+
+static char **headers() {
+ int len = 2;
+
+ /* The list of HTTP headers is unlikely to change, unless a new ethernet
+ * device suddenly shows up since last time we downloaded a file. So,
+ * cache the result here to save some time.
+ */
+ if (extraHeaders != NULL)
+ return extraHeaders;
+
+ if ((extraHeaders = realloc(extraHeaders, 2*sizeof(char *))) == NULL) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ checked_asprintf(&extraHeaders[0], "X-Anaconda-Architecture: %s", getProductArch());
+ checked_asprintf(&extraHeaders[1], "X-Anaconda-System-Release: %s", getProductName());
+
+ if (FL_KICKSTART_SEND_MAC(flags)) {
+ /* find all ethernet devices and make a header entry for each one */
+ int i;
+ char *dev, *mac;
+ struct device **devices;
+
+ devices = getDevices(DEVICE_NETWORK);
+ for (i = 0; devices && devices[i]; i++) {
+ dev = devices[i]->device;
+ mac = iface_mac2str(dev);
+
+ if (mac) {
+ extraHeaders = realloc(extraHeaders, (len+1)*sizeof(char *));
+ checked_asprintf(&extraHeaders[len], "X-RHN-Provisioning-MAC-%d: %s %s",
+ i, dev, mac);
+
+ len++;
+ free(mac);
+ }
+ }
+ }
+
+ if (FL_KICKSTART_SEND_SERIAL(flags) && !access("/sbin/dmidecode", X_OK)) {
+ FILE *f;
+ char sn[1024];
+ size_t sn_len;
+
+ if ((f = popen("/sbin/dmidecode -s system-serial-number", "r")) == NULL) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ sn_len = fread(sn, sizeof(char), 1023, f);
+ if (ferror(f)) {
+ logMessage(CRITICAL, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ sn[sn_len] = '\0';
+ pclose(f);
+
+ extraHeaders = realloc(extraHeaders, (len+1)*sizeof(char *));
+
+ checked_asprintf(&extraHeaders[len], "X-System-Serial-Number: %s", sn);
+
+ len++;
+ }
+
+ extraHeaders = realloc(extraHeaders, (len+1)*sizeof(char *));
+ extraHeaders[len] = NULL;
+ return extraHeaders;
+}
+
+static int loadSingleUrlImage(struct loaderData_s *loaderData, struct iurlinfo *ui,
+ char *dest, char *mntpoint, char *device, int silentErrors) {
+ char **ehdrs = NULL;
+ int status;
+
+ if (!strncmp(ui->url, "http", 4))
+ ehdrs = headers();
+
+ status = urlinstTransfer(loaderData, ui, ehdrs, dest);
+ if (status) {
+ if (!silentErrors) {
+ newtWinMessage(_("Error"), _("OK"),
+ _("Unable to retrieve %s."), ui->url);
+ }
+
+ return 2;
+ }
+
+ if (dest != NULL) {
+ if (mountLoopback(dest, mntpoint, device)) {
+ logMessage(ERROR, "Error mounting %s on %s: %m", device, mntpoint);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void copyWarnFn (char *msg) {
+ logMessage(WARNING, msg);
+}
+
+static void copyErrorFn (char *msg) {
+ newtWinMessage(_("Error"), _("OK"), _(msg));
+}
+
+static int loadUrlImages(struct loaderData_s *loaderData, struct iurlinfo *ui) {
+ char *oldUrl, *path, *dest, *slash;
+ int rc;
+
+ oldUrl = strdup(ui->url);
+ free(ui->url);
+
+ /* Figure out the path where updates.img and product.img files are
+ * kept. Since ui->url points to a stage2 image file, we just need
+ * to trim off the file name and look in the same directory.
+ */
+ if ((slash = strrchr(oldUrl, '/')) == NULL)
+ return 0;
+
+ if ((path = strndup(oldUrl, slash-oldUrl)) == NULL)
+ path = oldUrl;
+
+ /* grab the updates.img before install.img so that we minimize our
+ * ramdisk usage */
+ checked_asprintf(&ui->url, "%s/%s", path, "updates.img");
+
+ if (!loadSingleUrlImage(loaderData, ui, "/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(ui->url);
+
+ /* grab the product.img before install.img so that we minimize our
+ * ramdisk usage */
+ checked_asprintf(&ui->url, "%s/%s", path, "product.img");
+
+ if (!loadSingleUrlImage(loaderData, ui, "/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(ui->url);
+ ui->url = strdup(oldUrl);
+
+ checked_asprintf(&dest, "/tmp/install.img");
+
+ rc = loadSingleUrlImage(loaderData, ui, dest, "/mnt/runtime", "/dev/loop0", 0);
+ free(dest);
+ free(oldUrl);
+
+ 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) {
+ urlInstallData *stage2Data = (urlInstallData *) loaderData->stage2Data;
+ struct iurlinfo ui;
+
+ 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 && stage2Data) {
+ ui.url = strdup(stage2Data->url);
+ logMessage(INFO, "URL_STAGE_MAIN: url is %s", ui.url);
+
+ if (!ui.url) {
+ logMessage(ERROR, "missing URL specification");
+ loaderData->method = -1;
+ free(loaderData->stage2Data);
+ loaderData->stage2Data = NULL;
+
+ if (loaderData->inferredStage2)
+ loaderData->invalidRepoParam = 1;
+
+ break;
+ }
+
+ /* ks info was adequate, lets skip to fetching image */
+ stage = URL_STAGE_FETCH;
+ break;
+ } else {
+ char *substr;
+
+ if (urlMainSetupPanel(loaderData, &ui)) {
+ loaderData->stage2Data = NULL;
+ return NULL;
+ }
+
+ /* If the user-provided URL points at a repo instead of
+ * a stage2 image, fix it up now.
+ */
+ substr = strstr(ui.url, ".img");
+ if (!substr || (substr && *(substr+4) != '\0')) {
+ loaderData->instRepo = strdup(ui.url);
+
+ checked_asprintf(&ui.url, "%s/images/install.img",
+ ui.url);
+ }
+
+ loaderData->invalidRepoParam = 1;
+ }
+
+ stage = URL_STAGE_FETCH;
+ break;
+ }
+
+ case URL_STAGE_FETCH: {
+ if (loadUrlImages(loaderData, &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;
+ }
+ }
+
+ return ui.url;
+}
+
+int getFileFromUrl(char * url, char * dest,
+ struct loaderData_s * loaderData) {
+ struct iurlinfo ui;
+ char **ehdrs = NULL;
+ int rc;
+ iface_t iface;
+
+ iface_init_iface_t(&iface);
+
+ if (kickstartNetworkUp(loaderData, &iface)) {
+ logMessage(ERROR, "unable to bring up network");
+ return 1;
+ }
+
+ memset(&ui, 0, sizeof(ui));
+ ui.url = url;
+
+ logMessage(INFO, "file location: %s", url);
+
+ if (!strncmp(url, "http", 4)) {
+ ehdrs = headers();
+ }
+
+ rc = urlinstTransfer(loaderData, &ui, ehdrs, dest);
+ if (rc) {
+ logMessage(ERROR, "failed to retrieve %s", ui.url);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* 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 *substr = NULL;
+ gchar *url = NULL, *proxy = NULL;
+ GOptionContext *optCon = g_option_context_new(NULL);
+ GError *optErr = NULL;
+ GOptionEntry ksUrlOptions[] = {
+ { "url", 0, 0, G_OPTION_ARG_STRING, &url, NULL, NULL },
+ { "proxy", 0, 0, G_OPTION_ARG_STRING, &proxy, NULL, NULL },
+ { NULL },
+ };
+
+ logMessage(INFO, "kickstartFromUrl");
+
+ g_option_context_set_help_enabled(optCon, FALSE);
+ g_option_context_add_main_entries(optCon, ksUrlOptions, NULL);
+
+ if (!g_option_context_parse(optCon, &argc, &argv, &optErr)) {
+ startNewt();
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Bad argument to URL kickstart method "
+ "command: %s"), optErr->message);
+ g_error_free(optErr);
+ g_option_context_free(optCon);
+ return;
+ }
+
+ g_option_context_free(optCon);
+
+ if (!url) {
+ newtWinMessage(_("Kickstart Error"), _("OK"),
+ _("Must supply a --url argument to Url kickstart method."));
+ return;
+ }
+
+ /* determine install type */
+ if (strncmp(url, "http", 4) && strncmp(url, "ftp://", 6)) {
+ 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(urlInstallData *), 1)) == NULL)
+ return;
+
+ ((urlInstallData *)loaderData->stage2Data)->url = url;
+ loaderData->method = METHOD_URL;
+ }
+
+ if (proxy) {
+ splitProxyParam(proxy, &loaderData->proxyUser,
+ &loaderData->proxyPassword,
+ &loaderData->proxy);
+ }
+ 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 0000000..710b0ae
--- /dev/null
+++ b/loader/urlinstall.h
@@ -0,0 +1,36 @@
+/*
+ * 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"
+#include "urls.h"
+
+typedef struct iurlinfo urlInstallData;
+
+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 0000000..a0441fc
--- /dev/null
+++ b/loader/urls.c
@@ -0,0 +1,370 @@
+/*
+ * urls.c - url handling code
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2009 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>
+ * Chris Lumens <clumens@redhat.com>
+ */
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <newt.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <curl/curl.h>
+
+#include "../isys/log.h"
+
+#include "lang.h"
+#include "loader.h"
+#include "loadermisc.h"
+#include "urls.h"
+#include "windows.h"
+#include "net.h"
+
+#define NMATCH 10
+
+/* boot flags */
+extern uint64_t flags;
+
+/* This is just a wrapper around the windows.c progress callback that accepts
+ * the arguments libcurl provides.
+ */
+int progress_cb(void *data, double dltotal, double dlnow, double ultotal, double ulnow) {
+ struct progressCBdata *cb_data = (struct progressCBdata *) data;
+
+ progressCallback(cb_data, dlnow, dltotal);
+ return 0;
+}
+
+int splitProxyParam(char *param, char **user, char **password, char **proxy) {
+ /* proxy=[protocol://][username[:password]@]host[:port] */
+ char *pattern = "([[:alpha:]]+://)?(([[:alnum:]]+)(:[^:@]+)?@)?([^:]+)(:[[:digit:]]+)?(/.*)?";
+ regex_t re;
+ regmatch_t pmatch[NMATCH];
+
+ if (regcomp(&re, pattern, REG_EXTENDED)) {
+ return 0;
+ }
+
+ if (regexec(&re, param, NMATCH, pmatch, 0) == REG_NOMATCH) {
+ regfree(&re);
+ return 0;
+ }
+
+ /* Match 0 is always the whole string (assuming regexec matched anything)
+ * so skip it. Then, these indices are just the number of the starting
+ * paren in pattern above. Make sure to change these whenever changing
+ * the pattern.
+ */
+ if (pmatch[3].rm_so != -1)
+ *user = strndup(param+pmatch[3].rm_so, pmatch[3].rm_eo-pmatch[3].rm_so);
+
+ /* Skip the leading colon. */
+ if (pmatch[4].rm_so != -1)
+ *password = strndup(param+pmatch[4].rm_so+1, pmatch[4].rm_eo-pmatch[4].rm_so-1);
+
+ if (pmatch[5].rm_so != -1) {
+ char *portStr = "";
+
+ if (pmatch[6].rm_so != -1)
+ portStr = strndup(param+pmatch[6].rm_so, pmatch[6].rm_eo-pmatch[6].rm_so);
+
+ /* If no parameter was given, default to HTTP. yum will want to know
+ * the protocol, and curl will just ignore it if given.
+ */
+ if (pmatch[1].rm_so != -1) {
+ checked_asprintf(proxy, "%.*s%.*s%s", pmatch[1].rm_eo-pmatch[1].rm_so,
+ param+pmatch[1].rm_so,
+ pmatch[5].rm_eo-pmatch[5].rm_so,
+ param+pmatch[5].rm_so,
+ portStr);
+ } else {
+ checked_asprintf(proxy, "http://%.*s%s", pmatch[5].rm_eo-pmatch[5].rm_so,
+ param+pmatch[5].rm_so,
+ portStr);
+ }
+ }
+
+ regfree(&re);
+ return 1;
+}
+
+int urlinstTransfer(struct loaderData_s *loaderData, struct iurlinfo *ui,
+ char **extraHeaders, char *dest) {
+ struct progressCBdata *cb_data;
+ CURL *curl = NULL;
+ CURLcode status;
+ struct curl_slist *headers = NULL;
+ char *version;
+ FILE *f = NULL;
+
+ logMessage(INFO, "transferring %s", ui->url);
+
+ f = fopen(dest, "w");
+
+ /* Initialize libcurl */
+ curl_global_init(CURL_GLOBAL_SSL);
+ curl = curl_easy_init();
+
+ checked_asprintf(&version, "anaconda/%s", VERSION);
+
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, version);
+ curl_easy_setopt(curl, CURLOPT_URL, ui->url);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10);
+
+ /* If a proxy was provided, add the options for that now. */
+ if (loaderData->proxy && strcmp(loaderData->proxy, "")) {
+ curl_easy_setopt(curl, CURLOPT_PROXY, loaderData->proxy);
+
+ if (loaderData->proxyUser && strcmp(loaderData->proxyUser, ""))
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME,
+ loaderData->proxyUser);
+
+ if (loaderData->proxyPassword && strcmp(loaderData->proxyPassword, ""))
+ curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD,
+ loaderData->proxyPassword);
+ }
+
+ if (extraHeaders) {
+ int i;
+ for (i = 0; extraHeaders[i] != NULL; i++) {
+ headers = curl_slist_append(headers, extraHeaders[i]);
+ }
+
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ }
+
+ /* Only set up the progress bar if we've got a UI to display it. */
+ if (FL_CMDLINE(flags)) {
+ printf("%s %s...\n", _("Retrieving"), ui->url);
+ } else {
+ char *filename;
+
+ filename = strrchr(ui->url, '/');
+ if (!filename)
+ filename = ui->url;
+
+ cb_data = winProgressBar(70, 5, _("Retrieving"), "%s %s...", _("Retrieving"), filename);
+
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_cb);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, cb_data);
+ }
+
+ /* Finally, do the transfer. */
+ status = curl_easy_perform(curl);
+ if (status)
+ logMessage(ERROR, "Error downloading %s: %s", ui->url, curl_easy_strerror(status));
+
+ if (!FL_CMDLINE(flags))
+ newtPopWindow();
+
+ if (headers)
+ curl_slist_free_all(headers);
+
+ fclose(f);
+ free(version);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ return status;
+}
+
+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;
+}
+
+static void setProxySensitivity(newtComponent co, void *dptr) {
+ int i;
+
+ /* It's 3 because there are three entry boxes in the proxy grid. Lame. */
+ for (i = 0; i < 3; i++) {
+ newtEntrySetFlags(*((newtComponent *) dptr), NEWT_FLAG_DISABLED,
+ NEWT_FLAGS_TOGGLE);
+ dptr += sizeof(newtComponent);
+ }
+
+ return;
+}
+
+int urlMainSetupPanel(struct loaderData_s *loaderData, struct iurlinfo * ui) {
+ newtComponent form, okay, cancel, urlEntry, proxyCheckbox;
+ newtComponent proxyEntries[3];
+ newtComponent answer, text;
+ char enableProxy;
+ char *url = "", *proxy = "", *proxyUser = "", *proxyPassword = "";
+ char * reflowedText = NULL;
+ int width, height;
+ newtGrid buttons, grid, proxyGrid;
+ char * buf = NULL;
+
+ /* Populate the UI with whatever initial value we've got. */
+ if (ui && ui->url)
+ url = ui->url;
+
+ if (loaderData->proxy)
+ proxy = loaderData->proxy;
+
+ if (loaderData->proxyUser)
+ proxyUser = loaderData->proxyUser;
+
+ if (loaderData->proxyPassword)
+ proxyPassword = loaderData->proxyPassword;
+
+ buttons = newtButtonBar(_("OK"), &okay, _("Back"), &cancel, NULL);
+
+ checked_asprintf(&buf,
+ _("Please enter the URL containing the %s installation image on your server."),
+ getProductName());
+
+ 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);
+
+ /* If we've been provided with proxy settings already, enable the proxy
+ * grid. This will make sure all the fields get filled in, too.
+ */
+ enableProxy = loaderData->proxy != NULL && strcmp("", loaderData->proxy) ? '*' : ' ';
+
+ proxyCheckbox = newtCheckbox(-1, -1, _("Enable HTTP proxy"), enableProxy,
+ NULL, &enableProxy);
+ newtComponentAddCallback(proxyCheckbox, setProxySensitivity, &proxyEntries);
+
+ proxyEntries[0] = newtEntry(-1, -1, proxy, 35, (const char **) &proxy, NEWT_FLAG_SCROLL);
+ proxyEntries[1] = newtEntry(-1, -1, proxyUser, 15, (const char **) &proxyUser, NEWT_FLAG_SCROLL);
+ proxyEntries[2] = newtEntry(-1, -1, proxyPassword, 15, (const char **) &proxyPassword, NEWT_FLAG_SCROLL|NEWT_FLAG_PASSWORD);
+
+ /* Set the initial proxy grid sensitivity to match. */
+ if (enableProxy == ' ')
+ setProxySensitivity(proxyCheckbox, proxyEntries);
+
+ proxyGrid = newtCreateGrid(2, 3);
+ newtGridSetField(proxyGrid, 0, 0, NEWT_GRID_COMPONENT,
+ newtLabel(-1, -1, _("Proxy URL")),
+ 0, 0, 0, 0, 0, NEWT_ANCHOR_LEFT);
+ newtGridSetField(proxyGrid, 1, 0, NEWT_GRID_COMPONENT, proxyEntries[0],
+ 0, 0, 0, 0, 0, NEWT_ANCHOR_LEFT);
+ newtGridSetField(proxyGrid, 0, 1, NEWT_GRID_COMPONENT,
+ newtLabel(-1, -1, _("Username")),
+ 0, 0, 0, 1, 0, NEWT_ANCHOR_LEFT);
+ newtGridSetField(proxyGrid, 1, 1, NEWT_GRID_COMPONENT, proxyEntries[1],
+ 0, 0, 0, 1, 0, NEWT_ANCHOR_LEFT);
+ newtGridSetField(proxyGrid, 0, 2, NEWT_GRID_COMPONENT,
+ newtLabel(-1, -1, _("Password")),
+ 0, 0, 0, 1, 0, NEWT_ANCHOR_LEFT);
+ newtGridSetField(proxyGrid, 1, 2, NEWT_GRID_COMPONENT, proxyEntries[2],
+ 0, 0, 0, 1, 0, NEWT_ANCHOR_LEFT);
+
+ grid = newtCreateGrid(1, 5);
+ 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_COMPONENT, proxyCheckbox,
+ 0, 0, 0, 1, 0, NEWT_ANCHOR_LEFT);
+ newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, proxyGrid,
+ 0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);
+ newtGridSetField(grid, 0, 4, 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 (strncmp(url, "http", 4) && strncmp(url, "ftp://", 6)) {
+ newtWinMessage(_("Error"), _("OK"),
+ _("URL must be either an ftp or http URL"));
+ continue;
+ }
+
+ ui->url = strdup(url);
+
+ if (enableProxy == '*') {
+ loaderData->proxy = strdup(proxy);
+ loaderData->proxyUser = strdup(proxyUser);
+ loaderData->proxyPassword = strdup(proxyPassword);
+ } else {
+ loaderData->proxy = "";
+ loaderData->proxyUser = "";
+ loaderData->proxyPassword = "";
+ }
+
+ /* FIXME: add back in hostname checking */
+ }
+
+ break;
+ } while (1);
+
+ if (answer == cancel) {
+ newtFormDestroy(form);
+ newtPopWindow();
+
+ return LOADER_BACK;
+ }
+
+ newtFormDestroy(form);
+ newtPopWindow();
+
+ return 0;
+}
diff --git a/loader/urls.h b/loader/urls.h
new file mode 100644
index 0000000..4ca538f
--- /dev/null
+++ b/loader/urls.h
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+#include "loader.h"
+#include "windows.h"
+
+struct iurlinfo {
+ char * url;
+};
+
+int splitProxyParam(char *param, char **user, char **password, char **proxy);
+int urlMainSetupPanel(struct loaderData_s *loaderData, struct iurlinfo * ui);
+int urlinstTransfer(struct loaderData_s *loaderData, struct iurlinfo *ui,
+ char **extraHeaders, char *dest);
+
+#endif
diff --git a/loader/windows.c b/loader/windows.c
new file mode 100644
index 0000000..51b00db
--- /dev/null
+++ b/loader/windows.c
@@ -0,0 +1,121 @@
+/*
+ * 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 <math.h>
+
+#include "../isys/log.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);
+}
+
+int progressCallback(void *pbdata, long long pos, long long total) {
+ struct progressCBdata *data = pbdata;
+ char tickmark[2] = "-";
+ char *ticks = "-\\|/";
+ int x = ceil(pos * 100.0 / total);
+
+ newtScaleSet(data->scale, x);
+ *tickmark = ticks[x % 4];
+
+ newtLabelSetText(data->label, tickmark);
+ newtRefresh();
+ return 0;
+}
+
+struct progressCBdata *winProgressBar(int width, int height, char *title, char *text, ...) {
+ struct progressCBdata *data;
+ char *buf = NULL;
+ va_list args;
+ int llen;
+ newtComponent t, f, scale, label;
+
+ va_start(args, text);
+
+ if (vasprintf(&buf, text, args) != -1) {
+ va_end(args);
+ newtCenteredWindow(width, height, title);
+ t = newtTextbox(1, 1, width - 2, height - 2, NEWT_TEXTBOX_WRAP);
+ newtTextboxSetText(t, buf);
+ llen = strlen(buf);
+ free(buf);
+ label = newtLabel(llen + 1, 1, "-");
+ f = newtForm(NULL, NULL, 0);
+ newtFormAddComponent(f, t);
+ scale = newtScale(3, 3, width - 6, 100);
+ newtFormAddComponent(f, scale);
+ newtDrawForm(f);
+ newtRefresh();
+
+ if ((data = malloc(sizeof(struct progressCBdata))) == NULL) {
+ logMessage(ERROR, "%s: %d: %m", __func__, __LINE__);
+ abort();
+ }
+
+ data->scale = scale;
+ data->label = label;
+ return data;
+ }
+
+ return NULL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4: */
diff --git a/loader/windows.h b/loader/windows.h
new file mode 100644
index 0000000..a1646d4
--- /dev/null
+++ b/loader/windows.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <newt.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));
+
+typedef void (*progressCB) (void *pbdata, long long offset, long long total);
+
+struct progressCBdata {
+ newtComponent scale;
+ newtComponent label;
+};
+
+int progressCallback(void *pbdata, long long pos, long long total);
+struct progressCBdata *winProgressBar(int width, int height, char *title, char *text, ...);
+
+#endif /* _WINDOWS_H_ */