]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/initctl/initctl.c
tree-wide: sort includes
[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 <ctype.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <sys/epoll.h>
26 #include <unistd.h>
27
28 #include "sd-bus.h"
29 #include "sd-daemon.h"
30
31 #include "alloc-util.h"
32 #include "bus-error.h"
33 #include "bus-util.h"
34 #include "def.h"
35 #include "fd-util.h"
36 #include "formats-util.h"
37 #include "initreq.h"
38 #include "list.h"
39 #include "log.h"
40 #include "special.h"
41 #include "util.h"
42
43 #define SERVER_FD_MAX 16
44 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
45
46 typedef struct Fifo Fifo;
47
48 typedef struct Server {
49 int epoll_fd;
50
51 LIST_HEAD(Fifo, fifos);
52 unsigned n_fifos;
53
54 sd_bus *bus;
55
56 bool quit;
57 } Server;
58
59 struct 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
70 static const char *translate_runlevel(int runlevel, bool *isolate) {
71 static const struct {
72 const int runlevel;
73 const char *special;
74 bool isolate;
75 } table[] = {
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 },
85 };
86
87 unsigned i;
88
89 assert(isolate);
90
91 for (i = 0; i < ELEMENTSOF(table); i++)
92 if (table[i].runlevel == runlevel) {
93 *isolate = table[i].isolate;
94 if (runlevel == '6' && kexec_loaded())
95 return SPECIAL_KEXEC_TARGET;
96 return table[i].special;
97 }
98
99 return NULL;
100 }
101
102 static void change_runlevel(Server *s, int runlevel) {
103 const char *target;
104 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
105 const char *mode;
106 bool isolate = false;
107 int r;
108
109 assert(s);
110
111 target = translate_runlevel(runlevel, &isolate);
112 if (!target) {
113 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
114 return;
115 }
116
117 if (isolate)
118 mode = "isolate";
119 else
120 mode = "replace-irreversibly";
121
122 log_debug("Running request %s/start/%s", target, mode);
123
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;
136 }
137 }
138
139 static 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
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)
160 log_error_errno(errno, "kill() failed: %m");
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;
168 break;
169
170 case 'q':
171 case 'Q':
172 if (kill(1, SIGHUP) < 0)
173 log_error_errno(errno, "kill() failed: %m");
174 break;
175
176 default:
177 change_runlevel(s, req->runlevel);
178 }
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
202 static int fifo_process(Fifo *f) {
203 ssize_t l;
204
205 assert(f);
206
207 errno = EIO;
208 l = read(f->fd,
209 ((uint8_t*) &f->buffer) + f->bytes_read,
210 sizeof(f->buffer) - f->bytes_read);
211 if (l <= 0) {
212 if (errno == EAGAIN)
213 return 0;
214
215 return log_warning_errno(errno, "Failed to read from fifo: %m");
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
229 static 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--;
235 LIST_REMOVE(fifo, f->server->fifos, f);
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
242 safe_close(f->fd);
243 }
244
245 free(f);
246 }
247
248 static void server_done(Server *s) {
249 assert(s);
250
251 while (s->fifos)
252 fifo_free(s->fifos);
253
254 safe_close(s->epoll_fd);
255
256 if (s->bus) {
257 sd_bus_flush(s->bus);
258 sd_bus_unref(s->bus);
259 }
260 }
261
262 static int server_init(Server *s, unsigned n_sockets) {
263 int r;
264 unsigned i;
265
266 assert(s);
267 assert(n_sockets > 0);
268
269 zero(*s);
270
271 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
272 if (s->epoll_fd < 0) {
273 r = log_error_errno(errno,
274 "Failed to create epoll object: %m");
275 goto fail;
276 }
277
278 for (i = 0; i < n_sockets; i++) {
279 struct epoll_event ev;
280 Fifo *f;
281 int fd;
282
283 fd = SD_LISTEN_FDS_START+i;
284
285 r = sd_is_fifo(fd, NULL);
286 if (r < 0) {
287 log_error_errno(r, "Failed to determine file descriptor type: %m");
288 goto fail;
289 }
290
291 if (!r) {
292 log_error("Wrong file descriptor type.");
293 r = -EINVAL;
294 goto fail;
295 }
296
297 f = new0(Fifo, 1);
298 if (!f) {
299 r = -ENOMEM;
300 log_error_errno(errno, "Failed to create fifo object: %m");
301 goto fail;
302 }
303
304 f->fd = -1;
305
306 zero(ev);
307 ev.events = EPOLLIN;
308 ev.data.ptr = f;
309 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
310 r = -errno;
311 fifo_free(f);
312 log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
313 goto fail;
314 }
315
316 f->fd = fd;
317 LIST_PREPEND(fifo, s->fifos, f);
318 f->server = s;
319 s->n_fifos ++;
320 }
321
322 r = bus_connect_system_systemd(&s->bus);
323 if (r < 0) {
324 log_error_errno(r, "Failed to get D-Bus connection: %m");
325 r = -EIO;
326 goto fail;
327 }
328
329 return 0;
330
331 fail:
332 server_done(s);
333
334 return r;
335 }
336
337 static 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;
349 r = fifo_process(f);
350 if (r < 0) {
351 log_info_errno(r, "Got error on fifo: %m");
352 fifo_free(f);
353 return r;
354 }
355
356 return 0;
357 }
358
359 int main(int argc, char *argv[]) {
360 Server server;
361 int r = EXIT_FAILURE, n;
362
363 if (getppid() != 1) {
364 log_error("This program should be invoked by init only.");
365 return EXIT_FAILURE;
366 }
367
368 if (argc > 1) {
369 log_error("This program does not take arguments.");
370 return EXIT_FAILURE;
371 }
372
373 log_set_target(LOG_TARGET_AUTO);
374 log_parse_environment();
375 log_open();
376
377 umask(0022);
378
379 n = sd_listen_fds(true);
380 if (n < 0) {
381 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
382 return EXIT_FAILURE;
383 }
384
385 if (n <= 0 || n > SERVER_FD_MAX) {
386 log_error("No or too many file descriptors passed.");
387 return EXIT_FAILURE;
388 }
389
390 if (server_init(&server, (unsigned) n) < 0)
391 return EXIT_FAILURE;
392
393 log_debug("systemd-initctl running as pid "PID_FMT, getpid());
394
395 sd_notify(false,
396 "READY=1\n"
397 "STATUS=Processing requests...");
398
399 while (!server.quit) {
400 struct epoll_event event;
401 int k;
402
403 k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC);
404 if (k < 0) {
405 if (errno == EINTR)
406 continue;
407 log_error_errno(errno, "epoll_wait() failed: %m");
408 goto fail;
409 }
410
411 if (k <= 0)
412 break;
413
414 if (process_event(&server, &event) < 0)
415 goto fail;
416 }
417
418 r = EXIT_SUCCESS;
419
420 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid());
421
422 fail:
423 sd_notify(false,
424 "STOPPING=1\n"
425 "STATUS=Shutting down...");
426
427 server_done(&server);
428
429 return r;
430 }