Skip to content

Commit 5aeb778

Browse files
committed
Add os.posix_spawn[p]
1 parent f9b6faf commit 5aeb778

File tree

2 files changed

+209
-3
lines changed

2 files changed

+209
-3
lines changed

vm/src/obj/objdict.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ impl PyDictRef {
179179
}
180180

181181
#[pymethod(magic)]
182-
fn len(self) -> usize {
182+
pub fn len(self) -> usize {
183183
self.entries.len()
184184
}
185185

@@ -531,6 +531,11 @@ impl Iterator for DictIter {
531531
None => None,
532532
}
533533
}
534+
535+
fn size_hint(&self) -> (usize, Option<usize>) {
536+
let l = self.dict.entries.len_from_entry_index(self.position);
537+
(l, Some(l))
538+
}
534539
}
535540

536541
macro_rules! dict_iterator {

vm/src/stdlib/os.rs

Lines changed: 203 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
use std::convert::TryFrom;
12
use std::ffi;
23
use std::fs::File;
34
use std::fs::OpenOptions;
45
use std::io::{self, ErrorKind, Read, Write};
6+
use std::mem::MaybeUninit;
57
#[cfg(unix)]
68
use std::os::unix::fs::OpenOptionsExt;
79
#[cfg(windows)]
@@ -36,8 +38,8 @@ use crate::obj::objstr::{PyString, PyStringRef};
3638
use crate::obj::objtuple::PyTupleRef;
3739
use crate::obj::objtype::PyClassRef;
3840
use crate::pyobject::{
39-
Either, ItemProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
40-
TypeProtocol,
41+
Either, ItemProtocol, PyClassImpl, PyIterable, PyObjectRef, PyRef, PyResult, PyValue,
42+
TryFromObject, TypeProtocol,
4143
};
4244
use crate::vm::VirtualMachine;
4345

@@ -1565,6 +1567,196 @@ fn os_setgroups(group_ids: PyIterable<u32>, vm: &VirtualMachine) -> PyResult<()>
15651567
ret.map_err(|err| convert_nix_error(vm, err))
15661568
}
15671569

