| import os |
| import re |
|
|
| from .._core import SHELL_NAMES, ShellDetectionFailure |
| from . import proc, ps |
|
|
| |
| QEMU_BIN_REGEX = re.compile( |
| r"""qemu- |
| (alpha |
| |armeb |
| |arm |
| |m68k |
| |cris |
| |i386 |
| |x86_64 |
| |microblaze |
| |mips |
| |mipsel |
| |mips64 |
| |mips64el |
| |mipsn32 |
| |mipsn32el |
| |nios2 |
| |ppc64 |
| |ppc |
| |sh4eb |
| |sh4 |
| |sparc |
| |sparc32plus |
| |sparc64 |
| )""", |
| re.VERBOSE, |
| ) |
|
|
|
|
| def _iter_process_parents(pid, max_depth=10): |
| """Select a way to obtain process information from the system. |
| |
| * `/proc` is used if supported. |
| * The system `ps` utility is used as a fallback option. |
| """ |
| for impl in (proc, ps): |
| try: |
| iterator = impl.iter_process_parents(pid, max_depth) |
| except EnvironmentError: |
| continue |
| return iterator |
| raise ShellDetectionFailure("compatible proc fs or ps utility is required") |
|
|
|
|
| def _get_login_shell(proc_cmd): |
| """Form shell information from SHELL environ if possible.""" |
| login_shell = os.environ.get("SHELL", "") |
| if login_shell: |
| proc_cmd = login_shell |
| else: |
| proc_cmd = proc_cmd[1:] |
| return (os.path.basename(proc_cmd).lower(), proc_cmd) |
|
|
|
|
| _INTERPRETER_SHELL_NAMES = [ |
| (re.compile(r"^python(\d+(\.\d+)?)?$"), {"xonsh"}), |
| ] |
|
|
|
|
| def _get_interpreter_shell(proc_name, proc_args): |
| """Get shell invoked via an interpreter. |
| |
| Some shells are implemented on, and invoked with an interpreter, e.g. xonsh |
| is commonly executed with an executable Python script. This detects what |
| script the interpreter is actually running, and check whether that looks |
| like a shell. |
| |
| See sarugaku/shellingham#26 for rational. |
| """ |
| for pattern, shell_names in _INTERPRETER_SHELL_NAMES: |
| if not pattern.match(proc_name): |
| continue |
| for arg in proc_args: |
| name = os.path.basename(arg).lower() |
| if os.path.isfile(arg) and name in shell_names: |
| return (name, arg) |
| return None |
|
|
|
|
| def _get_shell(cmd, *args): |
| if cmd.startswith("-"): |
| return _get_login_shell(cmd) |
| name = os.path.basename(cmd).lower() |
| if name == "rosetta" or QEMU_BIN_REGEX.fullmatch(name): |
| |
| |
| cmd = args[0] |
| args = args[1:] |
| name = os.path.basename(cmd).lower() |
| if name in SHELL_NAMES: |
| return (name, cmd) |
| shell = _get_interpreter_shell(name, args) |
| if shell: |
| return shell |
| return None |
|
|
|
|
| def get_shell(pid=None, max_depth=10): |
| """Get the shell that the supplied pid or os.getpid() is running in.""" |
| pid = str(pid or os.getpid()) |
| for proc_args, _, _ in _iter_process_parents(pid, max_depth): |
| shell = _get_shell(*proc_args) |
| if shell: |
| return shell |
| return None |
|
|