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