mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 05:22:48 +07:00
The udev parity gaps that were blocked on IPC -- settle, trigger,
info, queue introspection, runtime rule reload -- become bus methods.
keventd serves its own socket the way Finit serves /run/finit/bus:
brokerless, libink, one socket per daemon, no forwarding between the
two.
Settle(u) -> b parked until the queue drains or the timeout
passes; true when settled
Trigger(s, s) replay events, action + subsystem glob
Info(s) -> a{ss} /run/udev/data properties for a devpath
RulesReload() -> u re-read rules dirs, returns rule count
QueueEmpty (b), SeqnumProcessed (t) properties
DeviceProcessed (ss) signal after each fully handled event
The queue state is the highest kernel seqnum keventd has handled,
baselined at startup, against /sys/kernel/uevent_seqnum. keventd -S
now asks the running daemon first and falls back to seqnum polling.
In passive mode Trigger and RulesReload refuse. Adds
link_call_connection() for the park bookkeeping.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
497 lines
22 KiB
C
497 lines
22 KiB
C
/* libink — brokerless D-Bus server library, born inside Finit
|
|
*
|
|
* Copyright (c) 2026 Joachim Wiberg <troglobit@gmail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#ifndef LIBINK_LINK_H_
|
|
#define LIBINK_LINK_H_
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef struct link_server link_server_t;
|
|
typedef struct link_connection link_connection_t;
|
|
typedef struct link_call link_call_t;
|
|
typedef struct link_client link_client_t;
|
|
|
|
/* D-Bus message type codes -- see link_reply_t.type. */
|
|
#define LINK_MSG_INVALID 0
|
|
#define LINK_MSG_METHOD_CALL 1
|
|
#define LINK_MSG_METHOD_RETURN 2
|
|
#define LINK_MSG_ERROR 3
|
|
#define LINK_MSG_SIGNAL 4
|
|
|
|
/* Writer is exposed so callers can stack-allocate one for marshalling
|
|
* signal/reply bodies. Treat the fields as opaque; use link_writer_init
|
|
* + the link_w_* helpers + link_writer_finish. Sized for typical D-Bus
|
|
* messages -- the array stack supports up to 8 levels of nesting. */
|
|
#define LINK_WRITER_MAX_NESTING 8
|
|
typedef struct link_writer {
|
|
uint8_t *buf;
|
|
size_t cap;
|
|
size_t off;
|
|
int err;
|
|
struct {
|
|
size_t lenpos;
|
|
size_t elemstart;
|
|
} arrays[LINK_WRITER_MAX_NESTING];
|
|
size_t array_depth;
|
|
} link_writer_t;
|
|
|
|
/* Reader is exposed so callers can stack-allocate one for decoding
|
|
* reply or signal bodies received from a peer. Treat fields as
|
|
* opaque; use link_reader_init + the link_r_* helpers. */
|
|
typedef struct link_reader {
|
|
const uint8_t *base;
|
|
size_t off;
|
|
size_t cap;
|
|
int err; /* sticky */
|
|
} link_reader_t;
|
|
|
|
/* View of an inbound message (method-return, error, or signal),
|
|
* populated by link_client_call(_v) and link_client_wait(), and
|
|
* returned by link_client_reply(). All pointers reference internal
|
|
* client storage and are invalidated by the next call or wait on the
|
|
* same client, or by link_client_close(). `body` is NULL iff
|
|
* body_len==0; `error_name` is non-NULL only when type == LINK_MSG_ERROR;
|
|
* `path`/`interface`/`member` are non-NULL on signals. */
|
|
typedef struct {
|
|
uint8_t type; /* LINK_MSG_METHOD_RETURN, _ERROR, or _SIGNAL */
|
|
const char *signature;
|
|
const char *error_name;
|
|
const char *path;
|
|
const char *interface;
|
|
const char *member;
|
|
const uint8_t *body;
|
|
size_t body_len;
|
|
} link_reply_t;
|
|
|
|
/* ---------- debug tracing ---------- */
|
|
|
|
/* Receives one line per connection, method call, and authorization
|
|
* decision. `func` is the libink function that emitted it, so a host
|
|
* can format it the way it formats its own traces.
|
|
*
|
|
* With no logger installed, the default, a trace point costs one NULL
|
|
* test. With one installed the message is formatted before the host
|
|
* sees it, so a host that only wants tracing sometimes should install
|
|
* and remove the callback rather than discard by level. */
|
|
typedef void (*link_log_cb_t)(void *userdata, const char *func, const char *msg);
|
|
|
|
void link_set_logger(link_log_cb_t cb, void *userdata);
|
|
|
|
/* ---------- caller identity on a broker connection ---------- */
|
|
|
|
/* Handle for a call parked while its caller is identified. Opaque,
|
|
* and safe to hold: it encodes a slot and a generation, so resolving
|
|
* a stale handle is a no-op rather than a use-after-free. */
|
|
typedef uint64_t link_authz_t;
|
|
|
|
/* "Nobody asked yet", distinct from any real uid. link_call_uid()
|
|
* returns this on a broker connection until something forces the
|
|
* question, which today only a LINK_METHOD_PRIVILEGED method does. */
|
|
#define LINK_UID_UNKNOWN ((uid_t)-1)
|
|
|
|
/* A broker sets SENDER to a unique name, ":1.<u32>", so this is very
|
|
* generous. Callers that key anything on a sender must reject longer
|
|
* names rather than truncate: two senders sharing a truncated key
|
|
* would share an identity. */
|
|
#define LINK_SENDER_MAX 64
|
|
|
|
/* Answer "which uid is `sender`?" for a privileged call arriving on a
|
|
* broker connection, where SO_PEERCRED describes the broker and not
|
|
* the caller.
|
|
*
|
|
* Return 0 with *uid set when the answer is already known, 1 to answer
|
|
* later by calling link_uid_resolved() with `tok`, or -1 when it
|
|
* cannot be determined, which fails the call closed. Returning 1
|
|
* without ever calling link_uid_resolved() leaks the slot and leaves
|
|
* the caller without a reply, so always answer. */
|
|
typedef int (*link_uid_resolver_t)(link_connection_t *conn, const char *sender,
|
|
link_authz_t tok, uid_t *uid, void *userdata);
|
|
|
|
void link_server_set_uid_resolver(link_server_t *server, link_uid_resolver_t cb,
|
|
void *userdata);
|
|
|
|
/* May this caller invoke a LINK_METHOD_PRIVILEGED method? Return
|
|
* non-zero to allow. `groups` is the caller's group set (primary gid
|
|
* first), captured from the kernel with SO_PEERGROUPS so the embedder
|
|
* can decide membership without an NSS lookup that would block PID 1;
|
|
* `ngroups` is 0 when the set is unknown, e.g. a call arriving over a
|
|
* broker, where only `uid` is available. Who counts as privileged is
|
|
* the embedder's policy; with no authorizer installed only uid 0 may. */
|
|
typedef int (*link_authorizer_t)(uid_t uid, const gid_t *groups, int ngroups,
|
|
void *userdata);
|
|
|
|
void link_server_set_authorizer(link_server_t *server, link_authorizer_t cb,
|
|
void *userdata);
|
|
|
|
/* Complete a deferred resolve and resume the parked call. Pass
|
|
* (uid_t)-1 to say the caller could not be identified, which denies
|
|
* it. Resolving a handle twice, or one whose connection has since
|
|
* closed, does nothing. The connection is the one the resolver was
|
|
* asked about; a reply callback is handed it as its first argument. */
|
|
void link_uid_resolved(link_connection_t *conn, link_authz_t tok, uid_t uid);
|
|
|
|
/* ---------- handler-deferred replies ---------- */
|
|
|
|
/* A handler that cannot answer yet parks the call and returns 0
|
|
* without replying; the framework sends nothing. Resume re-runs the
|
|
* handler with link_call_resumed() reading true, and this time it
|
|
* must reply -- a resumed call cannot park again. Parked calls that
|
|
* are never resumed are expired by link_connection_expire() with a
|
|
* TimedOut error. Room is limited (four per connection); a failed
|
|
* park means answer now. */
|
|
int link_call_park (link_call_t *call, link_authz_t *tok);
|
|
void link_call_resume (link_connection_t *conn, link_authz_t tok);
|
|
int link_call_resumed(const link_call_t *call);
|
|
|
|
/* Called with the reply to an outbound link_connection_call(). `reply`
|
|
* is NULL if the connection dropped before one arrived. */
|
|
typedef void (*link_reply_cb_t)(link_connection_t *conn, const link_reply_t *reply,
|
|
void *userdata);
|
|
|
|
/* Issue a method call on an established connection and invoke `cb`
|
|
* when the reply lands. Unlike link_client_call() this never blocks:
|
|
* the reply is picked up by the normal read loop. Argument marshalling
|
|
* matches link_client_call_v(). */
|
|
int link_connection_call(link_connection_t *conn, const char *destination,
|
|
const char *path, const char *interface, const char *member,
|
|
link_reply_cb_t cb, void *userdata,
|
|
const char *signature, ...);
|
|
|
|
/* Nothing in libink runs a clock, it has no event loop, so calls that
|
|
* go unanswered in either direction are the embedder's to time out.
|
|
* This drops anything held longer than `age_ms` on one connection: a
|
|
* park whose resolver never answered, which leaves its caller
|
|
* org.freedesktop.DBus.Error.TimedOut, and a call whose reply never
|
|
* came, whose callback runs once with a NULL reply exactly as a
|
|
* dropped connection would.
|
|
*
|
|
* Returns how many are still outstanding, so a sweep can stop
|
|
* rearming once nothing is left. */
|
|
int link_connection_expire(link_connection_t *conn, unsigned int age_ms);
|
|
|
|
/* ---------- server / connection lifecycle ---------- */
|
|
|
|
/* Bind a listening socket at `path` with file mode `mode`, e.g. 0660
|
|
* to keep it to root and one group. The mode is applied at bind(),
|
|
* so the socket is never briefly more permissive than asked; setting
|
|
* the owning group afterwards is the caller's job. */
|
|
int link_server_new (link_server_t **server, const char *path, mode_t mode);
|
|
void link_server_free (link_server_t *server);
|
|
int link_server_get_fd(const link_server_t *server);
|
|
|
|
int link_server_accept(link_server_t *server, link_connection_t **conn);
|
|
|
|
/* Insert an externally-authenticated fd into the server's connection
|
|
* set. Used to integrate an outbound peer (e.g. a client-side
|
|
* handshake against an external dbus-daemon) so the same dispatch +
|
|
* signal-fan-out machinery covers it. `peer_uid` becomes what
|
|
* privileged-method checks see; pass (uid_t)-1 to make all
|
|
* LINK_METHOD_PRIVILEGED methods reject by default.
|
|
*
|
|
* On success the connection takes ownership of `fd`. On any failure
|
|
* `fd` is closed before the function returns NULL, so callers never
|
|
* have to track partial state.
|
|
*
|
|
* LINK_ATTACH_BROKER says the peer is a message bus rather than an
|
|
* ordinary client. A broker subscribes on behalf of its own clients
|
|
* and never sends us AddMatch, so signals go to it unconditionally
|
|
* instead of being filtered by this connection's match rules. */
|
|
#define LINK_ATTACH_BROKER 0x01
|
|
|
|
link_connection_t *link_server_attach(link_server_t *server, int fd, uid_t peer_uid,
|
|
unsigned int attach_flags);
|
|
|
|
int link_connection_get_fd (const link_connection_t *conn);
|
|
uid_t link_connection_get_uid (const link_connection_t *conn);
|
|
int link_connection_process (link_connection_t *conn);
|
|
void link_connection_close (link_connection_t *conn);
|
|
|
|
/* ---------- object registration ---------- */
|
|
|
|
typedef int (*link_method_fn)(link_call_t *call, void *userdata);
|
|
|
|
/* Method flags for link_method_t.flags */
|
|
#define LINK_METHOD_PRIVILEGED (1u << 0) /* peer must be uid 0 (root) */
|
|
|
|
typedef struct {
|
|
const char *name; /* member name */
|
|
const char *in_sig; /* input signature (D-Bus, e.g. "" or "s") */
|
|
const char *out_sig; /* output signature */
|
|
unsigned flags; /* OR of LINK_METHOD_* */
|
|
link_method_fn handler;
|
|
} link_method_t;
|
|
|
|
/* A read-only property descriptor. Set via the Properties.Set side
|
|
* is not yet implemented; only Get and GetAll are. The framework
|
|
* emits the variant signature from `sig`; the getter writes only the
|
|
* bare value into the provided writer (link_w_string for "s",
|
|
* link_w_u32 for "u", ...), so the declared type is the single
|
|
* source of truth. */
|
|
typedef int (*link_property_getter_fn)(link_writer_t *w, void *userdata);
|
|
|
|
typedef struct {
|
|
const char *name; /* property name */
|
|
const char *sig; /* D-Bus signature, e.g. "s" */
|
|
link_property_getter_fn getter;
|
|
} link_property_t;
|
|
|
|
/*
|
|
* Declares a signal for introspection only; emission is unchecked,
|
|
* see link_connection_emit_signal().
|
|
*/
|
|
typedef struct {
|
|
const char *name; /* member name */
|
|
const char *sig; /* D-Bus signature, e.g. "sss" */
|
|
} link_signal_t;
|
|
|
|
typedef struct {
|
|
const char *interface; /* e.g. "org.finit.Manager1" */
|
|
const link_method_t *methods; /* terminated by {NULL, ...}, or NULL */
|
|
const link_property_t *properties; /* terminated by {NULL, ...}, or NULL */
|
|
const link_signal_t *signals; /* terminated by {NULL, ...}, or NULL */
|
|
} link_vtable_t;
|
|
|
|
/* Register one (interface, methods) at `path`. Calling repeatedly
|
|
* with the same path and different vtables adds more interfaces at
|
|
* that object. The vtable pointer must outlive the server (typically
|
|
* a static table). */
|
|
int link_server_add_object(link_server_t *server, const char *path,
|
|
const link_vtable_t *vt, void *userdata);
|
|
|
|
/* Remove every vtable registered at `path` and free the object.
|
|
* Returns 0 if the object existed, -1 (errno=ENOENT) otherwise. */
|
|
int link_server_remove_object(link_server_t *server, const char *path);
|
|
|
|
/* ---------- call accessors ---------- */
|
|
|
|
const char *link_call_path (const link_call_t *call);
|
|
const char *link_call_interface(const link_call_t *call);
|
|
const char *link_call_member (const link_call_t *call);
|
|
uid_t link_call_uid (const link_call_t *call);
|
|
link_connection_t *link_call_connection(const link_call_t *call);
|
|
|
|
/* ---------- reading method-call arguments ----------
|
|
*
|
|
* Cursor starts at the beginning of the request body. Each
|
|
* function returns 0 on success and advances the cursor; on
|
|
* failure it returns -1 and leaves the cursor in an error state
|
|
* (subsequent reads also fail). Strings reference the
|
|
* connection's rx buffer and are valid for the duration of the
|
|
* method handler. */
|
|
|
|
int link_call_read_byte (link_call_t *call, uint8_t *out);
|
|
int link_call_read_bool (link_call_t *call, int *out);
|
|
int link_call_read_u32 (link_call_t *call, uint32_t *out);
|
|
int link_call_read_u64 (link_call_t *call, uint64_t *out);
|
|
int link_call_read_string(link_call_t *call, const char **out); /* "s" */
|
|
int link_call_read_path (link_call_t *call, const char **out); /* "o" */
|
|
|
|
/* ---------- reply construction ---------- */
|
|
|
|
/* Get the writer for the reply body, write args into it, return 0
|
|
* from the handler. Dispatch finalizes and sends the reply with
|
|
* the out_sig declared on the vtable. May be called once per
|
|
* call. */
|
|
link_writer_t *link_call_reply(link_call_t *call);
|
|
|
|
/* Send a D-Bus error reply. `name` must be a valid D-Bus error
|
|
* name (e.g. "org.freedesktop.DBus.Error.UnknownMethod"); `message`
|
|
* may be NULL. */
|
|
int link_call_reply_error(link_call_t *call, const char *name, const char *message);
|
|
|
|
/* ---------- signal emission ----------
|
|
*
|
|
* Send a signal to a single peer if its AddMatch rules accept it.
|
|
* Callers marshal the body separately and pass the resulting bytes.
|
|
* Returns 0 on success (or "filtered out, nothing sent"), -1 with
|
|
* errno set on failure: EMSGSIZE and EINVAL mean nothing hit the
|
|
* wire and the connection is still usable; anything else is a
|
|
* transport failure that may have left a partial frame -- the
|
|
* caller must drop the peer. */
|
|
int link_connection_emit_signal(link_connection_t *conn,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *member,
|
|
const char *signature,
|
|
const uint8_t *body, size_t body_len);
|
|
|
|
/* ---------- client (outgoing method calls) ----------
|
|
*
|
|
* Connect, authenticate as the current effective uid, send BEGIN.
|
|
* Returns NULL on any failure (caller can fall back to another
|
|
* transport if it has one). */
|
|
link_client_t *link_client_open(const char *path);
|
|
|
|
/* As link_client_open but applies SO_SNDTIMEO + SO_RCVTIMEO before
|
|
* the connect/AUTH handshake. After link_server_attach flips the fd
|
|
* to non-blocking the timeout is silently inert; it only protects
|
|
* the synchronous open path against a hung peer. timeout_ms == 0
|
|
* disables the budget (same behaviour as link_client_open). */
|
|
link_client_t *link_client_open_timeout(const char *path, int timeout_ms);
|
|
|
|
void link_client_close(link_client_t *c);
|
|
|
|
/* Address subsequent calls on `c` to a well-known name. Needed when
|
|
* a broker routes the message, e.g. "org.freedesktop.DBus" to reach
|
|
* the bus driver itself; a brokerless link has a single peer and
|
|
* needs no destination, which is the default. `destination` is not
|
|
* copied, so it must outlive the client. */
|
|
void link_client_set_destination(link_client_t *c, const char *destination);
|
|
|
|
/* Detach the authenticated socket from the client and return the raw
|
|
* fd; subsequent link_client_close on `c` is invalid because the
|
|
* structure has already been freed. Used by callers (e.g. system-bus
|
|
* integration) that want to promote an outbound client connection
|
|
* into a server-attached peer via link_server_attach(). */
|
|
int link_client_steal_fd(link_client_t *c);
|
|
|
|
/* Status codes returned by link_client_call(_v). */
|
|
#define LINK_CALL_OK 0 /* method-return received */
|
|
#define LINK_CALL_ERROR 1 /* server replied with an error */
|
|
#define LINK_CALL_FAIL (-1) /* transport, parse, or invalid-arg failure */
|
|
|
|
/* Send a METHOD_CALL and read the reply synchronously.
|
|
*
|
|
* `signature` and `body`/`body_len` describe the outgoing body --
|
|
* marshal it yourself with link_writer_init + the link_w_* helpers
|
|
* + link_writer_finish. Pass signature=NULL and body=NULL for
|
|
* methods that take no arguments.
|
|
*
|
|
* After the call, inspect the reply via link_client_reply() -- it
|
|
* exposes the body bytes (for callers that want to decode them with
|
|
* link_reader_init + link_r_*) and the error name on LINK_CALL_ERROR.
|
|
* The reply view is invalidated by the next call on the same client
|
|
* or by link_client_close(). */
|
|
int link_client_call(link_client_t *c,
|
|
const char *obj_path,
|
|
const char *interface,
|
|
const char *member,
|
|
const char *signature,
|
|
const uint8_t *body, size_t body_len);
|
|
|
|
/* Convenience wrapper that marshals the outgoing body from varargs
|
|
* matching `signature`. Supported type codes (one per arg):
|
|
* 'y' -> int (promoted uint8_t)
|
|
* 'b' -> int (0/non-zero)
|
|
* 'u' -> uint32_t
|
|
* 's' -> const char *
|
|
* 'o' -> const char * (object path)
|
|
*
|
|
* Pass signature=NULL or "" for void calls. Return value matches
|
|
* link_client_call; an unsupported type code returns LINK_CALL_FAIL
|
|
* with no message sent. */
|
|
int link_client_call_v(link_client_t *c,
|
|
const char *obj_path,
|
|
const char *interface,
|
|
const char *member,
|
|
const char *signature, ...);
|
|
|
|
const link_reply_t *link_client_reply(link_client_t *c);
|
|
|
|
/* Convenience accessors for the common case where a reply carries
|
|
* exactly one string ("s" or "o") or one u32 ("u"). They wrap the
|
|
* link_reader_init + link_r_* pattern; on success return 0 and
|
|
* populate *out, on parse failure or missing body return -1. Use
|
|
* link_client_reply + link_reader_init directly for richer payloads. */
|
|
int link_reply_get_string(const link_reply_t *r, const char **out);
|
|
int link_reply_get_u32 (const link_reply_t *r, uint32_t *out);
|
|
|
|
/* Wait up to `timeout_ms` milliseconds for the next inbound message
|
|
* (typically a SIGNAL delivered after an AddMatch subscription), and
|
|
* populate the same view returned by link_client_reply().
|
|
* timeout_ms < 0 : block forever
|
|
* timeout_ms == 0 : non-blocking (returns 1 immediately if no data)
|
|
* timeout_ms > 0 : wait that long
|
|
* Returns 0 on success, 1 on timeout, -1 on transport/parse error.
|
|
*
|
|
* Note: the timeout gates only the wait for the first byte of the
|
|
* next frame. Once data starts arriving the rest of the message is
|
|
* read blockingly; callers that need a hard upper bound should pass
|
|
* a positive timeout AND have a watchdog at a higher level. */
|
|
int link_client_wait(link_client_t *c, int timeout_ms);
|
|
|
|
/* ---------- standalone writer ----------
|
|
*
|
|
* For marshalling bodies outside a method-call handler (signals,
|
|
* pre-computed replies). Initialise on a caller-owned buffer,
|
|
* write args via link_w_*, then call link_writer_finish which
|
|
* returns the body length or -1 on overflow. */
|
|
void link_writer_init (link_writer_t *w, uint8_t *buf, size_t cap);
|
|
ssize_t link_writer_finish(link_writer_t *w);
|
|
|
|
/* ---------- writer (mirrors the internal marshaller) ---------- */
|
|
|
|
void link_w_byte (link_writer_t *w, uint8_t v);
|
|
void link_w_bool (link_writer_t *w, int v);
|
|
void link_w_u32 (link_writer_t *w, uint32_t v);
|
|
void link_w_u64 (link_writer_t *w, uint64_t v); /* "t" */
|
|
void link_w_string (link_writer_t *w, const char *s); /* "s" */
|
|
void link_w_path (link_writer_t *w, const char *s); /* "o" */
|
|
void link_w_variant_string(link_writer_t *w, const char *s); /* "v" containing "s" */
|
|
void link_w_array_begin (link_writer_t *w, char element_sig);
|
|
void link_w_array_end (link_writer_t *w);
|
|
void link_w_struct_begin(link_writer_t *w);
|
|
void link_w_struct_end (link_writer_t *w);
|
|
|
|
/* ---------- standalone reader ----------
|
|
*
|
|
* For decoding bodies received off the wire (reply or signal).
|
|
* Initialise on the body pointer + length, read via link_r_*,
|
|
* check link_r_done() to confirm everything was consumed. */
|
|
void link_reader_init(link_reader_t *r, const uint8_t *body, size_t len);
|
|
int link_r_byte (link_reader_t *r, uint8_t *out);
|
|
int link_r_bool (link_reader_t *r, int *out);
|
|
int link_r_u32 (link_reader_t *r, uint32_t *out);
|
|
int link_r_u64 (link_reader_t *r, uint64_t *out);
|
|
int link_r_string (link_reader_t *r, const char **out); /* "s" */
|
|
int link_r_path (link_reader_t *r, const char **out); /* "o" */
|
|
int link_r_variant_begin (link_reader_t *r, char *type); /* sig header, cursor at value */
|
|
int link_r_skip_basic (link_reader_t *r, char type); /* skip one basic value */
|
|
int link_r_variant_string(link_reader_t *r, const char **out); /* "v" containing "s" */
|
|
int link_r_align (link_reader_t *r, size_t n); /* skip to next n-byte boundary */
|
|
int link_r_done (const link_reader_t *r);
|
|
|
|
/* Begin reading an "a<T>" array. On success returns 0 and sets
|
|
* *out_end to the absolute reader offset at which the array ends;
|
|
* caller loops while link_r_pos < *out_end. For dict-entry arrays
|
|
* ("a{T}") call link_r_align(r, 8) at the top of each iteration --
|
|
* the element-alignment skip from the array prefix only covers the
|
|
* first entry. */
|
|
int link_r_array_begin(link_reader_t *r, size_t *out_end);
|
|
|
|
/* Byte offset of the next read inside the original body buffer.
|
|
* Use together with the *out_end returned by link_r_array_begin to
|
|
* walk the elements of an "a<T>" payload. */
|
|
size_t link_r_pos (const link_reader_t *r);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* LIBINK_LINK_H_ */
|