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