aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/subprocess.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/subprocess.py')
-rw-r--r--Lib/subprocess.py105
1 files changed, 100 insertions, 5 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 85b9ea07854..85e7969c092 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -53,6 +53,14 @@ import warnings
import contextlib
from time import monotonic as _time
+try:
+ import pwd
+except ImportError:
+ pwd = None
+try:
+ import grp
+except ImportError:
+ grp = None
__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput",
"getoutput", "check_output", "run", "CalledProcessError", "DEVNULL",
@@ -719,6 +727,12 @@ class Popen(object):
start_new_session (POSIX only)
+ group (POSIX only)
+
+ extra_groups (POSIX only)
+
+ user (POSIX only)
+
pass_fds (POSIX only)
encoding and errors: Text mode encoding and error handling to use for
@@ -735,7 +749,8 @@ class Popen(object):
shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
- pass_fds=(), *, encoding=None, errors=None, text=None):
+ pass_fds=(), *, user=None, group=None, extra_groups=None,
+ encoding=None, errors=None, text=None):
"""Create new Popen instance."""
_cleanup()
# Held while anything is calling waitpid before returncode has been
@@ -833,6 +848,78 @@ class Popen(object):
else:
line_buffering = False
+ gid = None
+ if group is not None:
+ if not hasattr(os, 'setregid'):
+ raise ValueError("The 'group' parameter is not supported on the "
+ "current platform")
+
+ elif isinstance(group, str):
+ if grp is None:
+ raise ValueError("The group parameter cannot be a string "
+ "on systems without the grp module")
+
+ gid = grp.getgrnam(group).gr_gid
+ elif isinstance(group, int):
+ gid = group
+ else:
+ raise TypeError("Group must be a string or an integer, not {}"
+ .format(type(group)))
+
+ if gid < 0:
+ raise ValueError(f"Group ID cannot be negative, got {gid}")
+
+ gids = None
+ if extra_groups is not None:
+ if not hasattr(os, 'setgroups'):
+ raise ValueError("The 'extra_groups' parameter is not "
+ "supported on the current platform")
+
+ elif isinstance(extra_groups, str):
+ raise ValueError("Groups must be a list, not a string")
+
+ gids = []
+ for extra_group in extra_groups:
+ if isinstance(extra_group, str):
+ if grp is None:
+ raise ValueError("Items in extra_groups cannot be "
+ "strings on systems without the "
+ "grp module")
+
+ gids.append(grp.getgrnam(extra_group).gr_gid)
+ elif isinstance(extra_group, int):
+ gids.append(extra_group)
+ else:
+ raise TypeError("Items in extra_groups must be a string "
+ "or integer, not {}"
+ .format(type(extra_group)))
+
+ # make sure that the gids are all positive here so we can do less
+ # checking in the C code
+ for gid_check in gids:
+ if gid_check < 0:
+ raise ValueError(f"Group ID cannot be negative, got {gid_check}")
+
+ uid = None
+ if user is not None:
+ if not hasattr(os, 'setreuid'):
+ raise ValueError("The 'user' parameter is not supported on "
+ "the current platform")
+
+ elif isinstance(user, str):
+ if pwd is None:
+ raise ValueError("The user parameter cannot be a string "
+ "on systems without the pwd module")
+
+ uid = pwd.getpwnam(user).pw_uid
+ elif isinstance(user, int):
+ uid = user
+ else:
+ raise TypeError("User must be a string or an integer")
+
+ if uid < 0:
+ raise ValueError(f"User ID cannot be negative, got {uid}")
+
try:
if p2cwrite != -1:
self.stdin = io.open(p2cwrite, 'wb', bufsize)
@@ -857,7 +944,9 @@ class Popen(object):
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite,
- restore_signals, start_new_session)
+ restore_signals,
+ gid, gids, uid,
+ start_new_session)
except:
# Cleanup if the child failed starting.
for f in filter(None, (self.stdin, self.stdout, self.stderr)):
@@ -1227,7 +1316,9 @@ class Popen(object):
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite,
- unused_restore_signals, unused_start_new_session):
+ unused_restore_signals,
+ unused_gid, unused_gids, unused_uid,
+ unused_start_new_session):
"""Execute program (MS Windows version)"""
assert not pass_fds, "pass_fds not supported on Windows."
@@ -1553,7 +1644,9 @@ class Popen(object):
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite,
- restore_signals, start_new_session):
+ restore_signals,
+ gid, gids, uid,
+ start_new_session):
"""Execute program (POSIX version)"""
if isinstance(args, (str, bytes)):
@@ -1641,7 +1734,9 @@ class Popen(object):
p2cread, p2cwrite, c2pread, c2pwrite,
errread, errwrite,
errpipe_read, errpipe_write,
- restore_signals, start_new_session, preexec_fn)
+ restore_signals, start_new_session,
+ gid, gids, uid,
+ preexec_fn)
self._child_created = True
finally:
# be sure the FD is closed no matter what