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