smithay/backend/session/
libseat.rs

1//!
2//! Implementation of the [`Session`] trait through the libseat.
3//!
4//! This requires libseat to be available on the system.
5
6use libseat::{Seat, SeatEvent};
7use std::{
8    cell::RefCell,
9    collections::HashMap,
10    os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd},
11    path::Path,
12    rc::{Rc, Weak},
13    sync::{
14        atomic::{AtomicBool, Ordering},
15        Arc,
16    },
17};
18
19use rustix::{fs::OFlags, io::Errno};
20
21use calloop::{
22    channel::{self, Channel},
23    EventSource, Poll, PostAction, Readiness, Token, TokenFactory,
24};
25
26use crate::backend::session::{AsErrno, Event as SessionEvent, Session};
27
28use tracing::{debug, error, info_span, instrument};
29
30#[derive(Debug)]
31struct LibSeatSessionImpl {
32    seat: RefCell<Seat>,
33    active: Arc<AtomicBool>,
34    devices: RefCell<HashMap<RawFd, libseat::Device>>,
35}
36
37impl Drop for LibSeatSessionImpl {
38    fn drop(&mut self) {
39        debug!("Closing seat")
40    }
41}
42
43/// [`Session`] via the libseat
44#[derive(Debug, Clone)]
45pub struct LibSeatSession {
46    internal: Weak<LibSeatSessionImpl>,
47    seat_name: String,
48    span: tracing::Span,
49}
50
51/// `SessionNotifier` via the libseat
52#[derive(Debug)]
53pub struct LibSeatSessionNotifier {
54    internal: Rc<LibSeatSessionImpl>,
55    rx: Channel<SeatEvent>,
56    token: Option<Token>,
57    span: tracing::Span,
58}
59
60impl LibSeatSession {
61    /// Tries to create a new session via libseat.
62    pub fn new() -> Result<(LibSeatSession, LibSeatSessionNotifier), Error> {
63        let span = info_span!("backend_session", "type" = "libseat");
64        let _guard = span.enter();
65        let (tx, rx) = calloop::channel::channel();
66
67        let seat = {
68            Seat::open(move |_seat, event| match event {
69                SeatEvent::Enable => {
70                    debug!("Enable callback called");
71                    tx.send(event).unwrap();
72                }
73                SeatEvent::Disable => {
74                    debug!("Disable callback called");
75                    tx.send(event).unwrap();
76                }
77            })
78        };
79
80        drop(_guard);
81        seat.map(|mut seat| {
82            let seat_name = seat.name().to_owned();
83
84            // In some cases enable_seat event is avalible right after startup
85            // so, we can dispatch it
86            seat.dispatch(0).unwrap();
87            let active = matches!(rx.try_recv(), Ok(SeatEvent::Enable));
88
89            let internal = Rc::new(LibSeatSessionImpl {
90                seat: RefCell::new(seat),
91                active: Arc::new(AtomicBool::new(active)),
92                devices: RefCell::new(HashMap::new()),
93            });
94
95            let session = LibSeatSession {
96                internal: Rc::downgrade(&internal),
97                seat_name,
98                span: span.clone(),
99            };
100
101            let notifier = LibSeatSessionNotifier {
102                internal,
103                rx,
104                token: None,
105                span,
106            };
107
108            (session, notifier)
109        })
110        .map_err(|err| Error::FailedToOpenSession(Errno::from_raw_os_error(err.into())))
111    }
112}
113
114impl Session for LibSeatSession {
115    type Error = Error;
116
117    #[instrument(parent = &self.span, skip(self))]
118    fn open(&mut self, path: &Path, _flags: OFlags) -> Result<OwnedFd, Self::Error> {
119        if let Some(session) = self.internal.upgrade() {
120            debug!("Opening device: {:?}", path);
121
122            session
123                .seat
124                .borrow_mut()
125                .open_device(&path)
126                .map(|device| {
127                    let raw_fd = device.as_fd().as_raw_fd();
128
129                    session.devices.borrow_mut().insert(raw_fd, device);
130
131                    // SAFETY: `libseat::Device` does not close fd on drop
132                    unsafe { OwnedFd::from_raw_fd(raw_fd) }
133                })
134                .map_err(|err| Error::FailedToOpenDevice(Errno::from_raw_os_error(err.into())))
135        } else {
136            Err(Error::SessionLost)
137        }
138    }
139
140    #[instrument(parent = &self.span, skip(self))]
141    fn close(&mut self, fd: OwnedFd) -> Result<(), Self::Error> {
142        if let Some(session) = self.internal.upgrade() {
143            debug!("Closing device: {:?}", fd);
144
145            let out = if let Some(dev) = session.devices.borrow_mut().remove(&fd.as_fd().as_raw_fd()) {
146                session
147                    .seat
148                    .borrow_mut()
149                    .close_device(dev)
150                    .map_err(|err| Error::FailedToCloseDevice(Errno::from_raw_os_error(err.into())))
151            } else {
152                Ok(())
153            };
154
155            // `fd` is closed on drop
156
157            out
158        } else {
159            Err(Error::SessionLost)
160        }
161    }
162
163    #[instrument(parent = &self.span, skip(self))]
164    fn change_vt(&mut self, vt: i32) -> Result<(), Self::Error> {
165        if let Some(session) = self.internal.upgrade() {
166            debug!("Session switch: {:?}", vt);
167            session
168                .seat
169                .borrow_mut()
170                .switch_session(vt)
171                .map_err(|err| Error::FailedToChangeVt(Errno::from_raw_os_error(err.into())))
172        } else {
173            Err(Error::SessionLost)
174        }
175    }
176
177    fn is_active(&self) -> bool {
178        if let Some(internal) = self.internal.upgrade() {
179            internal.active.load(Ordering::SeqCst)
180        } else {
181            false
182        }
183    }
184
185    fn seat(&self) -> String {
186        self.seat_name.clone()
187    }
188}
189
190impl LibSeatSessionNotifier {
191    /// Creates a new session object belonging to this notifier.
192    pub fn session(&self) -> LibSeatSession {
193        LibSeatSession {
194            internal: Rc::downgrade(&self.internal),
195            seat_name: self.internal.seat.borrow_mut().name().to_owned(),
196            span: self.span.clone(),
197        }
198    }
199}
200
201impl EventSource for LibSeatSessionNotifier {
202    type Event = SessionEvent;
203    type Metadata = ();
204    type Ret = ();
205    type Error = Error;
206
207    #[profiling::function]
208    fn process_events<F>(
209        &mut self,
210        readiness: Readiness,
211        token: Token,
212        mut callback: F,
213    ) -> Result<PostAction, Error>
214    where
215        F: FnMut(SessionEvent, &mut ()),
216    {
217        if Some(token) == self.token {
218            self.internal.seat.borrow_mut().dispatch(0).unwrap();
219        }
220
221        let internal = &self.internal;
222        self.rx
223            .process_events(readiness, token, |event, _| match event {
224                channel::Event::Msg(event) => match event {
225                    SeatEvent::Enable => {
226                        internal.active.store(true, Ordering::SeqCst);
227                        callback(SessionEvent::ActivateSession, &mut ());
228                    }
229                    SeatEvent::Disable => {
230                        internal.active.store(false, Ordering::SeqCst);
231                        internal.seat.borrow_mut().disable().unwrap();
232                        callback(SessionEvent::PauseSession, &mut ());
233                    }
234                },
235                channel::Event::Closed => {
236                    // Tx is stored inside of Seat, and Rc<Seat> is stored in LibSeatSessionNotifier so this is unreachable
237                }
238            })
239            .map_err(|_| Error::SessionLost)
240    }
241
242    fn register(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
243        self.rx.register(poll, factory)?;
244
245        self.token = Some(factory.token());
246        let mut seat = self.internal.seat.borrow_mut();
247        // Safety: the seat fd cannot be close without removing the LibSeatSessionNotifier from the event loop
248        unsafe {
249            poll.register(
250                seat.get_fd().unwrap(),
251                calloop::Interest::READ,
252                calloop::Mode::Level,
253                self.token.unwrap(),
254            )
255        }
256    }
257
258    fn reregister(&mut self, poll: &mut Poll, factory: &mut TokenFactory) -> calloop::Result<()> {
259        self.rx.reregister(poll, factory)?;
260
261        self.token = Some(factory.token());
262        let mut seat = self.internal.seat.borrow_mut();
263        poll.reregister(
264            seat.get_fd().unwrap(),
265            calloop::Interest::READ,
266            calloop::Mode::Level,
267            self.token.unwrap(),
268        )
269    }
270
271    fn unregister(&mut self, poll: &mut Poll) -> calloop::Result<()> {
272        self.rx.unregister(poll)?;
273
274        self.token = None;
275        let mut seat = self.internal.seat.borrow_mut();
276        poll.unregister(seat.get_fd().unwrap())
277    }
278}
279
280/// Errors related to direct/tty sessions
281#[derive(thiserror::Error, Debug)]
282pub enum Error {
283    /// Failed to open session
284    #[error("Failed to open session: {0}")]
285    FailedToOpenSession(Errno),
286
287    /// Failed to open device
288    #[error("Failed to open device: {0}")]
289    FailedToOpenDevice(Errno),
290
291    /// Failed to close device
292    #[error("Failed to close device: {0}")]
293    FailedToCloseDevice(Errno),
294
295    /// Failed to close device
296    #[error("Failed to change vt: {0}")]
297    FailedToChangeVt(Errno),
298
299    /// Session is already closed,
300    #[error("Session is already closed")]
301    SessionLost,
302}
303
304impl AsErrno for Error {
305    fn as_errno(&self) -> Option<i32> {
306        match self {
307            &Self::FailedToOpenSession(errno)
308            | &Self::FailedToOpenDevice(errno)
309            | &Self::FailedToCloseDevice(errno)
310            | &Self::FailedToChangeVt(errno) => Some(errno.raw_os_error()),
311            _ => None,
312        }
313    }
314}