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>
29 #include "sd-daemon.h"
31 #include "alloc-util.h"
32 #include "bus-error.h"
36 #include "formats-util.h"
43 #define SERVER_FD_MAX 16
44 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
46 typedef struct Fifo Fifo
;
48 typedef struct Server
{
51 LIST_HEAD(Fifo
, fifos
);
64 struct init_request buffer
;
67 LIST_FIELDS(Fifo
, fifo
);
70 static const char *translate_runlevel(int runlevel
, bool *isolate
) {
76 { '0', SPECIAL_POWEROFF_TARGET
, false },
77 { '1', SPECIAL_RESCUE_TARGET
, true },
78 { 's', SPECIAL_RESCUE_TARGET
, true },
79 { 'S', SPECIAL_RESCUE_TARGET
, true },
80 { '2', SPECIAL_MULTI_USER_TARGET
, true },
81 { '3', SPECIAL_MULTI_USER_TARGET
, true },
82 { '4', SPECIAL_MULTI_USER_TARGET
, true },
83 { '5', SPECIAL_GRAPHICAL_TARGET
, true },
84 { '6', SPECIAL_REBOOT_TARGET
, false },
91 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
92 if (table
[i
].runlevel
== runlevel
) {
93 *isolate
= table
[i
].isolate
;
94 if (runlevel
== '6' && kexec_loaded())
95 return SPECIAL_KEXEC_TARGET
;
96 return table
[i
].special
;
102 static void change_runlevel(Server
*s
, int runlevel
) {
104 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
106 bool isolate
= false;
111 target
= translate_runlevel(runlevel
, &isolate
);
113 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel
);
120 mode
= "replace-irreversibly";
122 log_debug("Running request %s/start/%s", target
, mode
);
124 r
= sd_bus_call_method(
126 "org.freedesktop.systemd1",
127 "/org/freedesktop/systemd1",
128 "org.freedesktop.systemd1.Manager",
134 log_error("Failed to change runlevel: %s", bus_error_message(&error
, -r
));
139 static void request_process(Server
*s
, const struct init_request
*req
) {
143 if (req
->magic
!= INIT_MAGIC
) {
144 log_error("Got initctl request with invalid magic. Ignoring.");
150 case INIT_CMD_RUNLVL
:
151 if (!isprint(req
->runlevel
))
152 log_error("Got invalid runlevel. Ignoring.");
154 switch (req
->runlevel
) {
156 /* we are async anyway, so just use kill for reexec/reload */
159 if (kill(1, SIGTERM
) < 0)
160 log_error_errno(errno
, "kill() failed: %m");
162 /* The bus connection will be
163 * terminated if PID 1 is reexecuted,
164 * hence let's just exit here, and
165 * rely on that we'll be restarted on
166 * the next request */
172 if (kill(1, SIGHUP
) < 0)
173 log_error_errno(errno
, "kill() failed: %m");
177 change_runlevel(s
, req
->runlevel
);
181 case INIT_CMD_POWERFAIL
:
182 case INIT_CMD_POWERFAILNOW
:
183 case INIT_CMD_POWEROK
:
184 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
187 case INIT_CMD_CHANGECONS
:
188 log_warning("Received console change initctl request. This is not implemented in systemd.");
191 case INIT_CMD_SETENV
:
192 case INIT_CMD_UNSETENV
:
193 log_warning("Received environment initctl request. This is not implemented in systemd.");
197 log_warning("Received unknown initctl request. Ignoring.");
202 static int fifo_process(Fifo
*f
) {
209 ((uint8_t*) &f
->buffer
) + f
->bytes_read
,
210 sizeof(f
->buffer
) - f
->bytes_read
);
215 return log_warning_errno(errno
, "Failed to read from fifo: %m");
219 assert(f
->bytes_read
<= sizeof(f
->buffer
));
221 if (f
->bytes_read
== sizeof(f
->buffer
)) {
222 request_process(f
->server
, &f
->buffer
);
229 static void fifo_free(Fifo
*f
) {
233 assert(f
->server
->n_fifos
> 0);
234 f
->server
->n_fifos
--;
235 LIST_REMOVE(fifo
, f
->server
->fifos
, f
);
240 epoll_ctl(f
->server
->epoll_fd
, EPOLL_CTL_DEL
, f
->fd
, NULL
);
248 static void server_done(Server
*s
) {
254 safe_close(s
->epoll_fd
);
257 sd_bus_flush(s
->bus
);
258 sd_bus_unref(s
->bus
);
262 static int server_init(Server
*s
, unsigned n_sockets
) {
267 assert(n_sockets
> 0);
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");
278 for (i
= 0; i
< n_sockets
; i
++) {
279 struct epoll_event ev
;
283 fd
= SD_LISTEN_FDS_START
+i
;
285 r
= sd_is_fifo(fd
, NULL
);
287 log_error_errno(r
, "Failed to determine file descriptor type: %m");
292 log_error("Wrong file descriptor type.");
300 log_error_errno(errno
, "Failed to create fifo object: %m");
309 if (epoll_ctl(s
->epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
) < 0) {
312 log_error_errno(errno
, "Failed to add fifo fd to epoll object: %m");
317 LIST_PREPEND(fifo
, s
->fifos
, f
);
322 r
= bus_connect_system_systemd(&s
->bus
);
324 log_error_errno(r
, "Failed to get D-Bus connection: %m");
337 static int process_event(Server
*s
, struct epoll_event
*ev
) {
343 if (!(ev
->events
& EPOLLIN
)) {
344 log_info("Got invalid event from epoll. (3)");
348 f
= (Fifo
*) ev
->data
.ptr
;
351 log_info_errno(r
, "Got error on fifo: %m");
359 int main(int argc
, char *argv
[]) {
361 int r
= EXIT_FAILURE
, n
;
363 if (getppid() != 1) {
364 log_error("This program should be invoked by init only.");
369 log_error("This program does not take arguments.");
373 log_set_target(LOG_TARGET_AUTO
);
374 log_parse_environment();
379 n
= sd_listen_fds(true);
381 log_error_errno(r
, "Failed to read listening file descriptors from environment: %m");
385 if (n
<= 0 || n
> SERVER_FD_MAX
) {
386 log_error("No or too many file descriptors passed.");
390 if (server_init(&server
, (unsigned) n
) < 0)
393 log_debug("systemd-initctl running as pid "PID_FMT
, getpid());
397 "STATUS=Processing requests...");
399 while (!server
.quit
) {
400 struct epoll_event event
;
403 k
= epoll_wait(server
.epoll_fd
, &event
, 1, TIMEOUT_MSEC
);
407 log_error_errno(errno
, "epoll_wait() failed: %m");
414 if (process_event(&server
, &event
) < 0)
420 log_debug("systemd-initctl stopped as pid "PID_FMT
, getpid());
425 "STATUS=Shutting down...");
427 server_done(&server
);