]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/initctl/initctl.c
sd-memfd: use assert_return
[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
f5f6d0e2 220 log_warning("Failed to read from fifo: %m");
0b7964b8
LP
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 280 r = -errno;
f5f6d0e2 281 log_error("Failed to create epoll object: %m");
0b7964b8
LP
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;
f5f6d0e2 308 log_error("Failed to create fifo object: %m");
0b7964b8
LP
309 goto fail;
310 }
311
312 f->fd = -1;
313
314 zero(ev);
315 ev.events = EPOLLIN;
316 ev.data.ptr = f;
7c394faa 317 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
0b7964b8
LP
318 r = -errno;
319 fifo_free(f);
f5f6d0e2 320 log_error("Failed to add fifo fd to epoll object: %m");
0b7964b8
LP
321 goto fail;
322 }
323
63983207 324 f->fd = fd;
71fda00f 325 LIST_PREPEND(fifo, s->fifos, f);
0b7964b8
LP
326 f->server = s;
327 s->n_fifos ++;
328 }
329
0f8bd8de 330 r = bus_open_system_systemd(&s->bus);
b75b4db0
TG
331 if (r < 0) {
332 log_error("Failed to get D-Bus connection: %s", strerror(-r));
3143987f 333 r = -EIO;
0b7964b8
LP
334 goto fail;
335 }
336
337 return 0;
338
339fail:
340 server_done(s);
341
0b7964b8
LP
342 return r;
343}
344
345static int process_event(Server *s, struct epoll_event *ev) {
346 int r;
347 Fifo *f;
348
349 assert(s);
350
351 if (!(ev->events & EPOLLIN)) {
352 log_info("Got invalid event from epoll. (3)");
353 return -EIO;
354 }
355
356 f = (Fifo*) ev->data.ptr;
289f910e
ZJS
357 r = fifo_process(f);
358 if (r < 0) {
0b7964b8
LP
359 log_info("Got error on fifo: %s", strerror(-r));
360 fifo_free(f);
361 return r;
362 }
363
364 return 0;
365}
366
367int main(int argc, char *argv[]) {
368 Server server;
22f4096c 369 int r = EXIT_FAILURE, n;
8bfcc8ea 370
0ca3f374
LP
371 if (getppid() != 1) {
372 log_error("This program should be invoked by init only.");
22f4096c 373 return EXIT_FAILURE;
0ca3f374
LP
374 }
375
376 if (argc > 1) {
377 log_error("This program does not take arguments.");
22f4096c 378 return EXIT_FAILURE;
0ca3f374
LP
379 }
380
4cfa2c99 381 log_set_target(LOG_TARGET_AUTO);
8bfcc8ea 382 log_parse_environment();
2396fb04 383 log_open();
0b7964b8 384
4c12626c
LP
385 umask(0022);
386
8bfcc8ea
LP
387 if ((n = sd_listen_fds(true)) < 0) {
388 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 389 return EXIT_FAILURE;
8bfcc8ea
LP
390 }
391
392 if (n <= 0 || n > SERVER_FD_MAX) {
393 log_error("No or too many file descriptors passed.");
22f4096c 394 return EXIT_FAILURE;
8bfcc8ea 395 }
0b7964b8 396
8bfcc8ea 397 if (server_init(&server, (unsigned) n) < 0)
22f4096c 398 return EXIT_FAILURE;
0b7964b8 399
cd6d0a45
LP
400 log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
401
8c47c732
LP
402 sd_notify(false,
403 "READY=1\n"
404 "STATUS=Processing requests...");
405
f632a663 406 while (!server.quit) {
0b7964b8
LP
407 struct epoll_event event;
408 int k;
409
410 if ((k = epoll_wait(server.epoll_fd,
411 &event, 1,
ef9eb0a8 412 TIMEOUT_MSEC)) < 0) {
0b7964b8
LP
413
414 if (errno == EINTR)
415 continue;
416
f5f6d0e2 417 log_error("epoll_wait() failed: %m");
0b7964b8
LP
418 goto fail;
419 }
420
421 if (k <= 0)
422 break;
423
e364ad06 424 if (process_event(&server, &event) < 0)
0b7964b8
LP
425 goto fail;
426 }
cd6d0a45 427
22f4096c 428 r = EXIT_SUCCESS;
0b7964b8 429
cd6d0a45
LP
430 log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
431
0b7964b8 432fail:
8c47c732
LP
433 sd_notify(false,
434 "STATUS=Shutting down...");
435
0b7964b8
LP
436 server_done(&server);
437
0b7964b8
LP
438 return r;
439}