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