libink/finit: properties, and org.finit on the system bus

Runlevel and version are state, not actions, so they belong behind
org.freedesktop.DBus.Properties rather than another method each.

Finit also claims org.finit on the system bus when it finds one, so
ordinary D-Bus clients can reach it without knowing about
/run/finit/bus.  Opportunistic on purpose: no dbus-daemon is a normal
state for the systems Finit runs on, not an error to report.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-08-13 09:28:14 +02:00
parent dd1390a6c2
commit ebc0ef62e6
22 changed files with 1474 additions and 154 deletions
+338 -68
View File
@@ -31,9 +31,10 @@
#ifdef HAVE_DBUS
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <unistd.h>
#include <uev/uev.h>
#include <ftw.h>
@@ -50,6 +51,7 @@
#include "sig.h"
#include "sm.h"
#include "svc.h"
#include "util.h"
#define DBUS_MAX_PEERS 64
@@ -88,6 +90,39 @@ static void peer_cb(uev_t *w, void *arg, int events)
peer_drop(p);
}
/* Wrap an authenticated connection in a struct peer, insert into the
* peer list, and register an event-loop watcher. Enforces
* DBUS_MAX_PEERS. Closes the connection and returns NULL on failure.
* Used by both the accept path and the system-bus attach path. */
static struct peer *peer_register(uev_ctx_t *ctx, link_connection_t *conn)
{
struct peer *p;
if (peer_count >= DBUS_MAX_PEERS) {
logit(LOG_WARNING, "D-Bus peer cap reached (%zu), dropping",
peer_count);
link_connection_close(conn);
return NULL;
}
p = calloc(1, sizeof(*p));
if (!p) {
link_connection_close(conn);
return NULL;
}
p->conn = conn;
TAILQ_INSERT_TAIL(&peers, p, link);
peer_count++;
if (uev_io_init(ctx, &p->watcher, peer_cb, p,
link_connection_get_fd(conn), UEV_READ)) {
peer_drop(p);
return NULL;
}
return p;
}
static void accept_cb(uev_t *w, void *arg, int events)
{
(void)arg;
@@ -99,7 +134,6 @@ static void accept_cb(uev_t *w, void *arg, int events)
for (;;) {
link_connection_t *conn = NULL;
struct peer *p;
if (link_server_accept(server, &conn) < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK)
@@ -107,29 +141,8 @@ static void accept_cb(uev_t *w, void *arg, int events)
break;
}
if (peer_count >= DBUS_MAX_PEERS) {
logit(LOG_WARNING, "D-Bus peer cap reached (%zu), dropping",
peer_count);
link_connection_close(conn);
continue;
}
p = calloc(1, sizeof(*p));
if (!p) {
link_connection_close(conn);
err(1, "Out of memory accepting D-Bus client");
break;
}
p->conn = conn;
TAILQ_INSERT_TAIL(&peers, p, link);
peer_count++;
if (uev_io_init(w->ctx, &p->watcher, peer_cb, p,
link_connection_get_fd(conn), UEV_READ)) {
err(1, "Failed registering D-Bus peer watcher");
peer_drop(p);
}
if (!peer_register(w->ctx, conn))
continue; /* logged inside */
}
}
@@ -240,6 +253,7 @@ static int dbus_apply_restart(svc_t *svc, void *user_data)
struct dispatch_ctx {
int (*action)(svc_t *, void *);
void *udata;
int matched;
};
@@ -248,7 +262,7 @@ static int dispatch_found(svc_t *svc, void *udata)
struct dispatch_ctx *ctx = udata;
ctx->matched++;
return ctx->action(svc, NULL);
return ctx->action(svc, ctx->udata);
}
static int dispatch_missing(char *job, char *id, void *udata)
@@ -257,14 +271,15 @@ static int dispatch_missing(char *job, char *id, void *udata)
return 0; /* don't penalise the return; we'll check ->matched */
}
/* Apply `action` to every service matched by `ident`. Returns 0 if
* at least one service matched and the action succeeded on all;
* -1 if no service matched the identity (caller sends NoSuchService). */
/* Apply `action(svc, udata)` to every service matched by `ident`.
* Returns 0 if at least one service matched and the action succeeded
* on all; -1 if no service matched the identity (caller sends
* NoSuchService). */
static int dispatch_action(const char *ident,
int (*action)(svc_t *, void *))
int (*action)(svc_t *, void *), void *udata)
{
char buf[MAX_IDENT_LEN];
struct dispatch_ctx ctx = { .action = action };
struct dispatch_ctx ctx = { .action = action, .udata = udata };
int rc;
if (!ident || !*ident || strlen(ident) >= sizeof(buf))
@@ -281,14 +296,21 @@ static int manager_take_string_method(link_call_t *call,
int (*action)(svc_t *, void *))
{
const char *ident;
int rc;
if (link_call_read_string(call, &ident) < 0)
return link_call_reply_error(call,
"org.freedesktop.DBus.Error.InvalidArgs",
"expected (s)");
if (dispatch_action(ident, action) != 0)
rc = dispatch_action(ident, action, NULL);
if (rc < 0)
return link_call_reply_error(call,
"org.finit.Error.NoSuchService", ident);
if (rc)
return link_call_reply_error(call,
"org.finit.Error.Failed",
"failed on matched service(s)");
(void)link_call_reply(call); /* empty reply */
return 0;
@@ -351,6 +373,117 @@ static int manager_reboot (link_call_t *c, void *u) { (void)u; return dbus_shut
static int manager_poweroff(link_call_t *c, void *u) { (void)u; return dbus_shutdown(c, SHUT_OFF, 0); }
static int manager_halt (link_call_t *c, void *u) { (void)u; return dbus_shutdown(c, SHUT_HALT, 0); }
static int manager_set_debug(link_call_t *call, void *u)
{
(void)u;
log_debug();
(void)link_call_reply(call);
return 0;
}
static int signal_one(svc_t *svc, void *udata)
{
int signo = *(int *)udata;
/* Silently skip stopped services -- a multi-match ident
* (e.g. "sshd:*") should not fail the whole call just because
* one of the matches happens to be in a halted state. */
if (!svc_is_running(svc))
return 0;
return !!kill(svc->pid, signo);
}
static int manager_signal(link_call_t *call, void *u)
{
const char *ident;
uint32_t signo;
int sig, rc;
(void)u;
if (link_call_read_string(call, &ident) < 0 ||
link_call_read_u32 (call, &signo) < 0)
return link_call_reply_error(call,
"org.freedesktop.DBus.Error.InvalidArgs",
"expected (s, u)");
/* Match the upper bound `initctl signal` allows (1..31). RT
* signals are a future story; keep both sides in lockstep so
* users see the same range regardless of transport. */
if (signo == 0 || signo > 31)
return link_call_reply_error(call,
"org.freedesktop.DBus.Error.InvalidArgs",
"signal out of range (1..31)");
sig = (int)signo;
rc = dispatch_action(ident, signal_one, &sig);
if (rc < 0)
return link_call_reply_error(call,
"org.finit.Error.NoSuchService", ident);
if (rc)
return link_call_reply_error(call,
"org.finit.Error.Failed",
"failed signalling matched service(s)");
(void)link_call_reply(call);
return 0;
}
static int manager_suspend(link_call_t *call, void *u)
{
(void)u;
sync();
if (suspend() < 0) {
const char *msg = (errno == EINVAL)
? "Kernel does not support suspend to RAM"
: strerror(errno);
return link_call_reply_error(call,
"org.finit.Error.Failed", msg);
}
(void)link_call_reply(call);
return 0;
}
/* ---------- Manager1 properties ----------
*
* Read-only string properties exposed via the standard
* org.freedesktop.DBus.Properties interface. Getters write a
* variant containing a single string. */
/* Two distinct getters because the property table is static const --
* we can't bind &runlevel/&prevlevel through userdata. */
static int prop_runlevel(link_writer_t *w, void *u)
{
char buf[8];
(void)u;
snprintf(buf, sizeof(buf), "%d", runlevel);
link_w_variant_string(w, buf);
return 0;
}
static int prop_prevrunlevel(link_writer_t *w, void *u)
{
char buf[8];
(void)u;
snprintf(buf, sizeof(buf), "%d", prevlevel);
link_w_variant_string(w, buf);
return 0;
}
static int prop_version(link_writer_t *w, void *u)
{
(void)u;
link_w_variant_string(w, PACKAGE_VERSION);
return 0;
}
static const link_property_t manager_properties[] = {
{ .name = "Runlevel", .sig = "s", .getter = prop_runlevel },
{ .name = "PrevRunlevel", .sig = "s", .getter = prop_prevrunlevel },
{ .name = "Version", .sig = "s", .getter = prop_version },
{ NULL, NULL, NULL }
};
static const link_method_t manager_methods[] = {
{ .name = "ListServices", .in_sig = "", .out_sig = "as",
.handler = manager_list_services },
@@ -372,12 +505,19 @@ static const link_method_t manager_methods[] = {
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_poweroff },
{ .name = "Halt", .in_sig = "", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_halt },
{ .name = "Suspend", .in_sig = "", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_suspend },
{ .name = "SetDebug", .in_sig = "", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_set_debug },
{ .name = "Signal", .in_sig = "su", .out_sig = "",
.flags = LINK_METHOD_PRIVILEGED, .handler = manager_signal },
{ NULL, NULL, NULL, 0, NULL }
};
static const link_vtable_t manager_vtable = {
.interface = "org.finit.Manager1",
.methods = manager_methods,
.interface = "org.finit.Manager1",
.methods = manager_methods,
.properties = manager_properties,
};
/* ---------- org.finit.Service1 (one object per service) ----------
@@ -485,6 +625,38 @@ void dbus_unregister_service(svc_t *svc)
(void)link_server_remove_object(server, path);
}
/* ---------- signal fan-out helper ----------
*
* Fan out a pre-marshalled signal body to every connected peer,
* letting each connection apply its AddMatch filter. Short-circuits
* when no peers are connected so dbus_notify_* callers don't have
* to inspect that state themselves. */
static void dbus_emit_signal(const char *path,
const char *interface,
const char *member,
const char *signature,
const uint8_t *body, size_t body_len)
{
struct peer *p, *tmp;
if (!server || TAILQ_EMPTY(&peers))
return;
TAILQ_FOREACH_SAFE(p, &peers, link, tmp) {
if (link_connection_emit_signal(p->conn,
path, interface, member,
signature, body, body_len) < 0) {
/* nothing hit the wire, and same for every peer */
if (errno == EMSGSIZE || errno == EINVAL)
break;
logit(LOG_WARNING, "D-Bus peer fd %d write failed: "
"%s, dropping",
link_connection_get_fd(p->conn),
strerror(errno));
peer_drop(p);
}
}
}
/* ---------- signal emission: ServiceStateChanged ---------- */
/*
@@ -518,36 +690,51 @@ static const char *state_name(svc_state_t s)
void dbus_notify_service_state(svc_t *svc, int old_state, int new_state)
{
uint8_t body[256];
link_writer_t w;
struct peer *p;
char ident[MAX_IDENT_LEN];
ssize_t blen;
svc_state_t o = (svc_state_t)old_state;
svc_state_t n = (svc_state_t)new_state;
uint8_t body[256];
link_writer_t w;
char ident[MAX_IDENT_LEN];
ssize_t blen;
if (!server || !svc)
if (!svc)
return;
if (TAILQ_EMPTY(&peers))
return; /* nobody could possibly be listening */
svc_ident(svc, ident, sizeof(ident));
link_writer_init(&w, body, sizeof(body));
link_w_string(&w, ident);
link_w_string(&w, state_name(o));
link_w_string(&w, state_name(n));
link_w_string(&w, state_name((svc_state_t)old_state));
link_w_string(&w, state_name((svc_state_t)new_state));
blen = link_writer_finish(&w);
if (blen < 0)
return;
TAILQ_FOREACH(p, &peers, link)
(void)link_connection_emit_signal(p->conn,
"/org/finit/manager",
"org.finit.Manager1",
"ServiceStateChanged",
"sss",
body, (size_t)blen);
dbus_emit_signal("/org/finit/manager", "org.finit.Manager1",
"ServiceStateChanged", "sss", body, (size_t)blen);
}
/* ---------- signal emission: RunlevelChanged ----------
*
* Fired by sm.c right after the runlevel global flips. Body is
* (old, new) as one-digit strings, matching the format that
* Manager1.Runlevel (the property) returns. */
void dbus_notify_runlevel_change(int old_level, int new_level)
{
uint8_t body[64];
link_writer_t w;
char old_s[8], new_s[8];
ssize_t blen;
snprintf(old_s, sizeof(old_s), "%d", old_level);
snprintf(new_s, sizeof(new_s), "%d", new_level);
link_writer_init(&w, body, sizeof(body));
link_w_string(&w, old_s);
link_w_string(&w, new_s);
blen = link_writer_finish(&w);
if (blen < 0)
return;
dbus_emit_signal("/org/finit/manager", "org.finit.Manager1",
"RunlevelChanged", "ss", body, (size_t)blen);
}
/* ---------- org.finit.Cond1 ---------- */
@@ -766,14 +953,11 @@ static const link_vtable_t cond_vtable = {
void dbus_notify_condition_change(const char *name, const char *state)
{
uint8_t body[256];
link_writer_t w;
struct peer *p;
ssize_t blen;
uint8_t body[256];
link_writer_t w;
ssize_t blen;
if (!server || !name || !state)
return;
if (TAILQ_EMPTY(&peers))
if (!name || !state)
return;
link_writer_init(&w, body, sizeof(body));
@@ -783,13 +967,97 @@ void dbus_notify_condition_change(const char *name, const char *state)
if (blen < 0)
return;
TAILQ_FOREACH(p, &peers, link)
(void)link_connection_emit_signal(p->conn,
COND_PATH_OBJECT,
COND_INTERFACE,
"ConditionChanged",
"ss",
body, (size_t)blen);
dbus_emit_signal(COND_PATH_OBJECT, COND_INTERFACE,
"ConditionChanged", "ss", body, (size_t)blen);
}
/* ---------- system-bus attach (opportunistic) ----------
*
* If /var/run/dbus/system_bus_socket is reachable, libink connects to
* the system bus as a regular client, claims org.finit as a well-known
* name, then promotes the authenticated fd into a server-attached
* peer so the same vtables serve incoming method calls and outgoing
* signal fan-out reaches the system bus.
*
* peer_uid is set to (uid_t)-1 so LINK_METHOD_PRIVILEGED methods
* reject by default -- per-request sender uid lookup via
* GetConnectionUnixUser is a follow-up. Read-only methods
* (ListServices, Properties.Get, Introspect, ...) work as expected.
*
* A bounded SO_SNDTIMEO/SO_RCVTIMEO budget is applied via
* link_client_open_timeout so a hung dbus-daemon can't stall boot;
* once the connection is attached and flipped to non-blocking, those
* timeouts are silently inert. */
#define SYSTEM_BUS_PATH "/var/run/dbus/system_bus_socket"
#define FINIT_BUS_NAME "org.finit"
/* Budget for the synchronous AUTH + Hello + RequestName round-trips.
* If the system bus is alive but the daemon is wedged we'd rather
* give up after a couple of seconds than stall the rest of dbus_init
* (and through it, boot). */
#define SYSTEM_BUS_TIMEOUT_MS 2000
/* DBUS_NAME_FLAG_DO_NOT_QUEUE: fail fast if the name is taken
* (something else owns org.finit -- shouldn't happen and we'd
* rather log than silently sit in the queue). */
#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x04
static int sysbus_request_name(link_client_t *c)
{
uint32_t result = 0;
int rc;
rc = link_client_call_v(c, "/org/freedesktop/DBus",
"org.freedesktop.DBus", "RequestName",
"su", FINIT_BUS_NAME,
(uint32_t)DBUS_NAME_FLAG_DO_NOT_QUEUE);
if (rc != LINK_CALL_OK)
return -1;
if (link_reply_get_u32(link_client_reply(c), &result) < 0)
return -1;
/* 1 = primary owner; 2/3/4 mean we didn't get the name */
return (result == 1) ? 0 : -1;
}
static void try_attach_system_bus(uev_ctx_t *ctx)
{
link_client_t *c;
link_connection_t *conn;
int rc;
c = link_client_open_timeout(SYSTEM_BUS_PATH, SYSTEM_BUS_TIMEOUT_MS);
if (!c) {
dbg("System bus unavailable at %s; skipping registration",
SYSTEM_BUS_PATH);
return;
}
rc = link_client_call_v(c, "/org/freedesktop/DBus",
"org.freedesktop.DBus", "Hello", NULL);
if (rc != LINK_CALL_OK) {
dbg("System-bus Hello failed (rc=%d); skipping", rc);
link_client_close(c);
return;
}
if (sysbus_request_name(c) < 0) {
logit(LOG_WARNING, "Failed to claim %s on system bus", FINIT_BUS_NAME);
link_client_close(c);
return;
}
/* link_server_attach owns the fd from this point on whether it
* succeeds or fails, so the steal-then-attach pair has no leak
* window. */
conn = link_server_attach(server, link_client_steal_fd(c), (uid_t)-1);
if (!conn)
return;
if (!peer_register(ctx, conn)) {
logit(LOG_WARNING, "Failed registering system-bus peer");
return;
}
logit(LOG_NOTICE, "Registered %s on system bus", FINIT_BUS_NAME);
}
/* ---------- init / exit ---------- */
@@ -839,6 +1107,8 @@ int dbus_init(uev_ctx_t *ctx)
dbus_register_service(svc);
}
try_attach_system_bus(ctx);
return 0;
}
+198 -42
View File
@@ -41,6 +41,10 @@
#include "service.h"
#include "cgutil.h"
#include "utmp-api.h"
#ifdef HAVE_DBUS
#include "link.h"
#include "path.h"
#endif
/* Used by both do_cond_act and (with HAVE_DBUS) cond_dbus_call. */
typedef enum { COND_CLR, COND_SET, COND_GET } condop_t;
@@ -148,6 +152,11 @@ static int runlevel_get(int *prevlevel)
return rc;
}
#ifdef HAVE_DBUS
static int try_dbus_manager(const char *method, const char *arg_sig,
const char *arg);
#endif
static int toggle_debug(char *arg)
{
struct init_request rq = {
@@ -155,6 +164,13 @@ static int toggle_debug(char *arg)
.cmd = INIT_CMD_DEBUG,
};
(void)arg;
#ifdef HAVE_DBUS
{
int rc = try_dbus_manager("SetDebug", "", NULL);
if (rc >= 0) return rc;
}
#endif
return client_send(&rq, sizeof(rq));
}
@@ -196,6 +212,68 @@ static int show_log(char *arg)
return do_log(svc, "");
}
#ifdef HAVE_DBUS
/* Fetch all org.finit.Manager1 string properties in one Properties.GetAll
* round-trip, then pick out a subset. `wanted` is a NULL-terminated array
* of property names; `out` parallel-receives the values (each entry left
* untouched if its property wasn't returned). Returns 0 on transport
* success (even if some properties weren't present), -1 on transport or
* parse failure. */
static int dbus_get_manager_props(const char *const *wanted, char **out, size_t out_sz)
{
link_client_t *c;
const link_reply_t *r;
link_reader_t reader;
size_t end;
int rc;
c = link_client_open(FINIT_BUS_SOCKET);
if (!c)
return -1;
rc = link_client_call_v(c, "/org/finit/manager",
"org.freedesktop.DBus.Properties", "GetAll",
"s", "org.finit.Manager1");
if (rc != LINK_CALL_OK) {
link_client_close(c);
return -1;
}
r = link_client_reply(c);
if (!r || !r->body) {
link_client_close(c);
return -1;
}
link_reader_init(&reader, r->body, r->body_len);
if (link_r_array_begin(&reader, &end) < 0) {
link_client_close(c);
return -1;
}
while (link_r_pos(&reader) < end) {
const char *key = NULL;
const char *val = NULL;
size_t i;
if (link_r_align(&reader, 8) < 0) break;
if (link_r_string(&reader, &key) < 0) break;
if (link_r_variant_string(&reader, &val) < 0) break;
if (!key || !val) break;
for (i = 0; wanted[i]; i++) {
if (!strcmp(key, wanted[i])) {
strlcpy(out[i], val, out_sz);
break;
}
}
}
link_client_close(c);
return 0;
}
#endif
static int do_runlevel(char *arg)
{
struct init_request rq = {
@@ -208,6 +286,23 @@ static int do_runlevel(char *arg)
int currlevel;
char prev, curr;
#ifdef HAVE_DBUS
char curr_buf[16] = { 0 }, prev_buf[16] = { 0 };
const char *const wanted[] = { "Runlevel", "PrevRunlevel", NULL };
char *out[] = { curr_buf, prev_buf };
if (dbus_get_manager_props(wanted, out, sizeof(curr_buf)) == 0 &&
curr_buf[0] && prev_buf[0]) {
int cl = atoi(curr_buf);
int pl = atoi(prev_buf);
curr = (cl == INIT_LEVEL) ? 'S' : (char)(cl + '0');
prev = (pl > 0 && pl <= 9) ? (char)(pl + '0') : 'N';
printf("%c %c\n", prev, curr);
return 0;
}
#endif
currlevel = runlevel_get(&prevlevel);
switch (currlevel) {
case 255:
@@ -272,22 +367,46 @@ static int do_startstop(int cmd, char *arg)
}
#ifdef HAVE_DBUS
#include "link.h"
/* Map a LINK_CALL_ERROR reply on `c` to the appropriate ERRX exit:
* org.finit.Error.NoSuchService -> exit 69 (legacy "no such svc")
* org.freedesktop.DBus.Error.AccessDenied -> exit 1 (permission denied)
* anything else -> exit 1 (method: err)
* `c` is closed before exit either way. Use exact-match on the
* fully-qualified error name; a substring match would misfire on a
* future name that contained one of these as a prefix. */
static void map_dbus_err(link_client_t *c, const char *method, const char *ident)
{
const link_reply_t *r = link_client_reply(c);
char err[128];
/* The reply view points into c->rxbuf; copy the error name out
* before link_client_close() frees the client. Otherwise the
* strcmps below read freed memory. */
if (r && r->error_name)
strlcpy(err, r->error_name, sizeof(err));
else
err[0] = '\0';
link_client_close(c);
if (!strcmp(err, "org.finit.Error.NoSuchService"))
ERRX(noerr ? 0 : 69, "no such task or service(s): %s",
ident ? ident : "");
if (!strcmp(err, "org.freedesktop.DBus.Error.AccessDenied"))
ERRX(1, "permission denied: %s requires root", method);
ERRX(1, "%s: %s", method, *err ? err : "D-Bus error");
}
/* Try the D-Bus path for a Manager1 method. Returns:
* 0 succeeded via D-Bus
* 1 D-Bus replied with an error -- callers should error out
* -1 D-Bus not reachable -- callers should fall back to the
* legacy INIT_SOCKET transport
*
* On D-Bus error replies the function maps the org.* error name to
* the same exit code initctl historically printed for that case
* (e.g. NoSuchService -> 69 with the legacy message). */
* LINK_CALL_ERROR is handled internally via map_dbus_err (does not
* return). */
static int try_dbus_manager(const char *method, const char *arg_sig,
const char *arg)
{
link_client_t *c;
const char *err;
int rc;
c = link_client_open(FINIT_BUS_SOCKET);
@@ -306,29 +425,37 @@ static int try_dbus_manager(const char *method, const char *arg_sig,
else
rc = LINK_CALL_FAIL;
if (rc == LINK_CALL_ERROR) {
const link_reply_t *r = link_client_reply(c);
err = (r && r->error_name) ? r->error_name : "";
/* Exact match on the fully-qualified error name; substring
* matching would misfire on a future name that contains
* one of these as a substring. */
if (!strcmp(err, "org.finit.Error.NoSuchService")) {
link_client_close(c);
ERRX(noerr ? 0 : 69, "no such task or service(s): %s",
arg ? arg : "");
}
if (!strcmp(err, "org.freedesktop.DBus.Error.AccessDenied")) {
link_client_close(c);
ERRX(1, "permission denied: %s requires root", method);
}
link_client_close(c);
ERRX(1, "%s: %s", method, *err ? err : "D-Bus error");
}
if (rc == LINK_CALL_ERROR)
map_dbus_err(c, method, arg); /* exits */
link_client_close(c);
if (rc == LINK_CALL_OK)
return 0;
return -1; /* LINK_CALL_FAIL or anything else: fall back */
return (rc == LINK_CALL_OK) ? 0 : -1;
}
/* Call a void-arg method on Service1 at /org/finit/service/<encoded>.
* Same return convention as try_dbus_manager. */
static int try_dbus_service(const char *method, const char *ident)
{
char path[256];
const char *prefix = "/org/finit/service/";
size_t plen = strlen(prefix);
link_client_t *c;
int rc;
if (!ident || !*ident)
return -1;
memcpy(path, prefix, plen);
if (link_path_encode(ident, path + plen, sizeof(path) - plen) < 0)
return -1;
c = link_client_open(FINIT_BUS_SOCKET);
if (!c)
return -1;
rc = link_client_call_v(c, path, "org.finit.Service1", method, NULL);
if (rc == LINK_CALL_ERROR)
map_dbus_err(c, method, ident); /* exits */
link_client_close(c);
return (rc == LINK_CALL_OK) ? 0 : -1;
}
/* Try one Cond1.{Get,Set,Clear} call. On COND_GET success the helper
@@ -357,14 +484,9 @@ static int cond_dbus_call(link_client_t **bus, condop_t op,
"s", arg);
if (rc == LINK_CALL_OK) {
if (op == COND_GET) {
const link_reply_t *r = link_client_reply(*bus);
link_reader_t reader;
const char *state = NULL;
const char *state = NULL;
if (r && r->body) {
link_reader_init(&reader, r->body, r->body_len);
link_r_string(&reader, &state);
}
link_reply_get_string(link_client_reply(*bus), &state);
if (verbose && state)
puts(state);
*out_exit = (state && !strcmp(state, "on")) ? 0
@@ -493,6 +615,12 @@ static int do_reload (char *arg)
return do_svc(INIT_CMD_RELOAD, NULL);
}
#ifdef HAVE_DBUS
{
int rc = try_dbus_service("Reload", arg);
if (rc >= 0) return rc;
}
#endif
return do_startstop(INIT_CMD_RELOAD_SVC, arg);
}
@@ -530,10 +658,6 @@ int do_signal(int argc, char *argv[])
if (argc != 2)
ERRX(2, "invalid number of arguments to signal");
strlcpy(rq.data, argv[0], sizeof(rq.data));
if (client_send(&rq, sizeof(rq)))
ERRX(noerr ? 0 : 69, "no such task or service(s): %s", argv[0]);
signo = str2sig(argv[1]);
if (signo == -1) {
const char *errstr = NULL;
@@ -543,6 +667,29 @@ int do_signal(int argc, char *argv[])
ERRX(65, "%s signal: %s", errstr, argv[1]);
}
#ifdef HAVE_DBUS
{
link_client_t *c = link_client_open(FINIT_BUS_SOCKET);
if (c) {
int rc = link_client_call_v(c, "/org/finit/manager",
"org.finit.Manager1", "Signal",
"su", argv[0], (uint32_t)signo);
if (rc == LINK_CALL_ERROR)
map_dbus_err(c, "Signal", argv[0]); /* exits */
link_client_close(c);
if (rc == LINK_CALL_OK)
return 0;
/* LINK_CALL_FAIL: drop to legacy */
}
}
#endif
strlcpy(rq.data, argv[0], sizeof(rq.data));
if (client_send(&rq, sizeof(rq)))
ERRX(noerr ? 0 : 69, "no such task or service(s): %s", argv[0]);
/* Reuse runlevel for signal number. */
rq.magic = INIT_MAGIC;
rq.cmd = INIT_CMD_SIGNAL;
@@ -926,8 +1073,17 @@ int do_poweroff(char *arg)
return do_cmd(INIT_CMD_POWEROFF);
}
/* Suspend has no Manager1 equivalent yet; uses the legacy IPC. */
int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
int do_suspend(char *arg)
{
(void)arg;
#ifdef HAVE_DBUS
{
int rc = try_dbus_manager("Suspend", "", NULL);
if (rc >= 0) return rc;
}
#endif
return do_cmd(INIT_CMD_SUSPEND);
}
/**
* do_switch_root - Switch to a new root filesystem (initramfs only)
+1
View File
@@ -53,6 +53,7 @@ void dbus_register_service (svc_t *svc);
void dbus_unregister_service (svc_t *svc);
void dbus_notify_service_state (svc_t *svc, int old_state, int new_state);
void dbus_notify_condition_change(const char *name, const char *state);
void dbus_notify_runlevel_change(int old_level, int new_level);
#endif
void conf_flush_events(void);
+3
View File
@@ -388,6 +388,9 @@ restart:
prevlevel = runlevel;
runlevel = sm.newlevel;
sm.newlevel = -1;
#ifdef HAVE_DBUS
dbus_notify_runlevel_change(prevlevel, runlevel);
#endif
/* Restore terse mode and run hooks before shutdown */
if (runlevel == 0 || runlevel == 6) {