]> git.proxmox.com Git - systemd.git/blame - src/initctl/initctl.c
Imported Upstream version 217
[systemd.git] / src / initctl / initctl.c
CommitLineData
663996b3
MS
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
60f067b4
JS
36#include "sd-daemon.h"
37#include "sd-bus.h"
663996b3
MS
38
39#include "util.h"
40#include "log.h"
41#include "list.h"
42#include "initreq.h"
43#include "special.h"
60f067b4
JS
44#include "bus-util.h"
45#include "bus-error.h"
663996b3
MS
46#include "def.h"
47
48#define SERVER_FD_MAX 16
49#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
50
51typedef struct Fifo Fifo;
52
53typedef struct Server {
54 int epoll_fd;
55
56 LIST_HEAD(Fifo, fifos);
57 unsigned n_fifos;
58
60f067b4 59 sd_bus *bus;
663996b3
MS
60
61 bool quit;
62} Server;
63
64struct 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
75static 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
107static void change_runlevel(Server *s, int runlevel) {
108 const char *target;
60f067b4 109 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
663996b3
MS
110 const char *mode;
111 bool isolate = false;
60f067b4 112 int r;
663996b3
MS
113
114 assert(s);
115
60f067b4
JS
116 target = translate_runlevel(runlevel, &isolate);
117 if (!target) {
663996b3 118 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
60f067b4 119 return;
663996b3
MS
120 }
121
122 if (isolate)
123 mode = "isolate";
124 else
14228c0d 125 mode = "replace-irreversibly";
663996b3
MS
126
127 log_debug("Running request %s/start/%s", target, mode);
128
60f067b4
JS
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;
663996b3 141 }
663996b3
MS
142}
143
144static 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
207static int fifo_process(Fifo *f) {
208 ssize_t l;
209
210 assert(f);
211
212 errno = EIO;
14228c0d
MB
213 l = read(f->fd,
214 ((uint8_t*) &f->buffer) + f->bytes_read,
215 sizeof(f->buffer) - f->bytes_read);
216 if (l <= 0) {
663996b3
MS
217 if (errno == EAGAIN)
218 return 0;
219
60f067b4 220 log_warning("Failed to read from fifo: %m");
5eef597e 221 return -errno;
663996b3
MS
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
235static 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--;
60f067b4 241 LIST_REMOVE(fifo, f->server->fifos, f);
663996b3
MS
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
60f067b4 248 safe_close(f->fd);
663996b3
MS
249 }
250
251 free(f);
252}
253
254static void server_done(Server *s) {
255 assert(s);
256
257 while (s->fifos)
258 fifo_free(s->fifos);
259
60f067b4 260 safe_close(s->epoll_fd);
663996b3
MS
261
262 if (s->bus) {
60f067b4
JS
263 sd_bus_flush(s->bus);
264 sd_bus_unref(s->bus);
663996b3
MS
265 }
266}
267
268static int server_init(Server *s, unsigned n_sockets) {
269 int r;
270 unsigned i;
663996b3
MS
271
272 assert(s);
273 assert(n_sockets > 0);
274
663996b3
MS
275 zero(*s);
276
277 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
278 if (s->epoll_fd < 0) {
279 r = -errno;
60f067b4 280 log_error("Failed to create epoll object: %m");
663996b3
MS
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;
60f067b4 307 log_error("Failed to create fifo object: %m");
663996b3
MS
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);
60f067b4 319 log_error("Failed to add fifo fd to epoll object: %m");
663996b3
MS
320 goto fail;
321 }
322
323 f->fd = fd;
60f067b4 324 LIST_PREPEND(fifo, s->fifos, f);
663996b3
MS
325 f->server = s;
326 s->n_fifos ++;
327 }
328
60f067b4
JS
329 r = bus_open_system_systemd(&s->bus);
330 if (r < 0) {
331 log_error("Failed to get D-Bus connection: %s", strerror(-r));
663996b3
MS
332 r = -EIO;
333 goto fail;
334 }
335
336 return 0;
337
338fail:
339 server_done(s);
340
663996b3
MS
341 return r;
342}
343
344static 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;
14228c0d
MB
356 r = fifo_process(f);
357 if (r < 0) {
663996b3
MS
358 log_info("Got error on fifo: %s", strerror(-r));
359 fifo_free(f);
360 return r;
361 }
362
363 return 0;
364}
365
366int 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
60f067b4
JS
386 n = sd_listen_fds(true);
387 if (n < 0) {
663996b3
MS
388 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
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
60f067b4 400 log_debug("systemd-initctl running as pid "PID_FMT, getpid());
663996b3
MS
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
60f067b4 417 log_error("epoll_wait() failed: %m");
663996b3
MS
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
60f067b4 430 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid());
663996b3
MS
431
432fail:
433 sd_notify(false,
5eef597e 434 "STOPPING=1\n"
663996b3
MS
435 "STATUS=Shutting down...");
436
437 server_done(&server);
438
663996b3
MS
439 return r;
440}