desk-logind 1.1.1

Bindings for the systemd-logind D-Bus API
Documentation
//! `systemd-logind` client library
#![feature(backtrace)]
use std::env;
use std::time::Duration;

use dbus::blocking::{Connection, Proxy};
use dbus::Message;

use crate::api::manager::{
    OrgFreedesktopLogin1Manager, OrgFreedesktopLogin1ManagerPrepareForSleep,
};
pub use crate::error::LogindError;
use crate::inhibitor::{InhibitEventSet, InhibitMode, InhibitorLock};
pub use crate::session::{Session, SessionId};

mod api;
mod error;
pub mod inhibitor;
mod session;

pub fn session_id() -> Result<SessionId, LogindError> {
    match env::var("XDG_SESSION_ID") {
        Ok(id) => Ok(SessionId::new(id)),
        Err(_) => Err(LogindError::no_session_id()),
    }
}

/// A logind client connection. This is a relatively thin wrapper over the
/// [D-Bus API](https://www.freedesktop.org/wiki/Software/systemd/logind/).
pub struct Logind<'a> {
    conn: &'a Connection,
    timeout: Duration,
}

impl<'a> Logind<'a> {
    pub fn new(conn: &'a Connection) -> Logind {
        Logind {
            conn,
            timeout: Duration::from_millis(500),
        }
    }

    /// Get a handle to a logind session by ID.
    pub fn session(&self, id: &SessionId) -> Result<Session<'a>, LogindError> {
        let manager = self.manager();
        let path = manager.get_session(id.as_str())?;
        let proxy = Proxy::new(
            "org.freedesktop.login1",
            path,
            self.timeout,
            self.conn,
        );
        Ok(Session::new(proxy))
    }

    /// Get a handle to the current logind session.
    pub fn current_session(&self) -> Result<Session<'a>, LogindError> {
        let id = session_id()?;
        self.session(&id)
    }

    /// Attempt to suspend the system. If `interactive`, PolicyKit may prompt the current user
    /// for authentication if needed.
    pub fn suspend(&self, interactive: bool) -> Result<(), LogindError> {
        let manager = self.manager();
        manager.suspend(interactive)?;
        Ok(())
    }

    /// Attempt to reboot the system. If `interactive`, PolicyKit may prompt the current user for
    /// authentication.
    pub fn reboot(&self, interactive: bool) -> Result<(), LogindError> {
        let manager = self.manager();
        manager.reboot(interactive)?;
        Ok(())
    }

    /// Attempt to power off the system. If `interactive`, PolicyKit may prompt the current user for
    /// authentication.
    pub fn power_off(&self, interactive: bool) -> Result<(), LogindError> {
        let manager = self.manager();
        manager.power_off(interactive)?;
        Ok(())
    }

    /// Attempt to hibernate the system. If `interactive`, PolicyKit may prompt the current user for
    /// authentication.
    pub fn hibernate(&self, interactive: bool) -> Result<(), LogindError> {
        let manager = self.manager();
        manager.hibernate(interactive)?;
        Ok(())
    }

    pub fn inhibit(
        &self,
        who: &str,
        why: &str,
        events: &InhibitEventSet,
        mode: InhibitMode,
    ) -> Result<InhibitorLock, LogindError> {
        let manager = self.manager();
        let fd = manager.inhibit(events.as_str(), who, why, mode.as_str())?;
        Ok(InhibitorLock::new(fd))
    }

    pub fn on_sleep<F: Fn(Logind) + Send + 'static, G: Fn(Logind) + Send + 'static>(
        &self,
        pre_sleep: F,
        post_sleep: G,
    ) -> Result<(), LogindError> {
        let manager = self.manager();
        match manager.match_signal(
            move |signal: OrgFreedesktopLogin1ManagerPrepareForSleep,
                  conn: &Connection,
                  _: &Message| {
                if signal.arg0 {
                    // TODO: would be nice to make `Logind` less tied to dbus' threading model where we have to keep making new instances
                    pre_sleep(Logind::new(conn));
                } else {
                    post_sleep(Logind::new(conn));
                }
                true
            },
        ) {
            Ok(_) => Ok(()),
            Err(e) => Err(LogindError::match_failed("PrepareForSleep", e)),
        }
    }

    fn manager(&self) -> Proxy<'_, &'a Connection> {
        Proxy::new(
            "org.freedesktop.login1",
            "/org/freedesktop/login1",
            self.timeout,
            self.conn,
        )
    }
}