Merge pull request #5406 from coolreader18/improve-posixsubprocess

Improve posixsubprocess
This commit is contained in:
Jeong, YunWon
2024-09-22 17:37:34 +09:00
committed by GitHub
6 changed files with 286 additions and 167 deletions

View File

@@ -81,7 +81,6 @@ class HierarchyTest(unittest.TestCase):
return _map
_map = _make_map(_pep_map)
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_errno_mapping(self):
# The OSError constructor maps errnos to subclasses
# A sample test for the basic functionality

View File

@@ -1614,8 +1614,6 @@ class ProcessTestCase(BaseTestCase):
subprocess.call(['/opt/nonexistent_binary', 'with', 'some', 'args'])
self.assertEqual(c.exception.filename, '/opt/nonexistent_binary')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(mswindows, "behavior currently not supported on Windows")
def test_file_not_found_with_bad_cwd(self):
with self.assertRaises(FileNotFoundError) as c:
@@ -2033,8 +2031,6 @@ class POSIXProcessTestCase(BaseTestCase):
child_sid = int(output)
self.assertNotEqual(parent_sid, child_sid)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(hasattr(os, 'setpgid') and hasattr(os, 'getpgid'),
'no setpgid or getpgid on platform')
def test_process_group_0(self):
@@ -2053,8 +2049,6 @@ class POSIXProcessTestCase(BaseTestCase):
child_pgid = int(output)
self.assertNotEqual(parent_pgid, child_pgid)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(hasattr(os, 'setreuid'), 'no setreuid on platform')
def test_user(self):
# For code coverage of the user parameter. We don't care if we get a
@@ -2112,8 +2106,6 @@ class POSIXProcessTestCase(BaseTestCase):
with self.assertRaises(ValueError):
subprocess.check_call(ZERO_RETURN_CMD, user=65535)
# TODO: RUSTPYTHON, observed gids do not match expected gids
@unittest.expectedFailure
@unittest.skipUnless(hasattr(os, 'setregid'), 'no setregid() on platform')
def test_group(self):
gid = os.getegid()

View File

