Skip to content

Commit 7d47a5c

Browse files
committed
fix(sudo): handle root user case
1 parent 4cd08d0 commit 7d47a5c

1 file changed

Lines changed: 115 additions & 43 deletions

File tree

src/run/runner/helpers/run_with_sudo.rs

Lines changed: 115 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,137 @@ use std::{
44
process::{Command, Stdio},
55
};
66

7+
/// Check if we're running as root
8+
fn is_root() -> bool {
9+
#[cfg(unix)]
10+
{
11+
// On Unix-like systems, check if effective UID is 0
12+
nix::unistd::geteuid().is_root()
13+
}
14+
#[cfg(not(unix))]
15+
{
16+
false
17+
}
18+
}
19+
20+
/// Check if sudo is available and works without a password
21+
fn check_sudo_available() -> bool {
22+
// If we're already root, we don't need sudo
23+
if is_root() {
24+
debug!("Running as root, sudo not needed");
25+
return false;
26+
}
27+
28+
Command::new("sudo")
29+
.arg("--non-interactive")
30+
.arg("true")
31+
.stdout(Stdio::null())
32+
.stderr(Stdio::null())
33+
.status()
34+
.is_ok_and(|status| status.success())
35+
}
36+
737
/// Validate sudo access, prompting the user for their password if necessary
8-
fn validate_sudo_access() -> Result<()> {
9-
let needs_password = IsTerminal::is_terminal(&std::io::stdout())
10-
&& Command::new("sudo")
11-
.arg("--non-interactive") // Fail if password is required
12-
.arg("true")
13-
.stdout(Stdio::null())
14-
.stderr(Stdio::null())
38+
/// Returns true if sudo should be used, false if commands should run without sudo
39+
fn validate_sudo_access() -> Result<bool> {
40+
let is_terminal = IsTerminal::is_terminal(&std::io::stdout());
41+
42+
// First check if sudo is needed and available
43+
// This returns false if we're root (don't need sudo) or true if sudo works
44+
if check_sudo_available() {
45+
debug!("Sudo is available and configured for passwordless access");
46+
return Ok(true);
47+
}
48+
49+
// If we're running as root, we don't need sudo
50+
if is_root() {
51+
debug!("Running as root, commands will execute without sudo");
52+
return Ok(false);
53+
}
54+
55+
// If we're not in a terminal (e.g., CI environment), we can't prompt for password
56+
if !is_terminal {
57+
debug!("Not in a terminal and sudo requires password - will run without sudo");
58+
return Ok(false);
59+
}
60+
61+
// In a terminal, prompt for password
62+
suspend_progress_bar(|| {
63+
info!("Sudo privileges are required to continue. Please enter your password if prompted.");
64+
65+
// Validate and cache sudo credentials
66+
let auth_status = Command::new("sudo")
67+
.arg("--validate") // Validate and extend the timeout
68+
.stdin(Stdio::inherit())
69+
.stdout(Stdio::inherit())
70+
.stderr(Stdio::inherit())
1571
.status()
16-
.map(|status| !status.success())
17-
.unwrap_or(true);
18-
19-
if needs_password {
20-
suspend_progress_bar(|| {
21-
info!(
22-
"Sudo privileges are required to continue. Please enter your password if prompted."
23-
);
24-
25-
// Validate and cache sudo credentials
26-
let auth_status = Command::new("sudo")
27-
.arg("--validate") // Validate and extend the timeout
28-
.stdin(Stdio::inherit())
29-
.stdout(Stdio::inherit())
30-
.stderr(Stdio::inherit())
31-
.status()
32-
.map_err(|_| anyhow!("Failed to authenticate with sudo"))?;
33-
34-
if !auth_status.success() {
35-
bail!("Failed to authenticate with sudo");
36-
}
37-
Ok(())
38-
})?;
72+
.map_err(|_| anyhow!("Failed to authenticate with sudo"))?;
73+
74+
if !auth_status.success() {
75+
bail!("Failed to authenticate with sudo");
76+
}
77+
Ok(true)
78+
})
79+
}
80+
81+
/// Creates the base command (with or without sudo) after validating sudo access
82+
fn create_command() -> Result<(Command, bool)> {
83+
let use_sudo = validate_sudo_access()?;
84+
85+
if use_sudo {
86+
let mut cmd = Command::new("sudo");
87+
// Password prompt should not appear here since it has already been validated
88+
cmd.arg("--non-interactive");
89+
Ok((cmd, true))
90+
} else {
91+
// Return a dummy command - caller will need to handle this
92+
Ok((Command::new(""), false))
3993
}
40-
Ok(())
4194
}
4295

4396
/// Creates the base sudo command after validating sudo access
4497
pub fn validated_sudo_command() -> Result<Command> {
45-
validate_sudo_access()?;
98+
let use_sudo = validate_sudo_access()?;
99+
46100
let mut cmd = Command::new("sudo");
47-
// Password prompt should not appear here since it has already been validated
48-
cmd.arg("--non-interactive");
101+
if use_sudo {
102+
// Password prompt should not appear here since it has already been validated
103+
cmd.arg("--non-interactive");
104+
}
49105
Ok(cmd)
50106
}
51107

52-
/// Run a command with sudo after validating sudo access
108+
/// Run a command with sudo after validating sudo access (falls back to no sudo if unavailable)
53109
pub fn run_with_sudo(command_args: &[&str]) -> Result<()> {
54-
let command_str = command_args.join(" ");
55-
debug!("Running command with sudo: {command_str}");
56-
let output = validated_sudo_command()?
57-
.args(command_args)
58-
.stdout(Stdio::piped())
59-
.output()
60-
.map_err(|_| anyhow!("Failed to execute command with sudo: {command_str}"))?;
110+
let (mut base_cmd, use_sudo) = create_command()?;
111+
112+
let command_str = if use_sudo {
113+
format!("sudo {}", command_args.join(" "))
114+
} else {
115+
command_args.join(" ")
116+
};
117+
118+
debug!("Running command: {command_str}");
119+
120+
let output = if use_sudo {
121+
base_cmd
122+
.args(command_args)
123+
.stdout(Stdio::piped())
124+
.output()
125+
.map_err(|_| anyhow!("Failed to execute command with sudo: {command_str}"))?
126+
} else {
127+
Command::new(command_args[0])
128+
.args(&command_args[1..])
129+
.stdout(Stdio::piped())
130+
.output()
131+
.map_err(|_| anyhow!("Failed to execute command: {command_str}"))?
132+
};
61133

62134
if !output.status.success() {
63135
info!("stdout: {}", String::from_utf8_lossy(&output.stdout));
64136
error!("stderr: {}", String::from_utf8_lossy(&output.stderr));
65-
bail!("Failed to execute command with sudo: {command_str}");
137+
bail!("Failed to execute command: {command_str}");
66138
}
67139

68140
Ok(())

0 commit comments

Comments
 (0)