forked from Rust-related/RustPython
Merge pull request #5406 from coolreader18/improve-posixsubprocess
Improve posixsubprocess
This commit is contained in:
1
Lib/test/test_exception_hierarchy.py
vendored
1
Lib/test/test_exception_hierarchy.py
vendored
@@ -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
|
||||
|
||||
8
Lib/test/test_subprocess.py
vendored
8
Lib/test/test_subprocess.py
vendored
@@ -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()
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"))]
|
||||
|
||||
Reference in New Issue
Block a user