]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/initctl/initctl.c
Merge pull request #1668 from ssahani/net1
[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 <stdio.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <sys/epoll.h>
26 #include <ctype.h>
27
28 #include "sd-bus.h"
29 #include "sd-daemon.h"
30
31 #include "bus-error.h"
32 #include "bus-util.h"
33 #include "def.h"
34 #include "fd-util.h"
35 #include "formats-util.h"
36 #include "initreq.h"
37 #include "list.h"
38 #include "log.h"
39 #include "special.h"
40 #include "util.h"
41
42 #define SERVER_FD_MAX 16
43 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
44
45 typedef struct Fifo Fifo;
46
47 typedef struct Server {
48 int epoll_fd;
49
50 LIST_HEAD(Fifo, fifos);
51 unsigned n_fifos;
52
53 sd_bus *bus;
54
55 bool quit;
56 } Server;
57
58 struct Fifo {
59 Server *server;
60
61 int fd;
62
63 struct init_request buffer;
64 size_t bytes_read;
65
66 LIST_FIELDS(Fifo, fifo);
67 };
68
69 static const char *translate_runlevel(int runlevel, bool *isolate) {
70 static const struct {
71 const int runlevel;
72 const char *special;
73 bool isolate;
74 } table[] = {
75 { '0', SPECIAL_POWEROFF_TARGET, false },
76 { '1', SPECIAL_RESCUE_TARGET, true },
77 { 's', SPECIAL_RESCUE_TARGET, true },
78 { 'S', SPECIAL_RESCUE_TARGET, true },
79 { '2', SPECIAL_MULTI_USER_TARGET, true },
80 { '3', SPECIAL_MULTI_USER_TARGET, true },
81 { '4', SPECIAL_MULTI_USER_TARGET, true },
82 { '5', SPECIAL_GRAPHICAL_TARGET, true },
83 { '6', SPECIAL_REBOOT_TARGET, false },
84 };
85
86 unsigned i;
87
88 assert(isolate);
89
90 for (i = 0; i < ELEMENTSOF(table); i++)
91 if (table[i].runlevel == runlevel) {
92 *isolate = table[i].isolate;
93 if (runlevel == '6' && kexec_loaded())
94 return SPECIAL_KEXEC_TARGET;
95 return table[i].special;
96 }
97
98 return NULL;
99 }
100
101 static void change_runlevel(Server *s, int runlevel) {
102 const char *target;
103 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
104 const char *mode;
105 bool isolate = false;
106 int r;
107
108 assert(s);
109
110 target = translate_runlevel(runlevel, &isolate);
111 if (!target) {
112 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
113 return;
114 }
115
116 if (isolate)
117 mode = "isolate";
118 else
119 mode = "replace-irreversibly";
120
121 log_debug("Running request %s/start/%s", target, mode);
122
123 r = sd_bus_call_method(
124 s->bus,
125 "org.freedesktop.systemd1",
126 "/org/freedesktop/systemd1",
127 "org.freedesktop.systemd1.Manager",
128 "StartUnit",
129 &error,
130 NULL,
131 "ss", target, mode);
132 if (r < 0) {
133 log_error("Failed to change runlevel: %s", bus_error_message(&error, -r));
134 return;
135 }
136 }
137
138 static void request_process(Server *s, const struct init_request *req) {
139 assert(s);
140 assert(req);
141
142 if (req->magic != INIT_MAGIC) {
143 log_error("Got initctl request with invalid magic. Ignoring.");
144 return;
145 }
146
147 switch (req->cmd) {
148
149 case INIT_CMD_RUNLVL:
150 if (!isprint(req->runlevel))
151 log_error("Got invalid runlevel. Ignoring.");
152 else
153 switch (req->runlevel) {
154
155 /* we are async anyway, so just use kill for reexec/reload */
156 case 'u':
157 case 'U':
158 if (kill(1, SIGTERM) < 0)
159 log_error_errno(errno, "kill() failed: %m");
160
161 /* The bus connection will be
162 * terminated if PID 1 is reexecuted,
163 * hence let's just exit here, and
164 * rely on that we'll be restarted on
165 * the next request */
166 s->quit = true;
167 break;
168
169 case 'q':
170 case 'Q':
171 if (kill(1, SIGHUP) < 0)
172 log_error_errno(errno, "kill() failed: %m");
173 break;
174
175 default:
176 change_runlevel(s, req->runlevel);
177 }
178 return;
179
180 case INIT_CMD_POWERFAIL:
181 case INIT_CMD_POWERFAILNOW:
182 case INIT_CMD_POWEROK:
183 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
184 return;
185
186 case INIT_CMD_CHANGECONS:
187 log_warning("Received console change initctl request. This is not implemented in systemd.");
188 return;
189
190 case INIT_CMD_SETENV:
191 case INIT_CMD_UNSETENV:
192 log_warning("Received environment initctl request. This is not implemented in systemd.");
193 return;
194
195 default:
196 log_warning("Received unknown initctl request. Ignoring.");
197 return;
198 }
199 }
200
201 static int fifo_process(Fifo *f) {
202 ssize_t l;
203
204 assert(f);
205
206 errno = EIO;
207 l = read(f->fd,
208 ((uint8_t*) &f->buffer) + f->bytes_read,
209 sizeof(f->buffer) - f->bytes_read);
210 if (l <= 0) {
211 if (errno == EAGAIN)
212 return 0;
213
214 log_warning_errno(errno, "Failed to read from fifo: %m");
215 return -errno;
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 }