chiark / gitweb /
nspawn: suffix the nspawn cgroups with ".nspawn"
[elogind.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";
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         if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
227
228                 if (errno == EAGAIN)
229                         return 0;
230
231                 log_warning("Failed to read from fifo: %s", strerror(errno));
232                 return -1;
233         }
234
235         f->bytes_read += l;
236         assert(f->bytes_read <= sizeof(f->buffer));
237
238         if (f->bytes_read == sizeof(f->buffer)) {
239                 request_process(f->server, &f->buffer);
240                 f->bytes_read = 0;
241         }
242
243         return 0;
244 }
245
246 static void fifo_free(Fifo *f) {
247         assert(f);
248
249         if (f->server) {
250                 assert(f->server->n_fifos > 0);
251                 f->server->n_fifos--;
252                 LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
253         }
254
255         if (f->fd >= 0) {
256                 if (f->server)
257                         epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
258
259                 close_nointr_nofail(f->fd);
260         }
261
262         free(f);
263 }
264
265 static void server_done(Server *s) {
266         assert(s);
267
268         while (s->fifos)
269                 fifo_free(s->fifos);
270
271         if (s->epoll_fd >= 0)
272                 close_nointr_nofail(s->epoll_fd);
273
274         if (s->bus) {
275                 dbus_connection_flush(s->bus);
276                 dbus_connection_close(s->bus);
277                 dbus_connection_unref(s->bus);
278         }
279 }
280
281 static int server_init(Server *s, unsigned n_sockets) {
282         int r;
283         unsigned i;
284         DBusError error;
285
286         assert(s);
287         assert(n_sockets > 0);
288
289         dbus_error_init(&error);
290
291         zero(*s);
292
293         s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
294         if (s->epoll_fd < 0) {
295                 r = -errno;
296                 log_error("Failed to create epoll object: %s", strerror(errno));
297                 goto fail;
298         }
299
300         for (i = 0; i < n_sockets; i++) {
301                 struct epoll_event ev;
302                 Fifo *f;
303                 int fd;
304
305                 fd = SD_LISTEN_FDS_START+i;
306
307                 r = sd_is_fifo(fd, NULL);
308                 if (r < 0) {
309                         log_error("Failed to determine file descriptor type: %s",
310                                   strerror(-r));
311                         goto fail;
312                 }
313
314                 if (!r) {
315                         log_error("Wrong file descriptor type.");
316                         r = -EINVAL;
317                         goto fail;
318                 }
319
320                 f = new0(Fifo, 1);
321                 if (!f) {
322                         r = -ENOMEM;
323                         log_error("Failed to create fifo object: %s",
324                                   strerror(errno));
325                         goto fail;
326                 }
327
328                 f->fd = -1;
329
330                 zero(ev);
331                 ev.events = EPOLLIN;
332                 ev.data.ptr = f;
333                 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
334                         r = -errno;
335                         fifo_free(f);
336                         log_error("Failed to add fifo fd to epoll object: %s",
337                                   strerror(errno));
338                         goto fail;
339                 }
340
341                 f->fd = fd;
342                 LIST_PREPEND(Fifo, fifo, s->fifos, f);
343                 f->server = s;
344                 s->n_fifos ++;
345         }
346
347         if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
348                 log_error("Failed to get D-Bus connection: %s",
349                           bus_error_message(&error));
350                 r = -EIO;
351                 goto fail;
352         }
353
354         return 0;
355
356 fail:
357         server_done(s);
358
359         dbus_error_free(&error);
360         return r;
361 }
362
363 static int process_event(Server *s, struct epoll_event *ev) {
364         int r;
365         Fifo *f;
366
367         assert(s);
368
369         if (!(ev->events & EPOLLIN)) {
370                 log_info("Got invalid event from epoll. (3)");
371                 return -EIO;
372         }
373
374         f = (Fifo*) ev->data.ptr;
375
376         if ((r = fifo_process(f)) < 0) {
377                 log_info("Got error on fifo: %s", strerror(-r));
378                 fifo_free(f);
379                 return r;
380         }
381
382         return 0;
383 }
384
385 int main(int argc, char *argv[]) {
386         Server server;
387         int r = EXIT_FAILURE, n;
388
389         if (getppid() != 1) {
390                 log_error("This program should be invoked by init only.");
391                 return EXIT_FAILURE;
392         }
393
394         if (argc > 1) {
395                 log_error("This program does not take arguments.");
396                 return EXIT_FAILURE;
397         }
398
399         log_set_target(LOG_TARGET_AUTO);
400         log_parse_environment();
401         log_open();
402
403         umask(0022);
404
405         if ((n = sd_listen_fds(true)) < 0) {
406                 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
407                 return EXIT_FAILURE;
408         }
409
410         if (n <= 0 || n > SERVER_FD_MAX) {
411                 log_error("No or too many file descriptors passed.");
412                 return EXIT_FAILURE;
413         }
414
415         if (server_init(&server, (unsigned) n) < 0)
416                 return EXIT_FAILURE;
417
418         log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
419
420         sd_notify(false,
421                   "READY=1\n"
422                   "STATUS=Processing requests...");
423
424         while (!server.quit) {
425                 struct epoll_event event;
426                 int k;
427
428                 if ((k = epoll_wait(server.epoll_fd,
429                                     &event, 1,
430                                     TIMEOUT_MSEC)) < 0) {
431
432                         if (errno == EINTR)
433                                 continue;
434
435                         log_error("epoll_wait() failed: %s", strerror(errno));
436                         goto fail;
437                 }
438
439                 if (k <= 0)
440                         break;
441
442                 if (process_event(&server, &event) < 0)
443                         goto fail;
444         }
445
446         r = EXIT_SUCCESS;
447
448         log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
449
450 fail:
451         sd_notify(false,
452                   "STATUS=Shutting down...");
453
454         server_done(&server);
455
456         dbus_shutdown();
457
458         return r;
459 }