summaryrefslogtreecommitdiffstats
path: root/src/libsystemd/sd-varlink/varlink-internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd/sd-varlink/varlink-internal.h')
-rw-r--r--src/libsystemd/sd-varlink/varlink-internal.h249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/libsystemd/sd-varlink/varlink-internal.h b/src/libsystemd/sd-varlink/varlink-internal.h
new file mode 100644
index 0000000000..5469d9e345
--- /dev/null
+++ b/src/libsystemd/sd-varlink/varlink-internal.h
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/socket.h>
+
+#include "sd-event.h"
+#include "sd-varlink.h"
+
+#include "hashmap.h"
+#include "list.h"
+
+typedef enum VarlinkState {
+ /* Client side states */
+ VARLINK_IDLE_CLIENT,
+ VARLINK_AWAITING_REPLY,
+ VARLINK_AWAITING_REPLY_MORE,
+ VARLINK_CALLING,
+ VARLINK_CALLED,
+ VARLINK_COLLECTING,
+ VARLINK_COLLECTING_REPLY,
+ VARLINK_PROCESSING_REPLY,
+
+ /* Server side states */
+ VARLINK_IDLE_SERVER,
+ VARLINK_PROCESSING_METHOD,
+ VARLINK_PROCESSING_METHOD_MORE,
+ VARLINK_PROCESSING_METHOD_ONEWAY,
+ VARLINK_PROCESSED_METHOD,
+ VARLINK_PENDING_METHOD,
+ VARLINK_PENDING_METHOD_MORE,
+
+ /* Common states (only during shutdown) */
+ VARLINK_PENDING_DISCONNECT,
+ VARLINK_PENDING_TIMEOUT,
+ VARLINK_PROCESSING_DISCONNECT,
+ VARLINK_PROCESSING_TIMEOUT,
+ VARLINK_PROCESSING_FAILURE,
+ VARLINK_DISCONNECTED,
+
+ _VARLINK_STATE_MAX,
+ _VARLINK_STATE_INVALID = -EINVAL,
+} VarlinkState;
+
+/* Tests whether we are not yet disconnected. Note that this is true during all states where the connection
+ * is still good for something, and false only when it's dead for good. This means: when we are
+ * asynchronously connecting to a peer and the connect() is still pending, then this will return 'true', as
+ * the connection is still good, and we are likely to be able to properly operate on it soon. */
+#define VARLINK_STATE_IS_ALIVE(state) \
+ IN_SET(state, \
+ VARLINK_IDLE_CLIENT, \
+ VARLINK_AWAITING_REPLY, \
+ VARLINK_AWAITING_REPLY_MORE, \
+ VARLINK_CALLING, \
+ VARLINK_CALLED, \
+ VARLINK_COLLECTING, \
+ VARLINK_COLLECTING_REPLY, \
+ VARLINK_PROCESSING_REPLY, \
+ VARLINK_IDLE_SERVER, \
+ VARLINK_PROCESSING_METHOD, \
+ VARLINK_PROCESSING_METHOD_MORE, \
+ VARLINK_PROCESSING_METHOD_ONEWAY, \
+ VARLINK_PROCESSED_METHOD, \
+ VARLINK_PENDING_METHOD, \
+ VARLINK_PENDING_METHOD_MORE)
+
+typedef struct VarlinkJsonQueueItem VarlinkJsonQueueItem;
+
+/* A queued message we shall write into the socket, along with the file descriptors to send at the same
+ * time. This queue item binds them together so that message/fd boundaries are maintained throughout the
+ * whole pipeline. */
+struct VarlinkJsonQueueItem {
+ LIST_FIELDS(VarlinkJsonQueueItem, queue);
+ sd_json_variant *data;
+ size_t n_fds;
+ int fds[];
+};
+
+struct sd_varlink {
+ unsigned n_ref;
+
+ sd_varlink_server *server;
+
+ VarlinkState state;
+ bool connecting; /* This boolean indicates whether the socket fd we are operating on is currently
+ * processing an asynchronous connect(). In that state we watch the socket for
+ * EPOLLOUT, but we refrain from calling read() or write() on the socket as that
+ * will trigger ENOTCONN. Note that this boolean is kept separate from the
+ * VarlinkState above on purpose: while the connect() is still not complete we
+ * already want to allow queuing of messages and similar. Thus it's nice to keep
+ * these two state concepts separate: the VarlinkState encodes what our own view of
+ * the connection is, i.e. whether we think it's a server, a client, and has
+ * something queued already, while 'connecting' tells us a detail about the
+ * transport used below, that should have no effect on how we otherwise accept and
+ * process operations from the user.
+ *
+ * Or to say this differently: VARLINK_STATE_IS_ALIVE(state) tells you whether the
+ * connection is good to use, even if it might not be fully connected
+ * yet. connecting=true then informs you that actually we are still connecting, and
+ * the connection is actually not established yet and thus any requests you enqueue
+ * now will still work fine but will be queued only, not sent yet, but that
+ * shouldn't stop you from using the connection, since eventually whatever you queue
+ * *will* be sent.
+ *
+ * Or to say this even differently: 'state' is a high-level ("application layer"
+ * high, if you so will) state, while 'conecting' is a low-level ("transport layer"
+ * low, if you so will) state, and while they are not entirely unrelated and
+ * sometimes propagate effects to each other they are only asynchronously connected
+ * at most. */
+ unsigned n_pending;
+
+ int input_fd;
+ int output_fd;
+
+ char *input_buffer; /* valid data starts at input_buffer_index, ends at input_buffer_index+input_buffer_size */
+ size_t input_buffer_index;
+ size_t input_buffer_size;
+ size_t input_buffer_unscanned;
+
+ void *input_control_buffer;
+ size_t input_control_buffer_size;
+
+ char *output_buffer; /* valid data starts at output_buffer_index, ends at output_buffer_index+output_buffer_size */
+ size_t output_buffer_index;
+ size_t output_buffer_size;
+
+ int *input_fds; /* file descriptors associated with the data in input_buffer (for fd passing) */
+ size_t n_input_fds;
+
+ int *output_fds; /* file descriptors associated with the data in output_buffer (for fd passing) */
+ size_t n_output_fds;
+
+ /* Further messages to output not yet formatted into text, and thus not included in output_buffer
+ * yet. We keep them separate from output_buffer, to not violate fd message boundaries: we want that
+ * each fd that is sent is associated with its fds, and that fds cannot be accidentally associated
+ * with preceding or following messages. */
+ LIST_HEAD(VarlinkJsonQueueItem, output_queue);
+ VarlinkJsonQueueItem *output_queue_tail;
+
+ /* The fds to associate with the next message that is about to be enqueued. The user first pushes the
+ * fds it intends to send via varlink_push_fd() into this queue, and then once the message data is
+ * submitted we'll combine the fds and the message data into one. */
+ int *pushed_fds;
+ size_t n_pushed_fds;
+
+ sd_varlink_reply_t reply_callback;
+
+ sd_json_variant *current;
+ sd_json_variant *current_collected;
+ sd_varlink_reply_flags_t current_reply_flags;
+ sd_varlink_symbol *current_method;
+
+ int peer_pidfd;
+ struct ucred ucred;
+ bool ucred_acquired:1;
+
+ bool write_disconnected:1;
+ bool read_disconnected:1;
+ bool prefer_read:1;
+ bool prefer_write:1;
+ bool got_pollhup:1;
+
+ bool allow_fd_passing_input:1;
+ bool allow_fd_passing_output:1;
+
+ bool output_buffer_sensitive:1; /* whether to erase the output buffer after writing it to the socket */
+ bool input_sensitive:1; /* Whether incoming messages might be sensitive */
+
+ int af; /* address family if socket; AF_UNSPEC if not socket; negative if not known */
+
+ usec_t timestamp;
+ usec_t timeout;
+
+ void *userdata;
+ char *description;
+
+ sd_event *event;
+ sd_event_source *input_event_source;
+ sd_event_source *output_event_source;
+ sd_event_source *time_event_source;
+ sd_event_source *quit_event_source;
+ sd_event_source *defer_event_source;
+
+ pid_t exec_pid;
+};
+
+typedef struct VarlinkServerSocket VarlinkServerSocket;
+
+struct VarlinkServerSocket {
+ sd_varlink_server *server;
+
+ int fd;
+ char *address;
+
+ sd_event_source *event_source;
+
+ LIST_FIELDS(VarlinkServerSocket, sockets);
+};
+
+struct sd_varlink_server {
+ unsigned n_ref;
+ sd_varlink_server_flags_t flags;
+
+ LIST_HEAD(VarlinkServerSocket, sockets);
+
+ Hashmap *methods; /* Fully qualified symbol name of a method → VarlinkMethod */
+ Hashmap *interfaces; /* Fully qualified interface name → VarlinkInterface* */
+ Hashmap *symbols; /* Fully qualified symbol name of method/error → VarlinkSymbol* */
+ sd_varlink_connect_t connect_callback;
+ sd_varlink_disconnect_t disconnect_callback;
+
+ sd_event *event;
+ int64_t event_priority;
+
+ unsigned n_connections;
+ Hashmap *by_uid; /* UID_TO_PTR(uid) → UINT_TO_PTR(n_connections) */
+
+ void *userdata;
+ char *description;
+
+ unsigned connections_max;
+ unsigned connections_per_uid_max;
+
+ bool exit_on_idle;
+};
+
+#define varlink_log_errno(v, error, fmt, ...) \
+ log_debug_errno(error, "%s: " fmt, varlink_description(v), ##__VA_ARGS__)
+
+#define varlink_log(v, fmt, ...) \
+ log_debug("%s: " fmt, varlink_description(v), ##__VA_ARGS__)
+
+#define varlink_server_log_errno(s, error, fmt, ...) \
+ log_debug_errno(error, "%s: " fmt, varlink_server_description(s), ##__VA_ARGS__)
+
+#define varlink_server_log(s, fmt, ...) \
+ log_debug("%s: " fmt, varlink_server_description(s), ##__VA_ARGS__)
+
+static inline const char* varlink_description(sd_varlink *v) {
+ return (v ? v->description : NULL) ?: "varlink";
+}
+
+static inline const char* varlink_server_description(sd_varlink_server *s) {
+ return (s ? s->description : NULL) ?: "varlink";
+}
+
+VarlinkServerSocket* varlink_server_socket_free(VarlinkServerSocket *ss);
+DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServerSocket *, varlink_server_socket_free);
+
+int varlink_server_add_socket_event_source(sd_varlink_server *s, VarlinkServerSocket *ss, int64_t priority);