pitchfork_cli/
boot_manager.rs1use crate::{Result, env};
2#[cfg(target_os = "linux")]
3use auto_launcher::LinuxLaunchMode;
4#[cfg(target_os = "macos")]
5use auto_launcher::MacOSLaunchMode;
6use auto_launcher::{AutoLaunch, AutoLaunchBuilder};
7use miette::IntoDiagnostic;
8
9#[cfg(any(target_os = "macos", target_os = "linux"))]
10fn build_launcher(
11 app_path: &str,
12 #[cfg(target_os = "macos")] macos_mode: MacOSLaunchMode,
13 #[cfg(target_os = "linux")] linux_mode: LinuxLaunchMode,
14) -> Result<AutoLaunch> {
15 let mut builder = AutoLaunchBuilder::new();
16 builder
17 .set_app_name("pitchfork")
18 .set_app_path(app_path)
19 .set_args(&["supervisor", "run", "--boot"]);
20
21 #[cfg(target_os = "macos")]
22 builder.set_macos_launch_mode(macos_mode);
23
24 #[cfg(target_os = "linux")]
25 builder.set_linux_launch_mode(linux_mode);
26
27 builder.build().into_diagnostic()
28}
29
30pub struct BootManager {
31 current: AutoLaunch,
33 other: AutoLaunch,
35}
36
37impl BootManager {
38 pub fn new() -> Result<Self> {
39 let app_path = env::PITCHFORK_BIN.to_string_lossy().to_string();
40
41 #[cfg(target_os = "macos")]
42 let (current, other) = {
43 let is_root = nix::unistd::Uid::effective().is_root();
44 let (current_mode, other_mode) = if is_root {
45 (
46 MacOSLaunchMode::LaunchAgentSystem,
47 MacOSLaunchMode::LaunchAgentUser,
48 )
49 } else {
50 (
51 MacOSLaunchMode::LaunchAgentUser,
52 MacOSLaunchMode::LaunchAgentSystem,
53 )
54 };
55 (
56 build_launcher(&app_path, current_mode)?,
57 build_launcher(&app_path, other_mode)?,
58 )
59 };
60
61 #[cfg(target_os = "linux")]
62 let (current, other) = {
63 let is_root = nix::unistd::Uid::effective().is_root();
64 let (current_mode, other_mode) = if is_root {
65 (LinuxLaunchMode::SystemdSystem, LinuxLaunchMode::SystemdUser)
66 } else {
67 (LinuxLaunchMode::SystemdUser, LinuxLaunchMode::SystemdSystem)
68 };
69 (
70 build_launcher(&app_path, current_mode)?,
71 build_launcher(&app_path, other_mode)?,
72 )
73 };
74
75 #[cfg(windows)]
78 let (current, other) = (
79 AutoLaunchBuilder::new()
80 .set_app_name("pitchfork")
81 .set_app_path(&app_path)
82 .set_args(&["supervisor", "run", "--boot"])
83 .build()
84 .into_diagnostic()?,
85 AutoLaunchBuilder::new()
86 .set_app_name("pitchfork")
87 .set_app_path(&app_path)
88 .set_args(&["supervisor", "run", "--boot"])
89 .build()
90 .into_diagnostic()?,
91 );
92
93 #[cfg(not(any(target_os = "macos", target_os = "linux", windows)))]
95 compile_error!("pitchfork boot management is only supported on macOS, Linux, and Windows");
96
97 Ok(Self { current, other })
98 }
99
100 pub fn is_enabled(&self) -> Result<bool> {
102 Ok(self.current.is_enabled().into_diagnostic()?
103 || self.other.is_enabled().into_diagnostic()?)
104 }
105
106 pub fn is_current_level_enabled(&self) -> Result<bool> {
108 self.current.is_enabled().into_diagnostic()
109 }
110
111 pub fn is_other_level_enabled(&self) -> Result<bool> {
114 self.other.is_enabled().into_diagnostic()
115 }
116
117 pub fn enable(&self) -> Result<()> {
122 if self.other.is_enabled().into_diagnostic()? {
123 miette::bail!(
124 "boot start is already registered at the other privilege level; \
125 run `pitchfork boot disable` (with appropriate privileges) to remove \
126 it first"
127 );
128 }
129 self.current.enable().into_diagnostic()
130 }
131
132 pub fn disable(&self) -> Result<()> {
135 if self.current.is_enabled().into_diagnostic()? {
137 self.current.disable().into_diagnostic()?;
138 }
139 if self.other.is_enabled().into_diagnostic()? {
140 self.other.disable().into_diagnostic()?;
141 }
142 Ok(())
143 }
144}