1818import shlex
1919import subprocess
2020import tempfile
21+ import configparser
22+ import platform
23+ import logging
2124from abc import ABC , abstractmethod
2225
2326from avocado .utils import astring , process
2427
2528DATA_SIZE = 200000
29+ log = logging .getLogger ("avocado.sysinfo" )
2630
2731
2832class CollectibleException (Exception ):
@@ -168,6 +172,12 @@ def collect(self):
168172 # but the avocado.utils.process APIs define no timeouts as "None"
169173 if int (self .timeout ) <= 0 :
170174 self .timeout = None
175+
176+ # Determine whether to run with sudo (do not mutate the command string)
177+ sysinfo_cmd = SysinfoCommand ()
178+ sudo_flag = sysinfo_cmd .use_sudo () and sysinfo_cmd .is_sudo_cmd (self .cmd )
179+ log .info ("Executing Command%s: %s" , " (sudo)" if sudo_flag else "" , self .cmd )
180+
171181 try :
172182 result = process .run (
173183 self .cmd ,
@@ -176,6 +186,7 @@ def collect(self):
176186 ignore_status = True ,
177187 shell = True ,
178188 env = env ,
189+ sudo = sudo_flag ,
179190 )
180191 yield result .stdout
181192 except FileNotFoundError as exc_fnf :
@@ -394,3 +405,64 @@ def collect(self):
394405 raise CollectibleException (
395406 f"Not logging { self .path } " f"(lack of permissions)"
396407 ) from exc
408+
409+
410+ class SysinfoCommand :
411+ def __init__ (self ):
412+ from avocado .utils .process import can_sudo
413+ self .sudo_available = can_sudo ()
414+ config = configparser .ConfigParser ()
415+ candidates = [
416+ os .environ .get ("AVOCADO_SYSINFO_CONF" ),
417+ os .path .join (os .path .dirname (__file__ ), ".." , "etc" , "avocado" , "sysinfo.conf" ),
418+ "/etc/avocado/sysinfo.conf" ,
419+ os .path .expanduser ("~/.config/avocado/sysinfo.conf" ),
420+ ]
421+ config .read ([p for p in candidates if p ])
422+ raw = config .get ('sysinfo' , 'sudo_commands' , fallback = '' )
423+ self .sudo_cmds = {os .path .basename (c .strip ()).lower () for c in raw .split (',' ) if c .strip ()}
424+
425+ distros_raw = config .get ('sysinfo' , 'sudo_for_distros' , fallback = '' )
426+ self .sudo_distros = {d .strip ().lower () for d in distros_raw .split (',' ) if d .strip ()}
427+
428+ def use_sudo (self ):
429+ """
430+ Determine if 'sudo' should be used based on the system type.
431+
432+ Returns:
433+ bool: True if 'sudo' should be used, False otherwise.
434+ """
435+ if not self .sudo_available :
436+ return False
437+ system_name = platform .system ().lower ()
438+ if system_name == 'linux' :
439+ if hasattr (os , "geteuid" ) and os .geteuid () == 0 :
440+ return False
441+ try :
442+ with open ('/etc/os-release' ) as f :
443+ for line in f :
444+ if line .startswith ('ID=' ):
445+ os_id = line .strip ().split ('=' )[1 ].strip ('"' )
446+ return os_id .lower () in self .sudo_distros
447+ except FileNotFoundError :
448+ log .warning ("/etc/os-release not found." )
449+ return False
450+ return False
451+ return False
452+
453+ def is_sudo_cmd (self , cmd ):
454+ """
455+ Determine if 'sudo' should be used for a specific command based on the configuration.
456+
457+ Args:
458+ cmd (str): The command to check.
459+
460+ Returns:
461+ bool: True if 'sudo' should be used, False otherwise.
462+ """
463+ try :
464+ first = shlex .split (cmd or "" )[0 ]
465+ except (ValueError , IndexError ):
466+ return False
467+ base = os .path .basename (first ).lower ()
468+ return base in self .sudo_cmds
0 commit comments