10f66f451Sopenharmony_ci/* oneit.c - tiny init replacement to launch a single child process. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2005, 2007 by Rob Landley <rob@landley.net>. 40f66f451Sopenharmony_ci 50f66f451Sopenharmony_ciUSE_ONEIT(NEWTOY(oneit, "^<1nc:p3[!pn]", TOYFLAG_SBIN)) 60f66f451Sopenharmony_ci 70f66f451Sopenharmony_ciconfig ONEIT 80f66f451Sopenharmony_ci bool "oneit" 90f66f451Sopenharmony_ci default y 100f66f451Sopenharmony_ci help 110f66f451Sopenharmony_ci usage: oneit [-p] [-c /dev/tty0] command [...] 120f66f451Sopenharmony_ci 130f66f451Sopenharmony_ci Simple init program that runs a single supplied command line with a 140f66f451Sopenharmony_ci controlling tty (so CTRL-C can kill it). 150f66f451Sopenharmony_ci 160f66f451Sopenharmony_ci -c Which console device to use (/dev/console doesn't do CTRL-C, etc) 170f66f451Sopenharmony_ci -p Power off instead of rebooting when command exits 180f66f451Sopenharmony_ci -r Restart child when it exits 190f66f451Sopenharmony_ci -3 Write 32 bit PID of each exiting reparented process to fd 3 of child 200f66f451Sopenharmony_ci (Blocking writes, child must read to avoid eventual deadlock.) 210f66f451Sopenharmony_ci 220f66f451Sopenharmony_ci Spawns a single child process (because PID 1 has signals blocked) 230f66f451Sopenharmony_ci in its own session, reaps zombies until the child exits, then 240f66f451Sopenharmony_ci reboots the system (or powers off with -p, or restarts the child with -r). 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_ci Responds to SIGUSR1 by halting the system, SIGUSR2 by powering off, 270f66f451Sopenharmony_ci and SIGTERM or SIGINT reboot. 280f66f451Sopenharmony_ci*/ 290f66f451Sopenharmony_ci 300f66f451Sopenharmony_ci#define FOR_oneit 310f66f451Sopenharmony_ci#include "toys.h" 320f66f451Sopenharmony_ci#include <sys/reboot.h> 330f66f451Sopenharmony_ci 340f66f451Sopenharmony_ciGLOBALS( 350f66f451Sopenharmony_ci char *c; 360f66f451Sopenharmony_ci) 370f66f451Sopenharmony_ci 380f66f451Sopenharmony_ci// The minimum amount of work necessary to get ctrl-c and such to work is: 390f66f451Sopenharmony_ci// 400f66f451Sopenharmony_ci// - Fork a child (PID 1 is special: can't exit, has various signals blocked). 410f66f451Sopenharmony_ci// - Do a setsid() (so we have our own session). 420f66f451Sopenharmony_ci// - In the child, attach stdio to /dev/tty0 (/dev/console is special) 430f66f451Sopenharmony_ci// - Exec the rest of the command line. 440f66f451Sopenharmony_ci// 450f66f451Sopenharmony_ci// PID 1 then reaps zombies until the child process it spawned exits, at which 460f66f451Sopenharmony_ci// point it calls sync() and reboot(). I could stick a kill -1 in there. 470f66f451Sopenharmony_ci 480f66f451Sopenharmony_ci// Perform actions in response to signals. (Only root can send us signals.) 490f66f451Sopenharmony_cistatic void oneit_signaled(int signal) 500f66f451Sopenharmony_ci{ 510f66f451Sopenharmony_ci int action = RB_AUTOBOOT; 520f66f451Sopenharmony_ci 530f66f451Sopenharmony_ci toys.signal = signal; 540f66f451Sopenharmony_ci if (signal == SIGUSR1) action = RB_HALT_SYSTEM; 550f66f451Sopenharmony_ci if (signal == SIGUSR2) action = RB_POWER_OFF; 560f66f451Sopenharmony_ci 570f66f451Sopenharmony_ci // PID 1 can't call reboot() because it kills the task that calls it, 580f66f451Sopenharmony_ci // which causes the kernel to panic before the actual reboot happens. 590f66f451Sopenharmony_ci sync(); 600f66f451Sopenharmony_ci if (getpid()!=1) _exit(127+signal); 610f66f451Sopenharmony_ci if (!vfork()) reboot(action); 620f66f451Sopenharmony_ci} 630f66f451Sopenharmony_ci 640f66f451Sopenharmony_civoid oneit_main(void) 650f66f451Sopenharmony_ci{ 660f66f451Sopenharmony_ci int i, pid, pipes[] = {SIGUSR1, SIGUSR2, SIGTERM, SIGINT}; 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci // Setup signal handlers for signals of interest 690f66f451Sopenharmony_ci for (i = 0; i<ARRAY_LEN(pipes); i++) xsignal(pipes[i], oneit_signaled); 700f66f451Sopenharmony_ci 710f66f451Sopenharmony_ci if (FLAG(3)) { 720f66f451Sopenharmony_ci // Ensure next available filehandles are #3 and #4 730f66f451Sopenharmony_ci while (xopen_stdio("/", 0) < 3); 740f66f451Sopenharmony_ci close(3); 750f66f451Sopenharmony_ci close(4); 760f66f451Sopenharmony_ci xpipe(pipes); 770f66f451Sopenharmony_ci fcntl(4, F_SETFD, FD_CLOEXEC); 780f66f451Sopenharmony_ci } 790f66f451Sopenharmony_ci 800f66f451Sopenharmony_ci while (!toys.signal) { 810f66f451Sopenharmony_ci 820f66f451Sopenharmony_ci // Create a new child process. 830f66f451Sopenharmony_ci pid = XVFORK(); 840f66f451Sopenharmony_ci if (pid) { 850f66f451Sopenharmony_ci 860f66f451Sopenharmony_ci // pid 1 reaps zombies until it gets its child, then halts system. 870f66f451Sopenharmony_ci // We ignore the return value of write (what would we do with it?) 880f66f451Sopenharmony_ci // but save it in a variable we never read to make fortify shut up. 890f66f451Sopenharmony_ci // (Real problem is if pid2 never reads, write() fills pipe and blocks.) 900f66f451Sopenharmony_ci while (pid != wait(&i)) if (FLAG(3)) i = write(4, &pid, 4); 910f66f451Sopenharmony_ci if (FLAG(n)) continue; 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci oneit_signaled(FLAG(p) ? SIGUSR2 : SIGTERM); 940f66f451Sopenharmony_ci } else { 950f66f451Sopenharmony_ci // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works. 960f66f451Sopenharmony_ci setsid(); 970f66f451Sopenharmony_ci for (i=0; i<3; i++) { 980f66f451Sopenharmony_ci close(i); 990f66f451Sopenharmony_ci // Remember, O_CLOEXEC is backwards for xopen() 1000f66f451Sopenharmony_ci xopen_stdio(TT.c ? TT.c : "/dev/tty0", O_RDWR|O_CLOEXEC); 1010f66f451Sopenharmony_ci } 1020f66f451Sopenharmony_ci 1030f66f451Sopenharmony_ci // Can't xexec() here, we vforked so we don't want to error_exit(). 1040f66f451Sopenharmony_ci toy_exec(toys.optargs); 1050f66f451Sopenharmony_ci execvp(*toys.optargs, toys.optargs); 1060f66f451Sopenharmony_ci perror_msg("%s not in PATH=%s", *toys.optargs, getenv("PATH")); 1070f66f451Sopenharmony_ci 1080f66f451Sopenharmony_ci break; 1090f66f451Sopenharmony_ci } 1100f66f451Sopenharmony_ci } 1110f66f451Sopenharmony_ci 1120f66f451Sopenharmony_ci // Give reboot() time to kick in, or avoid rapid spinning if exec failed 1130f66f451Sopenharmony_ci sleep(5); 1140f66f451Sopenharmony_ci _exit(127); 1150f66f451Sopenharmony_ci} 116