/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include _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"; char * network_ka_install = "KA server"; char * network_ka_install_auto = "ka"; #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; means[i] = network_ka_install; means_auto[i++] = network_ka_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_ka_install)) results = ka_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]; log_message("move: before creating symlink we have %s", buf); 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 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_info_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 Move."); #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 Move."); #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!) */ }