/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #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) /* Tests whether we are expected to generate a method call reply, i.e. are processing a method call, except * one with the ONEWAY flag set. */ #define VARLINK_STATE_WANTS_REPLY(state) \ IN_SET(state, \ VARLINK_PROCESSING_METHOD, \ VARLINK_PROCESSING_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);