mirror of
https://github.com/troglobit/finit.git
synced 2026-11-03 13:28:27 +00:00
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:
1 parent
ae9a24f44f
commit
0a269f3298
39 files changed
+5530
-49
No files matched your search
+162
@@ -0,0 +1,162 @@
|
||||
/* libink — listening socket, accept, peer-credential capture
|
||||
*
|
||||
* Copyright (c) 2026 Joachim Wiberg <troglobit@gmail.com>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static void close_save_errno(int fd)
|
||||
{
|
||||
int saved = errno;
|
||||
close(fd);
|
||||
errno = saved;
|
||||
}
|
||||
|
||||
int link_server_new(link_server_t **out, const char *path)
|
||||
{
|
||||
struct sockaddr_un sun = { .sun_family = AF_UNIX };
|
||||
link_server_t *srv;
|
||||
size_t plen;
|
||||
int fd;
|
||||
|
||||
if (!out || !path || !*path) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
plen = strlen(path);
|
||||
if (plen >= sizeof(sun.sun_path) || plen >= LINK_PATH_MAX) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
srv = calloc(1, sizeof(*srv));
|
||||
if (!srv)
|
||||
return -1;
|
||||
TAILQ_INIT(&srv->objects);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
goto err_free;
|
||||
|
||||
memcpy(sun.sun_path, path, plen + 1);
|
||||
|
||||
(void)unlink(path);
|
||||
|
||||
/* fchmod() on a Unix-domain socket fd is a silent no-op on Linux:
|
||||
* the file mode is fixed at bind() time as (0777 & ~umask). Set
|
||||
* umask around the bind() so the socket appears with mode 0666
|
||||
* atomically, no race window. World-accessible by design;
|
||||
* per-method authorization happens later in dispatch via
|
||||
* SO_PEERCRED. */
|
||||
{
|
||||
mode_t oldmask = umask(0111);
|
||||
int rc = bind(fd, (struct sockaddr *)&sun, sizeof(sun));
|
||||
int saved = errno;
|
||||
|
||||
umask(oldmask);
|
||||
if (rc < 0) {
|
||||
errno = saved;
|
||||
goto err_close;
|
||||
}
|
||||
}
|
||||
|
||||
if (listen(fd, 16) < 0)
|
||||
goto err_unlink;
|
||||
|
||||
srv->fd = fd;
|
||||
memcpy(srv->path, path, plen + 1);
|
||||
*out = srv;
|
||||
return 0;
|
||||
|
||||
err_unlink:
|
||||
(void)unlink(path);
|
||||
err_close:
|
||||
close_save_errno(fd);
|
||||
err_free:
|
||||
free(srv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void link_server_free(link_server_t *srv)
|
||||
{
|
||||
struct link_object *o;
|
||||
|
||||
if (!srv)
|
||||
return;
|
||||
|
||||
o = TAILQ_FIRST(&srv->objects);
|
||||
while (o) {
|
||||
struct link_object *next_o = TAILQ_NEXT(o, link);
|
||||
struct link_vtable_entry *e = TAILQ_FIRST(&o->vtables);
|
||||
|
||||
while (e) {
|
||||
struct link_vtable_entry *next_e = TAILQ_NEXT(e, link);
|
||||
|
||||
free(e);
|
||||
e = next_e;
|
||||
}
|
||||
free(o);
|
||||
o = next_o;
|
||||
}
|
||||
|
||||
if (srv->fd >= 0)
|
||||
close(srv->fd);
|
||||
if (srv->path[0])
|
||||
(void)unlink(srv->path);
|
||||
free(srv);
|
||||
}
|
||||
|
||||
int link_server_get_fd(const link_server_t *srv)
|
||||
{
|
||||
if (!srv)
|
||||
return -1;
|
||||
|
||||
return srv->fd;
|
||||
}
|
||||
|
||||
int link_server_accept(link_server_t *srv, link_connection_t **out)
|
||||
{
|
||||
struct ucred cred = { 0 };
|
||||
socklen_t credlen = sizeof(cred);
|
||||
link_connection_t *conn;
|
||||
int cfd;
|
||||
|
||||
if (!srv || !out) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfd = accept4(srv->fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
if (cfd < 0)
|
||||
return -1;
|
||||
|
||||
conn = calloc(1, sizeof(*conn));
|
||||
if (!conn) {
|
||||
close_save_errno(cfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->fd = cfd;
|
||||
conn->auth = LINK_AUTH_NUL;
|
||||
conn->server = srv;
|
||||
|
||||
if (getsockopt(cfd, SOL_SOCKET, SO_PEERCRED, &cred, &credlen) == 0)
|
||||
conn->peer_uid = cred.uid;
|
||||
else
|
||||
conn->peer_uid = (uid_t)-1;
|
||||
|
||||
__auth_generate_guid(conn->guid);
|
||||
|
||||
*out = conn;
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user