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