@@ -5,23 +5,29 @@ use crate::vm::{
stdlib::posix,
{PyObjectRef, PyResult, TryFromObject, VirtualMachine},
};
use nix::{errno::Errno, unistd};
#[cfg(not(target_os = "redox"))]
use std::ffi::CStr;
#[cfg(not(target_os = "redox"))]
use std::os::unix::io::AsRawFd;
use itertools::Itertools;
use nix::{
errno::Errno,
unistd::{self, Pid},
};
use std::{
convert::Infallible as Never,
ffi::CString,
io::{self, prelude::*},
ffi::{CStr, CString},
io::prelude::*,
marker::PhantomData,
ops::Deref,
os::fd::FromRawFd,
};
use std::{fs::File, os::unix::io::AsRawFd};
use unistd::{Gid, Uid};
pub(crate) use _posixsubprocess::make_module;
#[pymodule]
mod _posixsubprocess {
use super::{exec, CStrPathLike, ForkExecArgs, ProcArgs};
use rustpython_vm::{AsObject, TryFromBorrowedObject};
use super::*;
use crate::vm::{convert::IntoPyException, PyResult, VirtualMachine};
#[pyfunction]
@@ -29,19 +35,20 @@ mod _posixsubprocess {
if args.preexec_fn.is_some() {
return Err(vm.new_not_implemented_error("preexec_fn not supported yet".to_owned()));
}
let cstrs_to_ptrs = |cstrs: &[CStrPathLike]| {
cstrs
.iter()
.map(|s| s.s.as_ptr())
.chain(std::iter::once(std::ptr::null()))
.collect::<Vec<_>>()
let extra_groups = args
.groups_list
.as_ref()
.map(|l| Vec::<Gid>::try_from_borrowed_object(vm, l.as_object()))
.transpose()?;
let argv = CharPtrVec::from_iter(args.args.iter());
let envp = args.env_list.as_ref().map(CharPtrVec::from_iter);
let procargs = ProcArgs {
argv: &argv,
envp: envp.as_deref(),
extra_groups: extra_groups.as_deref(),
};
let argv = cstrs_to_ptrs(&args.args);
let argv = &argv;
let envp = args.env_list.as_ref().map(|s| cstrs_to_ptrs(s));
let envp = envp.as_deref();
match unsafe { nix::unistd::fork() }.map_err(|err| err.into_pyexception(vm))? {
nix::unistd::ForkResult::Child => exec(&args, ProcArgs { argv, envp }),
nix::unistd::ForkResult::Child => exec(&args, procargs),
nix::unistd::ForkResult::Parent { child } => Ok(child.as_raw()),
}
}
@@ -49,7 +56,6 @@ mod _posixsubprocess {
macro_rules! gen_args {
($($field:ident: $t:ty),*$(,)?) => {
#[allow(dead_code)]
#[derive(FromArgs)]
struct ForkExecArgs {
$(#[pyarg(positional)] $field: $t,)*
@@ -66,6 +72,52 @@ impl TryFromObject for CStrPathLike {
Ok(CStrPathLike { s })
}
}
impl AsRef<CStr> for CStrPathLike {
fn as_ref(&self) -> &CStr {
&self.s
}
}
#[derive(Default)]
struct CharPtrVec<'a> {
vec: Vec<*const libc::c_char>,
marker: PhantomData<Vec<&'a CStr>>,
}
impl<'a, T: AsRef<CStr>> FromIterator<&'a T> for CharPtrVec<'a> {
fn from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Self {
let vec = iter
.into_iter()
.map(|x| x.as_ref().as_ptr())
.chain(std::iter::once(std::ptr::null()))
.collect();
Self {
vec,
marker: PhantomData,
}
}
}
impl<'a> Deref for CharPtrVec<'a> {
type Target = CharPtrSlice<'a>;
fn deref(&self) -> &Self::Target {
unsafe {
&*(self.vec.as_slice() as *const [*const libc::c_char] as *const CharPtrSlice<'a>)
}
}
}
#[repr(transparent)]
struct CharPtrSlice<'a> {
marker: PhantomData<[&'a CStr]>,
slice: [*const libc::c_char],
}
impl<'a> CharPtrSlice<'a> {
fn as_ptr(&self) -> *const *const libc::c_char {
self.slice.as_ptr()
}
}
gen_args! {
args: ArgSequence<CStrPathLike> /* list */,
@@ -84,39 +136,56 @@ gen_args! {
errpipe_write: i32,
restore_signals: bool,
call_setsid: bool,
// TODO: Difference between gid_to_set and gid_object.
// One is a `gid_t` and the other is a `PyObject` in CPython.
gid_to_set: Option<Option<Gid>>,
gid_object: PyObjectRef,
pgid_to_set: libc::pid_t,
gid: Option<Gid>,
groups_list: Option<PyListRef>,
uid: Option<Option<Uid>>,
uid: Option<Uid>,
child_umask: i32,
preexec_fn: Option<PyObjectRef>,
use_vfork: bool,
_use_vfork: bool,
}
// can't reallocate inside of exec(), so we reallocate prior to fork() and pass this along
struct ProcArgs<'a> {
argv: &'a [*const libc::c_char],
envp: Option<&'a [*const libc::c_char]>,
argv: &'a CharPtrSlice<'a>,
envp: Option<&'a CharPtrSlice<'a>>,
extra_groups: Option<&'a [Gid]>,
}
fn exec(args: &ForkExecArgs, procargs: ProcArgs) -> ! {
match exec_inner(args, procargs) {
let mut ctx = ExecErrorContext::NoExec;
match exec_inner(args, procargs, &mut ctx) {
Ok(x) => match x {},
Err(e) => {
let buf: &mut [u8] = &mut [0; 256];
let mut cur = io::Cursor::new(&mut *buf);
// TODO: check if reached preexec, if not then have "noexec" after
let _ = write!(cur, "OSError:{}:", e as i32);
let pos = cur.position();
let _ = unistd::write(args.errpipe_write, &buf[..pos as usize]);
let mut pipe =
std::mem::ManuallyDrop::new(unsafe { File::from_raw_fd(args.errpipe_write) });
let _ = write!(pipe, "OSError:{}:{}", e as i32, ctx.as_msg());
std::process::exit(255)
}
}
}
fn exec_inner(args: &ForkExecArgs, procargs: ProcArgs) -> nix::Result<Never> {
enum ExecErrorContext {
NoExec,
ChDir,
Exec,
}
impl ExecErrorContext {
fn as_msg(&self) -> &'static str {
match self {
ExecErrorContext::NoExec => "noexec",
ExecErrorContext::ChDir => "noexec:chdir",
ExecErrorContext::Exec => "",
}
}
}
fn exec_inner(
args: &ForkExecArgs,
procargs: ProcArgs,
ctx: &mut ExecErrorContext,
) -> nix::Result<Never> {
for &fd in args.fds_to_keep.as_slice() {
if fd != args.errpipe_write {
posix::raw_set_inheritable(fd, true)?
@@ -158,7 +227,7 @@ fn exec_inner(args: &ForkExecArgs, procargs: ProcArgs) -> nix::Result<Never> {
dup_into_stdio(errwrite, 2)?;
if let Some(ref cwd) = args.cwd {
unistd::chdir(cwd.s.as_c_str())?
unistd::chdir(cwd.s.as_c_str()).inspect_err(|_| *ctx = ExecErrorContext::ChDir)?
}
if args.child_umask >= 0 {
@@ -170,28 +239,35 @@ fn exec_inner(args: &ForkExecArgs, procargs: ProcArgs) -> nix::Result<Never> {
}
if args.call_setsid {
#[cfg(not(target_os = "redox"))]
unistd::setsid()?;
}
if let Some(_groups_list) = args.groups_list.as_ref() {
// TODO: setgroups
// unistd::setgroups(groups_size, groups);
if args.pgid_to_set > -1 {
unistd::setpgid(Pid::from_raw(0), Pid::from_raw(args.pgid_to_set))?;
}
if let Some(_gid) = args.gid_to_set.as_ref() {
// TODO: setgid
// unistd::setregid(gid, gid)?;
if let Some(_groups) = procargs.extra_groups {
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
unistd::setgroups(_groups)?;
}
if let Some(_uid) = args.uid.as_ref() {
// TODO: setuid
// unistd::setreuid(uid, uid)?;
if let Some(gid) = args.gid.filter(|x| x.as_raw() != u32::MAX) {
let ret = unsafe { libc::setregid(gid.as_raw(), gid.as_raw()) };
nix::Error::result(ret)?;
}
if let Some(uid) = args.uid.filter(|x| x.as_raw() != u32::MAX) {
let ret = unsafe { libc::setreuid(uid.as_raw(), uid.as_raw()) };
nix::Error::result(ret)?;
}
*ctx = ExecErrorContext::Exec;
if args.close_fds {
#[cfg(not(target_os = "redox"))]
close_fds(3, &args.fds_to_keep)?;
close_fds(KeepFds {
above: 2,
keep: &args.fds_to_keep,
});
}
let mut first_err = None;
@@ -211,47 +287,128 @@ fn exec_inner(args: &ForkExecArgs, procargs: ProcArgs) -> nix::Result<Never> {
Err(first_err.unwrap_or_else(Errno::last))
}
#[derive(Copy, Clone)]
struct KeepFds<'a> {
above: i32,
keep: &'a [i32],
}
impl KeepFds<'_> {
fn should_keep(self, fd: i32) -> bool {
fd > self.above && self.keep.binary_search(&fd).is_err()
}
}
fn close_fds(keep: KeepFds<'_>) {
#[cfg(not(target_os = "redox"))]
if close_dir_fds(keep).is_ok() {
return;
}
#[cfg(target_os = "redox")]
if close_filetable_fds(keep).is_ok() {
return;
}
close_fds_brute_force(keep)
}
#[cfg(not(target_os = "redox"))]
fn close_fds(above: i32, keep: &[i32]) -> nix::Result<()> {
fn close_dir_fds(keep: KeepFds<'_>) -> nix::Result<()> {
use nix::{dir::Dir, fcntl::OFlag};
// TODO: close fds by brute force if readdir doesn't work:
// https://github.com/python/cpython/blob/3.8/Modules/_posixsubprocess.c#L220
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_vendor = "apple",
))]
let fd_dir_name = c"/dev/fd";
#[cfg(any(target_os = "linux", target_os = "android"))]
let fd_dir_name = c"/proc/self/fd";
let mut dir = Dir::open(
FD_DIR_NAME,
fd_dir_name,
OFlag::O_RDONLY | OFlag::O_DIRECTORY,
nix::sys::stat::Mode::empty(),
)?;
let dirfd = dir.as_raw_fd();
for e in dir.iter() {
if let Some(fd) = pos_int_from_ascii(e?.file_name()) {
if fd != dirfd && fd > above && !keep.contains(&fd) {
unistd::close(fd)?
'outer: for e in dir.iter() {
let e = e?;
let mut parser = IntParser::default();
for &c in e.file_name().to_bytes() {
if parser.feed(c).is_err() {
continue 'outer;
}
}
let fd = parser.num;
if fd != dirfd && keep.should_keep(fd) {
let _ = unistd::close(fd);
}
}
Ok(())
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_vendor = "apple",
))]
const FD_DIR_NAME: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/dev/fd\0") };
#[cfg(any(target_os = "linux", target_os = "android"))]
const FD_DIR_NAME: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/proc/self/fd\0") };
#[cfg(not(target_os = "redox"))]
fn pos_int_from_ascii(name: &CStr) -> Option<i32> {
let mut num = 0;
for c in name.to_bytes() {
if !c.is_ascii_digit() {
return None;
#[cfg(target_os = "redox")]
fn close_filetable_fds(keep: KeepFds<'_>) -> nix::Result<()> {
use nix::fcntl;
use std::os::fd::{FromRawFd, OwnedFd};
let fd = fcntl::open(
c"/scheme/thisproc/current/filetable",
fcntl::OFlag::O_RDONLY,
nix::sys::stat::Mode::empty(),
)?;
let filetable = unsafe { OwnedFd::from_raw_fd(fd) };
let read_one = || -> nix::Result<_> {
let mut byte = 0;
let n = nix::unistd::read(filetable.as_raw_fd(), std::slice::from_mut(&mut byte))?;
Ok((n > 0).then_some(byte))
};
while let Some(c) = read_one()? {
let mut parser = IntParser::default();
if parser.feed(c).is_err() {
continue;
}
let done = loop {
let Some(c) = read_one()? else { break true };
if parser.feed(c).is_err() {
break false;
}
};
let fd = parser.num as i32;
if fd != filetable.as_raw_fd() && keep.should_keep(fd) {
let _ = unistd::close(fd);
}
if done {
break;
}
num = num * 10 + i32::from(c - b'0')
}
Some(num)
Ok(())
}
fn close_fds_brute_force(keep: KeepFds<'_>) {
let max_fd = nix::unistd::sysconf(nix::unistd::SysconfVar::OPEN_MAX)
.ok()
.flatten()
.unwrap_or(256) as i32;
let fds = itertools::chain![Some(keep.above), keep.keep.iter().copied(), Some(max_fd)];
for fd in fds.tuple_windows().flat_map(|(start, end)| start + 1..end) {
let _ = unistd::close(fd);
}
}
#[derive(Default)]
struct IntParser {
num: i32,
}
struct NonDigit;
impl IntParser {
fn feed(&mut self, c: u8) -> Result<(), NonDigit> {
let digit = (c as char).to_digit(10).ok_or(NonDigit)?;
self.num *= 10;
self.num += digit as i32;
Ok(())
}
}

View File

@@ -1139,7 +1139,8 @@ pub(crate) fn errno_to_exc_type(errno: i32, vm: &VirtualMachine) -> Option<&'sta
use crate::stdlib::errno::errors;
let excs = &vm.ctx.exceptions;
match errno {
errors::EWOULDBLOCK => Some(excs.blocking_io_error),
#[allow(unreachable_patterns)] // EAGAIN is sometimes the same as EWOULDBLOCK
errors::EWOULDBLOCK | errors::EAGAIN => Some(excs.blocking_io_error),
errors::EALREADY => Some(excs.blocking_io_error),
errors::EINPROGRESS => Some(excs.blocking_io_error),
errors::EPIPE => Some(excs.broken_pipe_error),

View File

@@ -224,6 +224,21 @@ impl<T> std::ops::Deref for ArgSequence<T> {
}
}
impl<'a, T> IntoIterator for &'a ArgSequence<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T> IntoIterator for ArgSequence<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<T: TryFromObject> TryFromObject for ArgSequence<T> {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
obj.try_to_value(vm).map(Self)

View File

@@ -1123,17 +1123,13 @@ pub mod module {
}
#[pyfunction]
fn setgid(gid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
let gid =
gid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
fn setgid(gid: Gid, vm: &VirtualMachine) -> PyResult<()> {
unistd::setgid(gid).map_err(|err| err.into_pyexception(vm))
}
#[cfg(not(target_os = "redox"))]
#[pyfunction]
fn setegid(egid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
let egid =
egid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
fn setegid(egid: Gid, vm: &VirtualMachine) -> PyResult<()> {
unistd::setegid(egid).map_err(|err| err.into_pyexception(vm))
}
@@ -1151,7 +1147,7 @@ pub mod module {
.map_err(|err| err.into_pyexception(vm))
}
fn try_from_id(vm: &VirtualMachine, obj: PyObjectRef, typ_name: &str) -> PyResult<Option<u32>> {
fn try_from_id(vm: &VirtualMachine, obj: PyObjectRef, typ_name: &str) -> PyResult<u32> {
use std::cmp::Ordering;
let i = obj
.try_to_ref::<PyInt>(vm)
@@ -1164,53 +1160,47 @@ pub mod module {
.try_to_primitive::<i64>(vm)?;
match i.cmp(&-1) {
Ordering::Greater => Ok(Some(i.try_into().map_err(|_| {
Ordering::Greater => Ok(i.try_into().map_err(|_| {
vm.new_overflow_error(format!("{typ_name} is larger than maximum"))
})?)),
})?),
Ordering::Less => {
Err(vm.new_overflow_error(format!("{typ_name} is less than minimum")))
}
Ordering::Equal => Ok(None), // -1 means does not change the value
// -1 means does not change the value
// In CPython, this is `(uid_t) -1`, rustc gets mad when we try to declare
// a negative unsigned integer :).
Ordering::Equal => Ok(-1i32 as u32),
}
}
impl TryFromObject for Option<Uid> {
impl TryFromObject for Uid {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
Ok(try_from_id(vm, obj, "uid")?.map(Uid::from_raw))
try_from_id(vm, obj, "uid").map(Uid::from_raw)
}
}
impl TryFromObject for Option<Gid> {
impl TryFromObject for Gid {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
Ok(try_from_id(vm, obj, "gid")?.map(Gid::from_raw))
try_from_id(vm, obj, "gid").map(Gid::from_raw)
}
}
#[pyfunction]
fn setuid(uid: Option<Uid>, vm: &VirtualMachine) -> PyResult<()> {
let uid =
uid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
unistd::setuid(uid).map_err(|err| err.into_pyexception(vm))
fn setuid(uid: Uid) -> nix::Result<()> {
unistd::setuid(uid)
}
#[cfg(not(target_os = "redox"))]
#[pyfunction]
fn seteuid(euid: Option<Uid>, vm: &VirtualMachine) -> PyResult<()> {
let euid =
euid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
unistd::seteuid(euid).map_err(|err| err.into_pyexception(vm))
fn seteuid(euid: Uid) -> nix::Result<()> {
unistd::seteuid(euid)
}
#[cfg(not(target_os = "redox"))]
#[pyfunction]
fn setreuid(ruid: Option<Uid>, euid: Option<Uid>, vm: &VirtualMachine) -> PyResult<()> {
if let Some(ruid) = ruid {
unistd::setuid(ruid).map_err(|err| err.into_pyexception(vm))?;
}
if let Some(euid) = euid {
unistd::seteuid(euid).map_err(|err| err.into_pyexception(vm))?;
}
Ok(())
fn setreuid(ruid: Uid, euid: Uid) -> nix::Result<()> {
let ret = unsafe { libc::setreuid(ruid.as_raw(), euid.as_raw()) };
nix::Error::result(ret).map(drop)
}
// cfg from nix
@@ -1221,20 +1211,8 @@ pub mod module {
target_os = "openbsd"
))]
#[pyfunction]
fn setresuid(
ruid: Option<Uid>,
euid: Option<Uid>,
suid: Option<Uid>,
vm: &VirtualMachine,
) -> PyResult<()> {
let unwrap_or_unchanged =
|u: Option<Uid>| u.unwrap_or_else(|| Uid::from_raw(libc::uid_t::MAX));
unistd::setresuid(
unwrap_or_unchanged(ruid),
unwrap_or_unchanged(euid),
unwrap_or_unchanged(suid),
)
.map_err(|err| err.into_pyexception(vm))
fn setresuid(ruid: Uid, euid: Uid, suid: Uid) -> nix::Result<()> {
unistd::setresuid(ruid, euid, suid)
}
#[cfg(not(target_os = "redox"))]
@@ -1283,8 +1261,8 @@ pub mod module {
// cfg from nix
#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
#[pyfunction]
fn getresuid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
let ret = unistd::getresuid().map_err(|e| e.into_pyexception(vm))?;
fn getresuid() -> nix::Result<(u32, u32, u32)> {
let ret = unistd::getresuid()?;
Ok((
ret.real.as_raw(),
ret.effective.as_raw(),
@@ -1295,8 +1273,8 @@ pub mod module {
// cfg from nix
#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
#[pyfunction]
fn getresgid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
let ret = unistd::getresgid().map_err(|e| e.into_pyexception(vm))?;
fn getresgid() -> nix::Result<(u32, u32, u32)> {
let ret = unistd::getresgid()?;
Ok((
ret.real.as_raw(),
ret.effective.as_raw(),
@@ -1312,32 +1290,15 @@ pub mod module {
target_os = "openbsd"
))]
#[pyfunction]
fn setresgid(
rgid: Option<Gid>,
egid: Option<Gid>,
sgid: Option<Gid>,
vm: &VirtualMachine,
) -> PyResult<()> {
let unwrap_or_unchanged =
|u: Option<Gid>| u.unwrap_or_else(|| Gid::from_raw(libc::gid_t::MAX));
unistd::setresgid(
unwrap_or_unchanged(rgid),
unwrap_or_unchanged(egid),
unwrap_or_unchanged(sgid),
)
.map_err(|err| err.into_pyexception(vm))
fn setresgid(rgid: Gid, egid: Gid, sgid: Gid, vm: &VirtualMachine) -> PyResult<()> {
unistd::setresgid(rgid, egid, sgid).map_err(|err| err.into_pyexception(vm))
}
#[cfg(not(target_os = "redox"))]
#[pyfunction]
fn setregid(rgid: Option<Gid>, egid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
if let Some(rgid) = rgid {
unistd::setgid(rgid).map_err(|err| err.into_pyexception(vm))?;
}
if let Some(egid) = egid {
unistd::setegid(egid).map_err(|err| err.into_pyexception(vm))?;
}
Ok(())
fn setregid(rgid: Gid, egid: Gid) -> nix::Result<()> {
let ret = unsafe { libc::setregid(rgid.as_raw(), egid.as_raw()) };
nix::Error::result(ret).map(drop)
}
// cfg from nix
@@ -1348,10 +1309,8 @@ pub mod module {
target_os = "openbsd"
))]
#[pyfunction]
fn initgroups(user_name: PyStrRef, gid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
fn initgroups(user_name: PyStrRef, gid: Gid, vm: &VirtualMachine) -> PyResult<()> {
let user = user_name.to_cstring(vm)?;
let gid =
gid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
unistd::initgroups(&user, gid).map_err(|err| err.into_pyexception(vm))
}
@@ -1359,15 +1318,11 @@ pub mod module {
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
#[pyfunction]
fn setgroups(
group_ids: crate::function::ArgIterable<Option<Gid>>,
group_ids: crate::function::ArgIterable<Gid>,
vm: &VirtualMachine,
) -> PyResult<()> {
let gids = group_ids
.iter(vm)?
.collect::<Result<Option<Vec<_>>, _>>()?
.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
let ret = unistd::setgroups(&gids);
ret.map_err(|err| err.into_pyexception(vm))
let gids = group_ids.iter(vm)?.collect::<Result<Vec<_>, _>>()?;
unistd::setgroups(&gids).map_err(|err| err.into_pyexception(vm))
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]