pitchfork_cli/
boot_manager.rs1#[cfg(any(target_os = "macos", target_os = "linux", windows))]
4mod imp {
5 use crate::{Result, env};
6 #[cfg(target_os = "linux")]
7 use auto_launcher::LinuxLaunchMode;
8 #[cfg(target_os = "macos")]
9 use auto_launcher::MacOSLaunchMode;
10 use auto_launcher::{AutoLaunch, AutoLaunchBuilder};
11 use miette::IntoDiagnostic;
12
13 #[cfg(any(target_os = "macos", target_os = "linux"))]
14 fn build_launcher(
15 app_path: &str,
16 #[cfg(target_os = "macos")] macos_mode: MacOSLaunchMode,
17 #[cfg(target_os = "linux")] linux_mode: LinuxLaunchMode,
18 ) -> Result<AutoLaunch> {
19 let mut builder = AutoLaunchBuilder::new();
20 builder
21 .set_app_name("pitchfork")
22 .set_app_path(app_path)
23 .set_args(&["supervisor", "run", "--boot"]);
24
25 #[cfg(target_os = "macos")]
26 builder.set_macos_launch_mode(macos_mode);
27
28 #[cfg(target_os = "linux")]
29 builder.set_linux_launch_mode(linux_mode);
30
31 builder.build().into_diagnostic()
32 }
33
34 pub struct BootManager {
35 current: AutoLaunch,
37 other: AutoLaunch,
39 #[cfg(target_os = "macos")]
42 legacy: AutoLaunch,
43 }
44
45 impl BootManager {
46 pub fn new() -> Result<Self> {
47 let app_path = env::PITCHFORK_BIN.to_string_lossy().to_string();
48
49 #[cfg(target_os = "macos")]
50 let (current, other, legacy) = {
51 let is_root = nix::unistd::Uid::effective().is_root();
52 let (current_mode, other_mode) = if is_root {
53 (
54 MacOSLaunchMode::LaunchDaemonSystem,
55 MacOSLaunchMode::LaunchAgentUser,
56 )
57 } else {
58 (
59 MacOSLaunchMode::LaunchAgentUser,
60 MacOSLaunchMode::LaunchDaemonSystem,
61 )
62 };
63 (
64 build_launcher(&app_path, current_mode)?,
65 build_launcher(&app_path, other_mode)?,
66 build_launcher(&app_path, MacOSLaunchMode::LaunchAgentSystem)?,
67 )
68 };
69
70 #[cfg(target_os = "linux")]
71 let (current, other) = {
72 let is_root = nix::unistd::Uid::effective().is_root();
73 let (current_mode, other_mode) = if is_root {
74 (LinuxLaunchMode::SystemdSystem, LinuxLaunchMode::SystemdUser)
75 } else {
76 (LinuxLaunchMode::SystemdUser, LinuxLaunchMode::SystemdSystem)
77 };
78 (
79 build_launcher(&app_path, current_mode)?,
80 build_launcher(&app_path, other_mode)?,
81 )
82 };
83
84 #[cfg(windows)]
87 let (current, other) = (
88 AutoLaunchBuilder::new()
89 .set_app_name("pitchfork")
90 .set_app_path(&app_path)
91 .set_args(&["supervisor", "run", "--boot"])
92 .build()
93 .into_diagnostic()?,
94 AutoLaunchBuilder::new()
95 .set_app_name("pitchfork")
96 .set_app_path(&app_path)
97 .set_args(&["supervisor", "run", "--boot"])
98 .build()
99 .into_diagnostic()?,
100 );
101
102 #[cfg(target_os = "macos")]
103 return Ok(Self {
104 current,
105 other,
106 legacy,
107 });
108
109 #[cfg(not(target_os = "macos"))]
110 Ok(Self { current, other })
111 }
112
113 pub fn is_enabled(&self) -> Result<bool> {
115 #[cfg(target_os = "macos")]
116 return Ok(self.current.is_enabled().into_diagnostic()?
117 || self.other.is_enabled().into_diagnostic()?
118 || self.legacy.is_enabled().into_diagnostic()?);
119
120 #[cfg(not(target_os = "macos"))]
121 Ok(self.current.is_enabled().into_diagnostic()?
122 || self.other.is_enabled().into_diagnostic()?)
123 }
124
125 pub fn is_current_level_enabled(&self) -> Result<bool> {
127 self.current.is_enabled().into_diagnostic()
128 }
129
130 pub fn is_other_level_enabled(&self) -> Result<bool> {
135 #[cfg(target_os = "macos")]
136 return Ok(self.other.is_enabled().into_diagnostic()?
137 || (!nix::unistd::Uid::effective().is_root()
138 && self.legacy.is_enabled().into_diagnostic()?));
139
140 #[cfg(not(target_os = "macos"))]
141 self.other.is_enabled().into_diagnostic()
142 }
143
144 #[cfg(target_os = "macos")]
151 pub fn cleanup_legacy(&self, migrated: bool) -> Result<()> {
152 if nix::unistd::Uid::effective().is_root()
153 && self.legacy.is_enabled().into_diagnostic()?
154 {
155 self.legacy.disable().into_diagnostic()?;
156 if migrated {
157 info!(
158 "migrated legacy system-level launch entry from /Library/LaunchAgents/ to /Library/LaunchDaemons/"
159 );
160 } else {
161 info!("removed legacy system-level launch entry from /Library/LaunchAgents/");
162 }
163 }
164 Ok(())
165 }
166
167 pub fn enable(&self) -> Result<()> {
175 #[cfg(target_os = "macos")]
178 let other_conflict = if nix::unistd::Uid::effective().is_root() {
179 self.other.is_enabled().into_diagnostic()?
180 } else {
181 self.is_other_level_enabled()?
182 };
183
184 #[cfg(not(target_os = "macos"))]
185 let other_conflict = self.other.is_enabled().into_diagnostic()?;
186
187 if other_conflict {
188 miette::bail!(
189 "boot start is already registered at the other privilege level; \
190 run `pitchfork boot disable` (with appropriate privileges) to remove \
191 it first"
192 );
193 }
194
195 self.current.enable().into_diagnostic()?;
196
197 #[cfg(target_os = "macos")]
198 self.cleanup_legacy(true)?;
199
200 Ok(())
201 }
202
203 pub fn disable(&self) -> Result<()> {
209 if self.current.is_enabled().into_diagnostic()? {
210 self.current.disable().into_diagnostic()?;
211 }
212 if self.other.is_enabled().into_diagnostic()? {
213 self.other.disable().into_diagnostic()?;
214 }
215 #[cfg(target_os = "macos")]
216 if nix::unistd::Uid::effective().is_root()
217 && self.legacy.is_enabled().into_diagnostic()?
218 {
219 self.legacy.disable().into_diagnostic()?;
220 }
221 Ok(())
222 }
223 }
224}
225
226#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))]
229mod imp {
230 use crate::Result;
231
232 pub struct BootManager;
233
234 impl BootManager {
235 pub fn new() -> Result<Self> {
236 miette::bail!(
237 "boot management is not supported on this platform; \
238 only macOS, Linux, and Windows are supported"
239 )
240 }
241
242 pub fn is_enabled(&self) -> Result<bool> {
243 miette::bail!(
244 "boot management is not supported on this platform; \
245 only macOS, Linux, and Windows are supported"
246 )
247 }
248
249 pub fn is_current_level_enabled(&self) -> Result<bool> {
250 miette::bail!(
251 "boot management is not supported on this platform; \
252 only macOS, Linux, and Windows are supported"
253 )
254 }
255
256 pub fn is_other_level_enabled(&self) -> Result<bool> {
257 miette::bail!(
258 "boot management is not supported on this platform; \
259 only macOS, Linux, and Windows are supported"
260 )
261 }
262
263 pub fn enable(&self) -> Result<()> {
264 miette::bail!(
265 "boot management is not supported on this platform; \
266 only macOS, Linux, and Windows are supported"
267 )
268 }
269
270 pub fn disable(&self) -> Result<()> {
271 miette::bail!(
272 "boot management is not supported on this platform; \
273 only macOS, Linux, and Windows are supported"
274 )
275 }
276 }
277}
278
279pub use imp::BootManager;