summaryrefslogtreecommitdiffstats
path: root/mdk-stage1
diff options
context:
space:
mode:
authorMystery Man <unknown@mandriva.org>2004-06-09 14:26:13 +0000
committerMystery Man <unknown@mandriva.org>2004-06-09 14:26:13 +0000
commit6bb4285c6632a430f060ab6d0830cecb6725d228 (patch)
treee1b48adb54b3337eacf51569f1b48a96301f7f21 /mdk-stage1
parent612bfda26a7945b07906be53ebc7bed98260afea (diff)
downloaddrakx-6bb4285c6632a430f060ab6d0830cecb6725d228.tar
drakx-6bb4285c6632a430f060ab6d0830cecb6725d228.tar.gz
drakx-6bb4285c6632a430f060ab6d0830cecb6725d228.tar.bz2
drakx-6bb4285c6632a430f060ab6d0830cecb6725d228.tar.xz
drakx-6bb4285c6632a430f060ab6d0830cecb6725d228.zip
This commit was manufactured by cvs2svn to create branch
'MDK-10-update'.
Diffstat (limited to 'mdk-stage1')
-rw-r--r--mdk-stage1/stage1.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/mdk-stage1/stage1.c b/mdk-stage1/stage1.c
new file mode 100644
index 000000000..24ad55fbb
--- /dev/null
+++ b/mdk-stage1/stage1.c
@@ -0,0 +1,669 @@
+/*
+ * Guillaume Cottenceau (was gc@mandrakesoft.com)
+ *
+ * Copyright 2000-2004 MandrakeSoft
+ *
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * public license.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * Portions from Erik Troan (ewt@redhat.com)
+ *
+ * Copyright 1996 Red Hat Software
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <linux/unistd.h>
+_syscall2(int,pivot_root,const char *,new_root,const char *,put_old)
+
+#include "stage1.h"
+
+#include "log.h"
+#include "probing.h"
+#include "frontend.h"
+#include "modules.h"
+#include "tools.h"
+#include "automatic.h"
+#include "mount.h"
+#include "lomount.h"
+#include "insmod.h"
+
+#ifdef ENABLE_PCMCIA
+#include "pcmcia_/pcmcia.h"
+#endif
+
+#ifndef DISABLE_CDROM
+#include "cdrom.h"
+#endif
+
+#ifndef DISABLE_NETWORK
+#include "network.h"
+#endif
+
+#ifndef DISABLE_DISK
+#include "disk.h"
+#endif
+
+
+/************************************************************
+ * globals */
+
+char * method_name;
+char * stage2_kickstart = NULL;
+
+
+void fatal_error(char *msg)
+{
+ printf("FATAL ERROR IN STAGE1: %s\n\nI can't recover from this.\nYou may reboot your system.\n", msg);
+ while (1);
+}
+
+
+/************************************************************
+ * special frontend functs
+ * (the principle is to not pollute frontend code with stage1-specific stuff) */
+
+void stg1_error_message(char *msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ log_message("unsetting automatic");
+ unset_param(MODE_AUTOMATIC);
+ verror_message(msg, args);
+ va_end(args);
+}
+
+void stg1_info_message(char *msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ if (IS_AUTOMATIC) {
+ vlog_message(msg, args);
+ return;
+ }
+ vinfo_message(msg, args);
+ va_end(args);
+}
+
+
+#ifdef SPAWN_SHELL
+static pid_t shell_pid = 0;
+
+/************************************************************
+ * spawns a shell on console #2 */
+static void spawn_shell(void)
+{
+ int fd;
+ char * shell_name[] = { "/tmp/sh", NULL };
+
+ log_message("spawning a shell");
+
+ if (!IS_TESTING) {
+ fd = open("/dev/tty2", O_RDWR);
+ if (fd == -1) {
+ log_message("cannot open /dev/tty2 -- no shell will be provided");
+ return;
+ }
+ else if (access(shell_name[0], X_OK)) {
+ log_message("cannot open shell - %s doesn't exist", shell_name[0]);
+ return;
+ }
+
+ if (!(shell_pid = fork())) {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+
+ close(fd);
+ setsid();
+ if (ioctl(0, TIOCSCTTY, NULL))
+ log_perror("could not set new controlling tty");
+
+ execve(shell_name[0], shell_name, grab_env());
+ log_message("execve of %s failed: %s", shell_name[0], strerror(errno));
+ exit(-1);
+ }
+
+ close(fd);
+ }
+}
+#endif
+
+
+char * interactive_fifo = "/tmp/stage1-fifo";
+static pid_t interactive_pid = 0;
+
+/* spawns my small interactive on console #6 */
+static void spawn_interactive(void)
+{
+#ifdef SPAWN_INTERACTIVE
+ int fd;
+ char * dev = "/dev/tty6";
+
+ printf("spawning my interactive on %s\n", dev);
+
+ if (!IS_TESTING) {
+ fd = open(dev, O_RDWR);
+ if (fd == -1) {
+ printf("cannot open %s -- no interactive\n", dev);
+ return;
+ }
+
+ if (mkfifo(interactive_fifo, O_RDWR)) {
+ printf("cannot create fifo -- no interactive\n");
+ return;
+ }
+
+ if (!(interactive_pid = fork())) {
+ int fif_out;
+
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+
+ close(fd);
+ setsid();
+ if (ioctl(0, TIOCSCTTY, NULL))
+ perror("could not set new controlling tty");
+
+ fif_out = open(interactive_fifo, O_WRONLY);
+ printf("Please enter your command (availables: [+,-] [rescue,expert]).\n");
+
+ while (1) {
+ char s[50];
+ int i = 0;
+ printf("? ");
+ fflush(stdout);
+ read(0, &(s[i++]), 1);
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ while (read(0, &(s[i++]), 1) > 0 && i < sizeof(s));
+ fcntl(0, F_SETFL, 0);
+ write(fif_out, s, i-2);
+ printf("Ok.\n");
+ }
+ }
+
+ close(fd);
+ }
+#endif
+}
+
+
+/************************************************************
+ */
+
+static void expert_third_party_modules(void)
+{
+ enum return_type results;
+ char * floppy_mount_location = "/tmp/floppy";
+ char ** modules;
+ char final_name[500];
+ char * choice;
+ int rc;
+ char * questions[] = { "Options", NULL };
+ static char ** answers = NULL;
+
+ results = ask_yes_no("If you want to insert third-party kernel modules, insert "
+ "a Linux (ext2fs) formatted floppy containing the modules and confirm. Otherwise, select \"no\".");;
+ if (results != RETURN_OK)
+ return;
+
+ if (my_mount(floppy_device(), floppy_mount_location, "ext2", 0) == -1) {
+ stg1_error_message("I can't find a Linux ext2 floppy in first floppy drive.");
+ return expert_third_party_modules();
+ }
+
+ modules = list_directory(floppy_mount_location);
+
+ if (!modules || !*modules) {
+ stg1_error_message("No modules found on floppy disk.");
+ umount(floppy_mount_location);
+ return expert_third_party_modules();
+ }
+
+ results = ask_from_list("Which driver would you like to insmod?", modules, &choice);
+ if (results != RETURN_OK) {
+ umount(floppy_mount_location);
+ return;
+ }
+
+ sprintf(final_name, "%s/%s", floppy_mount_location, choice);
+
+ results = ask_from_entries("Please enter the options:", questions, &answers, 24, NULL);
+ if (results != RETURN_OK) {
+ umount(floppy_mount_location);
+ return expert_third_party_modules();
+ }
+
+ rc = insmod_local_file(final_name, answers[0]);
+ umount(floppy_mount_location);
+
+ if (rc) {
+ log_message("\tfailed");
+ stg1_error_message("Insmod failed.");
+ }
+
+ return expert_third_party_modules();
+}
+
+
+#ifdef ENABLE_PCMCIA
+static void handle_pcmcia(char ** pcmcia_adapter)
+{
+ if (kernel_version() == 2) {
+ stg1_error_message("We now use kernel pcmcia support and this won't work with a 2.2 kernel.");
+ return;
+ }
+
+ *pcmcia_adapter = pcmcia_probe();
+ if (!*pcmcia_adapter) {
+ log_message("no pcmcia adapter found");
+ return;
+ }
+ my_insmod("pcmcia_core", ANY_DRIVER_TYPE, NULL, 0);
+ my_insmod(*pcmcia_adapter, ANY_DRIVER_TYPE, NULL, 0);
+ my_insmod("ds", ANY_DRIVER_TYPE, NULL, 0);
+
+ /* call to cardmgr takes time, let's use the wait message */
+ wait_message("Enabling PCMCIA extension cards...");
+ log_message("cardmgr rc: %d", cardmgr_call());
+ remove_wait_message();
+
+ if (IS_EXPERT)
+ expert_third_party_modules();
+}
+#endif
+
+
+/************************************************************
+ */
+
+static void method_select_and_prepare(void)
+{
+ enum return_type results;
+ char * choice;
+ char * means[10], * means_auto[10];
+ int i;
+
+#ifndef DISABLE_DISK
+ char * disk_install = "Hard disk"; char * disk_install_auto = "disk";
+#endif
+#ifndef DISABLE_CDROM
+ char * cdrom_install = "CDROM drive"; char * cdrom_install_auto = "cdrom";
+#endif
+#ifndef DISABLE_NETWORK
+ char * network_nfs_install = "NFS server"; char * network_nfs_install_auto = "nfs";
+ char * network_ftp_install = "FTP server"; char * network_ftp_install_auto = "ftp";
+ char * network_http_install = "HTTP server"; char * network_http_install_auto = "http";
+#endif
+
+ i = 0;
+#ifndef DISABLE_NETWORK
+ means[i] = network_nfs_install; means_auto[i++] = network_nfs_install_auto;
+ means[i] = network_ftp_install; means_auto[i++] = network_ftp_install_auto;
+ means[i] = network_http_install; means_auto[i++] = network_http_install_auto;
+#endif
+#ifndef DISABLE_CDROM
+ means[i] = cdrom_install; means_auto[i++] = cdrom_install_auto;
+ allow_additional_modules_floppy = 0;
+#endif
+#ifndef DISABLE_DISK
+ means[i] = disk_install; means_auto[i++] = disk_install_auto;
+ allow_additional_modules_floppy = 0;
+#endif
+ means[i] = NULL;
+
+ results = ask_from_list_auto("Please choose the installation method.", means, &choice, "method", means_auto);
+
+ if (results != RETURN_OK)
+ return method_select_and_prepare();
+
+#ifndef DISABLE_CDROM
+ if (!strcmp(choice, cdrom_install))
+ results = cdrom_prepare();
+#endif
+
+#ifndef DISABLE_DISK
+ if (!strcmp(choice, disk_install))
+ results = disk_prepare();
+#endif
+
+#ifndef DISABLE_NETWORK
+ if (!strcmp(choice, network_nfs_install))
+ results = nfs_prepare();
+
+ if (!strcmp(choice, network_ftp_install))
+ results = ftp_prepare();
+
+ if (!strcmp(choice, network_http_install))
+ results = http_prepare();
+#endif
+
+ if (results != RETURN_OK)
+ return method_select_and_prepare();
+}
+
+#ifdef MANDRAKE_MOVE
+int mandrake_move_pre(void)
+{
+ log_message("move: creating %s directory and mounting as tmpfs", SLASH_LOCATION);
+
+ if (scall(mkdir(SLASH_LOCATION, 0755), "mkdir"))
+ return RETURN_ERROR;
+
+ if (scall(mount("none", SLASH_LOCATION, "tmpfs", MS_MGC_VAL, NULL), "mount tmpfs"))
+ return RETURN_ERROR;
+
+ return RETURN_OK;
+}
+
+
+static enum return_type handle_clp(char* clp, char* live, char* location_live, char* location_mount, int* is_symlink, char* clp_tmpfs)
+{
+ static int count = 0;
+ if (access(clp, R_OK)) {
+ log_message("no %s found (or disabled), trying to fallback on plain tree", clp);
+ if (!access(live, R_OK)) {
+ if (scall(symlink(location_live, location_mount), "symlink"))
+ return RETURN_ERROR;
+ *is_symlink = 1;
+ return RETURN_OK;
+ } else {
+ log_message("move: can't find %s nor %s, proceeding hoping files will be there", clp, live);
+ return RETURN_OK;
+ }
+ }
+
+ if (clp_tmpfs) {
+ int ret;
+ char buf[5000];
+ sprintf(buf, "Loading (part %d)...", ++count);
+ init_progression(buf, file_size(clp));
+ ret = copy_file(clp, clp_tmpfs, update_progression);
+ end_progression();
+ if (ret != RETURN_OK)
+ return ret;
+ clp = clp_tmpfs;
+ }
+
+ if (lomount(clp, location_mount, NULL, 1)) {
+ stg1_error_message("Could not mount compressed loopback :(.");
+ return RETURN_ERROR;
+ }
+
+ return RETURN_OK;
+}
+
+int mandrake_move_post(void)
+{
+ FILE *f;
+ char buf[5000];
+ int fd;
+ char rootdev[] = "0x0100";
+ int boot__real_is_symlink_to_raw = 0;
+ int always__real_is_symlink_to_raw = 0;
+ int totem__real_is_symlink_to_raw = 0;
+ int main__real_is_symlink_to_raw = 0;
+
+ if (handle_clp(IMAGE_LOCATION "/live_tree_boot.clp", IMAGE_LOCATION "/live_tree_boot/usr/bin/runstage2.pl",
+ IMAGE_LOCATION "/live_tree_boot", BOOT_LOCATION,
+ &boot__real_is_symlink_to_raw, SLASH_LOCATION "/live_tree_boot.clp") != RETURN_OK)
+ return RETURN_ERROR;
+
+ if (handle_clp(IMAGE_LOCATION "/live_tree_always.clp", IMAGE_LOCATION "/live_tree_always/bin/bash",
+ IMAGE_LOCATION "/live_tree_always", ALWAYS_LOCATION,
+ &always__real_is_symlink_to_raw, SLASH_LOCATION "/live_tree_always.clp") != RETURN_OK)
+ return RETURN_ERROR;
+
+ if (handle_clp(IMAGE_LOCATION "/live_tree_totem.clp", IMAGE_LOCATION "/live_tree_totem/usr/bin/totem",
+ IMAGE_LOCATION "/live_tree_totem", TOTEM_LOCATION,
+ &totem__real_is_symlink_to_raw, SLASH_LOCATION "/live_tree_totem.clp") != RETURN_OK)
+ return RETURN_ERROR;
+
+ if (handle_clp(IMAGE_LOCATION "/live_tree.clp", IMAGE_LOCATION "/live_tree/etc/fstab",
+ IMAGE_LOCATION "/live_tree", IMAGE_LOCATION_REAL,
+ &main__real_is_symlink_to_raw, NULL) != RETURN_OK)
+ return RETURN_ERROR;
+
+ if (scall(!(f = fopen(IMAGE_LOCATION_REAL "/move/symlinks", "rb")), "fopen[" IMAGE_LOCATION_REAL "/move/symlinks]"))
+ return RETURN_ERROR;
+ while (fgets(buf, sizeof(buf), f)) {
+ char oldpath[500], newpath[500];
+ buf[strlen(buf)-1] = '\0'; // trim \n
+ sprintf(oldpath, "%s%s", LIVE_LOCATION_REL, buf);
+ sprintf(newpath, "%s%s", SLASH_LOCATION, buf);
+ log_message("move: creating symlink %s -> %s", oldpath, newpath);
+ if (scall(symlink(oldpath, newpath), "symlink"))
+ return RETURN_ERROR;
+ }
+ fclose(f);
+
+ // in case we didn't mount any clp, because gzloop.o is not available later in /lib/modules
+ my_insmod("gzloop", ANY_DRIVER_TYPE, NULL, 0);
+
+ // hardcoded :(
+ if (!access(TOTEM_LOCATION, R_OK)) {
+ if (scall(symlink("/image_totem/usr", SLASH_LOCATION "/usr"), "symlink"))
+ return RETURN_ERROR;
+ } else
+ // need a fallback in case we don't use image_totem.clp nor live_tree_totem, but we're in -u mode
+ if (scall(symlink(LIVE_LOCATION_REL "/usr", SLASH_LOCATION "/usr"), "symlink"))
+ return RETURN_ERROR;
+
+ // need to create the few devices needed to start up stage2 in a decent manner, we can't symlink or they will keep CD busy
+ // we need only the ones before mounting /dev as devfs
+ if (scall(mkdir(SLASH_LOCATION "/dev", 0755), "mkdir"))
+ return RETURN_ERROR;
+ if (scall(!(f = fopen(IMAGE_LOCATION_REAL "/move/devices", "rb")), "fopen"))
+ return RETURN_ERROR;
+ while (fgets(buf, sizeof(buf), f)) {
+ char name[500], path[500], type;
+ int major, minor;
+ sscanf(buf, "%s %c %d %d", name, &type, &major, &minor);
+ sprintf(path, "%s%s", SLASH_LOCATION, name);
+ log_message("move: creating device %s %c %d %d", path, type, major, minor);
+ if (scall(mknod(path, type == 'c' ? S_IFCHR : S_IFBLK, makedev(major, minor)), "mknod"))
+ return RETURN_ERROR;
+ }
+ fclose(f);
+
+ if (boot__real_is_symlink_to_raw) {
+ if (scall(unlink(BOOT_LOCATION), "unlink"))
+ return RETURN_ERROR;
+ if (scall(symlink(RAW_LOCATION_REL "/live_tree_boot", BOOT_LOCATION), "symlink"))
+ return RETURN_ERROR;
+ }
+
+ if (always__real_is_symlink_to_raw) {
+ if (scall(unlink(ALWAYS_LOCATION), "unlink"))
+ return RETURN_ERROR;
+ if (scall(symlink(RAW_LOCATION_REL "/live_tree_always", ALWAYS_LOCATION), "symlink"))
+ return RETURN_ERROR;
+ }
+
+ if (totem__real_is_symlink_to_raw) {
+ if (scall(unlink(TOTEM_LOCATION), "unlink"))
+ return RETURN_ERROR;
+ if (scall(symlink(RAW_LOCATION_REL "/live_tree_totem", TOTEM_LOCATION), "symlink"))
+ return RETURN_ERROR;
+ }
+
+ if (main__real_is_symlink_to_raw) {
+ if (scall(unlink(IMAGE_LOCATION_REAL), "unlink"))
+ return RETURN_ERROR;
+ if (scall(symlink(RAW_LOCATION_REL "/live_tree", IMAGE_LOCATION_REAL), "symlink"))
+ return RETURN_ERROR;
+ }
+
+ mkdir(SLASH_LOCATION "/etc", 0755);
+ copy_file("/etc/resolv.conf", SLASH_LOCATION "/etc/resolv.conf", NULL);
+
+ if (IS_DEBUGSTAGE1)
+ while (1);
+
+ log_message("move: pivot_rooting");
+ // trick so that kernel won't try to mount the root device when initrd exits
+ if (scall((fd = open("/proc/sys/kernel/real-root-dev", O_WRONLY)) < 0, "open"))
+ return RETURN_ERROR;
+ if (scall(write(fd, rootdev, strlen(rootdev)) != (signed)strlen(rootdev), "write")) {
+ close(fd);
+ return RETURN_ERROR;
+ }
+ close(fd);
+
+ if (scall(mkdir(SLASH_LOCATION "/stage1", 0755), "mkdir"))
+ return RETURN_ERROR;
+
+ if (scall(pivot_root(SLASH_LOCATION, SLASH_LOCATION "/stage1"), "pivot_root"))
+ return RETURN_ERROR;
+
+ return RETURN_OK;
+}
+#endif
+
+
+int main(int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)), char **env)
+{
+ char ** argptr;
+ char * stage2_args[30];
+#ifdef ENABLE_PCMCIA
+ char * pcmcia_adapter = NULL;
+#endif
+
+ if (getpid() > 50)
+ set_param(MODE_TESTING);
+
+ spawn_interactive();
+
+ open_log();
+ log_message("welcome to the " DISTRIB_NAME " install (mdk-stage1, version " VERSION " built " __DATE__ " " __TIME__")");
+ process_cmdline();
+ handle_env(env);
+#ifdef SPAWN_SHELL
+ spawn_shell();
+#endif
+ init_modules_insmoding();
+ init_frontend("Welcome to " DISTRIB_NAME
+#ifdef MANDRAKE_MOVE
+ ", "
+#else
+ " (" VERSION ") "
+#endif
+ __DATE__ " " __TIME__);
+
+ if (IS_EXPERT)
+ expert_third_party_modules();
+
+ if (IS_UPDATEMODULES)
+ update_modules();
+
+#ifdef ENABLE_PCMCIA
+ if (!IS_NOAUTO)
+ handle_pcmcia(&pcmcia_adapter);
+#endif
+
+ // load usb interface as soon as possible, helps usb mouse detection in stage2 */
+ probe_that_type(USB_CONTROLLERS, BUS_USB);
+
+ if (IS_CHANGEDISK)
+ stg1_info_message("You are starting the installation with an alternate booting method. "
+ "Please change your disk, and insert the Installation disk.");
+
+ if (IS_RESCUE && total_memory() < MEM_LIMIT_RESCUE) {
+ stg1_error_message("You are starting the rescue with a low memory configuration. "
+ "Our experience shows that your system may crash at any point "
+ "or lock up for no apparent reason. Continue at "
+ "your own risk. Alternatively, you may reboot your system now.");
+ }
+
+#ifdef MANDRAKE_MOVE
+ if (total_memory() < MEM_LIMIT_MOVE)
+ stg1_error_message(DISTRIB_NAME " typically needs more than %d Mbytes of memory (detected %d Mbytes). You may proceed, but the machine may crash or lock up for no apparent reason. Continue at your own risk. Alternatively, you may reboot your system now.",
+ MEM_LIMIT_MOVE, total_memory());
+ if (mandrake_move_pre() != RETURN_OK)
+ stg1_error_message("Fatal error when preparing Mandrakemove.");
+#endif
+
+#ifndef DISABLE_DISK
+ if (IS_RECOVERY && streq(get_auto_value("method"), "cdrom")) {
+ if (!process_recovery())
+ method_select_and_prepare();
+ } else
+#endif
+ method_select_and_prepare();
+
+ if (!IS_RAMDISK)
+ if (symlink(IMAGE_LOCATION_REAL LIVE_LOCATION, STAGE2_LOCATION) != 0)
+ log_perror("symlink from " IMAGE_LOCATION_REAL LIVE_LOCATION " to " STAGE2_LOCATION " failed");
+
+ if (interactive_pid != 0)
+ kill(interactive_pid, 9);
+
+#ifdef MANDRAKE_MOVE
+ if (mandrake_move_post() != RETURN_OK)
+ stg1_error_message("Fatal error when launching Mandrakemove.");
+#endif
+
+ if (shell_pid != 0) {
+ int fd;
+ kill(shell_pid, 9);
+ fd = open("/dev/tty2", O_RDWR);
+ write(fd, "Killed\n", 7);
+ close(fd);
+ }
+
+ umount("/tmp/tmpfs");
+ finish_frontend();
+ close_log();
+
+#ifndef MANDRAKE_MOVE
+ if (IS_RESCUE)
+#endif
+ return 66;
+ if (IS_TESTING)
+ return 0;
+
+ argptr = stage2_args;
+ *argptr++ = "/usr/bin/runinstall2";
+ *argptr++ = "--method";
+ *argptr++ = method_name;
+#ifdef ENABLE_PCMCIA
+ if (pcmcia_adapter) {
+ *argptr++ = "--pcmcia";
+ *argptr++ = pcmcia_adapter;
+ }
+#endif
+ if (stage2_kickstart) {
+ *argptr++ = "--kickstart";
+ *argptr++ = stage2_kickstart;
+ }
+ *argptr++ = NULL;
+
+ execve(stage2_args[0], stage2_args, grab_env());
+
+ printf("error in exec of stage2 :-(\n");
+ printf("trying to execute '/usr/bin/runinstall2' from the installation volume,\nthe following fatal error occurred\n");
+ fatal_error(strerror(errno));
+
+ return 0; /* shut up compiler (we can't get here anyway!) */
+}