]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/initctl/initctl.c
initctl: use irreversible jobs when switching runlevels
[thirdparty/systemd.git] / src / initctl / initctl.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
0b7964b8
LP
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
5430f7f2
LP
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
0b7964b8
LP
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
5430f7f2 16 Lesser General Public License for more details.
0b7964b8 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
0b7964b8
LP
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>
81527be1 37#include <systemd/sd-daemon.h>
0b7964b8
LP
38
39#include "util.h"
40#include "log.h"
41#include "list.h"
42#include "initreq.h"
514f4ef5 43#include "special.h"
a822056b 44#include "dbus-common.h"
ef9eb0a8 45#include "def.h"
0b7964b8 46
0b7964b8 47#define SERVER_FD_MAX 16
ef9eb0a8 48#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
0b7964b8
LP
49
50typedef struct Fifo Fifo;
51
52typedef struct Server {
53 int epoll_fd;
54
55 LIST_HEAD(Fifo, fifos);
56 unsigned n_fifos;
57
58 DBusConnection *bus;
f632a663
LP
59
60 bool quit;
0b7964b8
LP
61} Server;
62
63struct 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
ac83842a 74static const char *translate_runlevel(int runlevel, bool *isolate) {
6542952f
LP
75 static const struct {
76 const int runlevel;
77 const char *special;
ac83842a 78 bool isolate;
6542952f 79 } table[] = {
ac83842a
LP
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 },
6542952f
LP
89 };
90
91 unsigned i;
92
ac83842a
LP
93 assert(isolate);
94
6542952f 95 for (i = 0; i < ELEMENTSOF(table); i++)
ac83842a
LP
96 if (table[i].runlevel == runlevel) {
97 *isolate = table[i].isolate;
65457142
FC
98 if (runlevel == '6' && kexec_loaded())
99 return SPECIAL_KEXEC_TARGET;
6542952f 100 return table[i].special;
ac83842a 101 }
6542952f
LP
102
103 return NULL;
0b7964b8
LP
104}
105
106static void change_runlevel(Server *s, int runlevel) {
107 const char *target;
108 DBusMessage *m = NULL, *reply = NULL;
109 DBusError error;
ac83842a
LP
110 const char *mode;
111 bool isolate = false;
0b7964b8
LP
112
113 assert(s);
114
115 dbus_error_init(&error);
116
ac83842a 117 if (!(target = translate_runlevel(runlevel, &isolate))) {
0b7964b8
LP
118 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
119 goto finish;
120 }
121
ac83842a
LP
122 if (isolate)
123 mode = "isolate";
124 else
4c10771c 125 mode = "replace-irreversibly";
ac83842a 126
6f0d624e 127 log_debug("Running request %s/start/%s", target, mode);
0b7964b8 128
c87eba54 129 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
0b7964b8
LP
130 log_error("Could not allocate message.");
131 goto finish;
132 }
133
134 if (!dbus_message_append_args(m,
135 DBUS_TYPE_STRING, &target,
ac83842a 136 DBUS_TYPE_STRING, &mode,
0b7964b8 137 DBUS_TYPE_INVALID)) {
5192bd19 138 log_error("Could not attach target and flag information to message.");
0b7964b8
LP
139 goto finish;
140 }
141
0b7964b8 142 if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
4cf5d675 143 log_error("Failed to start unit: %s", bus_error_message(&error));
0b7964b8
LP
144 goto finish;
145 }
146
147finish:
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
157static 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
46e964c5
TG
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");
f632a663
LP
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;
46e964c5
TG
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 }
0b7964b8
LP
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
220static 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
246static 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
a16e1123 259 close_nointr_nofail(f->fd);
0b7964b8
LP
260 }
261
262 free(f);
263}
264
0b7964b8
LP
265static 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)
a16e1123 272 close_nointr_nofail(s->epoll_fd);
0b7964b8 273
fb1af5b0 274 if (s->bus) {
5d452f9c
LP
275 dbus_connection_flush(s->bus);
276 dbus_connection_close(s->bus);
277 dbus_connection_unref(s->bus);
fb1af5b0 278 }
0b7964b8
LP
279}
280
281static 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
3143987f
ZJS
293 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
294 if (s->epoll_fd < 0) {
0b7964b8
LP
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;
7c394faa
LP
303 int fd;
304
305 fd = SD_LISTEN_FDS_START+i;
306
3143987f
ZJS
307 r = sd_is_fifo(fd, NULL);
308 if (r < 0) {
309 log_error("Failed to determine file descriptor type: %s",
310 strerror(-r));
7c394faa
LP
311 goto fail;
312 }
313
314 if (!r) {
315 log_error("Wrong file descriptor type.");
316 r = -EINVAL;
317 goto fail;
318 }
0b7964b8 319
3143987f
ZJS
320 f = new0(Fifo, 1);
321 if (!f) {
0b7964b8 322 r = -ENOMEM;
3143987f
ZJS
323 log_error("Failed to create fifo object: %s",
324 strerror(errno));
0b7964b8
LP
325 goto fail;
326 }
327
328 f->fd = -1;
329
330 zero(ev);
331 ev.events = EPOLLIN;
332 ev.data.ptr = f;
7c394faa 333 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
0b7964b8
LP
334 r = -errno;
335 fifo_free(f);
3143987f
ZJS
336 log_error("Failed to add fifo fd to epoll object: %s",
337 strerror(errno));
0b7964b8
LP
338 goto fail;
339 }
340
63983207 341 f->fd = fd;
0b7964b8
LP
342 LIST_PREPEND(Fifo, fifo, s->fifos, f);
343 f->server = s;
344 s->n_fifos ++;
345 }
346
b574246b 347 if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
3143987f
ZJS
348 log_error("Failed to get D-Bus connection: %s",
349 bus_error_message(&error));
350 r = -EIO;
0b7964b8
LP
351 goto fail;
352 }
353
354 return 0;
355
356fail:
357 server_done(s);
358
359 dbus_error_free(&error);
360 return r;
361}
362
363static 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
385int main(int argc, char *argv[]) {
386 Server server;
22f4096c 387 int r = EXIT_FAILURE, n;
8bfcc8ea 388
0ca3f374
LP
389 if (getppid() != 1) {
390 log_error("This program should be invoked by init only.");
22f4096c 391 return EXIT_FAILURE;
0ca3f374
LP
392 }
393
394 if (argc > 1) {
395 log_error("This program does not take arguments.");
22f4096c 396 return EXIT_FAILURE;
0ca3f374
LP
397 }
398
4cfa2c99 399 log_set_target(LOG_TARGET_AUTO);
8bfcc8ea 400 log_parse_environment();
2396fb04 401 log_open();
0b7964b8 402
4c12626c
LP
403 umask(0022);
404
8bfcc8ea
LP
405 if ((n = sd_listen_fds(true)) < 0) {
406 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 407 return EXIT_FAILURE;
8bfcc8ea
LP
408 }
409
410 if (n <= 0 || n > SERVER_FD_MAX) {
411 log_error("No or too many file descriptors passed.");
22f4096c 412 return EXIT_FAILURE;
8bfcc8ea 413 }
0b7964b8 414
8bfcc8ea 415 if (server_init(&server, (unsigned) n) < 0)
22f4096c 416 return EXIT_FAILURE;
0b7964b8 417
cd6d0a45
LP
418 log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
419
8c47c732
LP
420 sd_notify(false,
421 "READY=1\n"
422 "STATUS=Processing requests...");
423
f632a663 424 while (!server.quit) {
0b7964b8
LP
425 struct epoll_event event;
426 int k;
427
428 if ((k = epoll_wait(server.epoll_fd,
429 &event, 1,
ef9eb0a8 430 TIMEOUT_MSEC)) < 0) {
0b7964b8
LP
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
e364ad06 442 if (process_event(&server, &event) < 0)
0b7964b8
LP
443 goto fail;
444 }
cd6d0a45 445
22f4096c 446 r = EXIT_SUCCESS;
0b7964b8 447
cd6d0a45
LP
448 log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
449
0b7964b8 450fail:
8c47c732
LP
451 sd_notify(false,
452 "STATUS=Shutting down...");
453
0b7964b8
LP
454 server_done(&server);
455
0b7964b8
LP
456 dbus_shutdown();
457
458 return r;
459}