Add documentation for logind/auto session

This commit is contained in:
Drakulix 2018-01-27 13:38:21 +01:00
parent 4501ca5fe1
commit 20e10612b9
3 changed files with 116 additions and 18 deletions

View File

@ -1,3 +1,34 @@
//!
//! Implementation of the `Session` trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```
use std::io::{Result as IoResult}; use std::io::{Result as IoResult};
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
@ -12,25 +43,39 @@ use super::{Session, SessionNotifier, SessionObserver, AsErrno};
use super::logind::{self, LogindSession, LogindSessionNotifier, BoundLogindSession, logind_session_bind}; use super::logind::{self, LogindSession, LogindSessionNotifier, BoundLogindSession, logind_session_bind};
use super::direct::{self, DirectSession, DirectSessionNotifier, direct_session_bind}; use super::direct::{self, DirectSession, DirectSessionNotifier, direct_session_bind};
/// `Session` using the best available inteface
#[derive(Clone)] #[derive(Clone)]
pub enum AutoSession { pub enum AutoSession {
/// Logind session
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
Logind(LogindSession), Logind(LogindSession),
/// Direct / tty session
Direct(Rc<RefCell<DirectSession>>), Direct(Rc<RefCell<DirectSession>>),
} }
/// `SessionNotifier` using the best available inteface
pub enum AutoSessionNotifier { pub enum AutoSessionNotifier {
/// Logind session nofifier
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
Logind(LogindSessionNotifier), Logind(LogindSessionNotifier),
/// Direct / tty session notifier
Direct(DirectSessionNotifier), Direct(DirectSessionNotifier),
} }
/// Bound session that is driven by the `wayland_server::EventLoop`.
///
/// See `auto_session_bind` for details.
///
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
pub enum BoundAutoSession { pub enum BoundAutoSession {
/// Bound logind session
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
Logind(BoundLogindSession), Logind(BoundLogindSession),
/// Bound direct / tty session
Direct(SignalEventSource<DirectSessionNotifier>), Direct(SignalEventSource<DirectSessionNotifier>),
} }
/// Id's used by the `AutoSessionNotifier` internally.
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub struct AutoId(AutoIdInternal); pub struct AutoId(AutoIdInternal);
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
@ -41,6 +86,7 @@ enum AutoIdInternal {
} }
impl AutoSession { impl AutoSession {
/// Tries to create a new session via the best available interface.
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)> pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
where L: Into<Option<::slog::Logger>> where L: Into<Option<::slog::Logger>>
@ -85,6 +131,11 @@ impl AutoSession {
} }
} }
/// Bind an `AutoSessionNotifier` to an `EventLoop`.
///
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind(notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle) -> IoResult<BoundAutoSession> { pub fn auto_session_bind(notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle) -> IoResult<BoundAutoSession> {
Ok(match notifier { Ok(match notifier {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
@ -171,10 +222,11 @@ impl SessionNotifier for AutoSessionNotifier {
} }
impl BoundAutoSession { impl BoundAutoSession {
pub fn remove(self) -> AutoSessionNotifier { /// Unbind the session from the `EventLoop` again
pub fn unbind(self) -> AutoSessionNotifier {
match self { match self {
#[cfg(feature = "backend_session_logind")] #[cfg(feature = "backend_session_logind")]
BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.close()), BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()), BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()),
} }
} }
@ -182,11 +234,11 @@ impl BoundAutoSession {
error_chain! { error_chain! {
links { links {
Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")]; Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
} }
foreign_links { foreign_links {
Direct(::nix::Error); Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
} }
} }

View File

@ -1,3 +1,35 @@
//!
//! Implementation of the `Session` trait through the logind dbus interface.
//!
//! This requires systemd and dbus to be available and started on the system.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `LogindSession::new`. A new session will be opened, if the
//! call is successful and will be closed once the `LogindSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `LogindSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `LogindSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```
use ::backend::session::{AsErrno, Session, SessionNotifier, SessionObserver}; use ::backend::session::{AsErrno, Session, SessionNotifier, SessionObserver};
use nix::fcntl::OFlag; use nix::fcntl::OFlag;
use nix::sys::stat::{stat, fstat, major, minor}; use nix::sys::stat::{stat, fstat, major, minor};
@ -21,17 +53,20 @@ struct LogindSessionImpl {
logger: ::slog::Logger, logger: ::slog::Logger,
} }
/// `Session` via the logind dbus interface
#[derive(Clone)] #[derive(Clone)]
pub struct LogindSession { pub struct LogindSession {
internal: Weak<LogindSessionImpl>, internal: Weak<LogindSessionImpl>,
seat: String, seat: String,
} }
/// `SessionNotifier` via the logind dbus interface
pub struct LogindSessionNotifier { pub struct LogindSessionNotifier {
internal: Rc<LogindSessionImpl> internal: Rc<LogindSessionImpl>
} }
impl LogindSession { impl LogindSession {
/// Tries to create a new session via the logind dbus interface.
pub fn new<L>(logger: L) -> Result<(LogindSession, LogindSessionNotifier)> pub fn new<L>(logger: L) -> Result<(LogindSession, LogindSessionNotifier)>
where where
L: Into<Option<::slog::Logger>> L: Into<Option<::slog::Logger>>
@ -39,17 +74,14 @@ impl LogindSession {
let logger = ::slog_or_stdlog(logger) let logger = ::slog_or_stdlog(logger)
.new(o!("smithay_module" => "backend_session", "session_type" => "logind")); .new(o!("smithay_module" => "backend_session", "session_type" => "logind"));
// Acquire session_id, seat and vt (if any) via libsystemd
let session_id = login::get_session(None).chain_err(|| ErrorKind::FailedToGetSession)?; let session_id = login::get_session(None).chain_err(|| ErrorKind::FailedToGetSession)?;
let seat = login::get_seat(session_id.clone()).chain_err(|| ErrorKind::FailedToGetSeat)?; let seat = login::get_seat(session_id.clone()).chain_err(|| ErrorKind::FailedToGetSeat)?;
/*let vt = if seat == "seat0" {
Some(login::get_vt(session_id.clone()).chain_err(|| ErrorKind::FailedToGetVT)?)
} else {
None
};*/
let vt = login::get_vt(session_id.clone()).ok(); let vt = login::get_vt(session_id.clone()).ok();
// Create dbus connection
let conn = Connection::get_private(BusType::System).chain_err(|| ErrorKind::FailedDbusConnection)?; let conn = Connection::get_private(BusType::System).chain_err(|| ErrorKind::FailedDbusConnection)?;
// and get the session path
let session_path = LogindSessionImpl::blocking_call( let session_path = LogindSessionImpl::blocking_call(
&conn, &conn,
"org.freedesktop.login1", "org.freedesktop.login1",
@ -60,6 +92,7 @@ impl LogindSession {
)?.get1::<DbusPath<'static>>() )?.get1::<DbusPath<'static>>()
.chain_err(|| ErrorKind::UnexpectedMethodReturn)?; .chain_err(|| ErrorKind::UnexpectedMethodReturn)?;
// Match all signals that we want to receive and handle
let match1 = String::from("type='signal',\ let match1 = String::from("type='signal',\
sender='org.freedesktop.login1',\ sender='org.freedesktop.login1',\
interface='org.freedesktop.login1.Manager',\ interface='org.freedesktop.login1.Manager',\
@ -85,6 +118,7 @@ impl LogindSession {
path='{}'", &session_path); path='{}'", &session_path);
conn.add_match(&match4).chain_err(|| ErrorKind::DbusMatchFailed(match4))?; conn.add_match(&match4).chain_err(|| ErrorKind::DbusMatchFailed(match4))?;
// Activate (switch to) the session and take control
LogindSessionImpl::blocking_call( LogindSessionImpl::blocking_call(
&conn, &conn,
"org.freedesktop.login1", "org.freedesktop.login1",
@ -93,7 +127,6 @@ impl LogindSession {
"Activate", "Activate",
None, None,
)?; )?;
LogindSessionImpl::blocking_call( LogindSessionImpl::blocking_call(
&conn, &conn,
"org.freedesktop.login1", "org.freedesktop.login1",
@ -128,6 +161,7 @@ impl LogindSession {
} }
impl LogindSessionNotifier { impl LogindSessionNotifier {
/// Creates a new session object beloging to this notifier.
pub fn session(&self) -> LogindSession { pub fn session(&self) -> LogindSession {
LogindSession { LogindSession {
internal: Rc::downgrade(&self.internal), internal: Rc::downgrade(&self.internal),
@ -357,12 +391,22 @@ impl SessionNotifier for LogindSessionNotifier {
} }
} }
/// Bound logind session that is driven by the `wayland_server::EventLoop`.
///
/// See `logind_session_bind` for details.
///
/// Dropping this object will close the logind session just like the `LogindSessionNotifier`.
pub struct BoundLogindSession { pub struct BoundLogindSession {
notifier: LogindSessionNotifier, notifier: LogindSessionNotifier,
watches: Vec<Watch>, _watches: Vec<Watch>,
sources: Vec<FdEventSource<Rc<LogindSessionImpl>>>, sources: Vec<FdEventSource<Rc<LogindSessionImpl>>>,
} }
/// Bind a `LogindSessionNotifier` to an `EventLoop`.
///
/// Allows the `LogindSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `LogindSessionNotifier` will not correctly tell you the logind
/// session state and call it's `SessionObservers`.
pub fn logind_session_bind( pub fn logind_session_bind(
notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle notifier: LogindSessionNotifier, evlh: &mut EventLoopHandle
) -> IoResult<BoundLogindSession> ) -> IoResult<BoundLogindSession>
@ -382,13 +426,14 @@ pub fn logind_session_bind(
Ok(BoundLogindSession { Ok(BoundLogindSession {
notifier, notifier,
watches, _watches: watches,
sources, sources,
}) })
} }
impl BoundLogindSession { impl BoundLogindSession {
pub fn close(self) -> LogindSessionNotifier { /// Unbind the logind session from the `EventLoop`
pub fn unbind(self) -> LogindSessionNotifier {
for source in self.sources { for source in self.sources {
source.remove(); source.remove();
} }
@ -399,6 +444,7 @@ impl BoundLogindSession {
impl Drop for LogindSessionNotifier { impl Drop for LogindSessionNotifier {
fn drop(&mut self) { fn drop(&mut self) {
info!(self.internal.logger, "Closing logind session"); info!(self.internal.logger, "Closing logind session");
// Release control again and drop everything closing the connection
let _ = LogindSessionImpl::blocking_call( let _ = LogindSessionImpl::blocking_call(
&*self.internal.conn.borrow(), &*self.internal.conn.borrow(),
"org.freedesktop.login1", "org.freedesktop.login1",

View File

@ -27,7 +27,7 @@
//! ### Usage of the session //! ### Usage of the session
//! //!
//! The session may be used to open devices manually through the `Session` interface //! The session may be used to open devices manually through the `Session` interface
//! or be passed to other object that need to open devices themselves. //! or be passed to other objects that need it to open devices themselves.
//! //!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a //! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`. //! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
@ -160,7 +160,7 @@ pub struct DirectSessionNotifier {
} }
impl DirectSession { impl DirectSession {
/// Tries to creates a new session via the legacy virtual terminal interface. /// Tries to create a new session via the legacy virtual terminal interface.
/// ///
/// If you do not provide a tty device path, it will try to open the currently active tty if any. /// If you do not provide a tty device path, it will try to open the currently active tty if any.
pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)> pub fn new<L>(tty: Option<&Path>, logger: L) -> Result<(DirectSession, DirectSessionNotifier)>
@ -362,9 +362,9 @@ impl SessionNotifier for DirectSessionNotifier {
/// Bind a `DirectSessionNotifier` to an `EventLoop`. /// Bind a `DirectSessionNotifier` to an `EventLoop`.
/// ///
/// Allows the `DirectSessionNotifier` to listen for the incoming signals signalling the session state. /// Allows the `DirectSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current /// If you don't use this function `DirectSessionNotifier` will not correctly tell you the current
/// session state. /// session state and call it's `SessionObservers`.
pub fn direct_session_bind( pub fn direct_session_bind(
notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle notifier: DirectSessionNotifier, evlh: &mut EventLoopHandle
) -> IoResult<SignalEventSource<DirectSessionNotifier>> { ) -> IoResult<SignalEventSource<DirectSessionNotifier>> {