Skip to main content

Module ipc

Module ipc 

Source
Expand description

Unix-socket IPC transport for the MAVLink and state sockets.

Mirrors the semantics of ADOSDroneAgent/src/ados/core/ipc.py:

  • The owning service binds a Unix socket (perms 0o660, group ados-operator, stale socket removed first) and broadcasts byte buffers to every connected client. The trusted local plane is root plus that group and nothing wider: two of these sockets forward inbound bytes straight to the flight controller. Plugins run as the ados user, which is never in the operator group, and every accept re-checks the peer’s kernel credentials (see OperatorListener).
  • Each client has a bounded outbound queue. A client whose queue fills is dropped rather than allowed to grow unbounded (slow-client policy).
  • The state socket additionally replays the last buffer to a client the moment it connects, so a late subscriber does not wait for the next publish.
  • The MAVLink socket is bidirectional: a frame written by a client is a command toward the flight controller, surfaced here on an inbound channel.

Framing is the caller’s concern. broadcast sends the exact bytes given (the caller frames via crate::frame or crate::state). The inbound reader, used only by the MAVLink socket, decodes 4-byte big-endian length-prefixed frames.

§Who wrote this

An inbound payload arrives as an InboundCommand, which carries the writing connection’s IpcPeer alongside the bytes. The owning service used to receive a bare buffer and had no way to tell one writer from another, so it recorded a single hardcoded provenance for every one of them. That reading was wrong for the writer that matters most: a process that accepts bytes from off the node and forwards them here is not the same as a process that produced them itself, and a service that cannot see the difference reports remote traffic as locally originated.

Structs§

InboundCommand
One inbound payload plus the identity of the connection that wrote it.
InjectorClaim
A writer’s declared autonomous-injector identity (see IPC_DECLARE_INJECTOR_PREFIX). Carried raw to the FC-write path, which verifies ticket against the pairing key before treating client_id as the attested injector; a None ticket is an unverified claim that can never win the lane away from a human holder.
IpcBroadcast
A Unix-socket broadcast server. Drops slow clients; optionally replays the last buffer on connect; optionally decodes inbound length-prefixed frames.
IpcPeer
Who wrote an inbound payload, as the accepting socket can tell.
OperatorListener
A bound command-plane socket. accept yields only peers operator_peer_allowed admits (plus, on a socket bound with bind_reader_socket, members of its reader group); any other peer is closed before a byte of it is read.

Enums§

PeerClass
What an admitted peer on a command socket is.

Constants§

CAPTURE_GROUP
The group that may write /run/ados/capture (the capture-active marker directory, 2775 root:ados-capture) and the capture root, which the supervisor’s janitor and the installer keep at 3775 root:ados-capture. A capture plugin joins it only while it holds capture.storage.write; it guards nothing else.
IPC_DECLARE_INJECTOR_PREFIX
Control-frame prefix a writer uses to declare that it is an AUTONOMOUS INJECTOR: a producer of commands generated on the node rather than by a human operator (the plugin host driving guided setpoints, the MSP relay). The rest of the payload is the writer’s client_id, optionally followed by ;ticket=<ticket> — the attestation the FC-write path verifies against the pairing key so a spoofed client_id cannot claim the injector lane.
IPC_DECLARE_OFF_BOX_SOURCE
The control payload a writer sends to declare that the bytes it is about to forward did not originate on this node.
LEGACY_PLUGIN_USER
The shared service user plugin processes ran as before each plugin got its own user. Still treated as plugin plane wherever it exists.
OPERATOR_GROUP
The group that owns every agent command-plane socket: the control socket, the *-cmd.sock command sockets, the MAVLink/MSP/state lanes, and the rest of the sockets bound through bind_command_socket. The installer creates it and adds the human operator; the plugin user is never a member, whatever capabilities it holds (a sensor-bus reader joins SENSORS_GROUP).
PLUGIN_GROUP
The group every plugin unit runs in as its primary group. Each plugin has its own system user; this shared group owns only the per-plugin sockets (bind_plugin_socket), which gate every request on the plugin’s own capability token, and it is what the plugin loopback guard and the router match a plugin process by.
SENSORS_GROUP
The reader group of the sensor bus (/run/ados/sensing/sensors.sock), bound with bind_reader_socket. A member may connect and subscribe; what it publishes is dropped. A plugin joins it only while it holds sensor.stream.read, which is how a sandboxed plugin reads the bus without joining OPERATOR_GROUP. The installer also adds the human operator, whose operator membership still decides what it may publish.

Functions§

bind_command_socket
Bind a command-plane socket: create the parent directory, remove a stale socket left by a prior run (so bind cannot fail with EADDRINUSE), bind, set the file mode, and hand the file to OPERATOR_GROUP. Every accept on the returned listener re-checks the peer (see OperatorListener).
bind_plugin_socket
Bind a socket on the plugin-reachable plane: the same hygiene as bind_command_socket, owned by PLUGIN_GROUP so a plugin’s unit can connect. There is no peer check here, so it is only for surfaces a plugin may legitimately reach: the per-plugin sockets, where the server gates every request on the plugin’s own capability token, and the log ingest sink, which only accepts log frames. Never use it for an agent command socket.
bind_reader_socket
Bind a command socket that reader_group may also open: the hygiene of super::bind_command_socket, but the file is handed to reader_group (so its members can connect()), and the listener admits service peers, operators and reader-group members, telling them apart through OperatorListener::accept_classified. An operator that is not also in reader_group cannot open the file; the installer adds the human operator to the reader groups it creates.
classify_peer
Classify a peer from its kernel-reported credentials, or None when the socket must refuse it. Service first, then the operator group, then the reader group, so a peer in both groups is an operator. With a group absent from the host (its gid None) nobody is admitted through it.
connect_with_retry
Connect to a Unix socket with bounded retry (mirrors the Python client’s connect-with-retry). Returns the stream once connected.
encode_injector_declaration
Build the IPC_DECLARE_INJECTOR_PREFIX control frame a writer sends to declare itself an autonomous injector. The inverse of parse_injector_declaration, kept beside it so a producer’s declaration and the router’s parse cannot drift on the wire format. ticket is the pairing-key attestation for client_id; omit it on an unpaired node.
is_operator_peer
Whether the process on the other end of stream is on the operator plane: root, this process’s own uid, or a member of OPERATOR_GROUP (the same policy OperatorListener enforces). For a socket that must also accept plugin-plane peers (the log ingest sink), this tells the two apart so what a plugin writes is recorded as the plugin’s. Unreadable credentials read as not-operator, which can only narrow trust, except for the development-host case peer_hung_up_before_accept documents.
is_plugin_peer
Whether a connecting peer is on the plugin plane: the shared plugin user, or any process carrying PLUGIN_GROUP as its primary or a supplementary group. Such a writer is autonomous by construction, so it is gated as an injector whether or not it declares itself one; a self-declaration only binds the cooperating ones.
operator_peer_allowed
Whether a connecting peer may use a command-plane socket.
parse_injector_declaration
Parse an IPC_DECLARE_INJECTOR_PREFIX control frame into an InjectorClaim. None when the payload is not an injector declaration or carries an empty client id (a declaration with no identity is not a claim).
read_length_prefixed
Read one length-prefixed frame from a stream. Returns Ok(None) on clean EOF. Used to consume the MAVLink broadcast on the client side.
read_newline_line
Read one newline-terminated line (state socket v1) from a stream. Returns Ok(None) on clean EOF. The returned buffer excludes the trailing newline.
serve_rpc
Serve a one-shot request/response command socket.