/* * Guillaume Cottenceau (gc@mandrakesoft.com) * * Copyright 2000 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 * */ #ifndef INIT_HEADERS #include "init-libc-headers.h" #else #include INIT_HEADERS #endif #include "config-stage1.h" #if defined(__powerpc__) #define TIOCSCTTY 0x540 #endif char * env[] = { "PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/X11R6/bin", "LD_LIBRARY_PATH=/lib:/usr/lib:/usr/X11R6/lib", "HOME=/", "TERM=linux", "TERMINFO=/etc/terminfo", NULL }; char ** myenv = 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 klog_pid; void fatal_error(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)); while (1); } 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() { int in, out, i; int log; 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 ((log = open("/tmp/syslog", O_WRONLY | O_CREAT, 0644)) < 0) { perror("/tmp/syslog"); 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) { perror("can't open /dev/tty4"); print_warning("couldn't open tty for syslog -- still using /tmp/syslog\n"); } /* disable on-console syslog output */ syslog(8, NULL, 1); while (1) { i = read(in, buf, sizeof(buf)); if (i > 0) { if (out >= 0) write(out, buf, i); write(log, buf, i); } } } void grab_env(int fd) { char buf[PIPE_BUF]; char *p = buf; char **ep; int i; if ((i = read(fd, buf, sizeof(buf))) < 0) { print_error("failed to read env from pipe"); myenv = env; return; } close(fd); buf[i] = '\0'; if ((ep = myenv = malloc(sizeof(char *) * 32)) == NULL) { print_error("can't malloc env"); myenv = env; return; } do { *ep++ = p; p += strlen(p); } while(++p < buf+i); *ep = NULL; } #define LOOP_CLR_FD 0x4C01 void del_loop(char *device) { 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); } /* initramfs cleaner */ static int nuke(const char *what); static int nuke_dirent(int len, const char *dir, const char *name, dev_t me) { int bytes = len+strlen(name)+2; char path[bytes]; int xlen; struct stat st; xlen = snprintf(path, bytes, "%s/%s", dir, name); /* assert(xlen < bytes); */ if ( lstat(path, &st) ) return ENOENT; /* Return 0 since already gone? */ if ( st.st_dev != me ) return 0; /* DO NOT recurse down mount points!!!!! */ return nuke(path); } /* Wipe the contents of a directory, but not the directory itself */ static int nuke_dir(const char *what) { int len = strlen(what); DIR *dir; struct dirent *d; int err = 0; struct stat st; if ( lstat(what, &st) ) return errno; if ( !S_ISDIR(st.st_mode) ) return ENOTDIR; if ( !(dir = opendir(what)) ) { /* EACCES means we can't read it. Might be empty and removable; if not, the rmdir() in nuke() will trigger an error. */ return (errno == EACCES) ? 0 : errno; } while ( (d = readdir(dir)) ) { /* Skip . and .. */ if ( d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0')) ) continue; err = nuke_dirent(len, what, d->d_name, st.st_dev); if ( err ) { closedir(dir); return err; } } closedir(dir); return 0; } static int nuke(const char *what) { int rv; int err = 0; rv = unlink(what); if ( rv < 0 ) { if ( errno == EISDIR ) { /* It's a directory. */ err = nuke_dir(what); if ( !err ) err = rmdir(what) ? errno : err; } else { err = errno; } } if ( err ) { errno = err; fatal_error(what); } else { return 0; } } struct filesystem { char * dev; char * name; char * fs; int mounted; }; /* 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; 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].name, "/") != 0) numfs++; /* skip if root, no need to take initrd root in account */ } /* 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);*/ 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 */ } if (nb) { printf("failed to umount some filesystems\n"); while (1); } } #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 #define MS_MOVE 8192 int main(int argc, char **argv) { struct stat rst, cst, ist; struct statfs sfs; pid_t installpid, childpid; int wait_status; int fd; 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"); /* 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"); 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)) { perror("TIOCSCTTY"); print_error("could not set new controlling tty"); } char my_hostname[] = "localhost.localdomain"; sethostname(my_hostname, sizeof(my_hostname)); /* the default domainname (as of 2.0.35) is "(none)", which confuses glibc */ setdomainname("", 0); doklog(); /* Go into normal init mode - keep going, and then do a orderly shutdown when: 1) install exits 2) we receive a SIGHUP */ printf("Running stage1...\n"); /* create a pipe for env passing */ pipe(fds); 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; } close(fds[1]); while (!end_stage1) { childpid = wait4(-1, &wait_status, 0, NULL); if (childpid == installpid) end_stage1 = 1; } if (!(WIFEXITED(wait_status))) { /* something went wrong */ printf("wait_status: %i, install exited abnormally :-( ", wait_status); if (WIFSIGNALED(wait_status)) printf("-- received signal %d", WTERMSIG(wait_status)); printf("\n"); sync(); sync(); 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(); printf("you may safely reboot your system\n"); while (1); } grab_env(fds[0]); kill(klog_pid, 9); waitpid(klog_pid, &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"); /* This is a potentially highly destructive program. Take some extra precautions. */ /* Make sure the current directory is not on the same filesystem as the root directory */ if ( stat("/", &rst) || stat(".", &cst) ) fatal_error("stat"); if ( rst.st_dev == cst.st_dev ) fatal_error("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"); /* Make sure we're on a ramfs */ if ( statfs("/", &sfs) ) fatal_error("statfs /"); if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC ) fatal_error("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); /* 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"); /* test for nested mount: disk or nfs with iso image */ if ( statfs(IMAGE_LOCATION, &sfs) ) fatal_error("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"); } umount("/sys"); umount("/proc/bus/usb"); umount("/proc"); /* Delete rootfs contents */ if ( nuke_dir("/") ) fatal_error("nuking initramfs contents"); /* Overmount the root */ if ( mount(".", "/", NULL, MS_MOVE, NULL) ) fatal_error("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); } /* Check for given init */ if (stat(STAGE2_BINNAME, &ist) || !S_ISREG(ist.st_mode)) fatal_error("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 */ return 0; }