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
s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
294
if (s->epoll_fd < 0) {
296
log_error("Failed to create epoll object: %s", strerror(errno));
300
for (i = 0; i < n_sockets; i++) {
301
struct epoll_event ev;
305
fd = SD_LISTEN_FDS_START+i;
307
r = sd_is_fifo(fd, NULL);
309
log_error("Failed to determine file descriptor type: %s",
315
log_error("Wrong file descriptor type.");
323
log_error("Failed to create fifo object: %s",
333
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
336
log_error("Failed to add fifo fd to epoll object: %s",
342
LIST_PREPEND(Fifo, fifo, s->fifos, f);
347
if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
348
log_error("Failed to get D-Bus connection: %s",
349
bus_error_message(&error));
359
dbus_error_free(&error);
363
static int process_event(Server *s, struct epoll_event *ev) {
369
if (!(ev->events & EPOLLIN)) {
370
log_info("Got invalid event from epoll. (3)");
374
f = (Fifo*) ev->data.ptr;
376
if ((r = fifo_process(f)) < 0) {
377
log_info("Got error on fifo: %s", strerror(-r));
385
int main(int argc, char *argv[]) {
387
int r = EXIT_FAILURE, n;
389
if (getppid() != 1) {
390
log_error("This program should be invoked by init only.");
395
log_error("This program does not take arguments.");
399
log_set_target(LOG_TARGET_AUTO);
400
log_parse_environment();
405
if ((n = sd_listen_fds(true)) < 0) {
406
log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
410
if (n <= 0 || n > SERVER_FD_MAX) {
411
log_error("No or too many file descriptors passed.");
415
if (server_init(&server, (unsigned) n) < 0)
418
log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
422
"STATUS=Processing requests...");
424
while (!server.quit) {
425
struct epoll_event event;
428
if ((k = epoll_wait(server.epoll_fd,
430
TIMEOUT_MSEC)) < 0) {
435
log_error("epoll_wait() failed: %s", strerror(errno));
442
if (process_event(&server, &event) < 0)
448
log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
452
"STATUS=Shutting down...");
454
server_done(&server);