1use std::fmt::Display;
2use std::path::PathBuf;
3#[cfg(not(target_os = "macos"))]
4use std::thread::sleep;
5use clap::{Parser};
6use lazy_static::lazy_static;
7use easy_trace::instruments::tracing;
8
9#[derive(Parser, Debug)]
10#[command(author, version, about)]
11pub struct PkgCli {
12 #[clap(long, short = 'i')]
14 pub install_service: bool,
15 #[clap(long, short = 'r')]
17 pub uninstall_service: bool,
18 #[arg(long, short = 'l')]
20 pub link_files: bool,
21 #[arg(long, short = 'd')]
23 pub remove_files: bool,
24 #[arg(long, short = 's')]
26 pub stop_services: bool,
27 #[arg(long, short = 't')]
29 pub launch_tray: bool,
30 #[arg(long, short = 'p')]
32 pub update_files: bool,
33 #[arg(long, short = 'u')]
36 pub check_and_update: bool,
37
38}
39
40
41lazy_static! {
42 pub static ref PKG_CLI: PkgCli = {
43 let cli = PkgCli::parse();
44 cli
45 };
46}
47
48pub fn parse_args() -> &'static PKG_CLI {
49 &PKG_CLI
50}
51
52impl PkgCli {
53 pub fn is_uninstall(&self) -> bool {
54 self.stop_services && self.remove_files
55 }
56
57 pub fn uninstall_args() -> &'static str {
58 "-sdr"
59 }
60
61 pub fn install_args() -> &'static str {
62 "-ltpi"
63 }
64
65 pub fn update_args() -> &'static str {
66 "-sultpi"
67 }
68}
69
70#[derive(PartialEq)]
71pub enum ElevatorMode {
72 Install,
73 Uninstall,
74 Update,
75}
76
77impl Display for ElevatorMode {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 ElevatorMode::Install => write!(f, "{}", PkgCli::install_args()),
81 ElevatorMode::Uninstall => write!(f, "{}", PkgCli::uninstall_args()),
82 ElevatorMode::Update => write!(f, "{}", PkgCli::update_args()),
83 }
84 }
85}
86
87
88
89pub fn launch_elevator(mode: ElevatorMode, working_dir: &PathBuf) -> sharing::PisPasResult<()> {
90 tracing::info!("Elevator in {} with args => {}", working_dir.display(), mode);
91 let elevator = sharing::paths::get_elevator_path(working_dir);
92
93 #[cfg(target_os = "macos")]
101 {
102 fn sh_quote(s: &str) -> String {
105 format!("'{}'", s.replace('\'', "'\\''"))
106 }
107
108 if unsafe { libc::geteuid() } == 0 {
114 let status = std::process::Command::new(&elevator)
115 .arg(mode.to_string())
116 .status()?;
117 if !status.success() {
118 return Err(anyhow::anyhow!("elevator (root) falló con estado {}", status));
119 }
120 tracing::info!("Elevator (root) terminó correctamente");
121 return Ok(());
122 }
123
124 let home = std::env::var("HOME").unwrap_or_default();
127 let shell_cmd = format!(
128 "HOME={} {} {}",
129 sh_quote(&home),
130 sh_quote(&elevator.to_string_lossy()),
131 mode
132 );
133 let script = format!(
134 "do shell script \"{}\" with administrator privileges",
135 shell_cmd.replace('\\', "\\\\").replace('"', "\\\"")
136 );
137 let status = std::process::Command::new("osascript")
138 .arg("-e")
139 .arg(&script)
140 .status()?;
141 if !status.success() {
142 return Err(anyhow::anyhow!("osascript (admin) falló con estado {}", status));
143 }
144 tracing::info!("Elevación (osascript admin) completada");
145 return Ok(());
146 }
147
148 #[cfg(not(target_os = "macos"))]
149 {
150 let child = std::process::Command::new(elevator)
151 .arg(mode.to_string())
152 .spawn()?;
153 tracing::info!("Elevated process launched detached with pid => {:?}", child.id());
154 if mode != ElevatorMode::Update {
156 sleep(std::time::Duration::from_secs(3));
157 }
158 Ok(())
159 }
160}
161