Skip to main content

podbox/
env.rs

1use std::env;
2use std::path::{Path, PathBuf};
3
4use anyhow::Result;
5use nix::unistd::getuid;
6
7/// Resolved host environment for socket and path detection.
8pub struct HostEnv {
9    pub uid: u32,
10    pub username: String,
11    pub xdg_runtime_dir: PathBuf,
12    pub wayland_display: Option<String>,
13    pub wayland_socket: Option<PathBuf>,
14    pub pipewire_socket: Option<PathBuf>,
15    pub pulse_dir: Option<PathBuf>,
16    pub dbus_socket: Option<PathBuf>,
17    pub gpu_has_dri: bool,
18    pub gpu_has_nvidia: bool,
19    pub gpu_has_nvidia_uvm: bool,
20    pub host_has_localtime: bool,
21    pub host_has_timezone_file: bool,
22    pub host_has_local_share_themes: bool,
23    pub host_has_local_share_icons: bool,
24    pub host_has_local_share_fonts: bool,
25    pub host_shell: Option<String>,
26    pub host_locale: Option<String>,
27    pub gpg_agent_socket: Option<PathBuf>,
28    pub gpg_home: Option<PathBuf>,
29}
30
31/// Resolve the host environment.
32///
33/// Reads `WAYLAND_DISPLAY`, `XDG_RUNTIME_DIR` from the environment.
34/// Detects presence of Wayland, PipeWire, PulseAudio, and D-Bus sockets.
35/// Detects timezone files and modern XDG theme/icon/font directories.
36pub fn resolve() -> Result<HostEnv> {
37    let uid = getuid().as_raw();
38    let username = env::var("USER")
39        .or_else(|_| env::var("LOGNAME"))
40        .ok()
41        .filter(|s| !s.is_empty())
42        .unwrap_or_else(|| {
43            nix::unistd::User::from_uid(nix::unistd::Uid::from_raw(uid))
44                .ok()
45                .flatten()
46                .map(|u| u.name)
47                .unwrap_or_else(|| "user".into())
48        });
49
50    let xdg_runtime_dir: PathBuf = env::var_os("XDG_RUNTIME_DIR")
51        .map(PathBuf::from)
52        .unwrap_or_else(|| PathBuf::from(format!("/run/user/{}", uid)));
53
54    let wayland_display = env::var("WAYLAND_DISPLAY").ok();
55    let wayland_socket = wayland_display
56        .as_ref()
57        .map(|d| xdg_runtime_dir.join(d))
58        .filter(|p| p.exists());
59
60    let pipewire_socket = Some(xdg_runtime_dir.join("pipewire-0")).filter(|p| p.exists());
61
62    let pulse_dir = Some(xdg_runtime_dir.join("pulse")).filter(|p| p.join("native").exists());
63
64    let dbus_socket = Some(xdg_runtime_dir.join("bus")).filter(|p| p.exists());
65
66    let gpu_has_dri = Path::new("/dev/dri").exists();
67    let gpu_has_nvidia = Path::new("/dev/nvidiactl").exists();
68    let gpu_has_nvidia_uvm = Path::new("/dev/nvidia-uvm").exists();
69
70    let host_has_localtime = Path::new("/etc/localtime").exists();
71    let host_has_timezone_file = Path::new("/etc/timezone").exists();
72
73    let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("/root"));
74    let host_has_local_share_themes = home.join(".local/share/themes").exists();
75    let host_has_local_share_icons = home.join(".local/share/icons").exists();
76    let host_has_local_share_fonts = home.join(".local/share/fonts").exists();
77
78    let host_shell = env::var("SHELL").ok().filter(|s| !s.is_empty());
79    let host_locale = env::var("LANG")
80        .ok()
81        .or_else(|| env::var("LC_ALL").ok())
82        .or_else(|| env::var("LC_CTYPE").ok())
83        .filter(|s| !s.is_empty());
84
85    let gpg_home = env::var("GNUPGHOME").ok().map(PathBuf::from).or_else(|| {
86        let fallback = dirs::home_dir()
87            .unwrap_or_else(|| PathBuf::from("/root"))
88            .join(".gnupg");
89        if fallback.exists() {
90            Some(fallback)
91        } else {
92            None
93        }
94    });
95    let gpg_agent_socket = gpg_home.as_ref().and_then(|gpg| {
96        let sock = gpg.join("S.gpg-agent");
97        if sock.exists() { Some(sock) } else { None }
98    });
99
100    Ok(HostEnv {
101        uid,
102        username,
103        xdg_runtime_dir,
104        wayland_display,
105        wayland_socket,
106        pipewire_socket,
107        pulse_dir,
108        dbus_socket,
109        gpu_has_dri,
110        gpu_has_nvidia,
111        gpu_has_nvidia_uvm,
112        host_has_localtime,
113        host_has_timezone_file,
114        host_has_local_share_themes,
115        host_has_local_share_icons,
116        host_has_local_share_fonts,
117        host_shell,
118        host_locale,
119        gpg_agent_socket,
120        gpg_home,
121    })
122}