4
4
from .._core import SHELL_NAMES , ShellDetectionFailure
5
5
from . import proc , ps
6
6
7
-
8
- def _get_process_mapping ():
7
+ # Based on QEMU docs: https://www.qemu.org/docs/master/user/main.html
8
+ QEMU_BIN_REGEX = re .compile (
9
+ r"""qemu-
10
+ (alpha
11
+ |armeb
12
+ |arm
13
+ |m68k
14
+ |cris
15
+ |i386
16
+ |x86_64
17
+ |microblaze
18
+ |mips
19
+ |mipsel
20
+ |mips64
21
+ |mips64el
22
+ |mipsn32
23
+ |mipsn32el
24
+ |nios2
25
+ |ppc64
26
+ |ppc
27
+ |sh4eb
28
+ |sh4
29
+ |sparc
30
+ |sparc32plus
31
+ |sparc64
32
+ )""" ,
33
+ re .VERBOSE ,
34
+ )
35
+
36
+
37
+ def _iter_process_parents (pid , max_depth = 10 ):
9
38
"""Select a way to obtain process information from the system.
10
39
11
40
* `/proc` is used if supported.
12
41
* The system `ps` utility is used as a fallback option.
13
42
"""
14
43
for impl in (proc , ps ):
15
44
try :
16
- mapping = impl .get_process_mapping ( )
45
+ iterator = impl .iter_process_parents ( pid , max_depth )
17
46
except EnvironmentError :
18
47
continue
19
- return mapping
48
+ return iterator
20
49
raise ShellDetectionFailure ("compatible proc fs or ps utility is required" )
21
50
22
51
23
- def _iter_process_args (mapping , pid , max_depth ):
24
- """Traverse up the tree and yield each process's argument list."""
25
- for _ in range (max_depth ):
26
- try :
27
- proc = mapping [pid ]
28
- except KeyError : # We've reached the root process. Give up.
29
- break
30
- if proc .args : # Presumably the process should always have a name?
31
- yield proc .args
32
- pid = proc .ppid # Go up one level.
33
-
34
-
35
52
def _get_login_shell (proc_cmd ):
36
53
"""Form shell information from SHELL environ if possible."""
37
54
login_shell = os .environ .get ("SHELL" , "" )
@@ -71,6 +88,12 @@ def _get_shell(cmd, *args):
71
88
if cmd .startswith ("-" ): # Login shell! Let's use this.
72
89
return _get_login_shell (cmd )
73
90
name = os .path .basename (cmd ).lower ()
91
+ if name == "rosetta" or QEMU_BIN_REGEX .fullmatch (name ):
92
+ # If the current process is Rosetta or QEMU, this likely is a
93
+ # containerized process. Parse out the actual command instead.
94
+ cmd = args [0 ]
95
+ args = args [1 :]
96
+ name = os .path .basename (cmd ).lower ()
74
97
if name in SHELL_NAMES : # Command looks like a shell.
75
98
return (name , cmd )
76
99
shell = _get_interpreter_shell (name , args )
@@ -82,8 +105,7 @@ def _get_shell(cmd, *args):
82
105
def get_shell (pid = None , max_depth = 10 ):
83
106
"""Get the shell that the supplied pid or os.getpid() is running in."""
84
107
pid = str (pid or os .getpid ())
85
- mapping = _get_process_mapping ()
86
- for proc_args in _iter_process_args (mapping , pid , max_depth ):
108
+ for proc_args , _ , _ in _iter_process_parents (pid , max_depth ):
87
109
shell = _get_shell (* proc_args )
88
110
if shell :
89
111
return shell
0 commit comments