use crate::{Error, Result};
use async_io::Async;
use nb_connect::unix;
use nix::unistd::Uid;
use polling::{Event, Poller};
use std::{env, ffi::OsString, os::unix::net::UnixStream, str::FromStr};
#[derive(Debug, PartialEq)]
pub(crate) enum Address {
Unix(OsString),
}
#[derive(Debug)]
pub(crate) enum Stream {
Unix(UnixStream),
}
#[derive(Debug)]
pub(crate) enum AsyncStream {
Unix(Async<UnixStream>),
}
impl Address {
pub(crate) fn connect(&self, nonblocking: bool) -> Result<Stream> {
match self {
Address::Unix(p) => {
let stream = unix(p)?;
let poller = Poller::new()?;
poller.add(&stream, Event::writable(0))?;
poller.wait(&mut Vec::new(), None)?;
stream.set_nonblocking(nonblocking)?;
Ok(Stream::Unix(stream))
}
}
}
pub(crate) async fn connect_async(&self) -> Result<AsyncStream> {
match self {
Address::Unix(p) => Async::<UnixStream>::connect(p)
.await
.map(AsyncStream::Unix)
.map_err(Error::Io),
}
}
pub(crate) fn session() -> Result<Self> {
match env::var("DBUS_SESSION_BUS_ADDRESS") {
Ok(val) => Self::from_str(&val),
_ => {
let uid = Uid::current();
let path = format!("unix:path=/run/user/{}/bus", uid);
Self::from_str(&path)
}
}
}
pub(crate) fn system() -> Result<Self> {
match env::var("DBUS_SYSTEM_BUS_ADDRESS") {
Ok(val) => Self::from_str(&val),
_ => Self::from_str("unix:path=/var/run/dbus/system_bus_socket"),
}
}
}
impl FromStr for Address {
type Err = Error;
fn from_str(address: &str) -> Result<Self> {
let first = address.split(',').next().unwrap();
let parts = first.split(':').collect::<Vec<&str>>();
if parts.len() != 2 {
return Err(Error::Address("address has no colon".into()));
}
if parts[0] != "unix" {
return Err(Error::Address(format!(
"unsupported transport '{}'",
parts[0]
)));
}
let pathparts = parts[1].split('=').collect::<Vec<&str>>();
if pathparts.len() != 2 {
return Err(Error::Address("address is missing '='".into()));
}
let path = match pathparts[0] {
"path" => OsString::from(pathparts[1]),
"abstract" => {
let mut s = OsString::from("\0");
s.push(pathparts[1]);
s
}
_ => {
return Err(Error::Address(
"unix address is missing path or abstract".to_owned(),
))
}
};
Ok(Address::Unix(path))
}
}
#[cfg(test)]
mod tests {
use super::Address;
use crate::Error;
use std::str::FromStr;
#[test]
fn parse_dbus_addresses() {
match Address::from_str("foo").unwrap_err() {
Error::Address(e) => assert_eq!(e, "address has no colon"),
_ => panic!(),
}
match Address::from_str("tcp:localhost").unwrap_err() {
Error::Address(e) => assert_eq!(e, "unsupported transport 'tcp'"),
_ => panic!(),
}
assert_eq!(
Address::Unix("/tmp/dbus-foo".into()),
Address::from_str("unix:path=/tmp/dbus-foo").unwrap()
);
assert_eq!(
Address::Unix("/tmp/dbus-foo".into()),
Address::from_str("unix:path=/tmp/dbus-foo,guid=123").unwrap()
);
}
}