]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/initctl/initctl.c
initctl: move /dev/initctl fifo into /run, replace it by symlink
[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 116 target = translate_runlevel(runlevel, &isolate);
f168c273 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
03e334a1 248 safe_close(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
03e334a1 260 safe_close(s->epoll_fd);
0b7964b8 261
fb1af5b0 262 if (s->bus) {
b75b4db0
TG
263 sd_bus_flush(s->bus);
264 sd_bus_unref(s->bus);
fb1af5b0 265 }
0b7964b8
LP
266}
267
268static int server_init(Server *s, unsigned n_sockets) {
269 int r;
270 unsigned i;
0b7964b8
LP
271
272 assert(s);
273 assert(n_sockets > 0);
274
0b7964b8
LP
275 zero(*s);
276
3143987f
ZJS
277 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
278 if (s->epoll_fd < 0) {
0b7964b8 279 r = -errno;
f5f6d0e2 280 log_error("Failed to create epoll object: %m");
0b7964b8
LP
281 goto fail;
282 }
283
284 for (i = 0; i < n_sockets; i++) {
285 struct epoll_event ev;
286 Fifo *f;
7c394faa
LP
287 int fd;
288
289 fd = SD_LISTEN_FDS_START+i;
290
3143987f
ZJS
291 r = sd_is_fifo(fd, NULL);
292 if (r < 0) {
293 log_error("Failed to determine file descriptor type: %s",
294 strerror(-r));
7c394faa
LP
295 goto fail;
296 }
297
298 if (!r) {
299 log_error("Wrong file descriptor type.");
300 r = -EINVAL;
301 goto fail;
302 }
0b7964b8 303
3143987f
ZJS
304 f = new0(Fifo, 1);
305 if (!f) {
0b7964b8 306 r = -ENOMEM;
f5f6d0e2 307 log_error("Failed to create fifo object: %m");
0b7964b8
LP
308 goto fail;
309 }
310
311 f->fd = -1;
312
313 zero(ev);
314 ev.events = EPOLLIN;
315 ev.data.ptr = f;
7c394faa 316 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
0b7964b8
LP
317 r = -errno;
318 fifo_free(f);
f5f6d0e2 319 log_error("Failed to add fifo fd to epoll object: %m");
0b7964b8
LP
320 goto fail;
321 }
322
63983207 323 f->fd = fd;
71fda00f 324 LIST_PREPEND(fifo, s->fifos, f);
0b7964b8
LP
325 f->server = s;
326 s->n_fifos ++;
327 }
328
0f8bd8de 329 r = bus_open_system_systemd(&s->bus);
b75b4db0
TG
330 if (r < 0) {
331 log_error("Failed to get D-Bus connection: %s", strerror(-r));
3143987f 332 r = -EIO;
0b7964b8
LP
333 goto fail;
334 }
335
336 return 0;
337
338fail:
339 server_done(s);
340
0b7964b8
LP
341 return r;
342}
343
344static int process_event(Server *s, struct epoll_event *ev) {
345 int r;
346 Fifo *f;
347
348 assert(s);
349
350 if (!(ev->events & EPOLLIN)) {
351 log_info("Got invalid event from epoll. (3)");
352 return -EIO;
353 }
354
355 f = (Fifo*) ev->data.ptr;
289f910e
ZJS
356 r = fifo_process(f);
357 if (r < 0) {
0b7964b8
LP
358 log_info("Got error on fifo: %s", strerror(-r));
359 fifo_free(f);
360 return r;
361 }
362
363 return 0;
364}
365
366int main(int argc, char *argv[]) {
367 Server server;
22f4096c 368 int r = EXIT_FAILURE, n;
8bfcc8ea 369
0ca3f374
LP
370 if (getppid() != 1) {
371 log_error("This program should be invoked by init only.");
22f4096c 372 return EXIT_FAILURE;
0ca3f374
LP
373 }
374
375 if (argc > 1) {
376 log_error("This program does not take arguments.");
22f4096c 377 return EXIT_FAILURE;
0ca3f374
LP
378 }
379
4cfa2c99 380 log_set_target(LOG_TARGET_AUTO);
8bfcc8ea 381 log_parse_environment();
2396fb04 382 log_open();
0b7964b8 383
4c12626c
LP
384 umask(0022);
385
8bfcc8ea
LP
386 if ((n = sd_listen_fds(true)) < 0) {
387 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 388 return EXIT_FAILURE;
8bfcc8ea
LP
389 }
390
391 if (n <= 0 || n > SERVER_FD_MAX) {
392 log_error("No or too many file descriptors passed.");
22f4096c 393 return EXIT_FAILURE;
8bfcc8ea 394 }
0b7964b8 395
8bfcc8ea 396 if (server_init(&server, (unsigned) n) < 0)
22f4096c 397 return EXIT_FAILURE;
0b7964b8 398
de0671ee 399 log_debug("systemd-initctl running as pid "PID_FMT, getpid());
cd6d0a45 400
8c47c732
LP
401 sd_notify(false,
402 "READY=1\n"
403 "STATUS=Processing requests...");
404
f632a663 405 while (!server.quit) {
0b7964b8
LP
406 struct epoll_event event;
407 int k;
408
409 if ((k = epoll_wait(server.epoll_fd,
410 &event, 1,
ef9eb0a8 411 TIMEOUT_MSEC)) < 0) {
0b7964b8
LP
412
413 if (errno == EINTR)
414 continue;
415
f5f6d0e2 416 log_error("epoll_wait() failed: %m");
0b7964b8
LP
417 goto fail;
418 }
419
420 if (k <= 0)
421 break;
422
e364ad06 423 if (process_event(&server, &event) < 0)
0b7964b8
LP
424 goto fail;
425 }
cd6d0a45 426
22f4096c 427 r = EXIT_SUCCESS;
0b7964b8 428
de0671ee 429 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid());
cd6d0a45 430
0b7964b8 431fail:
8c47c732
LP
432 sd_notify(false,
433 "STATUS=Shutting down...");
434
0b7964b8
LP
435 server_done(&server);
436
0b7964b8
LP
437 return r;
438}