Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

lightdm_client/
imp.rs

1// SPDX-FileCopyrightText: 2026 ZaynChen <[email protected]>
2//
3// SPDX-License-Identifier: MIT
4
5use gio::prelude::*;
6use glib::{
7    self, Properties, SourceId,
8    subclass::{Signal, prelude::*},
9};
10
11use std::{
12    cell::RefCell,
13    collections::{HashMap, VecDeque},
14    env,
15    io::{PipeReader, PipeWriter, Write},
16    os::{
17        fd::{AsRawFd, FromRawFd, OwnedFd},
18        unix::net::UnixStream,
19    },
20    rc::Rc,
21    sync::OnceLock,
22};
23
24use super::message::{GreeterMessage, GreeterMessageType, ServerMessage, ServerMessageType};
25use super::{GreeterError, Request};
26
27const API_VERSION: u32 = 1;
28
29pub const LIGHTDM_PROMPT_TYPE_VISIBLE: u32 = 0;
30pub const LIGHTDM_PROMPT_TYPE_SECRET: u32 = 1;
31pub const LIGHTDM_MESSAGE_TYPE_INFO: u32 = 0;
32pub const LIGHTDM_MESSAGE_TYPE_ERROR: u32 = 1;
33
34#[derive(Default, Properties)]
35#[properties(wrapper_type = super::Greeter)]
36pub struct Greeter {
37    /// API version the daemon is using
38    api_version: RefCell<u32>,
39    /// true if the daemon can reuse this greeter
40    #[property(get, set = Self::set_resettable)]
41    pub resettable: RefCell<bool>,
42    /// Socket connection to daemon
43    // socket: RefCell<Option<gio::Socket>>,
44    socket: RefCell<Option<UnixStream>>,
45    /// Channel to write to daemon
46    to_server_channel: RefCell<Option<PipeWriter>>,
47    /// Channel to read from daemon
48    from_server_channel: RefCell<Option<PipeReader>>,
49    from_server_watch: RefCell<Option<glib::SourceId>>,
50
51    pub(super) n_responses_waiting: RefCell<usize>,
52    pub(super) responses_received: RefCell<Vec<String>>,
53
54    /// true if have got a connect response
55    pub(super) connected: RefCell<bool>,
56
57    /// Pending connect requests
58    pub(super) connect_requests: RefCell<VecDeque<Rc<Request<()>>>>,
59
60    /// Pending start session requests
61    pub(super) start_session_requests: RefCell<VecDeque<Rc<Request<()>>>>,
62
63    /// Pending ensure shared data dir requests
64    pub(super) ensure_shared_data_dir_requests: RefCell<VecDeque<Rc<Request<String>>>>,
65
66    /// Hints provided by the daemon
67    pub(super) hints: RefCell<HashMap<String, String>>,
68
69    /// Timeout source to notify greeter to autologin
70    pub(super) autologin_timeout: RefCell<Option<SourceId>>,
71
72    #[property(get)]
73    pub authentication_user: RefCell<Option<String>>,
74    #[property(get)]
75    pub in_authentication: RefCell<bool>,
76    #[property(get)]
77    pub is_authenticated: RefCell<bool>,
78    pub(super) authenticate_sequence_number: RefCell<u32>,
79    pub(super) cancelling_authentication: RefCell<bool>,
80}
81
82#[glib::object_subclass]
83impl ObjectSubclass for Greeter {
84    const NAME: &'static str = "LightDMGreeter";
85    type Type = super::Greeter;
86    type ParentType = glib::Object;
87}
88
89#[glib::derived_properties]
90impl ObjectImpl for Greeter {
91    fn signals() -> &'static [Signal] {
92        static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
93        SIGNALS.get_or_init(|| {
94            vec![
95                Signal::builder("authentication-complete").build(),
96                Signal::builder("autologin-timer-expired").build(),
97                Signal::builder("idle").build(),
98                Signal::builder("reset").build(),
99                Signal::builder("show-message")
100                    .param_types([str::static_type(), u32::static_type()])
101                    .build(),
102                Signal::builder("show-prompt")
103                    .param_types([str::static_type(), u32::static_type()])
104                    .build(),
105            ]
106        })
107    }
108}
109
110impl Greeter {
111    ///
112    /// @resettable: Whether the greeter wants to be reset instead of killed after the user logs in
113    ///
114    /// Set whether the greeter will be reset instead of killed after the user lgs in,
115    /// This must be called before lightdm_greeter_connect is called.
116    fn set_resettable(&self, resettable: bool) {
117        if *self.connected.borrow() {
118            self.resettable.replace(resettable);
119        }
120    }
121
122    fn connect_to_daemon(&self) -> Result<(), glib::Error> {
123        if self.to_server_channel.borrow().is_some() && self.from_server_channel.borrow().is_some()
124        {
125            return Ok(());
126        }
127
128        let (to_fd, from_fd) = {
129            // Use private connection if one exists
130            let to_server_fd = env::var("LIGHTDM_TO_SERVER_FD");
131            let from_server_fd = env::var("LIGHTDM_FROM_SERVER_FD");
132            let pipe_path = env::var("LIGHTDM_GREETER_PIPE");
133
134            if let Ok(to_server_fd) = to_server_fd
135                && let Ok(from_server_fd) = from_server_fd
136            {
137                (
138                    to_server_fd.parse::<i32>().map_err(|_| {
139                        glib::Error::new(
140                            GreeterError::Connection,
141                            "Invalid file descriptor: LIGHTDM_TO_SERVER_FD={to_server_fd}",
142                        )
143                    })?,
144                    from_server_fd.parse::<i32>().map_err(|_| {
145                        glib::Error::new(
146                            GreeterError::Connection,
147                            "Invalid file descriptor: LIGHTDM_FROM_SERVER_FD={from_server_fd}",
148                        )
149                    })?,
150                )
151            } else if let Ok(pipe_path) = pipe_path {
152                let socket = UnixStream::connect(pipe_path)
153                    .map_err(|e| glib::Error::new(GreeterError::Connection, &e.to_string()))?;
154                let fd = socket.as_raw_fd();
155                self.socket.replace(Some(socket));
156                (fd, fd)
157            } else {
158                return Err(glib::Error::new(
159                    GreeterError::Connection,
160                    "Unable to dertermine socket to daemon",
161                ));
162            }
163        };
164
165        let to_server_writer =
166            PipeWriter::from(unsafe { OwnedFd::from_raw_fd(to_fd).try_clone().unwrap() });
167        self.to_server_channel.replace(Some(to_server_writer));
168        let from_server_reader =
169            PipeReader::from(unsafe { OwnedFd::from_raw_fd(from_fd).try_clone().unwrap() });
170        let from_server_reader_fd = from_server_reader.as_raw_fd();
171        self.from_server_channel.replace(Some(from_server_reader));
172
173        self.from_server_watch
174            .replace(Some(glib_unix::unix_fd_add_local(
175                from_server_reader_fd,
176                glib::IOCondition::IN,
177                glib::clone!(
178                    #[strong(rename_to = this)]
179                    self.obj(),
180                    move |_, _| {
181                        let imp = this.imp();
182                        match imp.recv_message() {
183                            Ok(message) => {
184                                imp.handle_message(message);
185                                glib::ControlFlow::Continue
186                            }
187                            Err(e) => {
188                                log::warn!("Failed to read from daemon: {e}");
189                                glib::ControlFlow::Break
190                            }
191                        }
192                    }
193                ),
194            )));
195
196        Ok(())
197    }
198
199    pub(super) fn send_message(&self, message: GreeterMessage) -> Result<(), glib::Error> {
200        self.connect_to_daemon()?;
201
202        let mut to_server_channel = self.to_server_channel.borrow_mut();
203        let buf = message.into_bytes();
204        let writer = to_server_channel.as_mut().unwrap();
205        writer.write_all(&buf).map_err(|e| {
206            glib::Error::new(
207                GreeterError::Communication,
208                &format!("Failed to write to daemon: {e}"),
209            )
210        })?;
211
212        log::debug!("Wrote {} bytes to daemon", buf.len());
213        writer.flush().map_err(|e| {
214            glib::Error::new(
215                GreeterError::Communication,
216                &format!("Failed to write to daemon: {e}"),
217            )
218        })
219    }
220
221    pub(super) fn send_connect(&self, resettable: bool) -> Result<(), glib::Error> {
222        log::debug!("Connecting to display manager...");
223        let mut message = GreeterMessage::builder(GreeterMessageType::Connect);
224        if resettable {
225            message = message.arg_u32(1);
226        } else {
227            message = message.arg_u32(0)
228        };
229        message = message.arg_u32(API_VERSION);
230        self.send_message(message.build())
231    }
232
233    pub(crate) fn send_start_session(&self, session: Option<String>) -> Result<(), glib::Error> {
234        let mut message = GreeterMessage::builder(GreeterMessageType::StartSession);
235        match session {
236            Some(session) => {
237                log::debug!("Starting session {session}");
238                message = message.arg_string(session);
239            }
240            None => log::debug!("Starting default session"),
241        }
242        self.send_message(message.build())
243    }
244
245    pub(crate) fn send_ensure_shared_data_dir(&self, username: String) -> Result<(), glib::Error> {
246        log::debug!("Ensuring data directory for user {username}");
247        let message = GreeterMessage::builder(GreeterMessageType::EnsureSharedDir)
248            .arg_string(username)
249            .build();
250        self.send_message(message)
251    }
252
253    pub(super) fn recv_message(&self) -> Result<ServerMessage, glib::Error> {
254        self.connect_to_daemon()?;
255
256        let mut from_server_channel = self.from_server_channel.borrow_mut();
257        let reader = from_server_channel.as_mut().unwrap();
258        ServerMessage::from_reader(reader)
259    }
260
261    pub(super) fn handle_message(&self, message: ServerMessage) {
262        match message.id {
263            ServerMessageType::Connected => self.handle_connected(false, message),
264            ServerMessageType::PromptAuthentication => self.handle_prompt_authentication(message),
265            ServerMessageType::EndAuthentication => self.handle_end_authentication(message),
266            ServerMessageType::SessionResult => self.handle_sessoin_result(message),
267            ServerMessageType::SharedDirResult => self.handle_shared_dir_result(message),
268            ServerMessageType::Idle => self.handle_idle(),
269            ServerMessageType::Reset => self.handle_reset(message),
270            ServerMessageType::ConnectedV2 => self.handle_connected(true, message),
271        }
272    }
273
274    fn handle_connected(&self, v2: bool, message: ServerMessage) {
275        let mut debug_string = String::from("Connected");
276        let mut reader = message.reader();
277        if v2 {
278            let api_version = reader.read_u32();
279            self.api_version.replace(api_version);
280            debug_string.push_str(&format!(" api={}", api_version));
281            let version = reader.read_string().unwrap();
282            debug_string.push_str(&format!(" version={}", version));
283            let n_env = reader.read_u32();
284            for _ in 0..n_env {
285                let name = reader.read_string().unwrap();
286                let value = reader.read_string().unwrap();
287                debug_string.push_str(&format!(" {}={}", name, value));
288                self.hints.borrow_mut().insert(name, value);
289            }
290        } else {
291            self.api_version.replace(0);
292            let version = reader.read_string().unwrap();
293            debug_string.push_str(&format!(" version={}", version));
294            while reader.is_empty() {
295                let name = reader.read_string().unwrap();
296                let value = reader.read_string().unwrap();
297                debug_string.push_str(&format!(" {}={}", name, value));
298                self.hints.borrow_mut().insert(name, value);
299            }
300        }
301
302        self.connected.replace(true);
303        log::debug!("{debug_string}");
304        let obj = self.obj();
305        let timeout = obj.autologin_timeout_hint();
306        if timeout > 0 {
307            log::debug!("Setting autologin timer for {timeout} seconds");
308            let source_id = glib::source::timeout_add_seconds_local_once(
309                timeout,
310                glib::clone!(
311                    #[strong(rename_to = this)]
312                    obj,
313                    move || {
314                        this.imp().autologin_timeout.take();
315                        this.imp().emit_autologin_timer_expired();
316                    },
317                ),
318            );
319            self.autologin_timeout.replace(Some(source_id));
320        }
321
322        if let Some(request) = self.connect_requests.borrow_mut().pop_front() {
323            request.finish(Ok(()));
324        }
325    }
326
327    fn handle_prompt_authentication(&self, message: ServerMessage) {
328        let mut reader = message.reader();
329
330        let sequence_number = reader.read_u32();
331        if sequence_number != *self.authenticate_sequence_number.borrow() {
332            log::debug!(
333                "Ignoring prompt authentication with invalid sequence number {sequence_number}"
334            );
335            return;
336        }
337
338        if *self.cancelling_authentication.borrow() {
339            log::debug!("Ignoring prompt authentication as waiting for it to cancel");
340            return;
341        }
342
343        let username = reader.read_string();
344        self.authentication_user.replace(username);
345        self.responses_received.borrow_mut().clear();
346
347        let n_messages = reader.read_u32();
348        log::debug!("Prompt user with {n_messages} message(s)");
349
350        for _ in 0..n_messages {
351            let style = reader.read_u32();
352            let text = reader.read_string();
353
354            match style {
355                1 => {
356                    self.n_responses_waiting.replace_with(|n| *n + 1);
357                    self.emit_show_prompt(text.unwrap(), LIGHTDM_PROMPT_TYPE_SECRET);
358                }
359                2 => {
360                    self.n_responses_waiting.replace_with(|n| *n + 1);
361                    self.emit_show_prompt(text.unwrap(), LIGHTDM_PROMPT_TYPE_VISIBLE);
362                }
363                3 => self.emit_show_message(text.unwrap(), LIGHTDM_MESSAGE_TYPE_ERROR),
364                4 => self.emit_show_message(text.unwrap(), LIGHTDM_MESSAGE_TYPE_INFO),
365                _ => {}
366            }
367        }
368    }
369
370    fn handle_end_authentication(&self, message: ServerMessage) {
371        let mut reader = message.reader();
372
373        let sequence_number = reader.read_u32();
374        if sequence_number != *self.authenticate_sequence_number.borrow() {
375            log::debug!(
376                "Ignoring prompt authentication with invalid sequence number {sequence_number}"
377            );
378            return;
379        }
380
381        let username = reader.read_string();
382        let return_code = reader.read_u32();
383        log::debug!(
384            "Authentication complete for user {} with return code {return_code}",
385            username.as_ref().unwrap_or(&"".to_string())
386        );
387
388        self.authentication_user.replace(username);
389        self.cancelling_authentication.replace(false);
390        self.is_authenticated.replace(return_code == 0);
391        self.in_authentication.replace(false);
392        self.emit_authentication_complete();
393    }
394
395    fn handle_sessoin_result(&self, message: ServerMessage) {
396        let mut reader = message.reader();
397        if let Some(request) = self.start_session_requests.borrow_mut().pop_front() {
398            let return_code = reader.read_u32();
399            let res = if return_code == 0 {
400                Ok(())
401            } else {
402                Err(glib::Error::new(
403                    GreeterError::Session,
404                    &format!("Session returned error code {return_code}"),
405                ))
406            };
407            request.finish(res);
408        }
409    }
410
411    fn handle_shared_dir_result(&self, message: ServerMessage) {
412        let mut reader = message.reader();
413        if let Some(request) = self
414            .ensure_shared_data_dir_requests
415            .borrow_mut()
416            .pop_front()
417        {
418            let res = match reader.read_string() {
419                Some(dir) => Ok(dir),
420                None => Err(glib::Error::new(GreeterError::InvalidUser, "No such user")),
421            };
422            request.finish(res);
423        }
424    }
425
426    fn handle_idle(&self) {
427        self.emit_idle()
428    }
429
430    fn handle_reset(&self, message: ServerMessage) {
431        self.hints.borrow_mut().clear();
432
433        let mut reader = message.reader();
434        let mut hint = String::new();
435        let mut hints = self.hints.borrow_mut();
436        while !reader.is_empty() {
437            let name = reader.read_string().unwrap();
438            let value = reader.read_string().unwrap();
439            hints.insert(name, value);
440            hint.push_str(" {name}={value}");
441        }
442
443        log::debug!("Reset{hint}");
444        self.emit_reset()
445    }
446
447    fn emit_authentication_complete(&self) {
448        self.obj()
449            .emit_by_name::<()>("authentication-complete", &[]);
450    }
451
452    fn emit_autologin_timer_expired(&self) {
453        self.obj()
454            .emit_by_name::<()>("autologin-timer-expired", &[]);
455    }
456
457    fn emit_show_prompt(&self, text: String, type_: u32) {
458        self.obj()
459            .emit_by_name::<()>("show-prompt", &[&text.to_value(), &type_.to_value()]);
460    }
461
462    fn emit_show_message(&self, text: String, type_: u32) {
463        self.obj()
464            .emit_by_name::<()>("show-message", &[&text.to_value(), &type_.to_value()]);
465    }
466
467    fn emit_idle(&self) {
468        self.obj().emit_by_name::<()>("idle", &[]);
469    }
470
471    fn emit_reset(&self) {
472        self.obj().emit_by_name::<()>("reset", &[]);
473    }
474}