Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <ctype.h>
4 : : #include <errno.h>
5 : : #include <stdio.h>
6 : : #include <sys/epoll.h>
7 : : #include <sys/stat.h>
8 : : #include <sys/types.h>
9 : : #include <unistd.h>
10 : :
11 : : #include "sd-bus.h"
12 : : #include "sd-daemon.h"
13 : :
14 : : #include "alloc-util.h"
15 : : #include "bus-error.h"
16 : : #include "bus-util.h"
17 : : #include "def.h"
18 : : #include "fd-util.h"
19 : : #include "format-util.h"
20 : : #include "initreq.h"
21 : : #include "list.h"
22 : : #include "log.h"
23 : : #include "memory-util.h"
24 : : #include "process-util.h"
25 : : #include "special.h"
26 : :
27 : : #define SERVER_FD_MAX 16
28 : : #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
29 : :
30 : : typedef struct Fifo Fifo;
31 : :
32 : : typedef struct Server {
33 : : int epoll_fd;
34 : :
35 : : LIST_HEAD(Fifo, fifos);
36 : : unsigned n_fifos;
37 : :
38 : : sd_bus *bus;
39 : :
40 : : bool quit;
41 : : } Server;
42 : :
43 : : struct Fifo {
44 : : Server *server;
45 : :
46 : : int fd;
47 : :
48 : : struct init_request buffer;
49 : : size_t bytes_read;
50 : :
51 : : LIST_FIELDS(Fifo, fifo);
52 : : };
53 : :
54 : 0 : static const char *translate_runlevel(int runlevel, bool *isolate) {
55 : : static const struct {
56 : : const int runlevel;
57 : : const char *special;
58 : : bool isolate;
59 : : } table[] = {
60 : : { '0', SPECIAL_POWEROFF_TARGET, false },
61 : : { '1', SPECIAL_RESCUE_TARGET, true },
62 : : { 's', SPECIAL_RESCUE_TARGET, true },
63 : : { 'S', SPECIAL_RESCUE_TARGET, true },
64 : : { '2', SPECIAL_MULTI_USER_TARGET, true },
65 : : { '3', SPECIAL_MULTI_USER_TARGET, true },
66 : : { '4', SPECIAL_MULTI_USER_TARGET, true },
67 : : { '5', SPECIAL_GRAPHICAL_TARGET, true },
68 : : { '6', SPECIAL_REBOOT_TARGET, false },
69 : : };
70 : :
71 : : unsigned i;
72 : :
73 [ # # ]: 0 : assert(isolate);
74 : :
75 [ # # ]: 0 : for (i = 0; i < ELEMENTSOF(table); i++)
76 [ # # ]: 0 : if (table[i].runlevel == runlevel) {
77 : 0 : *isolate = table[i].isolate;
78 [ # # # # ]: 0 : if (runlevel == '6' && kexec_loaded())
79 : 0 : return SPECIAL_KEXEC_TARGET;
80 : 0 : return table[i].special;
81 : : }
82 : :
83 : 0 : return NULL;
84 : : }
85 : :
86 : 0 : static int change_runlevel(Server *s, int runlevel) {
87 : : const char *target;
88 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
89 : : const char *mode;
90 : 0 : bool isolate = false;
91 : : int r;
92 : :
93 [ # # ]: 0 : assert(s);
94 : :
95 : 0 : target = translate_runlevel(runlevel, &isolate);
96 [ # # ]: 0 : if (!target) {
97 [ # # ]: 0 : log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
98 : 0 : return 0;
99 : : }
100 : :
101 [ # # ]: 0 : if (isolate)
102 : 0 : mode = "isolate";
103 : : else
104 : 0 : mode = "replace-irreversibly";
105 : :
106 [ # # ]: 0 : log_debug("Running request %s/start/%s", target, mode);
107 : :
108 : 0 : r = sd_bus_call_method(
109 : : s->bus,
110 : : "org.freedesktop.systemd1",
111 : : "/org/freedesktop/systemd1",
112 : : "org.freedesktop.systemd1.Manager",
113 : : "StartUnit",
114 : : &error,
115 : : NULL,
116 : : "ss", target, mode);
117 [ # # ]: 0 : if (r < 0)
118 [ # # ]: 0 : return log_error_errno(r, "Failed to change runlevel: %s", bus_error_message(&error, -r));
119 : :
120 : 0 : return 0;
121 : : }
122 : :
123 : 0 : static void request_process(Server *s, const struct init_request *req) {
124 [ # # ]: 0 : assert(s);
125 [ # # ]: 0 : assert(req);
126 : :
127 [ # # ]: 0 : if (req->magic != INIT_MAGIC) {
128 [ # # ]: 0 : log_error("Got initctl request with invalid magic. Ignoring.");
129 : 0 : return;
130 : : }
131 : :
132 [ # # # # : 0 : switch (req->cmd) {
# ]
133 : :
134 : 0 : case INIT_CMD_RUNLVL:
135 [ # # ]: 0 : if (!isprint(req->runlevel))
136 [ # # ]: 0 : log_error("Got invalid runlevel. Ignoring.");
137 : : else
138 [ # # # ]: 0 : switch (req->runlevel) {
139 : :
140 : : /* we are async anyway, so just use kill for reexec/reload */
141 : 0 : case 'u':
142 : : case 'U':
143 [ # # ]: 0 : if (kill(1, SIGTERM) < 0)
144 [ # # ]: 0 : log_error_errno(errno, "kill() failed: %m");
145 : :
146 : : /* The bus connection will be
147 : : * terminated if PID 1 is reexecuted,
148 : : * hence let's just exit here, and
149 : : * rely on that we'll be restarted on
150 : : * the next request */
151 : 0 : s->quit = true;
152 : 0 : break;
153 : :
154 : 0 : case 'q':
155 : : case 'Q':
156 [ # # ]: 0 : if (kill(1, SIGHUP) < 0)
157 [ # # ]: 0 : log_error_errno(errno, "kill() failed: %m");
158 : 0 : break;
159 : :
160 : 0 : default:
161 : 0 : (void) change_runlevel(s, req->runlevel);
162 : : }
163 : 0 : return;
164 : :
165 : 0 : case INIT_CMD_POWERFAIL:
166 : : case INIT_CMD_POWERFAILNOW:
167 : : case INIT_CMD_POWEROK:
168 [ # # ]: 0 : log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
169 : 0 : return;
170 : :
171 : 0 : case INIT_CMD_CHANGECONS:
172 [ # # ]: 0 : log_warning("Received console change initctl request. This is not implemented in systemd.");
173 : 0 : return;
174 : :
175 : 0 : case INIT_CMD_SETENV:
176 : : case INIT_CMD_UNSETENV:
177 [ # # ]: 0 : log_warning("Received environment initctl request. This is not implemented in systemd.");
178 : 0 : return;
179 : :
180 : 0 : default:
181 [ # # ]: 0 : log_warning("Received unknown initctl request. Ignoring.");
182 : 0 : return;
183 : : }
184 : : }
185 : :
186 : 0 : static int fifo_process(Fifo *f) {
187 : : ssize_t l;
188 : :
189 [ # # ]: 0 : assert(f);
190 : :
191 : 0 : errno = EIO;
192 : 0 : l = read(f->fd,
193 : 0 : ((uint8_t*) &f->buffer) + f->bytes_read,
194 : 0 : sizeof(f->buffer) - f->bytes_read);
195 [ # # ]: 0 : if (l <= 0) {
196 [ # # ]: 0 : if (errno == EAGAIN)
197 : 0 : return 0;
198 : :
199 [ # # ]: 0 : return log_warning_errno(errno, "Failed to read from fifo: %m");
200 : : }
201 : :
202 : 0 : f->bytes_read += l;
203 [ # # ]: 0 : assert(f->bytes_read <= sizeof(f->buffer));
204 : :
205 [ # # ]: 0 : if (f->bytes_read == sizeof(f->buffer)) {
206 : 0 : request_process(f->server, &f->buffer);
207 : 0 : f->bytes_read = 0;
208 : : }
209 : :
210 : 0 : return 0;
211 : : }
212 : :
213 : 0 : static void fifo_free(Fifo *f) {
214 [ # # ]: 0 : assert(f);
215 : :
216 [ # # ]: 0 : if (f->server) {
217 [ # # ]: 0 : assert(f->server->n_fifos > 0);
218 : 0 : f->server->n_fifos--;
219 [ # # # # : 0 : LIST_REMOVE(fifo, f->server->fifos, f);
# # # # ]
220 : : }
221 : :
222 [ # # ]: 0 : if (f->fd >= 0) {
223 [ # # ]: 0 : if (f->server)
224 : 0 : epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
225 : :
226 : 0 : safe_close(f->fd);
227 : : }
228 : :
229 : 0 : free(f);
230 : 0 : }
231 : :
232 : 0 : static void server_done(Server *s) {
233 [ # # ]: 0 : assert(s);
234 : :
235 [ # # ]: 0 : while (s->fifos)
236 : 0 : fifo_free(s->fifos);
237 : :
238 : 0 : s->epoll_fd = safe_close(s->epoll_fd);
239 : 0 : s->bus = sd_bus_flush_close_unref(s->bus);
240 : 0 : }
241 : :
242 : 0 : static int server_init(Server *s, unsigned n_sockets) {
243 : : int r;
244 : : unsigned i;
245 : :
246 [ # # ]: 0 : assert(s);
247 [ # # ]: 0 : assert(n_sockets > 0);
248 : :
249 [ # # ]: 0 : zero(*s);
250 : :
251 : 0 : s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
252 [ # # ]: 0 : if (s->epoll_fd < 0) {
253 [ # # ]: 0 : r = log_error_errno(errno,
254 : : "Failed to create epoll object: %m");
255 : 0 : goto fail;
256 : : }
257 : :
258 [ # # ]: 0 : for (i = 0; i < n_sockets; i++) {
259 : : struct epoll_event ev;
260 : : Fifo *f;
261 : : int fd;
262 : :
263 : 0 : fd = SD_LISTEN_FDS_START+i;
264 : :
265 : 0 : r = sd_is_fifo(fd, NULL);
266 [ # # ]: 0 : if (r < 0) {
267 [ # # ]: 0 : log_error_errno(r, "Failed to determine file descriptor type: %m");
268 : 0 : goto fail;
269 : : }
270 : :
271 [ # # ]: 0 : if (!r) {
272 [ # # ]: 0 : log_error("Wrong file descriptor type.");
273 : 0 : r = -EINVAL;
274 : 0 : goto fail;
275 : : }
276 : :
277 : 0 : f = new0(Fifo, 1);
278 [ # # ]: 0 : if (!f) {
279 : 0 : r = -ENOMEM;
280 [ # # ]: 0 : log_error_errno(errno, "Failed to create fifo object: %m");
281 : 0 : goto fail;
282 : : }
283 : :
284 : 0 : f->fd = -1;
285 : :
286 [ # # ]: 0 : zero(ev);
287 : 0 : ev.events = EPOLLIN;
288 : 0 : ev.data.ptr = f;
289 [ # # ]: 0 : if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
290 : 0 : r = -errno;
291 : 0 : fifo_free(f);
292 [ # # ]: 0 : log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
293 : 0 : goto fail;
294 : : }
295 : :
296 : 0 : f->fd = fd;
297 [ # # # # ]: 0 : LIST_PREPEND(fifo, s->fifos, f);
298 : 0 : f->server = s;
299 : 0 : s->n_fifos++;
300 : : }
301 : :
302 : 0 : r = bus_connect_system_systemd(&s->bus);
303 [ # # ]: 0 : if (r < 0) {
304 [ # # ]: 0 : log_error_errno(r, "Failed to get D-Bus connection: %m");
305 : 0 : r = -EIO;
306 : 0 : goto fail;
307 : : }
308 : :
309 : 0 : return 0;
310 : :
311 : 0 : fail:
312 : 0 : server_done(s);
313 : :
314 : 0 : return r;
315 : : }
316 : :
317 : 0 : static int process_event(Server *s, struct epoll_event *ev) {
318 : : int r;
319 : : Fifo *f;
320 : :
321 [ # # ]: 0 : assert(s);
322 : :
323 [ # # ]: 0 : if (!(ev->events & EPOLLIN))
324 [ # # ]: 0 : return log_info_errno(SYNTHETIC_ERRNO(EIO),
325 : : "Got invalid event from epoll. (3)");
326 : :
327 : 0 : f = (Fifo*) ev->data.ptr;
328 : 0 : r = fifo_process(f);
329 [ # # ]: 0 : if (r < 0) {
330 [ # # ]: 0 : log_info_errno(r, "Got error on fifo: %m");
331 : 0 : fifo_free(f);
332 : 0 : return r;
333 : : }
334 : :
335 : 0 : return 0;
336 : : }
337 : :
338 : 0 : int main(int argc, char *argv[]) {
339 : : Server server;
340 : 0 : int r = EXIT_FAILURE, n;
341 : :
342 [ # # ]: 0 : if (getppid() != 1) {
343 [ # # ]: 0 : log_error("This program should be invoked by init only.");
344 : 0 : return EXIT_FAILURE;
345 : : }
346 : :
347 [ # # ]: 0 : if (argc > 1) {
348 [ # # ]: 0 : log_error("This program does not take arguments.");
349 : 0 : return EXIT_FAILURE;
350 : : }
351 : :
352 : 0 : log_setup_service();
353 : :
354 : 0 : umask(0022);
355 : :
356 : 0 : n = sd_listen_fds(true);
357 [ # # ]: 0 : if (n < 0) {
358 [ # # ]: 0 : log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
359 : 0 : return EXIT_FAILURE;
360 : : }
361 : :
362 [ # # # # ]: 0 : if (n <= 0 || n > SERVER_FD_MAX) {
363 [ # # ]: 0 : log_error("No or too many file descriptors passed.");
364 : 0 : return EXIT_FAILURE;
365 : : }
366 : :
367 [ # # ]: 0 : if (server_init(&server, (unsigned) n) < 0)
368 : 0 : return EXIT_FAILURE;
369 : :
370 [ # # ]: 0 : log_debug("systemd-initctl running as pid "PID_FMT, getpid_cached());
371 : :
372 : 0 : sd_notify(false,
373 : : "READY=1\n"
374 : : "STATUS=Processing requests...");
375 : :
376 [ # # ]: 0 : while (!server.quit) {
377 : : struct epoll_event event;
378 : : int k;
379 : :
380 : 0 : k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC);
381 [ # # ]: 0 : if (k < 0) {
382 [ # # ]: 0 : if (errno == EINTR)
383 : 0 : continue;
384 [ # # ]: 0 : log_error_errno(errno, "epoll_wait() failed: %m");
385 : 0 : goto fail;
386 : : }
387 : :
388 [ # # ]: 0 : if (k <= 0)
389 : 0 : break;
390 : :
391 [ # # ]: 0 : if (process_event(&server, &event) < 0)
392 : 0 : goto fail;
393 : : }
394 : :
395 : 0 : r = EXIT_SUCCESS;
396 : :
397 [ # # ]: 0 : log_debug("systemd-initctl stopped as pid "PID_FMT, getpid_cached());
398 : :
399 : 0 : fail:
400 : 0 : sd_notify(false,
401 : : "STOPPING=1\n"
402 : : "STATUS=Shutting down...");
403 : :
404 : 0 : server_done(&server);
405 : :
406 : 0 : return r;
407 : : }
|