]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/initctl/initctl.c
Merge pull request #2495 from heftig/master
[thirdparty/systemd.git] / src / initctl / initctl.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <sys/epoll.h>
24 #include <unistd.h>
25
26 #include "sd-bus.h"
27 #include "sd-daemon.h"
28
29 #include "alloc-util.h"
30 #include "bus-error.h"
31 #include "bus-util.h"
32 #include "def.h"
33 #include "fd-util.h"
34 #include "formats-util.h"
35 #include "initreq.h"
36 #include "list.h"
37 #include "log.h"
38 #include "special.h"
39 #include "util.h"
40
41 #define SERVER_FD_MAX 16
42 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
43
44 typedef struct Fifo Fifo;
45
46 typedef struct Server {
47 int epoll_fd;
48
49 LIST_HEAD(Fifo, fifos);
50 unsigned n_fifos;
51
52 sd_bus *bus;
53
54 bool quit;
55 } Server;
56
57 struct Fifo {
58 Server *server;
59
60 int fd;
61
62 struct init_request buffer;
63 size_t bytes_read;
64
65 LIST_FIELDS(Fifo, fifo);
66 };
67
68 static const char *translate_runlevel(int runlevel, bool *isolate) {
69 static const struct {
70 const int runlevel;
71 const char *special;
72 bool isolate;
73 } table[] = {
74 { '0', SPECIAL_POWEROFF_TARGET, false },
75 { '1', SPECIAL_RESCUE_TARGET, true },
76 { 's', SPECIAL_RESCUE_TARGET, true },
77 { 'S', SPECIAL_RESCUE_TARGET, true },
78 { '2', SPECIAL_MULTI_USER_TARGET, true },
79 { '3', SPECIAL_MULTI_USER_TARGET, true },
80 { '4', SPECIAL_MULTI_USER_TARGET, true },
81 { '5', SPECIAL_GRAPHICAL_TARGET, true },
82 { '6', SPECIAL_REBOOT_TARGET, false },
83 };
84
85 unsigned i;
86
87 assert(isolate);
88
89 for (i = 0; i < ELEMENTSOF(table); i++)
90 if (table[i].runlevel == runlevel) {
91 *isolate = table[i].isolate;
92 if (runlevel == '6' && kexec_loaded())
93 return SPECIAL_KEXEC_TARGET;
94 return table[i].special;
95 }
96
97 return NULL;
98 }
99
100 static void change_runlevel(Server *s, int runlevel) {
101 const char *target;
102 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
103 const char *mode;
104 bool isolate = false;
105 int r;
106
107 assert(s);
108
109 target = translate_runlevel(runlevel, &isolate);
110 if (!target) {
111 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
112 return;
113 }
114
115 if (isolate)
116 mode = "isolate";
117 else
118 mode = "replace-irreversibly";
119
120 log_debug("Running request %s/start/%s", target, mode);
121
122 r = sd_bus_call_method(
123 s->bus,
124 "org.freedesktop.systemd1",
125 "/org/freedesktop/systemd1",
126 "org.freedesktop.systemd1.Manager",
127 "StartUnit",
128 &error,
129 NULL,
130 "ss", target, mode);
131 if (r < 0) {
132 log_error("Failed to change runlevel: %s", bus_error_message(&error, -r));
133 return;
134 }
135 }
136
137 static void request_process(Server *s, const struct init_request *req) {
138 assert(s);
139 assert(req);
140
141 if (req->magic != INIT_MAGIC) {
142 log_error("Got initctl request with invalid magic. Ignoring.");
143 return;
144 }
145
146 switch (req->cmd) {
147
148 case INIT_CMD_RUNLVL:
149 if (!isprint(req->runlevel))
150 log_error("Got invalid runlevel. Ignoring.");
151 else
152 switch (req->runlevel) {
153
154 /* we are async anyway, so just use kill for reexec/reload */
155 case 'u':
156 case 'U':
157 if (kill(1, SIGTERM) < 0)
158 log_error_errno(errno, "kill() failed: %m");
159
160 /* The bus connection will be
161 * terminated if PID 1 is reexecuted,
162 * hence let's just exit here, and
163 * rely on that we'll be restarted on
164 * the next request */
165 s->quit = true;
166 break;
167
168 case 'q':
169 case 'Q':
170 if (kill(1, SIGHUP) < 0)
171 log_error_errno(errno, "kill() failed: %m");
172 break;
173
174 default:
175 change_runlevel(s, req->runlevel);
176 }
177 return;
178
179 case INIT_CMD_POWERFAIL:
180 case INIT_CMD_POWERFAILNOW:
181 case INIT_CMD_POWEROK:
182 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
183 return;
184
185 case INIT_CMD_CHANGECONS:
186 log_warning("Received console change initctl request. This is not implemented in systemd.");
187 return;
188
189 case INIT_CMD_SETENV:
190 case INIT_CMD_UNSETENV:
191 log_warning("Received environment initctl request. This is not implemented in systemd.");
192 return;
193
194 default:
195 log_warning("Received unknown initctl request. Ignoring.");
196 return;
197 }
198 }
199
200 static int fifo_process(Fifo *f) {
201 ssize_t l;
202
203 assert(f);
204
205 errno = EIO;
206 l = read(f->fd,
207 ((uint8_t*) &f->buffer) + f->bytes_read,
208 sizeof(f->buffer) - f->bytes_read);
209 if (l <= 0) {
210 if (errno == EAGAIN)
211 return 0;
212
213 return log_warning_errno(errno, "Failed to read from fifo: %m");
214 }
215
216 f->bytes_read += l;
217 assert(f->bytes_read <= sizeof(f->buffer));
218
219 if (f->bytes_read == sizeof(f->buffer)) {
220 request_process(f->server, &f->buffer);
221 f->bytes_read = 0;
222 }
223
224 return 0;
225 }
226
227 static void fifo_free(Fifo *f) {
228 assert(f);
229
230 if (f->server) {
231 assert(f->server->n_fifos > 0);
232 f->server->n_fifos--;
233 LIST_REMOVE(fifo, f->server->fifos, f);
234 }
235
236 if (f->fd >= 0) {
237 if (f->server)
238 epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
239
240 safe_close(f->fd);
241 }
242
243 free(f);
244 }
245
246 static void server_done(Server *s) {
247 assert(s);
248
249 while (s->fifos)
250 fifo_free(s->fifos);
251
252 safe_close(s->epoll_fd);
253
254 if (s->bus) {
255 sd_bus_flush(s->bus);
256 sd_bus_unref(s->bus);
257 }
258 }
259
260 static int server_init(Server *s, unsigned n_sockets) {
261 int r;
262 unsigned i;
263
264 assert(s);
265 assert(n_sockets > 0);
266
267 zero(*s);
268
269 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
270 if (s->epoll_fd < 0) {
271 r = log_error_errno(errno,
272 "Failed to create epoll object: %m");
273 goto fail;
274 }
275
276 for (i = 0; i < n_sockets; i++) {
277 struct epoll_event ev;
278 Fifo *f;
279 int fd;
280
281 fd = SD_LISTEN_FDS_START+i;
282
283 r = sd_is_fifo(fd, NULL);
284 if (r < 0) {
285 log_error_errno(r, "Failed to determine file descriptor type: %m");
286 goto fail;
287 }
288
289 if (!r) {
290 log_error("Wrong file descriptor type.");
291 r = -EINVAL;
292 goto fail;
293 }
294
295 f = new0(Fifo, 1);
296 if (!f) {
297 r = -ENOMEM;
298 log_error_errno(errno, "Failed to create fifo object: %m");
299 goto fail;
300 }
301
302 f->fd = -1;
303
304 zero(ev);
305 ev.events = EPOLLIN;
306 ev.data.ptr = f;
307 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
308 r = -errno;
309 fifo_free(f);
310 log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
311 goto fail;
312 }
313
314 f->fd = fd;
315 LIST_PREPEND(fifo, s->fifos, f);
316 f->server = s;
317 s->n_fifos ++;
318 }
319
320 r = bus_connect_system_systemd(&s->bus);
321 if (r < 0) {
322 log_error_errno(r, "Failed to get D-Bus connection: %m");
323 r = -EIO;
324 goto fail;
325 }
326
327 return 0;
328
329 fail:
330 server_done(s);
331
332 return r;
333 }
334
335 static int process_event(Server *s, struct epoll_event *ev) {
336 int r;
337 Fifo *f;
338
339 assert(s);
340
341 if (!(ev->events & EPOLLIN)) {
342 log_info("Got invalid event from epoll. (3)");
343 return -EIO;
344 }
345
346 f = (Fifo*) ev->data.ptr;
347 r = fifo_process(f);
348 if (r < 0) {
349 log_info_errno(r, "Got error on fifo: %m");
350 fifo_free(f);
351 return r;
352 }
353
354 return 0;
355 }
356
357 int main(int argc, char *argv[]) {
358 Server server;
359 int r = EXIT_FAILURE, n;
360
361 if (getppid() != 1) {
362 log_error("This program should be invoked by init only.");
363 return EXIT_FAILURE;
364 }
365
366 if (argc > 1) {
367 log_error("This program does not take arguments.");
368 return EXIT_FAILURE;
369 }
370
371 log_set_target(LOG_TARGET_AUTO);
372 log_parse_environment();
373 log_open();
374
375 umask(0022);
376
377 n = sd_listen_fds(true);
378 if (n < 0) {
379 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
380 return EXIT_FAILURE;
381 }
382
383 if (n <= 0 || n > SERVER_FD_MAX) {
384 log_error("No or too many file descriptors passed.");
385 return EXIT_FAILURE;
386 }
387
388 if (server_init(&server, (unsigned) n) < 0)
389 return EXIT_FAILURE;
390
391 log_debug("systemd-initctl running as pid "PID_FMT, getpid());
392
393 sd_notify(false,
394 "READY=1\n"
395 "STATUS=Processing requests...");
396
397 while (!server.quit) {
398 struct epoll_event event;
399 int k;
400
401 k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC);
402 if (k < 0) {
403 if (errno == EINTR)
404 continue;
405 log_error_errno(errno, "epoll_wait() failed: %m");
406 goto fail;
407 }
408
409 if (k <= 0)
410 break;
411
412 if (process_event(&server, &event) < 0)
413 goto fail;
414 }
415
416 r = EXIT_SUCCESS;
417
418 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid());
419
420 fail:
421 sd_notify(false,
422 "STOPPING=1\n"
423 "STATUS=Shutting down...");
424
425 server_done(&server);
426
427 return r;
428 }