1use 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: RefCell<u32>,
39 #[property(get, set = Self::set_resettable)]
41 pub resettable: RefCell<bool>,
42 socket: RefCell<Option<UnixStream>>,
45 to_server_channel: RefCell<Option<PipeWriter>>,
47 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 pub(super) connected: RefCell<bool>,
56
57 pub(super) connect_requests: RefCell<VecDeque<Rc<Request<()>>>>,
59
60 pub(super) start_session_requests: RefCell<VecDeque<Rc<Request<()>>>>,
62
63 pub(super) ensure_shared_data_dir_requests: RefCell<VecDeque<Rc<Request<String>>>>,
65
66 pub(super) hints: RefCell<HashMap<String, String>>,
68
69 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 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 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}