use std::process::{Command, Stdio};
use eyre::bail;
use crate::cmd::CmdLineRunner;
use crate::config::Settings;
use crate::result::Result;
pub(crate) fn is_root() -> bool {
#[cfg(unix)]
{
nix::unistd::geteuid().is_root()
}
#[cfg(not(unix))]
{
false
}
}
pub(crate) fn argv(program: &str, args: &[String]) -> Vec<String> {
argv_with_env(program, args, &[])
}
pub(crate) fn argv_with_env(
program: &str,
args: &[String],
envs: &[(String, String)],
) -> Vec<String> {
let mut argv = vec![];
if !is_root() && Settings::get().system_packages.sudo {
argv.push("sudo".to_string());
if !envs.is_empty() {
argv.push("env".to_string());
argv.extend(envs.iter().map(|(k, v)| format!("{k}={v}")));
}
}
argv.push(program.to_string());
argv.extend(args.iter().cloned());
argv
}
pub(crate) fn run(program: &str, args: &[String], envs: &[(String, String)]) -> Result<()> {
let argv = argv_with_env(program, args, envs);
let mut manual = vec!["sudo".to_string()];
if !envs.is_empty() {
manual.push("env".to_string());
manual.extend(envs.iter().map(|(k, v)| format!("{k}={v}")));
}
manual.push(program.to_string());
manual.extend(args.iter().cloned());
let manual_cmd = manual.join(" ");
if !is_root() {
if !Settings::get().system_packages.sudo {
bail!(
"not running as root and system_packages.sudo is disabled. Run manually:\n {manual_cmd}"
);
}
if crate::file::which("sudo").is_none() {
bail!(
"sudo not found. Run as root:\n {}",
manual_cmd.trim_start_matches("sudo ")
);
}
if !console::user_attended_stderr() {
let ok = Command::new("sudo")
.args(["-n", "true"])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false);
if !ok {
bail!(
"sudo requires a password but no TTY is available. Run manually:\n {manual_cmd}"
);
}
}
}
info!("$ {}", argv.join(" "));
let mut cmd = CmdLineRunner::new(&argv[0]);
for arg in &argv[1..] {
cmd = cmd.arg(arg);
}
for (k, v) in envs {
cmd = cmd.env(k, v);
}
cmd.raw(true).execute()
}