comparison toys/other/oneit.c @ 1725:b2b2d214727a draft

Upgrade oneit with -r (restart), -3 (send exiting PID values to child), and signal handling.
author Rob Landley <rob@landley.net>
date Mon, 09 Mar 2015 14:52:32 -0500
parents 8c78a7e5486d
children
comparison
equal deleted inserted replaced
1724:81d8a7b20ce9 1725:b2b2d214727a
1 /* oneit.c - tiny init replacement to launch a single child process. 1 /* oneit.c - tiny init replacement to launch a single child process.
2 * 2 *
3 * Copyright 2005, 2007 by Rob Landley <rob@landley.net>. 3 * Copyright 2005, 2007 by Rob Landley <rob@landley.net>.
4 4
5 USE_ONEIT(NEWTOY(oneit, "^<1c:p", TOYFLAG_SBIN)) 5 USE_ONEIT(NEWTOY(oneit, "^<1nc:p3[!pn]", TOYFLAG_SBIN))
6 6
7 config ONEIT 7 config ONEIT
8 bool "oneit" 8 bool "oneit"
9 default y 9 default y
10 help 10 help
11 usage: oneit [-p] [-c /dev/tty0] command [...] 11 usage: oneit [-p] [-c /dev/tty0] command [...]
12 12
13 A simple init program that runs a single supplied command line with a 13 Simple init program that runs a single supplied command line with a
14 controlling tty (so CTRL-C can kill it). 14 controlling tty (so CTRL-C can kill it).
15 15
16 -c Which console device to use (/dev/console doesn't do CTRL-C, etc).
16 -p Power off instead of rebooting when command exits. 17 -p Power off instead of rebooting when command exits.
17 -c Which console device to use. 18 -r Restart child when it exits.
19 -3 Write 32 bit PID of each exiting reparented process to fd 3 of child.
20 (Blocking writes, child must read to avoid eventual deadlock.)
18 21
19 The oneit command runs the supplied command line as a child process 22 Spawns a single child process (because PID 1 has signals blocked)
20 (because PID 1 has signals blocked), attached to /dev/tty0, in its 23 in its own session, reaps zombies until the child exits, then
21 own session. Then oneit reaps zombies until the child exits, at 24 reboots the system (or powers off with -p, or restarts the child with -r).
22 which point it reboots (or with -p, powers off) the system.
23 */ 25 */
24 26
25 #define FOR_oneit 27 #define FOR_oneit
26 #include "toys.h" 28 #include "toys.h"
27 #include <sys/reboot.h> 29 #include <sys/reboot.h>
38 // - Exec the rest of the command line. 40 // - Exec the rest of the command line.
39 // 41 //
40 // PID 1 then reaps zombies until the child process it spawned exits, at which 42 // PID 1 then reaps zombies until the child process it spawned exits, at which
41 // point it calls sync() and reboot(). I could stick a kill -1 in there. 43 // point it calls sync() and reboot(). I could stick a kill -1 in there.
42 44
45 // Perform actions in response to signals. (Only root can send us signals.)
46 static void oneit_signaled(int signal)
47 {
48 int action = RB_AUTOBOOT;
49
50 toys.signal = signal;
51 if (signal == SIGUSR1) action = RB_HALT_SYSTEM;
52 if (signal == SIGUSR2) action = RB_POWER_OFF;
53
54 // PID 1 can't call reboot() because it kills the task that calls it,
55 // which causes the kernel to panic before the actual reboot happens.
56 sync();
57 if (!vfork()) reboot(action);
58 }
43 59
44 void oneit_main(void) 60 void oneit_main(void)
45 { 61 {
46 int i; 62 int i, pid, pipes[] = {SIGUSR1, SIGUSR2, SIGTERM, SIGINT};
47 pid_t pid;
48 63
49 // Create a new child process. 64 if (FLAG_3) {
50 pid = vfork(); 65 // Ensure next available filehandle is #3
51 if (pid) { 66 while (open("/", 0) < 3);
52 67 close(3);
53 // pid 1 just reaps zombies until it gets its child, then halts the system. 68 close(4);
54 while (pid != wait(&i)); 69 if (pipe(pipes)) perror_exit("pipe");
55 sync(); 70 fcntl(4, F_SETFD, FD_CLOEXEC);
56
57 // PID 1 can't call reboot() because it kills the task that calls it,
58 // which causes the kernel to panic before the actual reboot happens.
59 if (!vfork()) reboot((toys.optflags & FLAG_p) ? RB_POWER_OFF : RB_AUTOBOOT);
60 sleep(5);
61 _exit(1);
62 } 71 }
63 72
64 // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works. 73 // Setup signal handlers for signals of interest
65 setsid(); 74 for (i = 0; i<ARRAY_LEN(pipes); i++) xsignal(pipes[i], oneit_signaled);
66 for (i=0; i<3; i++) { 75
67 close(i); 76 while (!toys.signal) {
68 // Remember, O_CLOEXEC is backwards for xopen() 77
69 xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR|O_CLOEXEC); 78 // Create a new child process.
79 pid = vfork();
80 if (pid) {
81
82 // pid 1 reaps zombies until it gets its child, then halts system.
83 // We ignore the return value of write (what would we do with it?)
84 // but save it in a variable we never read to make fortify shut up.
85 // (Real problem is if pid2 never reads, write() fills pipe and blocks.)
86 while (pid != wait(&i)) if (FLAG_3) i = write(4, &pid, 4);
87 if (toys.optflags & FLAG_n) continue;
88
89 oneit_signaled((toys.optflags & FLAG_p) ? SIGUSR2 : SIGTERM);
90 } else {
91 // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works.
92 setsid();
93 for (i=0; i<3; i++) {
94 close(i);
95 // Remember, O_CLOEXEC is backwards for xopen()
96 xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR|O_CLOEXEC);
97 }
98
99 // Can't xexec() here, we vforked so we don't want to error_exit().
100 toy_exec(toys.optargs);
101 execvp(*toys.optargs, toys.optargs);
102 perror_msg("%s not in PATH=%s", *toys.optargs, getenv("PATH"));
103
104 break;
105 }
70 } 106 }
71 107
72 // Can't xexec() here, because we vforked so we don't want to error_exit(). 108 // Give reboot() time to kick in, or avoid rapid spinning if exec failed
73 toy_exec(toys.optargs); 109 sleep(5);
74 execvp(*toys.optargs, toys.optargs);
75 _exit(127); 110 _exit(127);
76 } 111 }