2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <ctype.h>
|
2010-04-04 22:53:42 +02:00
|
|
|
#include <errno.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <stdio.h>
|
2010-04-04 22:53:42 +02:00
|
|
|
#include <sys/epoll.h>
|
2019-03-27 11:32:41 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <unistd.h>
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2013-08-12 01:50:03 +02:00
|
|
|
#include "sd-bus.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "sd-daemon.h"
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-08-12 01:50:03 +02:00
|
|
|
#include "bus-error.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "bus-util.h"
|
2020-07-02 14:50:29 +02:00
|
|
|
#include "daemon-util.h"
|
2011-04-14 02:32:42 +02:00
|
|
|
#include "def.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "initreq.h"
|
|
|
|
#include "list.h"
|
|
|
|
#include "log.h"
|
2020-07-02 14:50:29 +02:00
|
|
|
#include "main-func.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
#include "memory-util.h"
|
2018-01-11 00:39:12 +01:00
|
|
|
#include "process-util.h"
|
2019-03-13 12:02:21 +01:00
|
|
|
#include "special.h"
|
2010-04-04 22:53:42 +02:00
|
|
|
|
|
|
|
#define SERVER_FD_MAX 16
|
2011-04-14 02:32:42 +02:00
|
|
|
#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
|
2010-04-04 22:53:42 +02:00
|
|
|
|
|
|
|
typedef struct Fifo Fifo;
|
|
|
|
|
|
|
|
typedef struct Server {
|
|
|
|
int epoll_fd;
|
|
|
|
|
|
|
|
LIST_HEAD(Fifo, fifos);
|
|
|
|
unsigned n_fifos;
|
|
|
|
|
2013-08-12 01:50:03 +02:00
|
|
|
sd_bus *bus;
|
2011-11-01 18:18:17 +01:00
|
|
|
|
|
|
|
bool quit;
|
2010-04-04 22:53:42 +02:00
|
|
|
} Server;
|
|
|
|
|
|
|
|
struct Fifo {
|
|
|
|
Server *server;
|
|
|
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
struct init_request buffer;
|
|
|
|
size_t bytes_read;
|
|
|
|
|
|
|
|
LIST_FIELDS(Fifo, fifo);
|
|
|
|
};
|
|
|
|
|
2010-09-14 01:56:14 +02:00
|
|
|
static const char *translate_runlevel(int runlevel, bool *isolate) {
|
2010-04-23 05:22:20 +02:00
|
|
|
static const struct {
|
|
|
|
const int runlevel;
|
|
|
|
const char *special;
|
2010-09-14 01:56:14 +02:00
|
|
|
bool isolate;
|
2010-04-23 05:22:20 +02:00
|
|
|
} table[] = {
|
2015-02-18 20:17:49 +01:00
|
|
|
{ '0', SPECIAL_POWEROFF_TARGET, false },
|
|
|
|
{ '1', SPECIAL_RESCUE_TARGET, true },
|
|
|
|
{ 's', SPECIAL_RESCUE_TARGET, true },
|
|
|
|
{ 'S', SPECIAL_RESCUE_TARGET, true },
|
|
|
|
{ '2', SPECIAL_MULTI_USER_TARGET, true },
|
|
|
|
{ '3', SPECIAL_MULTI_USER_TARGET, true },
|
|
|
|
{ '4', SPECIAL_MULTI_USER_TARGET, true },
|
|
|
|
{ '5', SPECIAL_GRAPHICAL_TARGET, true },
|
|
|
|
{ '6', SPECIAL_REBOOT_TARGET, false },
|
2010-04-23 05:22:20 +02:00
|
|
|
};
|
|
|
|
|
2010-09-14 01:56:14 +02:00
|
|
|
assert(isolate);
|
|
|
|
|
2020-07-02 14:25:06 +02:00
|
|
|
for (size_t i = 0; i < ELEMENTSOF(table); i++)
|
2010-09-14 01:56:14 +02:00
|
|
|
if (table[i].runlevel == runlevel) {
|
|
|
|
*isolate = table[i].isolate;
|
2011-08-22 14:58:50 +02:00
|
|
|
if (runlevel == '6' && kexec_loaded())
|
|
|
|
return SPECIAL_KEXEC_TARGET;
|
2010-04-23 05:22:20 +02:00
|
|
|
return table[i].special;
|
2010-09-14 01:56:14 +02:00
|
|
|
}
|
2010-04-23 05:22:20 +02:00
|
|
|
|
|
|
|
return NULL;
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
2018-08-07 03:06:49 +02:00
|
|
|
static int change_runlevel(Server *s, int runlevel) {
|
2010-04-04 22:53:42 +02:00
|
|
|
const char *target;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2010-09-14 01:56:14 +02:00
|
|
|
const char *mode;
|
|
|
|
bool isolate = false;
|
2013-08-12 01:50:03 +02:00
|
|
|
int r;
|
2010-04-04 22:53:42 +02:00
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
2013-08-12 01:50:03 +02:00
|
|
|
target = translate_runlevel(runlevel, &isolate);
|
2013-12-03 22:27:45 +01:00
|
|
|
if (!target) {
|
2010-04-04 22:53:42 +02:00
|
|
|
log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
|
2018-08-07 03:06:49 +02:00
|
|
|
return 0;
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
2010-09-14 01:56:14 +02:00
|
|
|
if (isolate)
|
|
|
|
mode = "isolate";
|
|
|
|
else
|
2013-07-24 04:01:39 +02:00
|
|
|
mode = "replace-irreversibly";
|
2010-09-14 01:56:14 +02:00
|
|
|
|
2010-09-14 02:22:55 +02:00
|
|
|
log_debug("Running request %s/start/%s", target, mode);
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2013-08-12 01:50:03 +02:00
|
|
|
r = sd_bus_call_method(
|
|
|
|
s->bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"StartUnit",
|
|
|
|
&error,
|
|
|
|
NULL,
|
|
|
|
"ss", target, mode);
|
2018-08-07 03:06:49 +02:00
|
|
|
if (r < 0)
|
2020-09-14 18:16:54 +02:00
|
|
|
return log_error_errno(r, "Failed to change runlevel: %s", bus_error_message(&error, r));
|
2018-08-07 03:06:49 +02:00
|
|
|
|
|
|
|
return 0;
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void request_process(Server *s, const struct init_request *req) {
|
|
|
|
assert(s);
|
|
|
|
assert(req);
|
|
|
|
|
|
|
|
if (req->magic != INIT_MAGIC) {
|
|
|
|
log_error("Got initctl request with invalid magic. Ignoring.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (req->cmd) {
|
|
|
|
|
|
|
|
case INIT_CMD_RUNLVL:
|
|
|
|
if (!isprint(req->runlevel))
|
|
|
|
log_error("Got invalid runlevel. Ignoring.");
|
|
|
|
else
|
2011-07-30 18:23:10 +02:00
|
|
|
switch (req->runlevel) {
|
|
|
|
|
|
|
|
/* we are async anyway, so just use kill for reexec/reload */
|
|
|
|
case 'u':
|
|
|
|
case 'U':
|
|
|
|
if (kill(1, SIGTERM) < 0)
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "kill() failed: %m");
|
2011-11-01 18:18:17 +01:00
|
|
|
|
|
|
|
/* The bus connection will be
|
|
|
|
* terminated if PID 1 is reexecuted,
|
|
|
|
* hence let's just exit here, and
|
|
|
|
* rely on that we'll be restarted on
|
|
|
|
* the next request */
|
|
|
|
s->quit = true;
|
2011-07-30 18:23:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'q':
|
|
|
|
case 'Q':
|
|
|
|
if (kill(1, SIGHUP) < 0)
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "kill() failed: %m");
|
2011-07-30 18:23:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-08-07 03:06:49 +02:00
|
|
|
(void) change_runlevel(s, req->runlevel);
|
2011-07-30 18:23:10 +02:00
|
|
|
}
|
2010-04-04 22:53:42 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
case INIT_CMD_POWERFAIL:
|
|
|
|
case INIT_CMD_POWERFAILNOW:
|
|
|
|
case INIT_CMD_POWEROK:
|
|
|
|
log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
|
|
|
|
return;
|
|
|
|
|
|
|
|
case INIT_CMD_CHANGECONS:
|
|
|
|
log_warning("Received console change initctl request. This is not implemented in systemd.");
|
|
|
|
return;
|
|
|
|
|
|
|
|
case INIT_CMD_SETENV:
|
|
|
|
case INIT_CMD_UNSETENV:
|
|
|
|
log_warning("Received environment initctl request. This is not implemented in systemd.");
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
log_warning("Received unknown initctl request. Ignoring.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fifo_process(Fifo *f) {
|
|
|
|
ssize_t l;
|
|
|
|
|
|
|
|
assert(f);
|
|
|
|
|
|
|
|
errno = EIO;
|
2013-09-10 14:20:24 +02:00
|
|
|
l = read(f->fd,
|
|
|
|
((uint8_t*) &f->buffer) + f->bytes_read,
|
|
|
|
sizeof(f->buffer) - f->bytes_read);
|
|
|
|
if (l <= 0) {
|
2010-04-04 22:53:42 +02:00
|
|
|
if (errno == EAGAIN)
|
|
|
|
return 0;
|
|
|
|
|
2015-11-05 13:44:20 +01:00
|
|
|
return log_warning_errno(errno, "Failed to read from fifo: %m");
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
f->bytes_read += l;
|
|
|
|
assert(f->bytes_read <= sizeof(f->buffer));
|
|
|
|
|
|
|
|
if (f->bytes_read == sizeof(f->buffer)) {
|
|
|
|
request_process(f->server, &f->buffer);
|
|
|
|
f->bytes_read = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fifo_free(Fifo *f) {
|
|
|
|
assert(f);
|
|
|
|
|
|
|
|
if (f->server) {
|
|
|
|
assert(f->server->n_fifos > 0);
|
|
|
|
f->server->n_fifos--;
|
2013-10-14 06:10:14 +02:00
|
|
|
LIST_REMOVE(fifo, f->server->fifos, f);
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (f->fd >= 0) {
|
|
|
|
if (f->server)
|
2020-01-10 09:29:11 +01:00
|
|
|
(void) epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2014-03-18 19:22:43 +01:00
|
|
|
safe_close(f->fd);
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
free(f);
|
|
|
|
}
|
2020-07-02 14:50:29 +02:00
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(Fifo*, fifo_free);
|
2010-04-04 22:53:42 +02:00
|
|
|
|
|
|
|
static void server_done(Server *s) {
|
|
|
|
assert(s);
|
|
|
|
|
|
|
|
while (s->fifos)
|
|
|
|
fifo_free(s->fifos);
|
|
|
|
|
2019-01-17 15:54:37 +01:00
|
|
|
s->epoll_fd = safe_close(s->epoll_fd);
|
|
|
|
s->bus = sd_bus_flush_close_unref(s->bus);
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int server_init(Server *s, unsigned n_sockets) {
|
|
|
|
int r;
|
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
/* This function will leave s partially initialized on failure. Caller needs to clean up. */
|
|
|
|
|
2010-04-04 22:53:42 +02:00
|
|
|
assert(s);
|
|
|
|
assert(n_sockets > 0);
|
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
|
|
|
if (s->epoll_fd < 0)
|
|
|
|
return log_error_errno(errno, "Failed to create epoll object: %m");
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2020-07-02 14:25:06 +02:00
|
|
|
for (unsigned i = 0; i < n_sockets; i++) {
|
2020-07-02 14:50:29 +02:00
|
|
|
_cleanup_(fifo_freep) Fifo *f = NULL;
|
|
|
|
int fd = SD_LISTEN_FDS_START + i;
|
2010-05-20 01:13:43 +02:00
|
|
|
|
2013-03-22 00:06:55 +01:00
|
|
|
r = sd_is_fifo(fd, NULL);
|
2020-07-02 14:50:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to determine file descriptor type: %m");
|
|
|
|
if (!r)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong file descriptor type.");
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2013-03-22 00:06:55 +01:00
|
|
|
f = new0(Fifo, 1);
|
2020-07-02 14:50:29 +02:00
|
|
|
if (!f)
|
|
|
|
return log_oom();
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2020-04-17 15:30:48 +02:00
|
|
|
struct epoll_event ev = {
|
|
|
|
.events = EPOLLIN,
|
|
|
|
.data.ptr = f,
|
|
|
|
};
|
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
|
|
|
|
return log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2010-07-01 00:26:44 +02:00
|
|
|
f->fd = fd;
|
2010-04-04 22:53:42 +02:00
|
|
|
f->server = s;
|
2020-07-02 14:50:29 +02:00
|
|
|
LIST_PREPEND(fifo, s->fifos, TAKE_PTR(f));
|
2016-02-23 05:32:04 +01:00
|
|
|
s->n_fifos++;
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
|
|
|
|
2015-09-24 13:30:10 +02:00
|
|
|
r = bus_connect_system_systemd(&s->bus);
|
2020-07-02 14:50:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get D-Bus connection: %m");
|
2010-04-04 22:53:42 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_event(Server *s, struct epoll_event *ev) {
|
|
|
|
int r;
|
|
|
|
Fifo *f;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (!(ev->events & EPOLLIN))
|
|
|
|
return log_info_errno(SYNTHETIC_ERRNO(EIO),
|
|
|
|
"Got invalid event from epoll. (3)");
|
2010-04-04 22:53:42 +02:00
|
|
|
|
|
|
|
f = (Fifo*) ev->data.ptr;
|
2013-09-10 14:20:24 +02:00
|
|
|
r = fifo_process(f);
|
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_info_errno(r, "Got error on fifo: %m");
|
2010-04-04 22:53:42 +02:00
|
|
|
fifo_free(f);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
static int run(int argc, char *argv[]) {
|
|
|
|
_cleanup_(server_done) Server server = { .epoll_fd = -1 };
|
|
|
|
_cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
|
|
|
|
int r, n;
|
2010-05-16 01:45:52 +02:00
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
if (argc > 1)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"This program does not take arguments.");
|
2010-06-19 16:56:57 +02:00
|
|
|
|
2018-11-20 11:18:22 +01:00
|
|
|
log_setup_service();
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2011-08-01 20:52:18 +02:00
|
|
|
umask(0022);
|
|
|
|
|
2014-06-04 16:59:13 +02:00
|
|
|
n = sd_listen_fds(true);
|
2020-07-02 14:50:29 +02:00
|
|
|
if (n < 0)
|
|
|
|
return log_error_errno(errno,
|
|
|
|
"Failed to read listening file descriptors from environment: %m");
|
2010-05-16 01:45:52 +02:00
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
if (n <= 0 || n > SERVER_FD_MAX)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"No or too many file descriptors passed.");
|
2010-04-04 22:53:42 +02:00
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
r = server_init(&server, (unsigned) n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2010-08-11 04:38:55 +02:00
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
|
2010-06-16 05:10:31 +02:00
|
|
|
|
2011-11-01 18:18:17 +01:00
|
|
|
while (!server.quit) {
|
2010-04-04 22:53:42 +02:00
|
|
|
struct epoll_event event;
|
|
|
|
int k;
|
|
|
|
|
2015-09-08 19:14:10 +02:00
|
|
|
k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC);
|
|
|
|
if (k < 0) {
|
2010-04-04 22:53:42 +02:00
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
2020-07-02 14:50:29 +02:00
|
|
|
return log_error_errno(errno, "epoll_wait() failed: %m");
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
2020-07-02 14:50:29 +02:00
|
|
|
if (k == 0)
|
2010-04-04 22:53:42 +02:00
|
|
|
break;
|
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
r = process_event(&server, &event);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
2010-08-11 04:38:55 +02:00
|
|
|
|
2020-07-02 14:50:29 +02:00
|
|
|
return 0;
|
2010-04-04 22:53:42 +02:00
|
|
|
}
|
2020-07-02 14:50:29 +02:00
|
|
|
|
|
|
|
DEFINE_MAIN_FUNCTION(run);
|