1570+
#[cfg(unix)]
1571+
fn envp_from_dict(dict: PyDictRef, vm: &VirtualMachine) -> PyResult<Vec<ffi::CString>> {
1572+
use std::os::unix::ffi::OsStringExt;
1573+
dict.into_iter()
1574+
.map(|(k, v)| {
1575+
let k = PyPathLike::try_from_object(vm, k)?.path.into_vec();
1576+
let v = PyPathLike::try_from_object(vm, v)?.path.into_vec();
1577+
if k.contains(&0) {
1578+
return Err(
1579+
vm.new_value_error("envp dict key cannot contain a nul byte".to_owned())
1580+
);
1581+
}
1582+
if k.contains(&b'=') {
1583+
return Err(
1584+
vm.new_value_error("envp dict key cannot contain a '=' character".to_owned())
1585+
);
1586+
}
1587+
if v.contains(&0) {
1588+
return Err(
1589+
vm.new_value_error("envp dict value cannot contain a nul byte".to_owned())
1590+
);
1591+
}
1592+
let mut env = k;
1593+
env.push(b'=');
1594+
env.extend(v);
1595+
Ok(unsafe { ffi::CString::from_vec_unchecked(env) })
1596+
})
1597+
.collect()
1598+
}
1599+
1600+
#[derive(FromArgs)]
1601+
struct PosixSpawnArgs {
1602+
#[pyarg(positional_only)]
1603+
path: PyPathLike,
1604+
#[pyarg(positional_only)]
1605+
args: PyIterable<PyPathLike>,
1606+
#[pyarg(positional_only)]
1607+
env: PyDictRef,
1608+
#[pyarg(keyword_only, default = "None")]
1609+
file_actions: Option<PyIterable<PyTupleRef>>,
1610+
#[pyarg(keyword_only, default = "None")]
1611+
setsigdef: Option<PyIterable<i32>>,
1612+
}
1613+
1614+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1615+
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
1616+
#[repr(i32)]
1617+
enum PosixSpawnFileActionIdentifier {
1618+
Open,
1619+
Close,
1620+
Dup2,
1621+
}
1622+
1623+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1624+
impl PosixSpawnArgs {
1625+
fn spawn(self, spawnp: bool, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1626+
use std::os::unix::ffi::OsStringExt;
1627+
let path = ffi::CString::new(self.path.path.into_vec())
1628+
.map_err(|_| vm.new_value_error("path should not have nul bytes".to_owned()))?;
1629+
1630+
let mut file_actions = unsafe {
1631+
let mut fa = MaybeUninit::uninit();
1632+
assert!(libc::posix_spawn_file_actions_init(fa.as_mut_ptr()) == 0);
1633+
fa.assume_init()
1634+
};
1635+
if let Some(it) = self.file_actions {
1636+
for action in it.iter(vm)? {
1637+
let action = action?;
1638+
let (id, args) = action.as_slice().split_first().ok_or_else(|| {
1639+
vm.new_type_error(
1640+
"Each file_actions element must be a non-empty tuple".to_owned(),
1641+
)
1642+
})?;
1643+
let id = i32::try_from_object(vm, id.clone())?;
1644+
let id = PosixSpawnFileActionIdentifier::try_from(id)
1645+
.map_err(|_| vm.new_type_error("Unknown file_actions identifier".to_owned()))?;
1646+
let args = PyFuncArgs::from(args.to_vec());
1647+
let ret = match id {
1648+
PosixSpawnFileActionIdentifier::Open => {
1649+
let (fd, path, oflag, mode): (_, PyPathLike, _, _) = args.bind(vm)?;
1650+
let path = ffi::CString::new(path.path.into_vec()).map_err(|_| {
1651+
vm.new_value_error(
1652+
"POSIX_SPAWN_OPEN path should not have nul bytes".to_owned(),
1653+
)
1654+
})?;
1655+
unsafe {
1656+
libc::posix_spawn_file_actions_addopen(
1657+
&mut file_actions,
1658+
fd,
1659+
path.as_ptr(),
1660+
oflag,
1661+
mode,
1662+
)
1663+
}
1664+
}
1665+
PosixSpawnFileActionIdentifier::Close => {
1666+
let (fd,) = args.bind(vm)?;
1667+
unsafe { libc::posix_spawn_file_actions_addclose(&mut file_actions, fd) }
1668+
}
1669+
PosixSpawnFileActionIdentifier::Dup2 => {
1670+
let (fd, newfd) = args.bind(vm)?;
1671+
unsafe {
1672+
libc::posix_spawn_file_actions_adddup2(&mut file_actions, fd, newfd)
1673+
}
1674+
}
1675+
};
1676+
if ret != 0 {
1677+
return Err(errno_err(vm));
1678+
}
1679+
}
1680+
}
1681+
1682+
let mut attrp = unsafe {
1683+
let mut sa = MaybeUninit::uninit();
1684+
assert!(libc::posix_spawnattr_init(sa.as_mut_ptr()) == 0);
1685+
sa.assume_init()
1686+
};
1687+
if let Some(sigs) = self.setsigdef {
1688+
use nix::sys::signal;
1689+
let mut set = signal::SigSet::empty();
1690+
for sig in sigs.iter(vm)? {
1691+
let sig = sig?;
1692+
let sig = signal::Signal::try_from(sig).map_err(|_| {
1693+
vm.new_value_error(format!("signal number {} out of range", sig))
1694+
})?;
1695+
set.add(sig);
1696+
}
1697+
assert!(unsafe { libc::posix_spawnattr_setsigdefault(&mut attrp, set.as_ref()) } == 0);
1698+
}
1699+
1700+
let mut args: Vec<ffi::CString> = self
1701+
.args
1702+
.iter(vm)?
1703+
.map(|res| {
1704+
ffi::CString::new(res?.path.into_vec())
1705+
.map_err(|_| vm.new_value_error("path should not have nul bytes".to_owned()))
1706+
})
1707+
.collect::<Result<_, _>>()?;
1708+
let argv: Vec<*mut libc::c_char> = args
1709+
.iter_mut()
1710+
.map(|s| s.as_ptr() as _)
1711+
.chain(std::iter::once(std::ptr::null_mut()))
1712+
.collect();
1713+
let mut env = envp_from_dict(self.env, vm)?;
1714+
let envp: Vec<*mut libc::c_char> = env
1715+
.iter_mut()
1716+
.map(|s| s.as_ptr() as _)
1717+
.chain(std::iter::once(std::ptr::null_mut()))
1718+
.collect();
1719+
1720+
let mut pid = 0;
1721+
let ret = unsafe {
1722+
if spawnp {
1723+
libc::posix_spawnp(
1724+
&mut pid,
1725+
path.as_ptr(),
1726+
&file_actions,
1727+
&attrp,
1728+
argv.as_ptr(),
1729+
envp.as_ptr(),
1730+
)
1731+
} else {
1732+
libc::posix_spawn(
1733+
&mut pid,
1734+
path.as_ptr(),
1735+
&file_actions,
1736+
&attrp,
1737+
argv.as_ptr(),
1738+
envp.as_ptr(),
1739+
)
1740+
}
1741+
};
1742+
1743+
if ret == 0 {
1744+
Ok(pid)
1745+
} else {
1746+
Err(errno_err(vm))
1747+
}
1748+
}
1749+
}
1750+
1751+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1752+
fn os_posix_spawn(args: PosixSpawnArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1753+
args.spawn(false, vm)
1754+
}
1755+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1756+
fn os_posix_spawnp(args: PosixSpawnArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1757+
args.spawn(true, vm)
1758+
}
1759+
15681760
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
15691761
let ctx = &vm.ctx;
15701762

@@ -1829,6 +2021,15 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: &PyObjectRef) {
18292021
extend_module!(vm, module, {
18302022
"pipe2" => ctx.new_function(os_pipe2),
18312023
});
2024+
2025+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
2026+
extend_module!(vm, module, {
2027+
"posix_spawn" => ctx.new_function(os_posix_spawn),
2028+
"posix_spawnp" => ctx.new_function(os_posix_spawnp),
2029+
"POSIX_SPAWN_OPEN" => ctx.new_int(i32::from(PosixSpawnFileActionIdentifier::Open)),
2030+
"POSIX_SPAWN_CLOSE" => ctx.new_int(i32::from(PosixSpawnFileActionIdentifier::Close)),
2031+
"POSIX_SPAWN_DUP2" => ctx.new_int(i32::from(PosixSpawnFileActionIdentifier::Dup2)),
2032+
});
18322033
}
18332034

18342035
#[cfg(windows)]

0 commit comments

Comments
 (0)