]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-stub-pid1.c
Merge pull request #2589 from keszybz/resolve-tool-2
[thirdparty/systemd.git] / src / nspawn / nspawn-stub-pid1.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <sys/reboot.h>
21 #include <sys/unistd.h>
22 #include <sys/wait.h>
23
24 #include "fd-util.h"
25 #include "log.h"
26 #include "nspawn-stub-pid1.h"
27 #include "process-util.h"
28 #include "signal-util.h"
29 #include "time-util.h"
30 #include "def.h"
31
32 int stub_pid1(void) {
33 enum {
34 STATE_RUNNING,
35 STATE_REBOOT,
36 STATE_POWEROFF,
37 } state = STATE_RUNNING;
38
39 sigset_t fullmask, oldmask, waitmask;
40 usec_t quit_usec = USEC_INFINITY;
41 pid_t pid;
42 int r;
43
44 /* Implements a stub PID 1, that reaps all processes and processes a couple of standard signals. This is useful
45 * for allowing arbitrary processes run in a container, and still have all zombies reaped. */
46
47 assert_se(sigfillset(&fullmask) >= 0);
48 assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0);
49
50 pid = fork();
51 if (pid < 0)
52 return log_error_errno(errno, "Failed to fork child pid: %m");
53
54 if (pid == 0) {
55 /* Return in the child */
56 assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0);
57 setsid();
58 return 0;
59 }
60
61 reset_all_signal_handlers();
62
63 log_close();
64 close_all_fds(NULL, 0);
65 log_open();
66
67 rename_process("STUBINIT");
68
69 assert_se(sigemptyset(&waitmask) >= 0);
70 assert_se(sigset_add_many(&waitmask,
71 SIGCHLD, /* posix: process died */
72 SIGINT, /* sysv: ctrl-alt-del */
73 SIGRTMIN+3, /* systemd: halt */
74 SIGRTMIN+4, /* systemd: poweroff */
75 SIGRTMIN+5, /* systemd: reboot */
76 SIGRTMIN+6, /* systemd: kexec */
77 SIGRTMIN+13, /* systemd: halt */
78 SIGRTMIN+14, /* systemd: poweroff */
79 SIGRTMIN+15, /* systemd: reboot */
80 SIGRTMIN+16, /* systemd: kexec */
81 -1) >= 0);
82
83 /* Note that we ignore SIGTERM (sysv's reexec), SIGHUP (reload), and all other signals here, since we don't
84 * support reexec/reloading in this stub process. */
85
86 for (;;) {
87 siginfo_t si;
88 usec_t current_usec;
89
90 si.si_pid = 0;
91 r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG);
92 if (r < 0) {
93 r = log_error_errno(errno, "Failed to reap children: %m");
94 goto finish;
95 }
96
97 current_usec = now(CLOCK_MONOTONIC);
98
99 if (si.si_pid == pid || current_usec >= quit_usec) {
100
101 /* The child we started ourselves died or we reached a timeout. */
102
103 if (state == STATE_REBOOT) { /* dispatch a queued reboot */
104 (void) reboot(RB_AUTOBOOT);
105 r = log_error_errno(errno, "Failed to reboot: %m");
106 goto finish;
107
108 } else if (state == STATE_POWEROFF)
109 (void) reboot(RB_POWER_OFF); /* if this fails, fall back to normal exit. */
110
111 if (si.si_pid == pid && si.si_code == CLD_EXITED)
112 r = si.si_status; /* pass on exit code */
113 else
114 r = 255; /* signal, coredump, timeout, … */
115
116 goto finish;
117 }
118 if (si.si_pid != 0)
119 /* We reaped something. Retry until there's nothing more to reap. */
120 continue;
121
122 if (quit_usec == USEC_INFINITY)
123 r = sigwaitinfo(&waitmask, &si);
124 else {
125 struct timespec ts;
126 r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec));
127 }
128 if (r < 0) {
129 if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */
130 continue;
131 if (errno == EAGAIN) /* timeout reached */
132 continue;
133
134 r = log_error_errno(errno, "Failed to wait for signal: %m");
135 goto finish;
136 }
137
138 if (si.si_signo == SIGCHLD)
139 continue; /* Let's reap this */
140
141 if (state != STATE_RUNNING)
142 continue;
143
144 /* Would love to use a switch() statement here, but SIGRTMIN is actually a function call, not a
145 * constant… */
146
147 if (si.si_signo == SIGRTMIN+3 ||
148 si.si_signo == SIGRTMIN+4 ||
149 si.si_signo == SIGRTMIN+13 ||
150 si.si_signo == SIGRTMIN+14)
151
152 state = STATE_POWEROFF;
153
154 else if (si.si_signo == SIGINT ||
155 si.si_signo == SIGRTMIN+5 ||
156 si.si_signo == SIGRTMIN+6 ||
157 si.si_signo == SIGRTMIN+15 ||
158 si.si_signo == SIGRTMIN+16)
159
160 state = STATE_REBOOT;
161 else
162 assert_not_reached("Got unexpected signal");
163
164 /* (void) kill_and_sigcont(pid, SIGTERM); */
165 quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC;
166 }
167
168 finish:
169 _exit(r < 0 ? EXIT_FAILURE : r);
170 }