]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/initctl/initctl.c
logind: fix printf format
[thirdparty/systemd.git] / src / initctl / initctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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
36 #include "sd-daemon.h"
37 #include "sd-bus.h"
38
39 #include "util.h"
40 #include "log.h"
41 #include "list.h"
42 #include "initreq.h"
43 #include "special.h"
44 #include "bus-util.h"
45 #include "bus-error.h"
46 #include "def.h"
47
48 #define SERVER_FD_MAX 16
49 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
50
51 typedef struct Fifo Fifo;
52
53 typedef struct Server {
54 int epoll_fd;
55
56 LIST_HEAD(Fifo, fifos);
57 unsigned n_fifos;
58
59 sd_bus *bus;
60
61 bool quit;
62 } Server;
63
64 struct 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
75 static const char *translate_runlevel(int runlevel, bool *isolate) {
76 static const struct {
77 const int runlevel;
78 const char *special;
79 bool isolate;
80 } table[] = {
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 },
90 };
91
92 unsigned i;
93
94 assert(isolate);
95
96 for (i = 0; i < ELEMENTSOF(table); i++)
97 if (table[i].runlevel == runlevel) {
98 *isolate = table[i].isolate;
99 if (runlevel == '6' && kexec_loaded())
100 return SPECIAL_KEXEC_TARGET;
101 return table[i].special;
102 }
103
104 return NULL;
105 }
106
107 static void change_runlevel(Server *s, int runlevel) {
108 const char *target;
109 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
110 const char *mode;
111 bool isolate = false;
112 int r;
113
114 assert(s);
115
116 target = translate_runlevel(runlevel, &isolate);
117 if (!target) {
118 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
119 return;
120 }
121
122 if (isolate)
123 mode = "isolate";
124 else
125 mode = "replace-irreversibly";
126
127 log_debug("Running request %s/start/%s", target, mode);
128
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;
141 }
142 }
143
144 static 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
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");
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;
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 }
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
207 static int fifo_process(Fifo *f) {
208 ssize_t l;
209
210 assert(f);
211
212 errno = EIO;
213 l = read(f->fd,
214 ((uint8_t*) &f->buffer) + f->bytes_read,
215 sizeof(f->buffer) - f->bytes_read);
216 if (l <= 0) {
217 if (errno == EAGAIN)
218 return 0;
219
220 log_warning("Failed to read from fifo: %m");
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
235 static 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--;
241 LIST_REMOVE(fifo, f->server->fifos, f);
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
248 close_nointr_nofail(f->fd);
249 }
250
251 free(f);
252 }
253
254 static 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)
261 close_nointr_nofail(s->epoll_fd);
262
263 if (s->bus) {
264 sd_bus_flush(s->bus);
265 sd_bus_unref(s->bus);
266 }
267 }
268
269 static int server_init(Server *s, unsigned n_sockets) {
270 int r;
271 unsigned i;
272
273 assert(s);
274 assert(n_sockets > 0);
275
276 zero(*s);
277
278 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
279 if (s->epoll_fd < 0) {
280 r = -errno;
281 log_error("Failed to create epoll object: %m");
282 goto fail;
283 }
284
285 for (i = 0; i < n_sockets; i++) {
286 struct epoll_event ev;
287 Fifo *f;
288 int fd;
289
290 fd = SD_LISTEN_FDS_START+i;
291
292 r = sd_is_fifo(fd, NULL);
293 if (r < 0) {
294 log_error("Failed to determine file descriptor type: %s",
295 strerror(-r));
296 goto fail;
297 }
298
299 if (!r) {
300 log_error("Wrong file descriptor type.");
301 r = -EINVAL;
302 goto fail;
303 }
304
305 f = new0(Fifo, 1);
306 if (!f) {
307 r = -ENOMEM;
308 log_error("Failed to create fifo object: %m");
309 goto fail;
310 }
311
312 f->fd = -1;
313
314 zero(ev);
315 ev.events = EPOLLIN;
316 ev.data.ptr = f;
317 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
318 r = -errno;
319 fifo_free(f);
320 log_error("Failed to add fifo fd to epoll object: %m");
321 goto fail;
322 }
323
324 f->fd = fd;
325 LIST_PREPEND(fifo, s->fifos, f);
326 f->server = s;
327 s->n_fifos ++;
328 }
329
330 r = bus_open_system_systemd(&s->bus);
331 if (r < 0) {
332 log_error("Failed to get D-Bus connection: %s", strerror(-r));
333 r = -EIO;
334 goto fail;
335 }
336
337 return 0;
338
339 fail:
340 server_done(s);
341
342 return r;
343 }
344
345 static 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;
357 r = fifo_process(f);
358 if (r < 0) {
359 log_info("Got error on fifo: %s", strerror(-r));
360 fifo_free(f);
361 return r;
362 }
363
364 return 0;
365 }
366
367 int main(int argc, char *argv[]) {
368 Server server;
369 int r = EXIT_FAILURE, n;
370
371 if (getppid() != 1) {
372 log_error("This program should be invoked by init only.");
373 return EXIT_FAILURE;
374 }
375
376 if (argc > 1) {
377 log_error("This program does not take arguments.");
378 return EXIT_FAILURE;
379 }
380
381 log_set_target(LOG_TARGET_AUTO);
382 log_parse_environment();
383 log_open();
384
385 umask(0022);
386
387 if ((n = sd_listen_fds(true)) < 0) {
388 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
389 return EXIT_FAILURE;
390 }
391
392 if (n <= 0 || n > SERVER_FD_MAX) {
393 log_error("No or too many file descriptors passed.");
394 return EXIT_FAILURE;
395 }
396
397 if (server_init(&server, (unsigned) n) < 0)
398 return EXIT_FAILURE;
399
400 log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
401
402 sd_notify(false,
403 "READY=1\n"
404 "STATUS=Processing requests...");
405
406 while (!server.quit) {
407 struct epoll_event event;
408 int k;
409
410 if ((k = epoll_wait(server.epoll_fd,
411 &event, 1,
412 TIMEOUT_MSEC)) < 0) {
413
414 if (errno == EINTR)
415 continue;
416
417 log_error("epoll_wait() failed: %m");
418 goto fail;
419 }
420
421 if (k <= 0)
422 break;
423
424 if (process_event(&server, &event) < 0)
425 goto fail;
426 }
427
428 r = EXIT_SUCCESS;
429
430 log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
431
432 fail:
433 sd_notify(false,
434 "STATUS=Shutting down...");
435
436 server_done(&server);
437
438 return r;
439 }