running_process_platform_internal/platform_linux/
autostart.rs1use std::path::PathBuf;
11use std::process::Command;
12
13use crate::platform::autostart::{shell_quote_single, AutostartError, AutostartProgram};
14
15pub fn render_registration(program: &AutostartProgram<'_>) -> String {
17 let binary = shell_quote_single(&program.program.to_string_lossy());
18 let description = program.description;
19 let start = program.start_argument;
20 let stop = program.stop_argument;
21 format!(
22 "[Unit]\n\
23 Description={description}\n\
24 After=default.target\n\
25 \n\
26 [Service]\n\
27 Type=simple\n\
28 ExecStart={binary} {start}\n\
29 ExecStop={binary} {stop}\n\
30 Restart=on-failure\n\
31 RestartSec=5\n\
32 \n\
33 [Install]\n\
34 WantedBy=default.target\n",
35 )
36}
37
38pub fn register(program: &AutostartProgram<'_>) -> Result<PathBuf, AutostartError> {
39 let path = unit_path(program)?;
40 if let Some(parent) = path.parent() {
41 std::fs::create_dir_all(parent)?;
42 }
43 std::fs::write(&path, render_registration(program))?;
44
45 let unit = unit_filename(program);
46 let _ = Command::new("systemctl")
47 .args(["--user", "daemon-reload"])
48 .status();
49 match Command::new("systemctl")
50 .args(["--user", "enable", &unit])
51 .status()
52 {
53 Ok(status) if status.success() => {}
54 Ok(status) => {
55 eprintln!("warning: systemctl --user enable {unit} returned non-zero ({status:?})");
56 }
57 Err(error) => {
58 eprintln!("warning: systemctl --user enable {unit} failed to spawn: {error}");
59 }
60 }
61
62 Ok(path)
63}
64
65pub fn unregister(program: &AutostartProgram<'_>) -> Result<(), AutostartError> {
66 let path = unit_path(program)?;
67 let unit = unit_filename(program);
68 let _ = Command::new("systemctl")
69 .args(["--user", "disable", &unit])
70 .status();
71 if path.exists() {
72 std::fs::remove_file(&path)?;
73 }
74 let _ = Command::new("systemctl")
75 .args(["--user", "daemon-reload"])
76 .status();
77 Ok(())
78}
79
80fn unit_filename(program: &AutostartProgram<'_>) -> String {
81 format!("{}.service", program.identifier)
82}
83
84fn unit_path(program: &AutostartProgram<'_>) -> Result<PathBuf, AutostartError> {
87 let base = match std::env::var_os("XDG_CONFIG_HOME") {
88 Some(value) if !value.is_empty() => PathBuf::from(value),
89 _ => {
90 let home = std::env::var_os("HOME").ok_or_else(|| {
91 AutostartError::Resolve("neither XDG_CONFIG_HOME nor HOME is set".into())
92 })?;
93 PathBuf::from(home).join(".config")
94 }
95 };
96 Ok(base
97 .join("systemd")
98 .join("user")
99 .join(unit_filename(program)))
100}