1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include <sys/epoll.h>
28 #include "sd-daemon.h"
37 #include "bus-error.h"
40 #define SERVER_FD_MAX 16
41 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
43 typedef struct Fifo Fifo;
45 typedef struct Server {
48 LIST_HEAD(Fifo, fifos);
61 struct init_request buffer;
64 LIST_FIELDS(Fifo, fifo);
67 static const char *translate_runlevel(int runlevel, bool *isolate) {
73 { '0', SPECIAL_POWEROFF_TARGET, false },
74 { '1', SPECIAL_RESCUE_TARGET, true },
75 { 's', SPECIAL_RESCUE_TARGET, true },
76 { 'S', SPECIAL_RESCUE_TARGET, true },
77 { '2', SPECIAL_MULTI_USER_TARGET, true },
78 { '3', SPECIAL_MULTI_USER_TARGET, true },
79 { '4', SPECIAL_MULTI_USER_TARGET, true },
80 { '5', SPECIAL_GRAPHICAL_TARGET, true },
81 { '6', SPECIAL_REBOOT_TARGET, false },
88 for (i = 0; i < ELEMENTSOF(table); i++)
89 if (table[i].runlevel == runlevel) {
90 *isolate = table[i].isolate;
91 if (runlevel == '6' && kexec_loaded())
92 return SPECIAL_KEXEC_TARGET;
93 return table[i].special;
99 static void change_runlevel(Server *s, int runlevel) {
101 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
103 bool isolate = false;
108 target = translate_runlevel(runlevel, &isolate);
110 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
117 mode = "replace-irreversibly";
119 log_debug("Running request %s/start/%s", target, mode);
121 r = sd_bus_call_method(
123 "org.freedesktop.systemd1",
124 "/org/freedesktop/systemd1",
125 "org.freedesktop.systemd1.Manager",
131 log_error("Failed to change runlevel: %s", bus_error_message(&error, -r));
136 static void request_process(Server *s, const struct init_request *req) {
140 if (req->magic != INIT_MAGIC) {
141 log_error("Got initctl request with invalid magic. Ignoring.");
147 case INIT_CMD_RUNLVL:
148 if (!isprint(req->runlevel))
149 log_error("Got invalid runlevel. Ignoring.");
151 switch (req->runlevel) {
153 /* we are async anyway, so just use kill for reexec/reload */
156 if (kill(1, SIGTERM) < 0)
157 log_error_errno(errno, "kill() failed: %m");
159 /* The bus connection will be
160 * terminated if PID 1 is reexecuted,
161 * hence let's just exit here, and
162 * rely on that we'll be restarted on
163 * the next request */
169 if (kill(1, SIGHUP) < 0)
170 log_error_errno(errno, "kill() failed: %m");
174 change_runlevel(s, req->runlevel);
178 case INIT_CMD_POWERFAIL:
179 case INIT_CMD_POWERFAILNOW:
180 case INIT_CMD_POWEROK:
181 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
184 case INIT_CMD_CHANGECONS:
185 log_warning("Received console change initctl request. This is not implemented in systemd.");
188 case INIT_CMD_SETENV:
189 case INIT_CMD_UNSETENV:
190 log_warning("Received environment initctl request. This is not implemented in systemd.");
194 log_warning("Received unknown initctl request. Ignoring.");
199 static int fifo_process(Fifo *f) {
206 ((uint8_t*) &f->buffer) + f->bytes_read,
207 sizeof(f->buffer) - f->bytes_read);
212 log_warning_errno(errno, "Failed to read from fifo: %m");
217 assert(f->bytes_read <= sizeof(f->buffer));
219 if (f->bytes_read == sizeof(f->buffer)) {
220 request_process(f->server, &f->buffer);
227 static void fifo_free(Fifo *f) {
231 assert(f->server->n_fifos > 0);
232 f->server->n_fifos--;
233 LIST_REMOVE(fifo, f->server->fifos, f);
238 epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
246 static void server_done(Server *s) {
252 safe_close(s->epoll_fd);
255 sd_bus_flush(s->bus);
256 sd_bus_unref(s->bus);
260 static int server_init(Server *s, unsigned n_sockets) {
265 assert(n_sockets > 0);
269 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
270 if (s->epoll_fd < 0) {
272 log_error_errno(errno, "Failed to create epoll object: %m");
276 for (i = 0; i < n_sockets; i++) {
277 struct epoll_event ev;
281 fd = SD_LISTEN_FDS_START+i;
283 r = sd_is_fifo(fd, NULL);
285 log_error_errno(r, "Failed to determine file descriptor type: %m");
290 log_error("Wrong file descriptor type.");
298 log_error_errno(errno, "Failed to create fifo object: %m");
307 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
310 log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
315 LIST_PREPEND(fifo, s->fifos, f);
320 r = bus_open_system_systemd(&s->bus);
322 log_error_errno(r, "Failed to get D-Bus connection: %m");
335 static int process_event(Server *s, struct epoll_event *ev) {
341 if (!(ev->events & EPOLLIN)) {
342 log_info("Got invalid event from epoll. (3)");
346 f = (Fifo*) ev->data.ptr;
349 log_info_errno(r, "Got error on fifo: %m");
357 int main(int argc, char *argv[]) {
359 int r = EXIT_FAILURE, n;
361 if (getppid() != 1) {
362 log_error("This program should be invoked by init only.");
367 log_error("This program does not take arguments.");
371 log_set_target(LOG_TARGET_AUTO);
372 log_parse_environment();
377 n = sd_listen_fds(true);
379 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
383 if (n <= 0 || n > SERVER_FD_MAX) {
384 log_error("No or too many file descriptors passed.");
388 if (server_init(&server, (unsigned) n) < 0)
391 log_debug("systemd-initctl running as pid "PID_FMT, getpid());
395 "STATUS=Processing requests...");
397 while (!server.quit) {
398 struct epoll_event event;
401 if ((k = epoll_wait(server.epoll_fd,
403 TIMEOUT_MSEC)) < 0) {
408 log_error_errno(errno, "epoll_wait() failed: %m");
415 if (process_event(&server, &event) < 0)
421 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid());
426 "STATUS=Shutting down...");
428 server_done(&server);