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 "sd-daemon.h"
45 #include "bus-error.h"
48 #define SERVER_FD_MAX 16
49 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
51 typedef struct Fifo Fifo
;
53 typedef struct Server
{
56 LIST_HEAD(Fifo
, fifos
);
69 struct init_request buffer
;
72 LIST_FIELDS(Fifo
, fifo
);
75 static const char *translate_runlevel(int runlevel
, bool *isolate
) {
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 },
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
;
107 static void change_runlevel(Server
*s
, int runlevel
) {
109 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
111 bool isolate
= false;
116 target
= translate_runlevel(runlevel
, &isolate
);
118 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel
);
125 mode
= "replace-irreversibly";
127 log_debug("Running request %s/start/%s", target
, mode
);
129 r
= sd_bus_call_method(
131 "org.freedesktop.systemd1",
132 "/org/freedesktop/systemd1",
133 "org.freedesktop.systemd1.Manager",
139 log_error("Failed to change runlevel: %s", bus_error_message(&error
, -r
));
144 static void request_process(Server
*s
, const struct init_request
*req
) {
148 if (req
->magic
!= INIT_MAGIC
) {
149 log_error("Got initctl request with invalid magic. Ignoring.");
155 case INIT_CMD_RUNLVL
:
156 if (!isprint(req
->runlevel
))
157 log_error("Got invalid runlevel. Ignoring.");
159 switch (req
->runlevel
) {
161 /* we are async anyway, so just use kill for reexec/reload */
164 if (kill(1, SIGTERM
) < 0)
165 log_error_errno(errno
, "kill() failed: %m");
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 */
177 if (kill(1, SIGHUP
) < 0)
178 log_error_errno(errno
, "kill() failed: %m");
182 change_runlevel(s
, req
->runlevel
);
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!");
192 case INIT_CMD_CHANGECONS
:
193 log_warning("Received console change initctl request. This is not implemented in systemd.");
196 case INIT_CMD_SETENV
:
197 case INIT_CMD_UNSETENV
:
198 log_warning("Received environment initctl request. This is not implemented in systemd.");
202 log_warning("Received unknown initctl request. Ignoring.");
207 static int fifo_process(Fifo
*f
) {
214 ((uint8_t*) &f
->buffer
) + f
->bytes_read
,
215 sizeof(f
->buffer
) - f
->bytes_read
);
220 log_warning_errno(errno
, "Failed to read from fifo: %m");
225 assert(f
->bytes_read
<= sizeof(f
->buffer
));
227 if (f
->bytes_read
== sizeof(f
->buffer
)) {
228 request_process(f
->server
, &f
->buffer
);
235 static void fifo_free(Fifo
*f
) {
239 assert(f
->server
->n_fifos
> 0);
240 f
->server
->n_fifos
--;
241 LIST_REMOVE(fifo
, f
->server
->fifos
, f
);
246 epoll_ctl(f
->server
->epoll_fd
, EPOLL_CTL_DEL
, f
->fd
, NULL
);
254 static void server_done(Server
*s
) {
260 safe_close(s
->epoll_fd
);
263 sd_bus_flush(s
->bus
);
264 sd_bus_unref(s
->bus
);
268 static int server_init(Server
*s
, unsigned n_sockets
) {
273 assert(n_sockets
> 0);
277 s
->epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
278 if (s
->epoll_fd
< 0) {
280 log_error_errno(errno
, "Failed to create epoll object: %m");
284 for (i
= 0; i
< n_sockets
; i
++) {
285 struct epoll_event ev
;
289 fd
= SD_LISTEN_FDS_START
+i
;
291 r
= sd_is_fifo(fd
, NULL
);
293 log_error_errno(r
, "Failed to determine file descriptor type: %m");
298 log_error("Wrong file descriptor type.");
306 log_error_errno(errno
, "Failed to create fifo object: %m");
315 if (epoll_ctl(s
->epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
) < 0) {
318 log_error_errno(errno
, "Failed to add fifo fd to epoll object: %m");
323 LIST_PREPEND(fifo
, s
->fifos
, f
);
328 r
= bus_open_system_systemd(&s
->bus
);
330 log_error_errno(r
, "Failed to get D-Bus connection: %m");
343 static int process_event(Server
*s
, struct epoll_event
*ev
) {
349 if (!(ev
->events
& EPOLLIN
)) {
350 log_info("Got invalid event from epoll. (3)");
354 f
= (Fifo
*) ev
->data
.ptr
;
357 log_info_errno(r
, "Got error on fifo: %m");
365 int main(int argc
, char *argv
[]) {
367 int r
= EXIT_FAILURE
, n
;
369 if (getppid() != 1) {
370 log_error("This program should be invoked by init only.");
375 log_error("This program does not take arguments.");
379 log_set_target(LOG_TARGET_AUTO
);
380 log_parse_environment();
385 n
= sd_listen_fds(true);
387 log_error_errno(r
, "Failed to read listening file descriptors from environment: %m");
391 if (n
<= 0 || n
> SERVER_FD_MAX
) {
392 log_error("No or too many file descriptors passed.");
396 if (server_init(&server
, (unsigned) n
) < 0)
399 log_debug("systemd-initctl running as pid "PID_FMT
, getpid());
403 "STATUS=Processing requests...");
405 while (!server
.quit
) {
406 struct epoll_event event
;
409 if ((k
= epoll_wait(server
.epoll_fd
,
411 TIMEOUT_MSEC
)) < 0) {
416 log_error_errno(errno
, "epoll_wait() failed: %m");
423 if (process_event(&server
, &event
) < 0)
429 log_debug("systemd-initctl stopped as pid "PID_FMT
, getpid());
434 "STATUS=Shutting down...");
436 server_done(&server
);