libink: a brokerless D-Bus implementation for Finit

Finit had no way to answer the question every service manager gets
asked: what is running, and change it.  D-Bus is how the rest of
userspace asks, but linking libdbus, sd-bus or GIO into PID 1 buys a
dependency, an allocator and a main loop we do not control.

So libink: the wire format, an object tree, and a bus of Finit's own
at /run/finit/bus, gated like INIT_SOCKET.  It speaks the standard
org.freedesktop.DBus, .Peer, .Introspectable interfaces, and Finit's
own Manager1, Service1 and Cond1 on top.  Methods that change
something are marked privileged and answered only for a caller the
kernel vouched for, via SO_PEERCRED.

Server and client both, since initctl is the first thing that needs
to talk to it, and its Start/Stop/Restart/Reload now go over the bus
rather than the legacy socket.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2026-08-13 09:28:14 +02:00
1 parent ae9a24f44f
commit 0a269f3298
39 files changed
+5530 -49

No files matched your search

+130 -6
View File
@@ -268,19 +268,105 @@ static int do_startstop(int cmd, char *arg)
return do_svc(cmd, arg);
}
static int do_start (char *arg) { return do_startstop(INIT_CMD_START_SVC, arg); }
static int do_stop (char *arg) { return do_startstop(INIT_CMD_STOP_SVC, arg); }
#ifdef HAVE_DBUS
#include "link.h"
/* 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). */
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);
if (!c)
return -1;
/* "s" methods take the service identity (arg may be NULL ->
* empty string); void methods pass no body. */
if (arg_sig && !strcmp(arg_sig, "s"))
rc = link_client_call_v(c, "/org/finit/manager",
"org.finit.Manager1", method,
"s", arg ? arg : "");
else if (!arg_sig || !*arg_sig)
rc = link_client_call_v(c, "/org/finit/manager",
"org.finit.Manager1", method, NULL);
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");
}
link_client_close(c);
if (rc == LINK_CALL_OK)
return 0;
return -1; /* LINK_CALL_FAIL or anything else: fall back */
}
#endif /* HAVE_DBUS */
static int do_start (char *arg)
{
#ifdef HAVE_DBUS
int rc = try_dbus_manager("Start", "s", arg);
if (rc >= 0) return rc;
#endif
return do_startstop(INIT_CMD_START_SVC, arg);
}
static int do_stop (char *arg)
{
#ifdef HAVE_DBUS
int rc = try_dbus_manager("Stop", "s", arg);
if (rc >= 0) return rc;
#endif
return do_startstop(INIT_CMD_STOP_SVC, arg);
}
static int do_reload (char *arg)
{
if (!arg || !arg[0])
if (!arg || !arg[0]) {
#ifdef HAVE_DBUS
int rc = try_dbus_manager("Reload", "", NULL);
if (rc >= 0) return rc;
#endif
return do_svc(INIT_CMD_RELOAD, NULL);
}
return do_startstop(INIT_CMD_RELOAD_SVC, arg);
}
static int do_restart(char *arg)
{
#ifdef HAVE_DBUS
int rc = try_dbus_manager("Restart", "s", arg);
if (rc == 0) return 0;
if (rc == 1) ERRX(noerr ? 0 : 7, "failed restarting %s", arg);
#endif
if (do_startstop(INIT_CMD_RESTART_SVC, arg))
ERRX(noerr ? 0 : 7, "failed restarting %s", arg);
@@ -643,9 +729,47 @@ static int do_cmd(int cmd)
return 0;
}
int do_reboot (char *arg) { return do_cmd(INIT_CMD_REBOOT); }
int do_halt (char *arg) { return do_cmd(INIT_CMD_HALT); }
int do_poweroff(char *arg) { return do_cmd(INIT_CMD_POWEROFF); }
#ifdef HAVE_DBUS
static int do_reboot_dbus(const char *method)
{
int rc = try_dbus_manager(method, "", NULL);
if (rc == 0) {
sleep(5); /* match legacy: wait for finit to shut down */
return 0;
}
return rc; /* 1 = error, -1 = fall back */
}
#endif
int do_reboot (char *arg)
{
#ifdef HAVE_DBUS
int rc = do_reboot_dbus("Reboot");
if (rc >= 0) return rc;
#endif
return do_cmd(INIT_CMD_REBOOT);
}
int do_halt (char *arg)
{
#ifdef HAVE_DBUS
int rc = do_reboot_dbus("Halt");
if (rc >= 0) return rc;
#endif
return do_cmd(INIT_CMD_HALT);
}
int do_poweroff(char *arg)
{
#ifdef HAVE_DBUS
int rc = do_reboot_dbus("Poweroff");
if (rc >= 0) return rc;
#endif
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); }
/**