From 419b4c1201e5a66aaf97e80be00a1c9e719e621e Mon Sep 17 00:00:00 2001 From: Sergey Bolshakov Date: Fri, 29 Feb 2008 17:37:40 +0300 Subject: [PATCH] spawn udevd & Co from 1st stage init --- init.c | 285 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 152 insertions(+), 133 deletions(-) diff --git a/init.c b/init.c index bd84fe1..073cd60 100644 --- a/init.c +++ b/init.c @@ -19,11 +19,18 @@ * */ -#ifndef INIT_HEADERS -#include "init-libc-headers.h" -#else -#include INIT_HEADERS -#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "config-stage1.h" @@ -31,9 +38,13 @@ #define TIOCSCTTY 0x540 #endif +#define MKDEV(ma,mi) ((ma)<<8 | (mi)) +#define RAMFS_MAGIC 0x858458f6 +#define TMPFS_MAGIC 0x01021994 + char * env[] = { - "PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/X11R6/bin", - "LD_LIBRARY_PATH=/lib:/usr/lib:/usr/X11R6/lib", + "PATH=/usr/bin:/bin:/sbin:/usr/sbin", + "LD_LIBRARY_PATH=/lib:/usr/lib", "HOME=/", "TERM=linux", "TERMINFO=/etc/terminfo", @@ -42,6 +53,11 @@ char * env[] = { char ** myenv = NULL; +char *stage[] = {"/sbin/stage1", NULL}; +char *udevd[] = {"/sbin/udevd", NULL}; +char *udevtrigger[] = {"/sbin/udevtrigger", NULL}; +char *udevsettle[] = {"/sbin/udevsettle", NULL}; + /* * this needs to handle the following cases: * @@ -51,21 +67,14 @@ char ** myenv = NULL; * 4) run from a floppy that's been loaded into a ramdisk */ -int klog_pid; - - -void fatal_error(const char *msg) +void fatal(const char *msg) { - printf("FATAL ERROR IN INIT: %s : %s\n\nI can't recover from this, please reboot manually and send bugreport.\n", msg,strerror(errno)); + printf("FATAL ERROR IN INIT: %s\nI can't recover from this," + "please reboot manually and send bugreport.\n", msg); while (1); } -void print_error(char *msg) -{ - printf("E: %s\n", msg); -} - -void print_warning(char *msg) +void warn(char *msg) { printf("W: %s\n", msg); } @@ -74,52 +83,50 @@ void print_warning(char *msg) * (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() +pid_t doklog() { int in, out, i, ii; - int log; + pid_t pid; char buf[1024]; /* open kernel message logger */ - in = open("/proc/kmsg", O_RDONLY,0); - if (in < 0) { - print_error("could not open /proc/kmsg"); - return; - } + if ((in = open("/proc/kmsg", O_RDONLY, 0)) < 0) + fatal("failed to open /proc/kmsg"); - if ((log = open("/tmp/syslog", O_WRONLY | O_CREAT, 0644)) < 0) { - perror("/tmp/syslog"); - print_error("error opening /tmp/syslog"); - sleep(5); - return; - } + if (mknod("/dev/tty4", S_IFCHR, MKDEV(4, 4)) < 0 || + (out = open("/dev/tty4", O_WRONLY, 0)) < 0) + fatal("failed to open /dev/tty4"); - if ((klog_pid = fork())) { + if ((pid = fork())) { + if (pid < 0) fatal("doklog"); close(in); - close(log); - return; - } else { - close(0); - close(1); - close(2); - } - - out = open("/dev/tty4", O_WRONLY, 0); - if (out < 0) { - perror("can't open /dev/tty4"); - print_warning("couldn't open tty for syslog -- still using /tmp/syslog\n"); + close(out); + return pid; } + /* child */ + close(0); + close(1); + close(2); + /* disable on-console syslog output */ - syslog(8, NULL, 1); + klogctl(8, NULL, 1); - while (1) { - i = read(in, buf, sizeof(buf)); - if (i > 0) { - if (out >= 0) - ii = write(out, buf, i); - ii = write(log, buf, i); - } + while (1) + if ((i = read(in, buf, sizeof(buf))) > 0) + ii = write(out, buf, i); +} + +pid_t spawn(char *av[]) +{ + pid_t pid; + if ((pid = fork())) { + if (pid < 0) fatal(av[0]); + return pid; + } else { + execve(av[0], av, env); + perror(av[0]); + exit(1); } } @@ -131,7 +138,7 @@ void grab_env(int fd) int i; if ((i = read(fd, buf, sizeof(buf))) < 0) { - print_error("failed to read env from pipe"); + warn("failed to read env from pipe"); myenv = env; return; } @@ -140,11 +147,11 @@ void grab_env(int fd) buf[i] = '\0'; if ((ep = myenv = malloc(sizeof(char *) * 32)) == NULL) { - print_error("can't malloc env"); + warn("can't malloc env"); myenv = env; return; } - + do { *ep++ = p; p += strlen(p); @@ -252,7 +259,7 @@ static int nuke(const char *what) if ( err ) { errno = err; - fatal_error(what); + fatal(what); } return 0; } @@ -279,7 +286,7 @@ void unmount_filesystems(void) fd = open("/proc/mounts", O_RDONLY, 0); if (fd < 1) { - print_error("failed to open /proc/mounts"); + warn("failed to open /proc/mounts"); sleep(2); return; } @@ -332,66 +339,89 @@ void unmount_filesystems(void) } if (nb) { - printf("failed to umount some filesystems\n"); - while (1); + fatal("failed to umount some filesystems\n"); } } -#define MAJOR(dev) ((dev)>>8) -#define MINOR(dev) ((dev) & 0xff) -#define MKDEV(ma,mi) ((ma)<<8 | (mi)) - -#define DEV_PERM 00400|00200|00040|00020 -#define CHR_DEV 0020000|DEV_PERM -#define BLK_DEV 0060000|DEV_PERM - -#define RAMFS_MAGIC 0x858458f6 -#define TMPFS_MAGIC 0x01021994 - int main(int argc, char **argv) { struct stat rst, cst, ist; struct statfs sfs; - pid_t installpid, childpid; + pid_t pid, klogpid, udevpid; int wait_status; - int fd; + int fd = -1; int fds[2]; - int end_stage1 = 0; - + if (mount("/proc", "/proc", "proc", 0, NULL)) - fatal_error("Unable to mount proc filesystem"); - if (mount("/sysfs", "/sys", "sysfs", 0, NULL)) - fatal_error("Unable to mount sysfs filesystem"); - + fatal("failed to mount proc filesystem"); + if (mount("sysfs", "/sys", "sysfs", 0, NULL)) + fatal("failed to mount sysfs filesystem"); + if (mount("udev", "/dev", "tmpfs", 0, "size=10M,mode=0755")) + fatal("failed to mount tmpfs filesystem"); + /* ignore Control-C and keyboard stop signals */ signal(SIGINT, SIG_IGN); signal(SIGTSTP, SIG_IGN); - fd = open("/dev/console", O_RDWR, 0); - if (fd < 0) - fatal_error("failed to open /dev/console"); - + if (mknod("/dev/console", S_IFCHR, MKDEV(5, 1)) < 0 || + (fd = open("/dev/console", O_RDWR, 0)) < 0) { + fatal("failed to open /dev/console"); + } + dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd); + if (mknod("/dev/null", S_IFCHR, MKDEV(1, 3)) < 0) + fatal("failed to create /dev/null"); + /* I set me up as session leader (probably not necessary?) */ setsid(); if (ioctl(0, TIOCSCTTY, NULL)) { perror("TIOCSCTTY"); - print_error("could not set new controlling tty"); + warn("could not set new controlling tty"); } - char my_hostname[] = "localhost.localdomain"; - if (sethostname(my_hostname, sizeof(my_hostname)) < 0) - print_error("could not set hostname"); + if (sethostname("localhost.localdomain", sizeof("localhost.localdomain")) < 0) + warn("could not set hostname"); /* the default domainname (as of 2.0.35) is "(none)", which confuses glibc */ if (setdomainname("", 0) < 0) - print_error("could not set domainname"); + warn("could not set domainname"); + + if (mkdir("/dev/.initramfs", 0755) < 0 || + mkdir("/dev/pts", 0755) < 0 || + mkdir("/dev/shm", 0755) < 0) + fatal("mkdir\n"); - doklog(); + if ((fd = open("/proc/sys/kernel/hotplug", O_WRONLY, 0)) < 0 || + write(fd, "\n", sizeof("\n")) < sizeof("\n")) + fatal("/proc/sys/kernel/hotplug\n"); + close(fd); + + klogpid = doklog(); + + if (mkdir("/dev/.udev", 0755) < 0 || + mkdir("/dev/.udev/db", 0755) < 0) + fatal("/dev/.udev/db"); + + printf("Spawning udevd..."); + + udevpid = spawn(udevd); + + if (mkdir("/dev/.udev/queue", 0755) < 0 && errno != EEXIST) + fatal("cannot create /dev/.udev/queue"); + + if (waitpid(spawn(udevtrigger), &wait_status, 0) < 0 || + !(WIFEXITED(wait_status))) + warn("udevtrigger"); + + if (waitpid(spawn(udevsettle), &wait_status, 0) < 0 || + !(WIFEXITED(wait_status))) + warn("udevsettle"); + + printf("done\n"); /* Go into normal init mode - keep going, and then do a orderly shutdown when: @@ -404,33 +434,19 @@ int main(int argc, char **argv) /* create a pipe for env passing */ if (pipe(fds) < 0) - fatal_error("failed to create env pipe"); + fatal("failed to create env pipe"); fcntl(fds[0], F_SETFD, 1); fcntl(fds[1], F_SETFD, 0); - - if (!(installpid = fork())) { - /* child */ - char * child_argv[2]; - child_argv[0] = "/sbin/stage1"; - child_argv[1] = NULL; - execve(child_argv[0], child_argv, env); - printf("error in exec of stage1 :-(\n"); - return 0; - } + pid = spawn(stage); close(fds[1]); - - while (!end_stage1) { - childpid = wait4(-1, &wait_status, 0, NULL); - if (childpid == installpid) - end_stage1 = 1; - } + while (pid != wait(&wait_status)); if (!(WIFEXITED(wait_status))) { /* something went wrong */ - printf("wait_status: %i, install exited abnormally :-( ", wait_status); + printf("wait_status: %i, install exited abnormally ", wait_status); if (WIFSIGNALED(wait_status)) printf("-- received signal %d", WTERMSIG(wait_status)); printf("\n"); @@ -455,15 +471,23 @@ int main(int argc, char **argv) grab_env(fds[0]); - kill(klog_pid, 9); - waitpid(klog_pid, &wait_status, 0); + if (waitpid(spawn(udevsettle), &wait_status, 0) < 0 || + !(WIFEXITED(wait_status))) + warn("udevsettle"); + + kill(udevpid, 9); + waitpid(udevpid, &wait_status, 0); + nuke_dir("/dev/.udev/queue"); + + kill(klogpid, 9); + waitpid(klogpid, &wait_status, 0); printf("Spawning init ..."); /* rest was seamlessy stolen from klibc */ /* First, change to the new root directory */ if (chdir(STAGE2_LOCATION)) - fatal_error("chdir to new root"); + fatal("chdir to new root"); /* This is a potentially highly destructive program. Take some extra precautions. */ @@ -471,71 +495,66 @@ int main(int argc, char **argv) /* Make sure the current directory is not on the same filesystem as the root directory */ if ( stat("/", &rst) || stat(".", &cst) ) - fatal_error("stat"); + fatal("stat"); if ( rst.st_dev == cst.st_dev ) - fatal_error("current directory on the same filesystem as the root"); + fatal("current directory on the same filesystem as the root"); /* The initramfs should have /init */ if ( stat("/init", &ist) || !S_ISREG(ist.st_mode) ) - fatal_error("can't find /init on initramfs"); + fatal("can't find /init on initramfs"); /* Make sure we're on a ramfs */ if ( statfs("/", &sfs) ) - fatal_error("statfs /"); + fatal("statfs /"); if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC ) - fatal_error("rootfs not a ramfs or tmpfs"); + fatal("rootfs not a ramfs or tmpfs"); /* Okay, I think we should be safe... */ /* overmount image under new root if needed */ if ( statfs(IMAGE_LOCATION, &sfs) ) - fatal_error("statfs "IMAGE_LOCATION); + fatal("statfs "IMAGE_LOCATION); /* if something is mounted under IMAGE_LOCATION ? */ if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC ) { if ( mount(IMAGE_LOCATION, "." IMAGE_LOCATION, NULL, MS_MOVE, NULL) ) - fatal_error("overmounting image location"); + fatal("overmounting image location"); /* test for nested mount: disk or nfs with iso image */ if ( statfs(IMAGE_LOCATION, &sfs) ) - fatal_error("nested statfs "IMAGE_LOCATION); + fatal("nested statfs "IMAGE_LOCATION); if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC ) if ( mount(IMAGE_LOCATION, "." IMAGE_LOCATION "/isolinux", NULL, MS_MOVE, NULL) ) - fatal_error("overmounting nested image location"); + fatal("overmounting nested image location"); } umount("/sys"); umount("/proc/bus/usb"); umount("/proc"); + if (mount("/dev", "./dev", NULL, MS_MOVE, NULL)) + fatal("overmounting /dev"); + /* Delete rootfs contents */ - if ( nuke_dir("/") ) - fatal_error("nuking initramfs contents"); + if (nuke_dir("/")) + fatal("nuking initramfs contents"); /* Overmount the root */ - if ( mount(".", "/", NULL, MS_MOVE, NULL) ) - fatal_error("overmounting root"); + if (mount(".", "/", NULL, MS_MOVE, NULL)) + fatal("overmounting root"); /* chroot, chdir */ if (chroot(".") || chdir("/")) - fatal_error("chroot"); - - /* Open /dev/console */ - if ((fd = open("/dev/console", O_RDWR)) != -1) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - close(fd); - } + fatal("chroot"); /* Check for given init */ if (stat(STAGE2_BINNAME, &ist) || !S_ISREG(ist.st_mode)) - fatal_error("can't find init on root fs"); + fatal("can't find init on root fs"); /* Spawn init */ printf(" done.\n"); argv[0] = STAGE2_BINNAME; execve(argv[0], argv, myenv); - fatal_error("stage2"); /* Failed to spawn init */ + fatal("stage2"); /* Failed to spawn init */ return 0; }