@@ -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
4497pub 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)
53109pub 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