Skip to main content

arcbox_helper/
lib.rs

1//! arcbox-helper shared types and client.
2//!
3//! This library defines the tarpc service interface for the privileged helper
4//! daemon and provides a high-level [`client::Client`] for consumers
5//! (arcbox-core, arcbox-daemon).
6
7pub mod client;
8pub mod validate;
9
10/// Unix socket path where the helper daemon listens.
11pub const HELPER_SOCKET: &str = arcbox_constants::paths::privileged::HELPER_SOCKET;
12
13/// Override the socket path for development/testing.
14pub const HELPER_SOCKET_ENV: &str = "ARCBOX_HELPER_SOCKET";
15
16/// Returns the effective socket path, checking the env override first.
17pub fn socket_path() -> String {
18    std::env::var(HELPER_SOCKET_ENV).unwrap_or_else(|_| HELPER_SOCKET.to_string())
19}
20
21/// Host name the managed `/etc/hosts` alias resolves to loopback.
22///
23/// The guest-data NFS mount uses `ArcBox:/` as its source when the alias
24/// is installed — Finder's Locations sidebar displays a network mount by
25/// its source host name.
26pub const HOSTS_ALIAS_NAME: &str = "ArcBox";
27
28/// The one `/etc/hosts` line the helper manages. The trailing comment
29/// tags the line so uninstall removes exactly what was installed.
30pub const HOSTS_ALIAS_LINE: &str = "127.0.0.1\tArcBox\t# managed by arcbox-helper";
31
32/// True when `hosts_content` (the text of `/etc/hosts`) carries the
33/// managed alias line. Shared by the helper mutation, the daemon's
34/// self-setup check, and the NFS mount's source selection.
35#[must_use]
36pub fn hosts_alias_installed(hosts_content: &str) -> bool {
37    hosts_content
38        .lines()
39        .any(|line| line.trim() == HOSTS_ALIAS_LINE)
40}
41
42/// The tarpc service definition for privileged host mutations.
43///
44/// All methods perform input validation server-side before executing
45/// any privileged operation. Results carry error strings on failure.
46#[tarpc::service]
47pub trait HelperService {
48    /// Adds a host route for `subnet` via `iface`.
49    /// Idempotent: returns Ok if the route already exists.
50    async fn route_add(subnet: String, iface: String) -> Result<(), String>;
51
52    /// Removes the host route for `subnet`.
53    /// Idempotent: returns Ok if the route is already absent.
54    async fn route_remove(subnet: String) -> Result<(), String>;
55
56    /// Installs a DNS resolver file for `domain` pointing to `127.0.0.1:port`.
57    async fn dns_install(domain: String, port: u16) -> Result<(), String>;
58
59    /// Removes the DNS resolver file for `domain`.
60    async fn dns_uninstall(domain: String) -> Result<(), String>;
61
62    /// Checks if a DNS resolver file is installed for `domain`.
63    async fn dns_status(domain: String) -> Result<bool, String>;
64
65    /// Creates `/var/run/docker.sock` symlink pointing to `target`.
66    async fn socket_link(target: String) -> Result<(), String>;
67
68    /// Removes the `/var/run/docker.sock` symlink.
69    async fn socket_unlink() -> Result<(), String>;
70
71    /// Creates `/usr/local/bin/{name}` symlink pointing to `target`.
72    /// Used to expose Docker CLI tools from the app bundle.
73    async fn cli_link(name: String, target: String) -> Result<(), String>;
74
75    /// Removes `/usr/local/bin/{name}` symlink if it points inside an ArcBox bundle.
76    async fn cli_unlink(name: String) -> Result<(), String>;
77
78    /// Returns the helper version string.
79    async fn version() -> String;
80
81    // New methods append ONLY: the bincode transport encodes the request
82    // enum's variant index, so inserting a method above shifts every later
83    // ordinal and an upgrade-window daemon talking to the previous helper
84    // (launchd keeps it until reinstall) misdecodes calls silently.
85
86    /// Appends the fixed `127.0.0.1 ArcBox` alias to `/etc/hosts` so the
87    /// guest-data NFS mount can use `ArcBox:/` as its source (Finder shows
88    /// a mount by its source host name). Takes no arguments — the helper
89    /// never writes caller-controlled hosts entries.
90    async fn hosts_alias_install() -> Result<(), String>;
91
92    /// Removes the ArcBox alias line from `/etc/hosts`.
93    async fn hosts_alias_uninstall() -> Result<(), String>;
94
95    /// Checks whether the ArcBox `/etc/hosts` alias is installed.
96    async fn hosts_alias_status() -> Result<bool, String>;
97}
98
99/// Low-level connect — use [`client::Client::connect()`] instead.
100pub(crate) async fn connect() -> Result<HelperServiceClient, std::io::Error> {
101    let path = socket_path();
102    let transport =
103        tarpc::serde_transport::unix::connect(&path, tarpc::tokio_serde::formats::Bincode::default)
104            .await?;
105    Ok(HelperServiceClient::new(tarpc::client::Config::default(), transport).spawn())
106}