use std::{
io::Write,
path::Path,
process::{Child, Command, ExitStatus, Stdio},
};
use crate::PrivilegedOutput;
use crate::error::Result;
const ESCALLATION_SCRIPT: &str = r#"
on run argv
set toolPath to item 1 of argv
set prompt to item 2 of argv
set cmd to quoted form of toolPath
repeat with i from 3 to (count of argv)
set cmd to cmd & " " & (quoted form of (item i of argv))
end repeat
return do shell script cmd with administrator privileges with prompt prompt
end run
"#;
pub struct PrivilegedChildInner {
child: Child,
}
impl PrivilegedChildInner {
pub fn wait(self) -> Result<PrivilegedOutput> {
let output = self.child.wait_with_output()?;
Ok(PrivilegedOutput {
status: output.status,
stdout: Some(output.stdout),
stderr: Some(output.stderr),
})
}
pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
Ok(self.child.try_wait()?)
}
pub fn id(&self) -> Option<u32> {
Some(self.child.id())
}
}
fn spawn_gui(program: &Path, args: &[&str], prompt: Option<&str>) -> Result<PrivilegedChildInner> {
let mut process = Command::new("osascript")
.arg("-")
.arg(program)
.arg(prompt.unwrap_or(&format!(
"Administrator privileges required to launch {}",
program.display()
)))
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
process
.stdin
.take()
.expect("stdin piped")
.write_all(ESCALLATION_SCRIPT.as_bytes())?;
Ok(PrivilegedChildInner { child: process })
}
fn spawn_cli(program: &Path, args: &[&str], prompt: Option<&str>) -> Result<PrivilegedChildInner> {
let mut command = Command::new("sudo");
if let Some(prompt) = prompt {
command.arg("-p").arg(prompt);
}
let process = command
.arg("--")
.arg(program)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
Ok(PrivilegedChildInner { child: process })
}
pub fn spawn(
program: &Path,
args: &[&str],
gui: bool,
prompt: Option<&str>,
) -> Result<PrivilegedChildInner> {
if gui {
spawn_gui(program, args, prompt)
} else {
spawn_cli(program, args, prompt)
}
}
pub fn run(
program: &Path,
args: &[&str],
gui: bool,
prompt: Option<&str>,
) -> Result<PrivilegedOutput> {
spawn(program, args, gui, prompt)?.wait()
}