login page

This commit is contained in:
Alicja Cięciwa
2020-10-27 12:57:58 +01:00
commit cb8886666c
8545 changed files with 1082463 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
import os
import re
from .._core import SHELL_NAMES, ShellDetectionFailure
from . import proc, ps
def _get_process_mapping():
"""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:
mapping = impl.get_process_mapping()
except EnvironmentError:
continue
return mapping
raise ShellDetectionFailure('compatible proc fs or ps utility is required')
def _iter_process_args(mapping, pid, max_depth):
"""Iterator to traverse up the tree, yielding each process's argument list.
"""
for _ in range(max_depth):
try:
proc = mapping[pid]
except KeyError: # We've reached the root process. Give up.
break
if proc.args: # Persumably the process should always have a name?
yield proc.args
pid = proc.ppid # Go up one level.
def _get_login_shell(proc_cmd):
"""Form shell information from the SHELL environment variable 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('-'): # Login shell! Let's use this.
return _get_login_shell(cmd)
name = os.path.basename(cmd).lower()
if name in SHELL_NAMES: # Command looks like a shell.
return (name, cmd)
shell = _get_interpreter_shell(name, args)
if shell:
return shell
return None
def get_shell(pid=None, max_depth=6):
"""Get the shell that the supplied pid or os.getpid() is running in.
"""
pid = str(pid or os.getpid())
mapping = _get_process_mapping()
for proc_args in _iter_process_args(mapping, pid, max_depth):
shell = _get_shell(*proc_args)
if shell:
return shell
return None

View File

@@ -0,0 +1,3 @@
import collections
Process = collections.namedtuple('Process', 'args pid ppid')

View File

@@ -0,0 +1,35 @@
import os
import re
from ._default import Process
STAT_PPID = 3
STAT_TTY = 6
def get_process_mapping():
"""Try to look up the process tree via the /proc interface.
"""
with open('/proc/{0}/stat'.format(os.getpid())) as f:
self_tty = f.read().split()[STAT_TTY]
processes = {}
for pid in os.listdir('/proc'):
if not pid.isdigit():
continue
try:
stat = '/proc/{0}/stat'.format(pid)
cmdline = '/proc/{0}/cmdline'.format(pid)
with open(stat) as fstat, open(cmdline) as fcmdline:
stat = re.findall(r'\(.+\)|\S+', fstat.read())
cmd = fcmdline.read().split('\x00')[:-1]
ppid = stat[STAT_PPID]
tty = stat[STAT_TTY]
if tty == self_tty:
processes[pid] = Process(
args=tuple(cmd), pid=pid, ppid=ppid,
)
except IOError:
# Process has disappeared - just ignore it.
continue
return processes

View File

@@ -0,0 +1,27 @@
import collections
import shlex
import subprocess
import sys
Process = collections.namedtuple('Process', 'args pid ppid')
def get_process_mapping():
"""Try to look up the process tree via the output of `ps`.
"""
output = subprocess.check_output([
'ps', '-ww', '-o', 'pid=', '-o', 'ppid=', '-o', 'args=',
])
if not isinstance(output, str):
output = output.decode(sys.stdout.encoding)
processes = {}
for line in output.split('\n'):
try:
pid, ppid, args = line.strip().split(None, 2)
except ValueError:
continue
processes[pid] = Process(
args=tuple(shlex.split(args)), pid=pid, ppid=ppid,
)
return processes

View File

@@ -0,0 +1,72 @@
import io
import os
import re
import sys
from ._core import Process
STAT_PPID = 3
STAT_TTY = 6
STAT_PATTERN = re.compile(r'\(.+\)|\S+')
def detect_proc():
"""Detect /proc filesystem style.
This checks the /proc/{pid} directory for possible formats. Returns one of
the followings as str:
* `stat`: Linux-style, i.e. ``/proc/{pid}/stat``.
* `status`: BSD-style, i.e. ``/proc/{pid}/status``.
"""
pid = os.getpid()
for name in ('stat', 'status'):
if os.path.exists(os.path.join('/proc', str(pid), name)):
return name
raise ProcFormatError('unsupported proc format')
def _get_stat(pid, name):
path = os.path.join('/proc', str(pid), name)
with io.open(path, encoding='ascii', errors='replace') as f:
# We only care about TTY and PPID -- all numbers.
parts = STAT_PATTERN.findall(f.read())
return parts[STAT_TTY], parts[STAT_PPID]
def _get_cmdline(pid):
path = os.path.join('/proc', str(pid), 'cmdline')
encoding = sys.getfilesystemencoding() or 'utf-8'
with io.open(path, encoding=encoding, errors='replace') as f:
# XXX: Command line arguments can be arbitrary byte sequences, not
# necessarily decodable. For Shellingham's purpose, however, we don't
# care. (pypa/pipenv#2820)
# cmdline appends an extra NULL at the end, hence the [:-1].
return tuple(f.read().split('\0')[:-1])
class ProcFormatError(EnvironmentError):
pass
def get_process_mapping():
"""Try to look up the process tree via the /proc interface.
"""
stat_name = detect_proc()
self_tty = _get_stat(os.getpid(), stat_name)[0]
processes = {}
for pid in os.listdir('/proc'):
if not pid.isdigit():
continue
try:
tty, ppid = _get_stat(pid, stat_name)
if tty != self_tty:
continue
args = _get_cmdline(pid)
processes[pid] = Process(args=args, pid=pid, ppid=ppid)
except IOError:
# Process has disappeared - just ignore it.
continue
return processes

View File

@@ -0,0 +1,45 @@
import errno
import subprocess
import sys
from ._core import Process
class PsNotAvailable(EnvironmentError):
pass
def get_process_mapping():
"""Try to look up the process tree via the output of `ps`.
"""
try:
output = subprocess.check_output([
'ps', '-ww', '-o', 'pid=', '-o', 'ppid=', '-o', 'args=',
])
except OSError as e: # Python 2-compatible FileNotFoundError.
if e.errno != errno.ENOENT:
raise
raise PsNotAvailable('ps not found')
except subprocess.CalledProcessError as e:
# `ps` can return 1 if the process list is completely empty.
# (sarugaku/shellingham#15)
if not e.output.strip():
return {}
raise
if not isinstance(output, str):
encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
output = output.decode(encoding)
processes = {}
for line in output.split('\n'):
try:
pid, ppid, args = line.strip().split(None, 2)
# XXX: This is not right, but we are really out of options.
# ps does not offer a sane way to decode the argument display,
# and this is "Good Enough" for obtaining shell names. Hopefully
# people don't name their shell with a space, or have something
# like "/usr/bin/xonsh is uber". (sarugaku/shellingham#14)
args = tuple(a.strip() for a in args.split(' '))
except ValueError:
continue
processes[pid] = Process(args=args, pid=pid, ppid=ppid)
return processes