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