Merge pull request #4512 from RustPython/upd-nix

Update nix & use its functions more
This commit is contained in:
Jeong YunWon
2023-02-17 16:00:36 +09:00
committed by GitHub
7 changed files with 368 additions and 447 deletions

463
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@ insta = "1.14.0"
itertools = "0.10.3"
libc = "0.2.133"
log = "0.4.16"
nix = "0.24"
nix = "0.26"
num-complex = "0.4.0"
num-bigint = "0.4.3"
num-integer = "0.1.44"

View File

@@ -177,6 +177,8 @@ fn exec_inner(args: &ForkExecArgs, procargs: ProcArgs) -> nix::Result<Never> {
let mut first_err = None;
for exec in args.exec_list.as_slice() {
// not using nix's versions of these functions because those allocate the char-ptr array,
// and we can't allocate
if let Some(envp) = procargs.envp {
unsafe { libc::execve(exec.s.as_ptr(), procargs.argv.as_ptr(), envp.as_ptr()) };
} else {
@@ -195,9 +197,8 @@ fn close_fds(above: i32, keep: &[i32]) -> 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
let path = unsafe { CStr::from_bytes_with_nul_unchecked(FD_DIR_NAME) };
let mut dir = Dir::open(
path,
FD_DIR_NAME,
OFlag::O_RDONLY | OFlag::O_DIRECTORY,
nix::sys::stat::Mode::empty(),
)?;
@@ -219,10 +220,10 @@ fn close_fds(above: i32, keep: &[i32]) -> nix::Result<()> {
target_os = "openbsd",
target_vendor = "apple",
))]
const FD_DIR_NAME: &[u8] = b"/dev/fd\0";
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: &[u8] = b"/proc/self/fd\0";
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> {

View File

@@ -891,53 +891,8 @@ mod _socket {
use std::os::unix::ffi::OsStrExt;
let buf = crate::vm::function::ArgStrOrBytesLike::try_from_object(vm, addr)?;
let path = &*buf.borrow_bytes();
if cfg!(any(target_os = "linux", target_os = "android"))
&& path.first() == Some(&0)
{
use libc::{sa_family_t, socklen_t};
use {socket2::SockAddr, std::ptr};
unsafe {
// based on SockAddr::unix
// TODO: upstream or fix socklen check for SockAddr::unix()?
SockAddr::init(|storage, len| {
// Safety: `SockAddr::init` zeros the address, which is a valid
// representation.
let storage: &mut libc::sockaddr_un = &mut *storage.cast();
let len: &mut socklen_t = &mut *len;
let bytes = path;
if bytes.len() > storage.sun_path.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path must be shorter than SUN_LEN",
));
}
storage.sun_family = libc::AF_UNIX as sa_family_t;
// Safety: `bytes` and `addr.sun_path` are not overlapping and
// both point to valid memory.
// `SockAddr::init` zeroes the memory, so the path is already
// null terminated.
ptr::copy_nonoverlapping(
bytes.as_ptr(),
storage.sun_path.as_mut_ptr() as *mut u8,
bytes.len(),
);
let base = storage as *const _ as usize;
let path = &storage.sun_path as *const _ as usize;
let sun_path_offset = path - base;
let length = sun_path_offset + bytes.len();
*len = length as socklen_t;
Ok(())
})
}
.map(|(_, addr)| addr)
} else {
socket2::SockAddr::unix(ffi::OsStr::from_bytes(path))
}
.map_err(|_| vm.new_os_error("AF_UNIX path too long".to_owned()).into())
socket2::SockAddr::unix(ffi::OsStr::from_bytes(path))
.map_err(|_| vm.new_os_error("AF_UNIX path too long".to_owned()).into())
}
c::AF_INET => {
let tuple: PyTupleRef = addr.downcast().map_err(|obj| {
@@ -1087,20 +1042,7 @@ mod _socket {
_ => {}
}
if socket_kind == -1 {
// TODO: when socket2 cuts a new release, type will be available on all os
// socket_kind = sock.r#type().map_err(|e| e.into_pyexception(vm))?.into();
let res = unsafe {
c::getsockopt(
sock_fileno(&sock) as _,
c::SOL_SOCKET,
c::SO_TYPE,
&mut socket_kind as *mut libc::c_int as *mut _,
&mut (std::mem::size_of::<i32>() as _),
)
};
if res < 0 {
return Err(crate::common::os::errno().into());
}
socket_kind = sock.r#type().map_err(|e| e.into_pyexception(vm))?.into();
}
cfg_if::cfg_if! {
if #[cfg(any(
@@ -1601,30 +1543,23 @@ mod _socket {
if let Some(addr) = addr.as_socket() {
return get_ip_addr_tuple(&addr, vm);
}
match addr.family() as i32 {
#[cfg(unix)]
libc::AF_UNIX => {
let addr_len = addr.len() as usize;
let unix_addr = unsafe { &*(addr.as_ptr() as *const libc::sockaddr_un) };
let path_u8 = unsafe { &*(&unix_addr.sun_path[..] as *const [_] as *const [u8]) };
let sun_path_offset =
&unix_addr.sun_path as *const _ as usize - unix_addr as *const _ as usize;
if cfg!(any(target_os = "linux", target_os = "android"))
&& addr_len > sun_path_offset
&& unix_addr.sun_path[0] == 0
{
let abstractaddrlen = addr_len - sun_path_offset;
let abstractpath = &path_u8[..abstractaddrlen];
vm.ctx.new_bytes(abstractpath.to_vec()).into()
} else {
let len = memchr::memchr(b'\0', path_u8).unwrap_or(path_u8.len());
let path = &path_u8[..len];
vm.ctx.new_str(String::from_utf8_lossy(path)).into()
}
#[cfg(unix)]
use nix::sys::socket::{SockaddrLike, UnixAddr};
#[cfg(unix)]
if let Some(unix_addr) = unsafe { UnixAddr::from_raw(addr.as_ptr(), Some(addr.len())) } {
use std::os::unix::ffi::OsStrExt;
#[cfg(any(target_os = "android", target_os = "linux"))]
if let Some(abstractpath) = unix_addr.as_abstract() {
return vm.ctx.new_bytes([b"\0", abstractpath].concat()).into();
}
// TODO: support more address families
_ => (String::new(), 0).to_pyobject(vm),
// necessary on macos
let path = ffi::OsStr::as_bytes(unix_addr.path().unwrap_or("".as_ref()).as_ref());
let nul_pos = memchr::memchr(b'\0', path).unwrap_or(path.len());
let path = ffi::OsStr::from_bytes(&path[..nul_pos]);
return vm.ctx.new_str(path.to_string_lossy()).into();
}
// TODO: support more address families
(String::new(), 0).to_pyobject(vm)
}
#[pyfunction]
@@ -1764,25 +1699,19 @@ mod _socket {
let fd = sock_fileno(sock);
#[cfg(unix)]
{
let mut pollfd = libc::pollfd {
fd,
events: match kind {
SelectKind::Read => libc::POLLIN,
SelectKind::Write => libc::POLLOUT,
SelectKind::Connect => libc::POLLOUT | libc::POLLERR,
},
revents: 0,
use nix::poll::*;
let events = match kind {
SelectKind::Read => PollFlags::POLLIN,
SelectKind::Write => PollFlags::POLLOUT,
SelectKind::Connect => PollFlags::POLLOUT | PollFlags::POLLERR,
};
let mut pollfd = [PollFd::new(fd, events)];
let timeout = match interval {
Some(d) => d.as_millis() as _,
None => -1,
};
let ret = unsafe { libc::poll(&mut pollfd, 1, timeout) };
if ret < 0 {
Err(io::Error::last_os_error())
} else {
Ok(ret == 0)
}
let ret = poll(&mut pollfd, timeout)?;
Ok(ret == 0)
}
#[cfg(windows)]
{

View File

@@ -26,7 +26,9 @@ mod _ssl {
use crate::{
common::{
ascii,
lock::{PyMutex, PyRwLock, PyRwLockWriteGuard},
lock::{
PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard,
},
},
socket::{self, PySocket},
vm::{
@@ -504,12 +506,8 @@ mod _ssl {
fn builder(&self) -> PyRwLockWriteGuard<'_, SslContextBuilder> {
self.ctx.write()
}
fn exec_ctx<F, R>(&self, func: F) -> R
where
F: Fn(&ssl::SslContextRef) -> R,
{
let c = self.ctx.read();
func(builder_as_ctx(&c))
fn ctx(&self) -> PyMappedRwLockReadGuard<'_, ssl::SslContextRef> {
PyRwLockReadGuard::map(self.ctx.read(), builder_as_ctx)
}
#[pygetset]
@@ -554,7 +552,7 @@ mod _ssl {
}
#[pygetset]
fn verify_mode(&self) -> i32 {
let mode = self.exec_ctx(|ctx| ctx.verify_mode());
let mode = self.ctx().verify_mode();
if mode == SslVerifyMode::NONE {
CertRequirements::None.into()
} else if mode == SslVerifyMode::PEER {
@@ -703,16 +701,15 @@ mod _ssl {
vm: &VirtualMachine,
) -> PyResult<Vec<PyObjectRef>> {
let binary_form = binary_form.unwrap_or(false);
self.exec_ctx(|ctx| {
let certs = ctx
.cert_store()
.objects()
.iter()
.filter_map(|obj| obj.x509())
.map(|cert| cert_to_py(vm, cert, binary_form))
.collect::<Result<Vec<_>, _>>()?;
Ok(certs)
})
let certs = self
.ctx()
.cert_store()
.objects()
.iter()
.filter_map(|obj| obj.x509())
.map(|cert| cert_to_py(vm, cert, binary_form))
.collect::<Result<Vec<_>, _>>()?;
Ok(certs)
}
#[pymethod]
@@ -746,9 +743,7 @@ mod _ssl {
args: WrapSocketArgs,
vm: &VirtualMachine,
) -> PyResult<PySslSocket> {
let mut ssl = zelf
.exec_ctx(ssl::Ssl::new)
.map_err(|e| convert_openssl_error(vm, e))?;
let mut ssl = ssl::Ssl::new(&zelf.ctx()).map_err(|e| convert_openssl_error(vm, e))?;
let socket_type = if args.server_side {
ssl.set_accept_state();

View File

@@ -1091,13 +1091,9 @@ pub mod module {
#[pyfunction]
fn ttyname(fd: i32, vm: &VirtualMachine) -> PyResult {
let name = unsafe { libc::ttyname(fd) };
if name.is_null() {
Err(errno_err(vm))
} else {
let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap();
Ok(vm.ctx.new_str(name).into())
}
let name = unistd::ttyname(fd).map_err(|e| e.into_pyexception(vm))?;
let name = name.into_os_string().into_string().unwrap();
Ok(vm.ctx.new_str(name).into())
}
#[pyfunction]
@@ -1129,30 +1125,24 @@ pub mod module {
#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
#[pyfunction]
fn getresuid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
let mut ruid = 0;
let mut euid = 0;
let mut suid = 0;
let ret = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) };
if ret == 0 {
Ok((ruid, euid, suid))
} else {
Err(errno_err(vm))
}
let ret = unistd::getresuid().map_err(|e| e.into_pyexception(vm))?;
Ok((
ret.real.as_raw(),
ret.effective.as_raw(),
ret.saved.as_raw(),
))
}
// cfg from nix
#[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
#[pyfunction]
fn getresgid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
let mut rgid = 0;
let mut egid = 0;
let mut sgid = 0;
let ret = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) };
if ret == 0 {
Ok((rgid, egid, sgid))
} else {
Err(errno_err(vm))
}
let ret = unistd::getresgid().map_err(|e| e.into_pyexception(vm))?;
Ok((
ret.real.as_raw(),
ret.effective.as_raw(),
ret.saved.as_raw(),
))
}
// cfg from nix

View File

@@ -389,11 +389,11 @@ mod unix {
use super::{SEC_TO_NS, US_TO_NS};
#[cfg_attr(target_os = "macos", allow(unused_imports))]
use crate::{
builtins::{try_bigint_to_f64, PyFloat, PyIntRef, PyNamespace, PyStrRef},
function::Either,
stdlib::os,
PyRef, PyResult, VirtualMachine,
builtins::{PyNamespace, PyStrRef},
convert::IntoPyException,
PyObject, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine,
};
use nix::{sys::time::TimeSpec, time::ClockId};
use std::time::Duration;
#[cfg(target_os = "solaris")]
@@ -425,83 +425,62 @@ mod unix {
#[pyattr]
use libc::{CLOCK_PROF, CLOCK_UPTIME};
fn get_clock_time(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult<Duration> {
let mut timespec = std::mem::MaybeUninit::uninit();
let ts: libc::timespec = unsafe {
if libc::clock_gettime(clk_id.try_to_primitive(vm)?, timespec.as_mut_ptr()) == -1 {
return Err(os::errno_err(vm));
}
timespec.assume_init()
};
Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32))
impl TryFromBorrowedObject for ClockId {
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult<Self> {
obj.try_to_value(vm).map(ClockId::from_raw)
}
}
fn get_clock_time(clk_id: ClockId, vm: &VirtualMachine) -> PyResult<Duration> {
let ts = nix::time::clock_gettime(clk_id).map_err(|e| e.into_pyexception(vm))?;
Ok(ts.into())
}
#[pyfunction]
fn clock_gettime(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult<f64> {
fn clock_gettime(clk_id: ClockId, vm: &VirtualMachine) -> PyResult<f64> {
get_clock_time(clk_id, vm).map(|d| d.as_secs_f64())
}
#[pyfunction]
fn clock_gettime_ns(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult<u128> {
fn clock_gettime_ns(clk_id: ClockId, vm: &VirtualMachine) -> PyResult<u128> {
get_clock_time(clk_id, vm).map(|d| d.as_nanos())
}
#[cfg(not(target_os = "redox"))]
#[pyfunction]
fn clock_getres(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult<f64> {
let mut timespec = std::mem::MaybeUninit::uninit();
let ts: libc::timespec = unsafe {
if libc::clock_getres(clk_id.try_to_primitive(vm)?, timespec.as_mut_ptr()) == -1 {
return Err(os::errno_err(vm));
}
timespec.assume_init()
};
Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32).as_secs_f64())
fn clock_getres(clk_id: ClockId, vm: &VirtualMachine) -> PyResult<f64> {
let ts = nix::time::clock_getres(clk_id).map_err(|e| e.into_pyexception(vm))?;
Ok(Duration::from(ts).as_secs_f64())
}
#[cfg(not(target_os = "redox"))]
#[cfg(any(not(target_vendor = "apple"), target_os = "macos"))]
fn set_clock_time(
clk_id: PyIntRef,
timespec: libc::timespec,
vm: &VirtualMachine,
) -> PyResult<()> {
let res = unsafe { libc::clock_settime(clk_id.try_to_primitive(vm)?, &timespec) };
if res == -1 {
return Err(os::errno_err(vm));
}
Ok(())
#[cfg(not(target_vendor = "apple"))]
fn set_clock_time(clk_id: ClockId, timespec: TimeSpec, vm: &VirtualMachine) -> PyResult<()> {
nix::time::clock_settime(clk_id, timespec).map_err(|e| e.into_pyexception(vm))
}
#[cfg(not(target_os = "redox"))]
#[cfg(target_os = "macos")]
fn set_clock_time(clk_id: ClockId, timespec: TimeSpec, vm: &VirtualMachine) -> PyResult<()> {
// idk why nix disables clock_settime on macos
let ret = unsafe { libc::clock_settime(clk_id.as_raw(), timespec.as_ref()) };
nix::Error::result(ret)
.map(drop)
.map_err(|e| e.into_pyexception(vm))
}
#[cfg(not(target_os = "redox"))]
#[cfg(any(not(target_vendor = "apple"), target_os = "macos"))]
#[pyfunction]
fn clock_settime(
clk_id: PyIntRef,
time: Either<PyRef<PyFloat>, PyIntRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
let time = match time {
Either::A(f) => f.to_f64(),
Either::B(z) => try_bigint_to_f64(z.as_bigint(), vm)?,
};
let nanos = time.fract() * (SEC_TO_NS as f64);
let ts = libc::timespec {
tv_sec: time.floor() as libc::time_t,
tv_nsec: nanos as _,
};
set_clock_time(clk_id, ts, vm)
fn clock_settime(clk_id: ClockId, time: Duration, vm: &VirtualMachine) -> PyResult<()> {
set_clock_time(clk_id, time.into(), vm)
}
#[cfg(not(target_os = "redox"))]
#[cfg(any(not(target_vendor = "apple"), target_os = "macos"))]
#[pyfunction]
fn clock_settime_ns(clk_id: PyIntRef, time: PyIntRef, vm: &VirtualMachine) -> PyResult<()> {
let time: libc::time_t = time.try_to_primitive(vm)?;
let ts = libc::timespec {
tv_sec: time / (SEC_TO_NS as libc::time_t),
tv_nsec: time.rem_euclid(SEC_TO_NS as libc::time_t) as _,
};
fn clock_settime_ns(clk_id: ClockId, time: libc::time_t, vm: &VirtualMachine) -> PyResult<()> {
let ts = Duration::from_nanos(time as _).into();
set_clock_time(clk_id, ts, vm)
}
@@ -522,25 +501,25 @@ mod unix {
false,
"time.clock_gettime(CLOCK_MONOTONIC)",
true,
clock_getres(vm.ctx.new_int(CLOCK_MONOTONIC), vm)?,
clock_getres(ClockId::CLOCK_MONOTONIC, vm)?,
),
"process_time" => (
false,
"time.clock_gettime(CLOCK_PROCESS_CPUTIME_ID)",
true,
clock_getres(vm.ctx.new_int(CLOCK_PROCESS_CPUTIME_ID), vm)?,
clock_getres(ClockId::CLOCK_PROCESS_CPUTIME_ID, vm)?,
),
"thread_time" => (
false,
"time.clock_gettime(CLOCK_THREAD_CPUTIME_ID)",
true,
clock_getres(vm.ctx.new_int(CLOCK_THREAD_CPUTIME_ID), vm)?,
clock_getres(ClockId::CLOCK_THREAD_CPUTIME_ID, vm)?,
),
"time" => (
true,
"time.clock_gettime(CLOCK_REALTIME)",
false,
clock_getres(vm.ctx.new_int(CLOCK_REALTIME), vm)?,
clock_getres(ClockId::CLOCK_REALTIME, vm)?,
),
_ => return Err(vm.new_value_error("unknown clock".to_owned())),
};
@@ -568,22 +547,19 @@ mod unix {
}
pub(super) fn get_monotonic_time(vm: &VirtualMachine) -> PyResult<Duration> {
get_clock_time(vm.ctx.new_int(CLOCK_MONOTONIC), vm)
get_clock_time(ClockId::CLOCK_MONOTONIC, vm)
}
pub(super) fn get_perf_time(vm: &VirtualMachine) -> PyResult<Duration> {
get_clock_time(vm.ctx.new_int(CLOCK_MONOTONIC), vm)
get_clock_time(ClockId::CLOCK_MONOTONIC, vm)
}
#[pyfunction]
fn sleep(dur: Duration, vm: &VirtualMachine) -> PyResult<()> {
// this is basically std::thread::sleep, but that catches interrupts and we don't want to;
let mut ts = libc::timespec {
tv_sec: std::cmp::min(libc::time_t::max_value() as u64, dur.as_secs()) as libc::time_t,
tv_nsec: dur.subsec_nanos() as _,
};
let res = unsafe { libc::nanosleep(&ts, &mut ts) };
let ts = TimeSpec::from(dur);
let res = unsafe { libc::nanosleep(ts.as_ref(), std::ptr::null_mut()) };
let interrupted = res == -1 && nix::errno::errno() == libc::EINTR;
if interrupted {
@@ -600,14 +576,7 @@ mod unix {
target_os = "redox"
)))]
pub(super) fn get_thread_time(vm: &VirtualMachine) -> PyResult<Duration> {
let time: libc::timespec = unsafe {
let mut time = std::mem::MaybeUninit::uninit();
if libc::clock_gettime(CLOCK_THREAD_CPUTIME_ID, time.as_mut_ptr()) == -1 {
return Err(vm.new_os_error("Failed to get clock time".to_owned()));
}
time.assume_init()
};
Ok(Duration::new(time.tv_sec as u64, time.tv_nsec as u32))
get_clock_time(ClockId::CLOCK_THREAD_CPUTIME_ID, vm)
}
#[cfg(target_os = "solaris")]
@@ -622,14 +591,7 @@ mod unix {
target_os = "openbsd",
)))]
pub(super) fn get_process_time(vm: &VirtualMachine) -> PyResult<Duration> {
let time: libc::timespec = unsafe {
let mut time = std::mem::MaybeUninit::uninit();
if libc::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, time.as_mut_ptr()) == -1 {
return Err(vm.new_os_error("Failed to get clock time".to_owned()));
}
time.assume_init()
};
Ok(Duration::new(time.tv_sec as u64, time.tv_nsec as u32))
get_clock_time(ClockId::CLOCK_PROCESS_CPUTIME_ID, vm)
}
#[cfg(any(
@@ -639,6 +601,7 @@ mod unix {
target_os = "openbsd",
))]
pub(super) fn get_process_time(vm: &VirtualMachine) -> PyResult<Duration> {
use nix::sys::resource::{getrusage, UsageWho};
fn from_timeval(tv: libc::timeval, vm: &VirtualMachine) -> PyResult<i64> {
(|tv: libc::timeval| {
let t = tv.tv_sec.checked_mul(SEC_TO_NS)?;
@@ -649,15 +612,9 @@ mod unix {
vm.new_overflow_error("timestamp too large to convert to i64".to_owned())
})
}
let ru: libc::rusage = unsafe {
let mut ru = std::mem::MaybeUninit::uninit();
if libc::getrusage(libc::RUSAGE_SELF, ru.as_mut_ptr()) == -1 {
return Err(vm.new_os_error("Failed to get clock time".to_owned()));
}
ru.assume_init()
};
let utime = from_timeval(ru.ru_utime, vm)?;
let stime = from_timeval(ru.ru_stime, vm)?;
let ru = getrusage(UsageWho::RUSAGE_SELF).map_err(|e| e.into_pyexception(vm))?;
let utime = from_timeval(ru.user_time().into(), vm)?;
let stime = from_timeval(ru.system_time().into(), vm)?;
Ok(Duration::from_nanos((utime + stime) as u64))
}