changeset 1133:1ad777637d2d

Migrate oneit out of toybox into sources/toys as a standalone program, and fix it so pid 1 has / as its working directory so we can umount /home if necessary.
author Rob Landley <rob@landley.net>
date Sun, 20 Jun 2010 20:32:59 -0500
parents ea4fd1dd8c3b
children 7bd052346d86
files simple-root-filesystem.sh sources/native-root/sbin/init.sh sources/sections/toybox.build sources/toys/oneit.c
diffstat 4 files changed, 99 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/simple-root-filesystem.sh	Sun Jun 20 18:44:46 2010 -0500
+++ b/simple-root-filesystem.sh	Sun Jun 20 20:32:59 2010 -0500
@@ -44,6 +44,12 @@
 cp "$WORK"/config-busybox "$STAGE_DIR"/src || dienow
 build_section toybox
 
+# Build the world's simplest init program: spawns one task with a controlling
+# TTY, waits (reaping zombies) until it exits, then shuts down the system.
+
+${ARCH}-cc "$SOURCES/toys/oneit.c" -Os $CFLAGS -o "$STAGE_DIR/sbin/oneit" ||
+  dienow
+
 # Put statically and dynamically linked hello world programs on there for
 # test purposes.
 
--- a/sources/native-root/sbin/init.sh	Sun Jun 20 18:44:46 2010 -0500
+++ b/sources/native-root/sbin/init.sh	Sun Jun 20 20:32:59 2010 -0500
@@ -75,7 +75,7 @@
       HANDOFF=/mnt/init
     fi
   fi
-  exec /bin/oneit -c /dev/"$CONSOLE" "$HANDOFF"
+  exec /sbin/oneit -c /dev/"$CONSOLE" "$HANDOFF"
 
 # If we're not PID 1, it's probably a chroot.
 else
--- a/sources/sections/toybox.build	Sun Jun 20 18:44:46 2010 -0500
+++ b/sources/sections/toybox.build	Sun Jun 20 20:32:59 2010 -0500
@@ -10,7 +10,6 @@
 if [ -z "$USE_TOYBOX" ]
 then
   ln -sf toybox "$INSTDIR/patch" &&
-  ln -sf toybox "$INSTDIR/oneit" &&
   ln -sf toybox "$INSTDIR/netcat" || dienow
 else
   make install_flat PREFIX="$INSTDIR" || dienow
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/toys/oneit.c	Sun Jun 20 20:32:59 2010 -0500
@@ -0,0 +1,92 @@
+/* vi: set sw=4 ts=4:
+ *
+ * oneit.c, tiny one-process init replacement.
+ *
+ * Copyright 2005, 2010 by Rob Landley <rob@landley.net>.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+
+char *console;
+int poweroff;
+
+// The minimum amount of work necessary to get ctrl-c and such to work is:
+//
+// - Fork a child (PID 1 is special: can't exit, has various signals blocked).
+// - Do a setsid() (so we have our own session).
+// - In the child, attach stdio to /dev/tty0 (/dev/console is special)
+// - Exec the rest of the command line.
+//
+// PID 1 then reaps zombies until the child process it spawned exits, at which
+// point it calls sync() and reboot().  I could stick a kill -1 in there.
+
+
+int main(int argc, char *argv[])
+{
+  int i, args;
+  pid_t pid;
+
+  for (args=1; args<argc; args++) {
+    if (*argv[args]=='-') switch (argv[args][1]) {
+      case 'c':
+        console=argv[++args];
+        continue;
+      case 'p':
+        poweroff++;
+        continue;
+      default:
+        args=argc;
+        break;
+    }
+    break;
+  }
+  if (args>=argc) {
+    fprintf(stderr,
+      "usage: oneit [-p] [-c /dev/tty0] command [...]\n\n"
+      "A simple init program that runs a single supplied command line with a\n"
+      "controlling tty (so CTRL-C can kill it).\n\n"
+      "-p\tPower off instead of rebooting when command exits.\n"
+      "-c\tWhich console device to use.\n\n"
+      "The oneit command runs the supplied command line as a child process\n"
+      "(because PID 1 has signals blocked), attached to /dev/tty0, in its\n"
+      "own session.  Then oneit reaps zombies until the child exits, at\n"
+      "which point it reboots (or with -p, powers off) the system.\n");
+    exit(1);
+  }
+
+  // Create a new child process.
+  pid = vfork();
+  if (pid) {
+    chdir("/");
+
+    // pid 1 just reaps zombies until it gets its child, then halts the system.
+    while (pid!=wait(&i));
+    sync();
+
+    // PID 1 can't call reboot() because that syscall kills the task that calls
+    // it, which causes the kernel to panic before the actual reboot happens.
+    if (!vfork()) reboot(poweroff ? RB_POWER_OFF : RB_AUTOBOOT);
+    sleep(5);
+    _exit(1);
+  }
+
+  // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works.
+  setsid();
+  if (!console) console="/dev/tty0";
+  for (i=0; i<3; i++) {
+    close(i);
+    if(-1==open(console, O_RDWR)) {
+      fprintf(stderr, "Can't open '%s'\n", console);
+      exit(1);
+    }
+  }
+
+  execvp(argv[args], argv+args);
+  _exit(127);
+}