1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/epoll.h>
28 #include "sd-daemon.h"
30 #include "alloc-util.h"
31 #include "bus-error.h"
35 #include "format-util.h"
42 #define SERVER_FD_MAX 16
43 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
45 typedef struct Fifo Fifo
;
47 typedef struct Server
{
50 LIST_HEAD(Fifo
, fifos
);
63 struct init_request buffer
;
66 LIST_FIELDS(Fifo
, fifo
);
69 static const char *translate_runlevel(int runlevel
, bool *isolate
) {
75 { '0', SPECIAL_POWEROFF_TARGET
, false },
76 { '1', SPECIAL_RESCUE_TARGET
, true },
77 { 's', SPECIAL_RESCUE_TARGET
, true },
78 { 'S', SPECIAL_RESCUE_TARGET
, true },
79 { '2', SPECIAL_MULTI_USER_TARGET
, true },
80 { '3', SPECIAL_MULTI_USER_TARGET
, true },
81 { '4', SPECIAL_MULTI_USER_TARGET
, true },
82 { '5', SPECIAL_GRAPHICAL_TARGET
, true },
83 { '6', SPECIAL_REBOOT_TARGET
, false },
90 for (i
= 0; i
< ELEMENTSOF(table
); i
++)
91 if (table
[i
].runlevel
== runlevel
) {
92 *isolate
= table
[i
].isolate
;
93 if (runlevel
== '6' && kexec_loaded())
94 return SPECIAL_KEXEC_TARGET
;
95 return table
[i
].special
;
101 static void change_runlevel(Server
*s
, int runlevel
) {
103 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
105 bool isolate
= false;
110 target
= translate_runlevel(runlevel
, &isolate
);
112 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel
);
119 mode
= "replace-irreversibly";
121 log_debug("Running request %s/start/%s", target
, mode
);
123 r
= sd_bus_call_method(
125 "org.freedesktop.systemd1",
126 "/org/freedesktop/systemd1",
127 "org.freedesktop.systemd1.Manager",
133 log_error("Failed to change runlevel: %s", bus_error_message(&error
, -r
));
138 static void request_process(Server
*s
, const struct init_request
*req
) {
142 if (req
->magic
!= INIT_MAGIC
) {
143 log_error("Got initctl request with invalid magic. Ignoring.");
149 case INIT_CMD_RUNLVL
:
150 if (!isprint(req
->runlevel
))
151 log_error("Got invalid runlevel. Ignoring.");
153 switch (req
->runlevel
) {
155 /* we are async anyway, so just use kill for reexec/reload */
158 if (kill(1, SIGTERM
) < 0)
159 log_error_errno(errno
, "kill() failed: %m");
161 /* The bus connection will be
162 * terminated if PID 1 is reexecuted,
163 * hence let's just exit here, and
164 * rely on that we'll be restarted on
165 * the next request */
171 if (kill(1, SIGHUP
) < 0)
172 log_error_errno(errno
, "kill() failed: %m");
176 change_runlevel(s
, req
->runlevel
);
180 case INIT_CMD_POWERFAIL
:
181 case INIT_CMD_POWERFAILNOW
:
182 case INIT_CMD_POWEROK
:
183 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
186 case INIT_CMD_CHANGECONS
:
187 log_warning("Received console change initctl request. This is not implemented in systemd.");
190 case INIT_CMD_SETENV
:
191 case INIT_CMD_UNSETENV
:
192 log_warning("Received environment initctl request. This is not implemented in systemd.");
196 log_warning("Received unknown initctl request. Ignoring.");
201 static int fifo_process(Fifo
*f
) {
208 ((uint8_t*) &f
->buffer
) + f
->bytes_read
,
209 sizeof(f
->buffer
) - f
->bytes_read
);
214 return log_warning_errno(errno
, "Failed to read from fifo: %m");
218 assert(f
->bytes_read
<= sizeof(f
->buffer
));
220 if (f
->bytes_read
== sizeof(f
->buffer
)) {
221 request_process(f
->server
, &f
->buffer
);
228 static void fifo_free(Fifo
*f
) {
232 assert(f
->server
->n_fifos
> 0);
233 f
->server
->n_fifos
--;
234 LIST_REMOVE(fifo
, f
->server
->fifos
, f
);
239 epoll_ctl(f
->server
->epoll_fd
, EPOLL_CTL_DEL
, f
->fd
, NULL
);
247 static void server_done(Server
*s
) {
253 safe_close(s
->epoll_fd
);
256 sd_bus_flush(s
->bus
);
257 sd_bus_unref(s
->bus
);
261 static int server_init(Server
*s
, unsigned n_sockets
) {
266 assert(n_sockets
> 0);
270 s
->epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
271 if (s
->epoll_fd
< 0) {
272 r
= log_error_errno(errno
,
273 "Failed to create epoll object: %m");
277 for (i
= 0; i
< n_sockets
; i
++) {
278 struct epoll_event ev
;
282 fd
= SD_LISTEN_FDS_START
+i
;
284 r
= sd_is_fifo(fd
, NULL
);
286 log_error_errno(r
, "Failed to determine file descriptor type: %m");
291 log_error("Wrong file descriptor type.");
299 log_error_errno(errno
, "Failed to create fifo object: %m");
308 if (epoll_ctl(s
->epoll_fd
, EPOLL_CTL_ADD
, fd
, &ev
) < 0) {
311 log_error_errno(errno
, "Failed to add fifo fd to epoll object: %m");
316 LIST_PREPEND(fifo
, s
->fifos
, f
);
321 r
= bus_connect_system_systemd(&s
->bus
);
323 log_error_errno(r
, "Failed to get D-Bus connection: %m");
336 static int process_event(Server
*s
, struct epoll_event
*ev
) {
342 if (!(ev
->events
& EPOLLIN
)) {
343 log_info("Got invalid event from epoll. (3)");
347 f
= (Fifo
*) ev
->data
.ptr
;
350 log_info_errno(r
, "Got error on fifo: %m");
358 int main(int argc
, char *argv
[]) {
360 int r
= EXIT_FAILURE
, n
;
362 if (getppid() != 1) {
363 log_error("This program should be invoked by init only.");
368 log_error("This program does not take arguments.");
372 log_set_target(LOG_TARGET_AUTO
);
373 log_parse_environment();
378 n
= sd_listen_fds(true);
380 log_error_errno(r
, "Failed to read listening file descriptors from environment: %m");
384 if (n
<= 0 || n
> SERVER_FD_MAX
) {
385 log_error("No or too many file descriptors passed.");
389 if (server_init(&server
, (unsigned) n
) < 0)
392 log_debug("systemd-initctl running as pid "PID_FMT
, getpid_cached());
396 "STATUS=Processing requests...");
398 while (!server
.quit
) {
399 struct epoll_event event
;
402 k
= epoll_wait(server
.epoll_fd
, &event
, 1, TIMEOUT_MSEC
);
406 log_error_errno(errno
, "epoll_wait() failed: %m");
413 if (process_event(&server
, &event
) < 0)
419 log_debug("systemd-initctl stopped as pid "PID_FMT
, getpid_cached());
424 "STATUS=Shutting down...");
426 server_done(&server
);