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/>.
22 #include <sys/socket.h>
23 #include <sys/types.h>
31 #include <sys/epoll.h>
36 #include <dbus/dbus.h>
37 #include <systemd/sd-daemon.h>
44 #include "dbus-common.h"
47 #define SERVER_FD_MAX 16
48 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
50 typedef struct Fifo Fifo;
52 typedef struct Server {
55 LIST_HEAD(Fifo, fifos);
68 struct init_request buffer;
71 LIST_FIELDS(Fifo, fifo);
74 static const char *translate_runlevel(int runlevel, bool *isolate) {
80 { '0', SPECIAL_POWEROFF_TARGET, false },
81 { '1', SPECIAL_RESCUE_TARGET, true },
82 { 's', SPECIAL_RESCUE_TARGET, true },
83 { 'S', SPECIAL_RESCUE_TARGET, true },
84 { '2', SPECIAL_RUNLEVEL2_TARGET, true },
85 { '3', SPECIAL_RUNLEVEL3_TARGET, true },
86 { '4', SPECIAL_RUNLEVEL4_TARGET, true },
87 { '5', SPECIAL_RUNLEVEL5_TARGET, true },
88 { '6', SPECIAL_REBOOT_TARGET, false },
95 for (i = 0; i < ELEMENTSOF(table); i++)
96 if (table[i].runlevel == runlevel) {
97 *isolate = table[i].isolate;
98 if (runlevel == '6' && kexec_loaded())
99 return SPECIAL_KEXEC_TARGET;
100 return table[i].special;
106 static void change_runlevel(Server *s, int runlevel) {
108 DBusMessage *m = NULL, *reply = NULL;
111 bool isolate = false;
115 dbus_error_init(&error);
117 if (!(target = translate_runlevel(runlevel, &isolate))) {
118 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
127 log_debug("Running request %s/start/%s", target, mode);
129 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
130 log_error("Could not allocate message.");
134 if (!dbus_message_append_args(m,
135 DBUS_TYPE_STRING, &target,
136 DBUS_TYPE_STRING, &mode,
137 DBUS_TYPE_INVALID)) {
138 log_error("Could not attach target and flag information to message.");
142 if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
143 log_error("Failed to start unit: %s", bus_error_message(&error));
149 dbus_message_unref(m);
152 dbus_message_unref(reply);
154 dbus_error_free(&error);
157 static void request_process(Server *s, const struct init_request *req) {
161 if (req->magic != INIT_MAGIC) {
162 log_error("Got initctl request with invalid magic. Ignoring.");
168 case INIT_CMD_RUNLVL:
169 if (!isprint(req->runlevel))
170 log_error("Got invalid runlevel. Ignoring.");
172 switch (req->runlevel) {
174 /* we are async anyway, so just use kill for reexec/reload */
177 if (kill(1, SIGTERM) < 0)
178 log_error("kill() failed: %m");
180 /* The bus connection will be
181 * terminated if PID 1 is reexecuted,
182 * hence let's just exit here, and
183 * rely on that we'll be restarted on
184 * the next request */
190 if (kill(1, SIGHUP) < 0)
191 log_error("kill() failed: %m");
195 change_runlevel(s, req->runlevel);
199 case INIT_CMD_POWERFAIL:
200 case INIT_CMD_POWERFAILNOW:
201 case INIT_CMD_POWEROK:
202 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
205 case INIT_CMD_CHANGECONS:
206 log_warning("Received console change initctl request. This is not implemented in systemd.");
209 case INIT_CMD_SETENV:
210 case INIT_CMD_UNSETENV:
211 log_warning("Received environment initctl request. This is not implemented in systemd.");
215 log_warning("Received unknown initctl request. Ignoring.");
220 static int fifo_process(Fifo *f) {
226 if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
231 log_warning("Failed to read from fifo: %s", strerror(errno));
236 assert(f->bytes_read <= sizeof(f->buffer));
238 if (f->bytes_read == sizeof(f->buffer)) {
239 request_process(f->server, &f->buffer);
246 static void fifo_free(Fifo *f) {
250 assert(f->server->n_fifos > 0);
251 f->server->n_fifos--;
252 LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
257 epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
259 close_nointr_nofail(f->fd);
265 static void server_done(Server *s) {
271 if (s->epoll_fd >= 0)
272 close_nointr_nofail(s->epoll_fd);
275 dbus_connection_flush(s->bus);
276 dbus_connection_close(s->bus);
277 dbus_connection_unref(s->bus);
281 static int server_init(Server *s, unsigned n_sockets) {
287 assert(n_sockets > 0);
289 dbus_error_init(&error);
293 if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
295 log_error("Failed to create epoll object: %s", strerror(errno));
299 for (i = 0; i < n_sockets; i++) {
300 struct epoll_event ev;
304 fd = SD_LISTEN_FDS_START+i;
306 if ((r = sd_is_fifo(fd, NULL)) < 0) {
307 log_error("Failed to determine file descriptor type: %s", strerror(-r));
312 log_error("Wrong file descriptor type.");
317 if (!(f = new0(Fifo, 1))) {
319 log_error("Failed to create fifo object: %s", strerror(errno));
328 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
331 log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
336 LIST_PREPEND(Fifo, fifo, s->fifos, f);
341 if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
342 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
351 dbus_error_free(&error);
355 static int process_event(Server *s, struct epoll_event *ev) {
361 if (!(ev->events & EPOLLIN)) {
362 log_info("Got invalid event from epoll. (3)");
366 f = (Fifo*) ev->data.ptr;
368 if ((r = fifo_process(f)) < 0) {
369 log_info("Got error on fifo: %s", strerror(-r));
377 int main(int argc, char *argv[]) {
379 int r = EXIT_FAILURE, n;
381 if (getppid() != 1) {
382 log_error("This program should be invoked by init only.");
387 log_error("This program does not take arguments.");
391 log_set_target(LOG_TARGET_AUTO);
392 log_parse_environment();
397 if ((n = sd_listen_fds(true)) < 0) {
398 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
402 if (n <= 0 || n > SERVER_FD_MAX) {
403 log_error("No or too many file descriptors passed.");
407 if (server_init(&server, (unsigned) n) < 0)
410 log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
414 "STATUS=Processing requests...");
416 while (!server.quit) {
417 struct epoll_event event;
420 if ((k = epoll_wait(server.epoll_fd,
422 TIMEOUT_MSEC)) < 0) {
427 log_error("epoll_wait() failed: %s", strerror(errno));
434 if (process_event(&server, &event) < 0)
440 log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
444 "STATUS=Shutting down...");
446 server_done(&server);