spawn udevd & Co from 1st stage init

This commit is contained in:
Sergey Bolshakov 2008-02-29 17:37:40 +03:00
parent 85b34d353b
commit 419b4c1201

285
init.c
View File

@ -19,11 +19,18 @@
*
*/
#ifndef INIT_HEADERS
#include "init-libc-headers.h"
#else
#include INIT_HEADERS
#endif
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/klog.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/wait.h>
#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;
}