]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/initctl/initctl.c
treewide: use log_*_errno whenever %m is in the format string
[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)
56f64d95 165 log_error_errno(errno, "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)
56f64d95 178 log_error_errno(errno, "kill() failed: %m");
46e964c5
TG
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
56f64d95 220 log_warning_errno(errno, "Failed to read from fifo: %m");
fd006cce 221 return -errno;
0b7964b8
LP
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;
56f64d95 280 log_error_errno(errno, "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) {
c33b3297 293 log_error_errno(r, "Failed to determine file descriptor type: %m");
7c394faa
LP
294 goto fail;
295 }
296
297 if (!r) {
298 log_error("Wrong file descriptor type.");
299 r = -EINVAL;
300 goto fail;
301 }
0b7964b8 302
3143987f
ZJS
303 f = new0(Fifo, 1);
304 if (!f) {
0b7964b8 305 r = -ENOMEM;
56f64d95 306 log_error_errno(errno, "Failed to create fifo object: %m");
0b7964b8
LP
307 goto fail;
308 }
309
310 f->fd = -1;
311
312 zero(ev);
313 ev.events = EPOLLIN;
314 ev.data.ptr = f;
7c394faa 315 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
0b7964b8
LP
316 r = -errno;
317 fifo_free(f);
56f64d95 318 log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
0b7964b8
LP
319 goto fail;
320 }
321
63983207 322 f->fd = fd;
71fda00f 323 LIST_PREPEND(fifo, s->fifos, f);
0b7964b8
LP
324 f->server = s;
325 s->n_fifos ++;
326 }
327
0f8bd8de 328 r = bus_open_system_systemd(&s->bus);
b75b4db0 329 if (r < 0) {
da927ba9 330 log_error_errno(r, "Failed to get D-Bus connection: %m");
3143987f 331 r = -EIO;
0b7964b8
LP
332 goto fail;
333 }
334
335 return 0;
336
337fail:
338 server_done(s);
339
0b7964b8
LP
340 return r;
341}
342
343static int process_event(Server *s, struct epoll_event *ev) {
344 int r;
345 Fifo *f;
346
347 assert(s);
348
349 if (!(ev->events & EPOLLIN)) {
350 log_info("Got invalid event from epoll. (3)");
351 return -EIO;
352 }
353
354 f = (Fifo*) ev->data.ptr;
289f910e
ZJS
355 r = fifo_process(f);
356 if (r < 0) {
da927ba9 357 log_info_errno(r, "Got error on fifo: %m");
0b7964b8
LP
358 fifo_free(f);
359 return r;
360 }
361
362 return 0;
363}
364
365int main(int argc, char *argv[]) {
366 Server server;
22f4096c 367 int r = EXIT_FAILURE, n;
8bfcc8ea 368
0ca3f374
LP
369 if (getppid() != 1) {
370 log_error("This program should be invoked by init only.");
22f4096c 371 return EXIT_FAILURE;
0ca3f374
LP
372 }
373
374 if (argc > 1) {
375 log_error("This program does not take arguments.");
22f4096c 376 return EXIT_FAILURE;
0ca3f374
LP
377 }
378
4cfa2c99 379 log_set_target(LOG_TARGET_AUTO);
8bfcc8ea 380 log_parse_environment();
2396fb04 381 log_open();
0b7964b8 382
4c12626c
LP
383 umask(0022);
384
82d25240
LP
385 n = sd_listen_fds(true);
386 if (n < 0) {
da927ba9 387 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
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
56f64d95 416 log_error_errno(errno, "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 432 sd_notify(false,
af4ec430 433 "STOPPING=1\n"
8c47c732
LP
434 "STATUS=Shutting down...");
435
0b7964b8
LP
436 server_done(&server);
437
0b7964b8
LP
438 return r;
439}