dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 1 | //! headroom control-protocol types and framing. spec: `IPC.md` (repo root); this is its rust binding. |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 2 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 3 | #![forbid(unsafe_code)] |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 4 | #![warn(missing_docs)] |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 5 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 6 | mod codec; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 7 | mod error; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 8 | mod proto; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 9 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 10 | pub use codec::{Codec, DEFAULT_MAX_FRAME_BYTES, MIN_MAX_FRAME_BYTES}; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 11 | pub use error::{Error, ErrorCode, ProtoError}; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 12 | pub use proto::{ |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 13 | DaemonEvent, Event, HelloData, LayerALevel, LayerASnapshot, MeterTick, Op, ProfileEvent, |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 14 | ProfileInfo, Request, Response, ResponsePayload, Route, RouteList, RouteRule, RouteRuleMatch, |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 15 | RoutingEvent, ServerFrame, SinkInfo, Sinks, Status, StreamRoute, Topic, |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 16 | }; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 17 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 18 | /// wire version; bumped only on incompatible changes. |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 19 | pub const PROTOCOL_VERSION: u32 = 1; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 20 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 21 | /// socket dir stem; full path `${XDG_RUNTIME_DIR}/headroom/control.sock`, falling back to `/run/user/$UID/...`. |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 22 | pub const DEFAULT_SOCKET_DIR: &str = "headroom"; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 23 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 24 | pub const DEFAULT_SOCKET_NAME: &str = "control.sock"; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 25 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 26 | /// honours `XDG_RUNTIME_DIR`, else falls back to `/run/user/$UID/...`. `None` when neither is determinable. |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 27 | #[must_use] |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 28 | pub fn default_socket_path() -> Option<std::path::PathBuf> { |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 29 | use std::path::PathBuf; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 30 | if let Ok(dir) = std::env::var("XDG_RUNTIME_DIR") { |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 31 | if !dir.is_empty() { |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 32 | return Some( |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 33 | PathBuf::from(dir) |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 34 | .join(DEFAULT_SOCKET_DIR) |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 35 | .join(DEFAULT_SOCKET_NAME), |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 36 | ); |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 37 | } |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 38 | } |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 39 | // uid from /proc/self/status: nix-free, dependency-light |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 40 | let uid = read_self_uid()?; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 41 | Some( |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 42 | PathBuf::from(format!("/run/user/{uid}")) |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 43 | .join(DEFAULT_SOCKET_DIR) |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 44 | .join(DEFAULT_SOCKET_NAME), |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 45 | ) |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 46 | } |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 47 | |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 48 | fn read_self_uid() -> Option<u32> { |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 49 | let s = std::fs::read_to_string("/proc/self/status").ok()?; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 50 | for line in s.lines() { |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 51 | if let Some(rest) = line.strip_prefix("Uid:") { |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 52 | let first = rest.split_whitespace().next()?; |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 53 | return first.parse().ok(); |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 54 | } |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 55 | } |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 56 | None |
dd6b8e786e1f | atagen | 2026-05-27 15:49 UTC | 57 | } |