Systemd/src/shared/varlink.h
Lennart Poettering d41bd96f54 shared: add minimal varlink implementation
This adds a minimal Varlink (https://varlink.org/) implementation to our
tree. Given that we already have a JSON logic it's an easy thing to add.

Why bother?

We currently have major problems with IPC before dbus-daemon is up, and
in all components that dbus-daemon itself makes use of (such as various
NSS modules to resolve users as well as the journal which dbus-daemon
logs to). Because of that we so far ended up creating various (usually
crappy) work-arounds either coming up with secondary IPC systems or
sharing data statelessly in /run or similar. Let's clean this up, and
instead use a clean, well-defined, broker-less IPC for cases like that.

This is a minimal implementation of Varlink, i.e. the most basic logic
only. Stuff that's missing is left out on purpose: there's no
introspection/validation and there's no name service. It might make
sense to add that later, but for now let's only do the minimum buy-in we
can get away with. In particular as I'd assume that at least initially
we only use this IPC for our internal communication avoiding
introspection and the name service should be fine.

Specifically, I'd expect that we add IPC interfaces to the following
concepts with this scheme:

1. nss-resolve (so that hostname lookups with resolved work before
   resolved is up)
2. journald (so that IPC calls to journald don't have to go through
   dbus-daemon thus creating a cyclic dependency between journald and
   dbus-daemon)
3. nss-systemd (so that dynamic user lookups via PID 1 work sanely even
   inside of dbus-daemon, because otherwise we'd want to use dbus to run
   dbus which causes deadlocks)
4. networkd (to make sure one can talk to it in the initrd already,
   long before dbus is around)

And there might be other cases similar to this.
2019-05-09 14:14:20 -04:00

163 lines
6.8 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-event.h"
#include "json.h"
#include "time-util.h"
/* A minimal Varlink implementation. We only implement the minimal, obvious bits here though. No validation,
* no introspection, no name service, just the stuff actually needed.
*
* You might wonder why we aren't using libvarlink here? Varlink is a very simple protocol, which allows us
* to write our own implementation relatively easily. However, the main reasons are these:
*
* • We want to use our own JSON subsystem, with all the benefits that brings (i.e. accurate unsigned+signed
* 64bit integers, full fuzzing, logging during parsing and so on). If we'd want to use that with
* libvarlink we'd have to serialize and deserialize all the time from its own representation which is
* inefficient and nasty.
*
* • We want integration into sd-event, but also synchronous event-loop-less operation
*
* • We need proper per-UID accounting and access control, since we want to allow communication between
* unprivileged clients and privileged servers.
*
* • And of course, we don't want the name service and introspection stuff for now (though that might
* change).
*/
typedef struct Varlink Varlink;
typedef struct VarlinkServer VarlinkServer;
typedef enum VarlinkReplyFlags {
VARLINK_REPLY_ERROR = 1 << 0,
VARLINK_REPLY_CONTINUES = 1 << 1,
VARLINK_REPLY_LOCAL = 1 << 2,
} VarlinkReplyFlags;
typedef enum VarlinkMethodFlags {
VARLINK_METHOD_ONEWAY = 1 << 0,
VARLINK_METHOD_MORE = 2 << 1,
} VarlinkMethodFlags;
typedef enum VarlinkServerFlags {
VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */
VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */
VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */
_VARLINK_SERVER_FLAGS_ALL = (1 << 3) - 1,
} VarlinkServerFlags;
typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
typedef int (*VarlinkReply)(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata);
typedef int (*VarlinkConnect)(VarlinkServer *server, Varlink *link, void *userdata);
int varlink_connect_address(Varlink **ret, const char *address);
int varlink_connect_fd(Varlink **ret, int fd);
Varlink* varlink_ref(Varlink *link);
Varlink* varlink_unref(Varlink *v);
int varlink_get_fd(Varlink *v);
int varlink_get_events(Varlink *v);
int varlink_get_timeout(Varlink *v, usec_t *ret);
int varlink_attach_event(Varlink *v, sd_event *e, int64_t priority);
void varlink_detach_event(Varlink *v);
sd_event *varlink_get_event(Varlink *v);
int varlink_process(Varlink *v);
int varlink_wait(Varlink *v, usec_t timeout);
int varlink_flush(Varlink *v);
int varlink_close(Varlink *v);
Varlink* varlink_flush_close_unref(Varlink *v);
/* Enqueue method call, not expecting a reply */
int varlink_send(Varlink *v, const char *method, JsonVariant *parameters);
int varlink_sendb(Varlink *v, const char *method, ...);
/* Send method call and wait for reply */
int varlink_call(Varlink *v, const char *method, JsonVariant *parameters, JsonVariant **ret_parameters, const char **ret_error_id, VarlinkReplyFlags *ret_flags);
int varlink_callb(Varlink *v, const char *method, JsonVariant **ret_parameters, const char **ret_error_id, VarlinkReplyFlags *ret_flags, ...);
/* Enqueue method call, expect a reply, which is eventually delivered to the reply callback */
int varlink_invoke(Varlink *v, const char *method, JsonVariant *parameters);
int varlink_invokeb(Varlink *v, const char *method, ...);
/* Enqueue a final reply */
int varlink_reply(Varlink *v, JsonVariant *parameters);
int varlink_replyb(Varlink *v, ...);
/* Enqueue a (final) error */
int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters);
int varlink_errorb(Varlink *v, const char *error_id, ...);
int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters);
/* Enqueue a "more" reply */
int varlink_notify(Varlink *v, JsonVariant *parameters);
int varlink_notifyb(Varlink *v, ...);
/* Bind a disconnect, reply or timeout callback */
int varlink_bind_reply(Varlink *v, VarlinkReply reply);
void* varlink_set_userdata(Varlink *v, void *userdata);
void* varlink_get_userdata(Varlink *v);
int varlink_get_peer_uid(Varlink *v, uid_t *ret);
int varlink_get_peer_pid(Varlink *v, pid_t *ret);
int varlink_set_relative_timeout(Varlink *v, usec_t usec);
VarlinkServer* varlink_get_server(Varlink *v);
int varlink_set_description(Varlink *v, const char *d);
/* Create a varlink server */
int varlink_server_new(VarlinkServer **ret, VarlinkServerFlags flags);
VarlinkServer *varlink_server_ref(VarlinkServer *s);
VarlinkServer *varlink_server_unref(VarlinkServer *s);
/* Add addresses or fds to listen on */
int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t mode);
int varlink_server_listen_fd(VarlinkServer *s, int fd);
int varlink_server_add_connection(VarlinkServer *s, int fd, Varlink **ret);
/* Bind callbacks */
int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMethod callback);
int varlink_server_bind_method_many_internal(VarlinkServer *s, ...);
#define varlink_server_bind_method_many(s, ...) varlink_server_bind_method_many_internal(s, __VA_ARGS__, NULL)
int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect connect);
void* varlink_server_set_userdata(VarlinkServer *s, void *userdata);
void* varlink_server_get_userdata(VarlinkServer *s);
int varlink_server_attach_event(VarlinkServer *v, sd_event *e, int64_t priority);
int varlink_server_detach_event(VarlinkServer *v);
sd_event *varlink_server_get_event(VarlinkServer *v);
int varlink_server_shutdown(VarlinkServer *server);
unsigned varlink_server_connections_max(VarlinkServer *s);
unsigned varlink_server_connections_per_uid_max(VarlinkServer *s);
int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m);
int varlink_server_set_connections_max(VarlinkServer *s, unsigned m);
int varlink_server_set_description(VarlinkServer *s, const char *description);
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_flush_close_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
#define VARLINK_ERROR_DISCONNECTED "io.systemd.Disconnected"
#define VARLINK_ERROR_TIMEOUT "io.systemd.TimedOut"
#define VARLINK_ERROR_PROTOCOL "io.systemd.Protocol"
#define VARLINK_ERROR_SYSTEM "io.systemd.System"
#define VARLINK_ERROR_INTERFACE_NOT_FOUND "org.varlink.service.InterfaceNotFound"
#define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound"
#define VARLINK_ERROR_METHOD_NOT_IMPLEMENTED "org.varlink.service.MethodNotImplemented"
#define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter"