]> git.proxmox.com Git - systemd.git/blame - src/initctl/initctl.c
Imported Upstream version 218
[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)
f47781d8 165 log_error_errno(errno, "kill() failed: %m");
663996b3
MS
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)
f47781d8 178 log_error_errno(errno, "kill() failed: %m");
663996b3
MS
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
f47781d8 220 log_warning_errno(errno, "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;
f47781d8 280 log_error_errno(errno, "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) {
f47781d8 293 log_error_errno(r, "Failed to determine file descriptor type: %m");
663996b3
MS
294 goto fail;
295 }
296
297 if (!r) {
298 log_error("Wrong file descriptor type.");
299 r = -EINVAL;
300 goto fail;
301 }
302
303 f = new0(Fifo, 1);
304 if (!f) {
305 r = -ENOMEM;
f47781d8 306 log_error_errno(errno, "Failed to create fifo object: %m");
663996b3
MS
307 goto fail;
308 }
309
310 f->fd = -1;
311
312 zero(ev);
313 ev.events = EPOLLIN;
314 ev.data.ptr = f;
315 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
316 r = -errno;
317 fifo_free(f);
f47781d8 318 log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
663996b3
MS
319 goto fail;
320 }
321
322 f->fd = fd;
60f067b4 323 LIST_PREPEND(fifo, s->fifos, f);
663996b3
MS
324 f->server = s;
325 s->n_fifos ++;
326 }
327
60f067b4
JS
328 r = bus_open_system_systemd(&s->bus);
329 if (r < 0) {
f47781d8 330 log_error_errno(r, "Failed to get D-Bus connection: %m");
663996b3
MS
331 r = -EIO;
332 goto fail;
333 }
334
335 return 0;
336
337fail:
338 server_done(s);
339
663996b3
MS
340 return r;
341}
342
343static int process_event(Server *s, struct epoll_event *ev) {
344 int r;
345 Fifo *f;
346
347 assert(s);
348
349 if (!(ev->events & EPOLLIN)) {
350 log_info("Got invalid event from epoll. (3)");
351 return -EIO;
352 }
353
354 f = (Fifo*) ev->data.ptr;
14228c0d
MB
355 r = fifo_process(f);
356 if (r < 0) {
f47781d8 357 log_info_errno(r, "Got error on fifo: %m");
663996b3
MS
358 fifo_free(f);
359 return r;
360 }
361
362 return 0;
363}
364
365int main(int argc, char *argv[]) {
366 Server server;
367 int r = EXIT_FAILURE, n;
368
369 if (getppid() != 1) {
370 log_error("This program should be invoked by init only.");
371 return EXIT_FAILURE;
372 }
373
374 if (argc > 1) {
375 log_error("This program does not take arguments.");
376 return EXIT_FAILURE;
377 }
378
379 log_set_target(LOG_TARGET_AUTO);
380 log_parse_environment();
381 log_open();
382
383 umask(0022);
384
60f067b4
JS
385 n = sd_listen_fds(true);
386 if (n < 0) {
f47781d8 387 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
663996b3
MS
388 return EXIT_FAILURE;
389 }
390
391 if (n <= 0 || n > SERVER_FD_MAX) {
392 log_error("No or too many file descriptors passed.");
393 return EXIT_FAILURE;
394 }
395
396 if (server_init(&server, (unsigned) n) < 0)
397 return EXIT_FAILURE;
398
60f067b4 399 log_debug("systemd-initctl running as pid "PID_FMT, getpid());
663996b3
MS
400
401 sd_notify(false,
402 "READY=1\n"
403 "STATUS=Processing requests...");
404
405 while (!server.quit) {
406 struct epoll_event event;
407 int k;
408
409 if ((k = epoll_wait(server.epoll_fd,
410 &event, 1,
411 TIMEOUT_MSEC)) < 0) {
412
413 if (errno == EINTR)
414 continue;
415
f47781d8 416 log_error_errno(errno, "epoll_wait() failed: %m");
663996b3
MS
417 goto fail;
418 }
419
420 if (k <= 0)
421 break;
422
423 if (process_event(&server, &event) < 0)
424 goto fail;
425 }
426
427 r = EXIT_SUCCESS;
428
60f067b4 429 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid());
663996b3
MS
430
431fail:
432 sd_notify(false,
5eef597e 433 "STOPPING=1\n"
663996b3
MS
434 "STATUS=Shutting down...");
435
436 server_done(&server);
437
663996b3
MS
438 return r;
439}