]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/initctl/initctl.c
treewide: auto-convert the simple cases to log_*_errno()
[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("Failed to determine file descriptor type: %s",
294 strerror(-r));
295 goto fail;
296 }
297
298 if (!r) {
299 log_error("Wrong file descriptor type.");
300 r = -EINVAL;
301 goto fail;
302 }
303
304 f = new0(Fifo, 1);
305 if (!f) {
306 r = -ENOMEM;
307 log_error("Failed to create fifo object: %m");
308 goto fail;
309 }
310
311 f->fd = -1;
312
313 zero(ev);
314 ev.events = EPOLLIN;
315 ev.data.ptr = f;
316 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
317 r = -errno;
318 fifo_free(f);
319 log_error("Failed to add fifo fd to epoll object: %m");
320 goto fail;
321 }
322
323 f->fd = fd;
324 LIST_PREPEND(fifo, s->fifos, f);
325 f->server = s;
326 s->n_fifos ++;
327 }
328
329 r = bus_open_system_systemd(&s->bus);
330 if (r < 0) {
331 log_error_errno(-r, "Failed to get D-Bus connection: %m");
332 r = -EIO;
333 goto fail;
334 }
335
336 return 0;
337
338 fail:
339 server_done(s);
340
341 return r;
342 }
343
344 static 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;
356 r = fifo_process(f);
357 if (r < 0) {
358 log_info_errno(-r, "Got error on fifo: %m");
359 fifo_free(f);
360 return r;
361 }
362
363 return 0;
364 }
365
366 int main(int argc, char *argv[]) {
367 Server server;
368 int r = EXIT_FAILURE, n;
369
370 if (getppid() != 1) {
371 log_error("This program should be invoked by init only.");
372 return EXIT_FAILURE;
373 }
374
375 if (argc > 1) {
376 log_error("This program does not take arguments.");
377 return EXIT_FAILURE;
378 }
379
380 log_set_target(LOG_TARGET_AUTO);
381 log_parse_environment();
382 log_open();
383
384 umask(0022);
385
386 n = sd_listen_fds(true);
387 if (n < 0) {
388 log_error_errno(-r, "Failed to read listening file descriptors from environment: %m");
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 "PID_FMT, 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 "PID_FMT, getpid());
431
432 fail:
433 sd_notify(false,
434 "STOPPING=1\n"
435 "STATUS=Shutting down...");
436
437 server_done(&server);
438
439 return r;
440 }