running_process/boot_autostart/
mod.rs1use std::fmt;
22use std::path::{Path, PathBuf};
23
24#[cfg(target_os = "linux")]
25pub mod linux;
26#[cfg(target_os = "macos")]
27pub mod macos;
28#[cfg(target_os = "windows")]
29pub mod windows;
30
31#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct UnitPath(pub PathBuf);
36
37impl UnitPath {
38 pub fn as_path(&self) -> &Path {
39 &self.0
40 }
41
42 pub fn into_inner(self) -> PathBuf {
43 self.0
44 }
45}
46
47impl fmt::Display for UnitPath {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "{}", self.0.display())
50 }
51}
52
53#[derive(Debug)]
55pub enum BootAutostartError {
56 Resolve(String),
58 Io(std::io::Error),
60 InitSystem(String),
65 Unsupported(String),
69}
70
71impl fmt::Display for BootAutostartError {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 BootAutostartError::Resolve(m) => write!(f, "could not resolve install path: {m}"),
75 BootAutostartError::Io(e) => write!(f, "filesystem error: {e}"),
76 BootAutostartError::InitSystem(m) => write!(f, "init system error: {m}"),
77 BootAutostartError::Unsupported(os) => {
78 write!(f, "boot autostart not supported on {os}")
79 }
80 }
81 }
82}
83
84impl std::error::Error for BootAutostartError {}
85
86impl From<std::io::Error> for BootAutostartError {
87 fn from(e: std::io::Error) -> Self {
88 BootAutostartError::Io(e)
89 }
90}
91
92pub fn install(daemon_binary: &Path) -> Result<UnitPath, BootAutostartError> {
99 #[cfg(target_os = "linux")]
100 {
101 linux::install(daemon_binary)
102 }
103 #[cfg(target_os = "macos")]
104 {
105 macos::install(daemon_binary)
106 }
107 #[cfg(target_os = "windows")]
108 {
109 windows::install(daemon_binary)
110 }
111 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
112 {
113 let _ = daemon_binary;
114 Err(BootAutostartError::Unsupported(
115 std::env::consts::OS.to_string(),
116 ))
117 }
118}
119
120pub fn uninstall() -> Result<(), BootAutostartError> {
122 #[cfg(target_os = "linux")]
123 {
124 linux::uninstall()
125 }
126 #[cfg(target_os = "macos")]
127 {
128 macos::uninstall()
129 }
130 #[cfg(target_os = "windows")]
131 {
132 windows::uninstall()
133 }
134 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
135 {
136 Err(BootAutostartError::Unsupported(
137 std::env::consts::OS.to_string(),
138 ))
139 }
140}
141
142pub fn render_unit(daemon_binary: &Path) -> String {
145 #[cfg(target_os = "linux")]
146 {
147 linux::render_unit(daemon_binary)
148 }
149 #[cfg(target_os = "macos")]
150 {
151 macos::render_unit(daemon_binary)
152 }
153 #[cfg(target_os = "windows")]
154 {
155 windows::render_unit(daemon_binary)
156 }
157 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
158 {
159 let _ = daemon_binary;
160 String::new()
161 }
162}
163
164#[cfg(any(target_os = "linux", test))]
173pub(crate) fn shell_quote_single(s: &str) -> String {
174 let mut out = String::with_capacity(s.len() + 2);
175 out.push('\'');
176 for ch in s.chars() {
177 if ch == '\'' {
178 out.push_str("'\\''");
180 } else {
181 out.push(ch);
182 }
183 }
184 out.push('\'');
185 out
186}
187
188#[cfg(any(target_os = "macos", test))]
191pub(crate) fn xml_escape(s: &str) -> String {
192 let mut out = String::with_capacity(s.len());
193 for ch in s.chars() {
194 match ch {
195 '<' => out.push_str("<"),
196 '>' => out.push_str(">"),
197 '&' => out.push_str("&"),
198 '"' => out.push_str("""),
199 '\'' => out.push_str("'"),
200 other => out.push(other),
201 }
202 }
203 out
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn shell_quote_wraps_simple_path() {
212 assert_eq!(shell_quote_single("/usr/bin/foo"), "'/usr/bin/foo'");
213 }
214
215 #[test]
216 fn shell_quote_escapes_embedded_single_quote() {
217 assert_eq!(shell_quote_single("o'malley"), "'o'\\''malley'");
218 }
219
220 #[test]
221 fn xml_escape_handles_metacharacters() {
222 assert_eq!(
223 xml_escape("a<b&c>d\"e'f"),
224 "a<b&c>d"e'f"
225 );
226 }
227}