async_rustbus/conn/
addr.rs

1use std::collections::HashMap;
2use std::ffi::OsStr;
3use std::os::unix::ffi::{OsStrExt, OsStringExt};
4
5use std::io::ErrorKind;
6use std::path::{Path, PathBuf};
7use tokio::net::ToSocketAddrs;
8
9/// The filesystem path for all system DBuses.
10pub const DBUS_SYS_PATH: &str = "/run/dbus/system_bus_socket";
11/// The environmental variable that contains address of the session DBus.
12pub const DBUS_SESS_ENV: &str = "DBUS_SESSION_BUS_ADDRESS";
13
14/// A address for connecting to a DBus dubs.
15/// These can be file systems paths to a Unix socket,
16/// a TCP address, or an abstract Unix socket.
17pub enum DBusAddr<P: AsRef<Path>, S: ToSocketAddrs, B: AsRef<[u8]>> {
18    Path(P),
19    Tcp(S),
20    #[cfg(target_os = "linux")]
21    Abstract(B),
22}
23/// Create a DbusAddr from a filesystem path.
24impl<P: AsRef<Path>> DBusAddr<P, &str, [u8; 0]> {
25    pub fn unix_path(path: P) -> Self {
26        Self::Path(path)
27    }
28}
29
30/// Create a DbusAddr from a TCP socket address.
31impl<S: ToSocketAddrs> DBusAddr<&str, S, [u8; 0]> {
32    pub fn tcp_addr(s: S) -> Self {
33        Self::Tcp(s)
34    }
35}
36
37/// Create a DbusAddr from an abstract unix socket address.
38#[cfg(target_os = "linux")]
39impl<B: AsRef<[u8]>> DBusAddr<&str, &str, B> {
40    pub fn unx_abstract(b: B) -> Self {
41        Self::Abstract(b)
42    }
43}
44
45/// Get the path of the system bus if it exists.
46pub async fn get_system_bus_addr() -> std::io::Result<DBusAddr<&'static Path, &'static str, [u8; 0]>>
47{
48    let path = Path::new(DBUS_SYS_PATH);
49    if path.exists() {
50        Ok(DBusAddr::Path(path))
51    } else {
52        Err(std::io::Error::new(
53            ErrorKind::NotFound,
54            "Could not find system bus.",
55        ))
56    }
57}
58
59const BAD_SESSION_ERR_MSG: &str = "Invalid session bus address in environment.";
60fn default_session_err() -> std::io::Error {
61    std::io::Error::new(ErrorKind::InvalidData, BAD_SESSION_ERR_MSG)
62}
63/// Get and parse address of the session DBus from the environment.
64pub async fn get_session_bus_addr() -> std::io::Result<DBusAddr<PathBuf, String, Vec<u8>>> {
65    let bytes = std::env::var_os(DBUS_SESS_ENV)
66        .ok_or_else(|| std::io::Error::new(ErrorKind::NotFound, "No DBus session in environment."))?
67        .into_vec();
68    let mut iter = bytes.split(|b| *b == b':');
69    let family = iter.next().unwrap();
70    if family.len() == bytes.len() {
71        return Err(default_session_err());
72    }
73    let data = &bytes[family.len() + 1..];
74    let data_pairs: HashMap<&[u8], &[u8]> = data
75        .split(|b| *b == b',')
76        .filter_map(|pair| {
77            let mut split = pair.split(|b| *b == b'=');
78            let name = split.next().unwrap();
79            let data = split.next()?;
80            match split.next() {
81                Some(_) => None,
82                None => Some((name, data)),
83            }
84        })
85        .collect();
86    match family {
87        b"unix" => {
88            #[cfg(target_os = "linux")]
89            {
90                if let Some(abs) = data_pairs.get(&b"abstract"[..]) {
91                    //return Ok(DBusAddr::Abstract(bytes[14..].to_owned()));
92                    return Ok(DBusAddr::Abstract((*abs).to_owned()));
93                }
94            }
95            if let Some(path) = data_pairs.get(&b"path"[..]) {
96                let path: &Path = OsStr::from_bytes(path).as_ref();
97                return if path.exists() {
98                    Ok(DBusAddr::Path(path.to_path_buf()))
99                } else {
100                    Err(std::io::Error::new(
101                        ErrorKind::NotFound,
102                        format!("Could not find session bus at {:?}.", path),
103                    ))
104                };
105            }
106            Err(default_session_err())
107        }
108        b"tcp" => {
109            let addr = || {
110                let host_data = data_pairs.get(&b"host"[..])?;
111                let mut host_str = std::str::from_utf8(host_data).ok()?.to_string();
112                let port_data = data_pairs.get(&b"port"[..])?;
113                let port_str = std::str::from_utf8(port_data).ok()?;
114                host_str.push(':');
115                host_str.push_str(port_str);
116                Some(host_str)
117            };
118            let addr = addr().ok_or_else(default_session_err)?;
119            Ok(DBusAddr::Tcp(addr))
120        }
121        _ => Err(default_session_err()),
122    }
123}