Rust · 3117 bytes Raw Blame History
1 //! Greeter process management
2 //!
3 //! Spawns and manages the greeter UI process.
4
5 use anyhow::{Context, Result};
6 use nix::unistd::User;
7 use std::os::unix::process::CommandExt;
8 use std::process::{Child, Command, ExitStatus, Stdio};
9
10 /// Greeter process wrapper
11 pub struct GreeterProcess {
12 process: Child,
13 }
14
15 impl GreeterProcess {
16 /// Start the greeter process
17 ///
18 /// If a `gardm` user exists, runs the greeter as that user.
19 /// Otherwise runs as the current user (for development).
20 pub fn start(greeter_cmd: &str, display: &str) -> Result<Self> {
21 let mut cmd = Command::new(greeter_cmd);
22
23 cmd.env("DISPLAY", display)
24 .env("XDG_SESSION_CLASS", "greeter")
25 .env("XDG_SESSION_TYPE", "x11")
26 .stdout(Stdio::inherit())
27 .stderr(Stdio::inherit());
28
29 // Try to run as gardm user if it exists
30 if let Some(user) = User::from_name("gardm").ok().flatten() {
31 let uid = user.uid;
32 let gid = user.gid;
33 let home = user.dir.to_string_lossy().to_string();
34
35 cmd.env("HOME", &home);
36 cmd.env("USER", "gardm");
37 cmd.env("LOGNAME", "gardm");
38
39 unsafe {
40 cmd.pre_exec(move || {
41 nix::unistd::setgid(gid)?;
42 nix::unistd::setuid(uid)?;
43 Ok(())
44 });
45 }
46
47 tracing::debug!("Starting greeter as gardm user");
48 } else {
49 tracing::warn!("gardm user not found, running greeter as current user");
50 }
51
52 let process = cmd.spawn().context("Failed to spawn greeter")?;
53
54 tracing::info!(
55 greeter = greeter_cmd,
56 pid = process.id(),
57 "Started greeter"
58 );
59
60 Ok(Self { process })
61 }
62
63 /// Check if greeter is still running
64 pub fn is_running(&mut self) -> bool {
65 matches!(self.process.try_wait(), Ok(None))
66 }
67
68 /// Wait for greeter to exit
69 pub fn wait(&mut self) -> Result<ExitStatus> {
70 self.process.wait().context("Failed to wait for greeter")
71 }
72
73 /// Kill the greeter process
74 pub fn kill(&mut self) -> Result<()> {
75 tracing::info!(pid = self.process.id(), "Killing greeter");
76
77 // Send SIGTERM first
78 let _ = nix::sys::signal::kill(
79 nix::unistd::Pid::from_raw(self.process.id() as i32),
80 nix::sys::signal::Signal::SIGTERM,
81 );
82
83 // Wait briefly for graceful exit
84 std::thread::sleep(std::time::Duration::from_millis(200));
85
86 // Force kill if still running
87 if self.is_running() {
88 self.process.kill().ok();
89 }
90
91 self.process.wait().context("Failed to wait for greeter")?;
92 Ok(())
93 }
94
95 /// Get the process ID
96 pub fn pid(&self) -> u32 {
97 self.process.id()
98 }
99 }
100
101 impl Drop for GreeterProcess {
102 fn drop(&mut self) {
103 if self.is_running() {
104 if let Err(e) = self.kill() {
105 tracing::warn!(error = %e, "Failed to kill greeter on drop");
106 }
107 }
108 }
109 }
110