diff options
Diffstat (limited to 'mdk-stage1/init.c')
-rw-r--r-- | mdk-stage1/init.c | 410 |
1 files changed, 283 insertions, 127 deletions
diff --git a/mdk-stage1/init.c b/mdk-stage1/init.c index 1e8c115a7..7c48f64ec 100644 --- a/mdk-stage1/init.c +++ b/mdk-stage1/init.c @@ -1,7 +1,7 @@ /* - * Guillaume Cottenceau (gc@mandrakesoft.com) + * Guillaume Cottenceau (gc) * - * Copyright 2000 MandrakeSoft + * Copyright 2000 Mandriva * * This software may be freely redistributed under the terms of the GNU * public license. @@ -19,24 +19,54 @@ * */ -#ifndef INIT_HEADERS -#include "init-libc-headers.h" -#else -#include INIT_HEADERS -#endif +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <linux/un.h> +#include <errno.h> +#include <signal.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <linux/unistd.h> +#include <sys/select.h> +#include <sys/ioctl.h> +#include <linux/reboot.h> + +#include <sys/syscall.h> +#define syslog(...) syscall(__NR_syslog, __VA_ARGS__) + +static unsigned int reboot_magic = LINUX_REBOOT_CMD_RESTART; + +static inline long reboot(unsigned int command) +{ + return (long) syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, command, 0); +} #include "config-stage1.h" +#include <linux/cdrom.h> + + +#define BINARY_STAGE2 "/usr/bin/runinstall2" -#if defined(__powerpc__) -#define TIOCSCTTY 0x540 -#endif char * env[] = { "PATH=/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sbin:/mnt/usr/sbin:/mnt/bin:/mnt/usr/bin", - "LD_LIBRARY_PATH=/lib:/usr/lib:/mnt/lib:/mnt/usr/lib:/usr/X11R6/lib:/mnt/usr/X11R6/lib", + "LD_LIBRARY_PATH=/lib:/usr/lib:/mnt/lib:/mnt/usr/lib" +#if defined(__x86_64__) + ":/lib64:/usr/lib64:/mnt/lib64:/mnt/usr/lib64" +#endif + , "HOME=/", "TERM=linux", "TERMINFO=/etc/terminfo", + "LC_CTYPE=UTF-8", NULL }; @@ -51,14 +81,14 @@ char * env[] = { * */ -int testing; +int testing = 0; int klog_pid; void fatal_error(char *msg) { printf("FATAL ERROR IN INIT: %s\n\nI can't recover from this, please reboot manually and send bugreport.\n", msg); - while (1); + select(0, NULL, NULL, NULL, NULL); } void print_error(char *msg) @@ -76,20 +106,20 @@ void print_int_init(int fd, int i) char buf[10]; char * chptr = buf + 9; int j = 0; - + if (i < 0) { write(1, "-", 1); i = -1 * i; } - + while (i) { *chptr-- = '0' + (i % 10); j++; i = i / 10; } - + write(fd, chptr + 1, j); } @@ -98,7 +128,6 @@ void print_str_init(int fd, char * string) write(fd, string, strlen(string)); } - /* fork to: * (1) watch /proc/kmsg and copy the stuff to /dev/tty4 * (2) listens to /dev/log and copy also this stuff (log from programs) @@ -108,7 +137,7 @@ void doklog() fd_set readset, unixs; int in, out, i; int log; - int s; + socklen_t s; int sock = -1; struct sockaddr_un sockaddr; char buf[1024]; @@ -121,7 +150,7 @@ void doklog() return; } - if ((log = open("/tmp/syslog", O_WRONLY | O_CREAT, 0644)) < 0) { + if ((log = open("/tmp/syslog", O_WRONLY | O_CREAT | O_APPEND, 0644)) < 0) { print_error("error opening /tmp/syslog"); sleep(5); return; @@ -136,7 +165,7 @@ void doklog() close(1); close(2); } - + out = open("/dev/tty4", O_WRONLY, 0); if (out < 0) print_warning("couldn't open tty for syslog -- still using /tmp/syslog\n"); @@ -178,7 +207,7 @@ void doklog() if (sock >= 0) FD_SET(sock, &readset); FD_SET(in, &readset); - + i = select(20, &readset, NULL, NULL, NULL); if (i <= 0) continue; @@ -193,25 +222,6 @@ void doklog() } } - /* examine some fd's in the hope to find some syslog outputs from programs */ - for (readfd = 0; readfd < 20; ++readfd) { - if (FD_ISSET(readfd, &readset) && FD_ISSET(readfd, &unixs)) { - i = read(readfd, buf, sizeof(buf)); - if (i > 0) { - /* grep out the output of RPM telling that it installed/removed some packages */ - if (!strstr(buf, "mdk installed") && !strstr(buf, "mdk removed")) { - if (out >= 0) - write(out, buf, i); - write(log, buf, i); - } - } else if (i == 0) { - /* socket closed */ - close(readfd); - FD_CLR(readfd, &unixs); - } - } - } - /* the socket has moved, new stuff to do */ if (sock >= 0 && FD_ISSET(sock, &readset)) { s = sizeof(sockaddr); @@ -233,20 +243,28 @@ void doklog() #define LOOP_CLR_FD 0x4C01 -void del_loop(char *device) +void del_loops(void) { - int fd; - if ((fd = open(device, O_RDONLY, 0)) < 0) { - printf("del_loop open failed\n"); - return; - } - - if (ioctl(fd, LOOP_CLR_FD, 0) < 0) { - printf("del_loop ioctl failed"); - return; - } - - close(fd); + char loopdev[] = "/dev/loop0"; + char chloopdev[] = "/dev/chloop0"; + int i; + for (i=0; i<8; i++) { + int fd; + loopdev[9] = '0' + i; + fd = open(loopdev, O_RDONLY, 0); + if (fd > 0) { + if (!ioctl(fd, LOOP_CLR_FD, 0)) + printf("\t%s\n", loopdev); + close(fd); + } + chloopdev[11] = '0' + i; + fd = open(chloopdev, O_RDONLY, 0); + if (fd > 0) { + if (!ioctl(fd, LOOP_CLR_FD, 0)) + printf("\t%s\n", chloopdev); + close(fd); + } + } } struct filesystem @@ -257,6 +275,18 @@ struct filesystem int mounted; }; +char* strcat(register char* s,register const char* t) +{ + char *dest=s; + s+=strlen(s); + for (;;) { + if (!(*s = *t)) + break; + ++s; ++t; + } + return dest; +} + /* attempt to unmount all filesystems in /proc/mounts */ void unmount_filesystems(void) { @@ -266,9 +296,9 @@ void unmount_filesystems(void) struct filesystem fs[500]; int numfs = 0; int i, nb; - + printf("unmounting filesystems...\n"); - + fd = open("/proc/mounts", O_RDONLY, 0); if (fd < 1) { print_error("failed to open /proc/mounts"); @@ -295,7 +325,12 @@ void unmount_filesystems(void) *p++ = '\0'; while (*p != '\n') p++; p++; - if (strcmp(fs[numfs].name, "/") != 0) numfs++; /* skip if root, no need to take initrd root in account */ + if (strcmp(fs[numfs].name, "/") + && !strstr(fs[numfs].dev, "ram") + && strcmp(fs[numfs].name, "/dev") + && strcmp(fs[numfs].name, "/sys") + && strncmp(fs[numfs].name, "/proc", 5)) + numfs++; } /* Pixel's ultra-optimized sorting algorithm: @@ -305,31 +340,123 @@ void unmount_filesystems(void) nb = 0; for (i = 0; i < numfs; i++) { /*printf("trying with %s\n", fs[i].name);*/ + del_loops(); if (fs[i].mounted && umount(fs[i].name) == 0) { - if (strncmp(fs[i].dev + sizeof("/dev/") - 1, "loop", - sizeof("loop") - 1) == 0) - del_loop(fs[i].dev); - printf("\t%s\n", fs[i].name); fs[i].mounted = 0; nb++; } } } while (nb); - + for (i = nb = 0; i < numfs; i++) if (fs[i].mounted) { - printf("\t%s umount failed\n", fs[i].name); - if (strcmp(fs[i].fs, "ext2") == 0) nb++; /* don't count not-ext2 umount failed */ + printf("\tumount failed: %s\n", fs[i].name); + if (strcmp(fs[i].fs, "ext3") == 0) nb++; /* don't count not-ext3 umount failed */ } - + + if (nb) { printf("failed to umount some filesystems\n"); - while (1); + select(0, NULL, NULL, NULL, NULL); + } +} + +int in_reboot(void) +{ + int fd; + if ((fd = open("/var/run/rebootctl", O_RDONLY, 0)) > 0) { + char buf[100]; + int i = read(fd, buf, sizeof(buf)); + close(fd); + if (strstr(buf, "halt")) + reboot_magic = LINUX_REBOOT_CMD_POWER_OFF; + return i > 0; + } + return 0; +} + +int recursive_remove(char *file); +int recursive_remove(char *file) +{ + struct stat sb; + + if (lstat(file, &sb) != 0) { + printf("failed to stat %s: %d\n", file, errno); + return -1; + } + + /* only descend into subdirectories if device is same as dir */ + if (S_ISDIR(sb.st_mode)) { + char * strBuf = alloca(strlen(file) + 1024); + DIR * dir; + struct dirent * d; + + if (!(dir = opendir(file))) { + printf("error opening %s: %d\n", file, errno); + return -1; + } + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + strcpy(strBuf, file); + strcat(strBuf, "/"); + strcat(strBuf, d->d_name); + + if (recursive_remove(strBuf) != 0) { + closedir(dir); + return -1; + } + } + closedir(dir); + + if (rmdir(file)) { + printf("failed to rmdir %s: %d\n", file, errno); + return -1; + } + } else { + if (unlink(file) != 0) { + printf("failed to remove %s: %d\n", file, errno); + return -1; + } } + return 0; +} + + +int create_initial_fs_symlinks(char* symlinks) +{ + FILE *f; + char buf[5000]; + + if (!(f = fopen(symlinks, "rb"))) { + printf("Error opening symlink definitions file '%s'\n", symlinks); + return -1; + } + while (fgets(buf, sizeof(buf), f)) { + char oldpath[500], newpath[500]; + struct stat sb; + + buf[strlen(buf)-1] = '\0'; // trim \n + if (sscanf(buf, "%s %s", oldpath, newpath) != 2) { + snprintf(oldpath, sizeof(oldpath), "%s%s", STAGE2_LOCATION, buf); + snprintf(newpath, sizeof(newpath), "%s", buf); + } + if (lstat(newpath, &sb) == 0) + recursive_remove(newpath); + printf("Creating symlink %s -> %s\n", oldpath, newpath); + if (symlink(oldpath, newpath)) { + printf("Error creating symlink\n"); + return -1; + } + } + fclose(f); + return 0; } -int exit_value_rescue = 66; + +int exit_value_restart = 0x35; int main(int argc, char **argv) { @@ -337,10 +464,12 @@ int main(int argc, char **argv) int wait_status; int fd; int abnormal_termination = 0; - int end_stage2 = 0; - /* getpid() != 1 should work, by linuxrc tends to get a larger pid */ - testing = (getpid() > 50); + if (argc > 1 && argv[1][0] >= '0' && argv[1][0] <= '9') { + printf("This is no normal init, sorry.\n" + "Call `reboot' or `halt' directly.\n"); + return 0; + } if (!testing) { /* turn off screen blanking */ @@ -348,45 +477,38 @@ int main(int argc, char **argv) printf("\033[8]"); } else - printf("*** TESTING MODE ***\n"); + printf("*** TESTING MODE *** (pid is %d)\n", getpid()); - printf("\n\t\t\t\033[1;40mWelcome to \033[1;36mMandrake\033[0;39m Linux\n\n"); - - if (!testing) { - if (mount("/proc", "/proc", "proc", 0, NULL)) - fatal_error("Unable to mount proc filesystem"); - } - + // needed for ldetect: + if (!testing) + if (mount("none", "/sys/kernel/debug", "debugfs", MS_NOSUID, "mode=0755")) + fatal_error("Unable to mount debugfs filesystem"); + /* ignore Control-C and keyboard stop signals */ signal(SIGINT, SIG_IGN); signal(SIGTSTP, SIG_IGN); - if (!testing) { - fd = open("/dev/tty1", O_RDWR, 0); - if (fd < 0) - /* try with devfs */ - fd = open("/dev/vc/1", O_RDWR, 0); - + fd = open("/dev/console", O_RDWR, 0); if (fd < 0) - fatal_error("failed to open /dev/tty1 and /dev/vc/1"); - + fatal_error("failed to open /dev/console"); + dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd); } - + /* I set me up as session leader (probably not necessary?) */ setsid(); - if (ioctl(0, TIOCSCTTY, NULL)) - print_error("could not set new controlling tty"); +// if (ioctl(0, TIOCSCTTY, NULL)) +// print_error("could not set new controlling tty"); if (!testing) { - char my_hostname[] = "localhost.localdomain"; + char my_hostname[] = "localhost"; sethostname(my_hostname, sizeof(my_hostname)); /* the default domainname (as of 2.0.35) is "(none)", which confuses glibc */ @@ -396,52 +518,78 @@ int main(int argc, char **argv) if (!testing) doklog(); - /* Go into normal init mode - keep going, and then do a orderly shutdown - when: - - 1) install exits - 2) we receive a SIGHUP - */ - - printf("If more people were to meet doing raklets, this planet\n"); - printf("would be a safer place.\n"); - printf("\n"); - printf("Running install...\n"); - - if (!(installpid = fork())) { - /* child */ - char * child_argv[2]; - child_argv[0] = "/sbin/stage1"; - child_argv[1] = NULL; + if (create_initial_fs_symlinks(STAGE2_LOCATION "/usr/share/symlinks") != 0) + fatal_error("Fatal error finishing initialization (could not create symlinks)."); + + /* kernel modules and firmware is needed by stage2, so move them to the root */ + if (rename("/usr/lib/modules", "/modules")) + fatal_error("Cannot rename modules folder"); + if (rename("/usr/lib/firmware", "/firmware")) + fatal_error("Cannot rename firmware folder"); + + /* Add some symlinks so stage1 is still valid on it's own - not strictly needed */ + if (symlink("/modules", "/usr/lib/modules")) + fatal_error("Cannot symlink modules folder"); + + if (symlink("/firmware", "/usr/lib/firmware")) + fatal_error("Cannot symlink firmware folder"); + + if (mount(STAGE2_LOCATION "/usr", "/usr", "none", MS_BIND|MS_RDONLY, NULL)) + fatal_error("Unable to bind mount /usr filesystem from rescue or installer stage2"); + + + if (access("/run/drakx/run-init", R_OK) == 0) { + /* This is typically used in rescue mode */ + char * child_argv[2] = { "/sbin/init", NULL }; + + kill(klog_pid, 9); + printf("proceeding, please wait...\n"); execve(child_argv[0], child_argv, env); - printf("error in exec of stage1 :-(\n"); - return 0; + fatal_error("failed to exec /sbin/init"); } - while (!end_stage2) { - childpid = wait4(-1, &wait_status, 0, NULL); - if (childpid == installpid) - end_stage2 = 1; - } + /* This is installer mode */ + do { + printf("proceeding, please wait...\n"); + + if (!(installpid = fork())) { + /* child */ + char * child_argv[2] = { BINARY_STAGE2, NULL }; + execve(child_argv[0], child_argv, env); + printf("error in exec of %s :-( [%d]\n", child_argv[0], errno); + return 0; + } - if (!WIFEXITED(wait_status) || (WEXITSTATUS(wait_status) != 0 && WEXITSTATUS(wait_status) != exit_value_rescue)) { - printf("install exited abnormally :-( "); + do { + childpid = wait4(-1, &wait_status, 0, NULL); + } while (childpid != installpid); + } while (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) == exit_value_restart); + + /* allow Ctrl Alt Del to reboot */ + reboot(LINUX_REBOOT_CMD_CAD_ON); + + if (in_reboot()) { + // any exitcode is valid if we're in_reboot + } else if (!WIFEXITED(wait_status) || WEXITSTATUS(wait_status) != 0) { + printf("exited abnormally :-( "); if (WIFSIGNALED(wait_status)) printf("-- received signal %d", WTERMSIG(wait_status)); printf("\n"); - abnormal_termination = 1; - } else if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) == exit_value_rescue) { - kill(klog_pid, 9); - printf("exiting init -- giving hand to rescue\n"); - return 0; - } else - printf("install succeeded\n"); + abnormal_termination = 1; + } + + if (!abnormal_termination) { + int i; + for (i=0; i<50; i++) + printf("\n"); /* cleanup startkde messages */ + } if (testing) return 0; sync(); sync(); + sleep(2); printf("sending termination signals..."); kill(-1, 15); @@ -455,14 +603,22 @@ int main(int argc, char **argv) unmount_filesystems(); + sync(); sync(); + if (!abnormal_termination) { - printf("rebooting system\n"); - sleep(2); - reboot(0xfee1dead, 672274793, 0x01234567); + if (reboot_magic == LINUX_REBOOT_CMD_RESTART) { +#ifdef DEBUG + printf("automatic reboot in 10 seconds\n"); + sleep(10); +#endif + reboot(reboot_magic); + } else { + printf("you may safely poweroff your computer now\n"); + } } else { - printf("you may safely reboot your system\n"); - while (1); + printf("you may safely reboot or halt your system\n"); } + select(0, NULL, NULL, NULL, NULL); return 0; } |