mirror of
https://github.com/troglobit/finit.git
synced 2026-10-10 16:52:39 +07:00
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:
+198
-42
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user