diff options
author | Dexter Morgan <dmorgan@mageia.org> | 2011-06-02 20:51:35 +0000 |
---|---|---|
committer | Dexter Morgan <dmorgan@mageia.org> | 2011-06-02 20:51:35 +0000 |
commit | a9b2bdafaf625d10aef2f476aa4014fd36c846bc (patch) | |
tree | 2364afc0ee6739b59a25c44d68c9f003bcaf03d9 /mdk-stage1/init.c | |
download | drakx-a9b2bdafaf625d10aef2f476aa4014fd36c846bc.tar drakx-a9b2bdafaf625d10aef2f476aa4014fd36c846bc.tar.gz drakx-a9b2bdafaf625d10aef2f476aa4014fd36c846bc.tar.bz2 drakx-a9b2bdafaf625d10aef2f476aa4014fd36c846bc.tar.xz drakx-a9b2bdafaf625d10aef2f476aa4014fd36c846bc.zip |
Branch for updates
Diffstat (limited to 'mdk-stage1/init.c')
-rw-r--r-- | mdk-stage1/init.c | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/mdk-stage1/init.c b/mdk-stage1/init.c new file mode 100644 index 000000000..1d5841610 --- /dev/null +++ b/mdk-stage1/init.c @@ -0,0 +1,553 @@ +/* + * Guillaume Cottenceau (gc@mandriva.com) + * + * Copyright 2000 Mandriva + * + * 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 <stdio.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 <sys/syscall.h> +#define syslog(...) syscall(__NR_syslog, __VA_ARGS__) + +#define LINUX_REBOOT_MAGIC1 0xfee1dead +#define LINUX_REBOOT_MAGIC2 672274793 +#define BMAGIC_HARD 0x89ABCDEF +#define BMAGIC_SOFT 0 +#define BMAGIC_REBOOT 0x01234567 +#define BMAGIC_HALT 0xCDEF0123 +#define BMAGIC_POWEROFF 0x4321FEDC + +static unsigned int reboot_magic = BMAGIC_REBOOT; + +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> + +#if defined(__powerpc__) +#define TIOCSCTTY 0x540E +#endif + + +#define BINARY "/sbin/stage1" +#define BINARY_STAGE2 "/usr/bin/runinstall2" + + +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" +#if defined(__x86_64__) || defined(__ppc64__) + ":/lib64:/usr/lib64:/usr/X11R6/lib64:/mnt/lib64:/mnt/usr/lib64:/mnt/usr/X11R6/lib64" +#endif + , + "HOME=/", + "TERM=linux", + "TERMINFO=/etc/terminfo", + NULL +}; + + +/* + * this needs to handle the following cases: + * + * 1) run from a CD root filesystem + * 2) run from a read only nfs rooted filesystem + * 3) run from a floppy + * 4) run from a floppy that's been loaded into a ramdisk + * + */ + +int testing = 0; +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); + select(0, NULL, NULL, NULL, NULL); +} + +void print_error(char *msg) +{ + printf("E: %s\n", msg); +} + +void print_warning(char *msg) +{ + printf("W: %s\n", msg); +} + +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); +} + +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) + */ +void doklog() +{ + fd_set readset, unixs; + int in, out, i; + int log; + socklen_t s; + int sock = -1; + struct sockaddr_un sockaddr; + char buf[1024]; + int readfd; + + /* open kernel message logger */ + in = open("/proc/kmsg", O_RDONLY,0); + if (in < 0) { + print_error("could not open /proc/kmsg"); + return; + } + + mkdir("/tmp", 0755); + if ((log = open("/tmp/syslog", O_WRONLY | O_CREAT | O_APPEND, 0644)) < 0) { + print_error("error opening /tmp/syslog"); + sleep(5); + return; + } + + if ((klog_pid = fork())) { + close(in); + close(log); + return; + } else { + close(0); + 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"); + + /* now open the syslog socket */ +// ############# LINUX 2.4 /dev/log IS BUGGED! --> apparently the syslogs can't reach me, and it's full up after a while +// sockaddr.sun_family = AF_UNIX; +// strncpy(sockaddr.sun_path, "/dev/log", UNIX_PATH_MAX); +// sock = socket(AF_UNIX, SOCK_STREAM, 0); +// if (sock < 0) { +// printf("error creating socket: %d\n", errno); +// sleep(5); +// } +// +// print_str_init(log, "] got socket\n"); +// if (bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr.sun_family) + strlen(sockaddr.sun_path))) { +// print_str_init(log, "] bind error: "); +// print_int_init(log, errno); +// print_str_init(log, "\n"); +// sleep(// } +// +// print_str_init(log, "] bound socket\n"); +// chmod("/dev/log", 0666); +// if (listen(sock, 5)) { +// print_str_init(log, "] listen error: "); +// print_int_init(log, errno); +// print_str_init(log, "\n"); +// sleep(5); +// } + + /* disable on-console syslog output */ + syslog(8, NULL, 1); + + print_str_init(log, "] kernel/system logger ok\n"); + FD_ZERO(&unixs); + while (1) { + memcpy(&readset, &unixs, sizeof(unixs)); + + if (sock >= 0) + FD_SET(sock, &readset); + FD_SET(in, &readset); + + i = select(20, &readset, NULL, NULL, NULL); + if (i <= 0) + continue; + + /* has /proc/kmsg things to tell us? */ + if (FD_ISSET(in, &readset)) { + i = read(in, buf, sizeof(buf)); + if (i > 0) { + if (out >= 0) + write(out, buf, i); + write(log, buf, i); + } + } + + /* the socket has moved, new stuff to do */ + if (sock >= 0 && FD_ISSET(sock, &readset)) { + s = sizeof(sockaddr); + readfd = accept(sock, (struct sockaddr *) &sockaddr, &s); + if (readfd < 0) { + char * msg_error = "] error in accept\n"; + if (out >= 0) + write(out, msg_error, strlen(msg_error)); + write(log, msg_error, strlen(msg_error)); + close(sock); + sock = -1; + } + else + FD_SET(readfd, &unixs); + } + } +} + + +#define LOOP_CLR_FD 0x4C01 + +void del_loops(void) +{ + 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 +{ + char * dev; + char * name; + char * fs; + 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) +{ + int fd, size; + char buf[65535]; /* this should be big enough */ + char *p; + struct filesystem fs[500]; + int numfs = 0; + int i, nb; + int disallow_eject = 0; + + printf("unmounting filesystems...\n"); + + fd = open("/proc/mounts", O_RDONLY, 0); + if (fd < 1) { + print_error("failed to open /proc/mounts"); + sleep(2); + return; + } + + size = read(fd, buf, sizeof(buf) - 1); + buf[size] = '\0'; + + close(fd); + + p = buf; + while (*p) { + fs[numfs].mounted = 1; + fs[numfs].dev = p; + while (*p != ' ') p++; + *p++ = '\0'; + fs[numfs].name = p; + while (*p != ' ') p++; + *p++ = '\0'; + fs[numfs].fs = p; + while (*p != ' ') p++; + *p++ = '\0'; + while (*p != '\n') p++; + p++; + if (!strcmp(fs[numfs].fs, "nfs")) + disallow_eject = 1; + 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: + multiple passes trying to umount everything until nothing moves + anymore (a.k.a holy shotgun method) */ + do { + 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) { + 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("\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"); + 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 = BMAGIC_POWEROFF; + return i > 0; + } + return 0; +} + +int exit_value_proceed = 66; +int exit_value_restart = 0x35; + +int main(int argc, char **argv) +{ + pid_t installpid, childpid; + int wait_status; + int fd; + int counter = 0; + int abnormal_termination = 0; + + 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 */ + printf("\033[9;0]"); + printf("\033[8]"); + } + else + printf("*** TESTING MODE *** (pid is %d)\n", getpid()); + + + if (!testing) { + mkdir("/proc", 0755); + if (mount("/proc", "/proc", "proc", 0, NULL)) + fatal_error("Unable to mount proc filesystem"); + mkdir("/sys", 0755); + if (mount("none", "/sys", "sysfs", 0, NULL)) + fatal_error("Unable to mount sysfs filesystem"); + } + + + /* ignore Control-C and keyboard stop signals */ + signal(SIGINT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + + if (!testing) { + fd = open("/dev/console", O_RDWR, 0); + if (fd < 0) + 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 (!testing) { + char my_hostname[] = "localhost"; + sethostname(my_hostname, sizeof(my_hostname)); + /* the default domainname (as of 2.0.35) is "(none)", which confuses + glibc */ + setdomainname("", 0); + } + + 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 + */ + + do { + if (counter == 1) { + printf("proceeding, please wait...\n"); + } + + if (!(installpid = fork())) { + /* child */ + char * child_argv[2]; + child_argv[0] = counter == 0 ? BINARY : BINARY_STAGE2; + child_argv[1] = NULL; + + execve(child_argv[0], child_argv, env); + printf("error in exec of %s :-( [%d]\n", child_argv[0], errno); + return 0; + } + + do { + childpid = wait4(-1, &wait_status, 0, NULL); + } while (childpid != installpid); + + counter++; + } while (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) == exit_value_restart); + + /* allow Ctrl Alt Del to reboot */ + reboot(BMAGIC_HARD); + + if (in_reboot()) { + // any exitcode is valid if we're in_reboot + } else if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) == exit_value_proceed) { + kill(klog_pid, 9); + printf("proceeding, please wait...\n"); + + { + char * child_argv[2] = { "/sbin/init", NULL }; + execve(child_argv[0], child_argv, env); + } + fatal_error("failed to exec /sbin/init"); + } 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; + } + + 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); + sleep(2); + printf("done\n"); + + printf("sending kill signals..."); + kill(-1, 9); + sleep(2); + printf("done\n"); + + unmount_filesystems(); + + sync(); sync(); + + if (!abnormal_termination) { + if (reboot_magic == BMAGIC_REBOOT) { +#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 or halt your system\n"); + } + + select(0, NULL, NULL, NULL, NULL); + return 0; +} |