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