Skip to content

Commit 05a0202

Browse files
coolreader18youknowone
authored andcommitted
Add os.posix_spawn[p]
1 parent 490c638 commit 05a0202

File tree

2 files changed

+210
-8
lines changed

2 files changed

+210
-8
lines changed

vm/src/obj/objdict.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ impl PyValue for PyDict {
3939
}
4040

4141
// Python dict methods:
42+
#[allow(clippy::len_without_is_empty)]
4243
#[pyimpl(flags(BASETYPE))]
4344
impl PyDictRef {
4445
#[pyslot]
@@ -179,7 +180,7 @@ impl PyDictRef {
179180
}
180181

181182
#[pymethod(magic)]
182-
fn len(self) -> usize {
183+
pub fn len(self) -> usize {
183184
self.entries.len()
184185
}
185186

@@ -531,6 +532,11 @@ impl Iterator for DictIter {
531532
None => None,
532533
}
533534
}
535+
536+
fn size_hint(&self) -> (usize, Option<usize>) {
537+
let l = self.dict.entries.len_from_entry_index(self.position);
538+
(l, Some(l))
539+
}
534540
}
535541

536542
macro_rules! dict_iterator {

vm/src/stdlib/os.rs

Lines changed: 203 additions & 7 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)]
@@ -41,13 +43,8 @@ use crate::pyobject::{
4143
};
4244
use crate::vm::VirtualMachine;
4345

44-
// cfg from nix
45-
#[cfg(any(
46-
target_os = "android",
47-
target_os = "freebsd",
48-
target_os = "linux",
49-
target_os = "openbsd"
50-
))]
46+
// just to avoid unused import warnings
47+
#[cfg(unix)]
5148
use crate::pyobject::PyIterable;
5249

5350
#[cfg(windows)]
@@ -1581,6 +1578,196 @@ fn os_setgroups(group_ids: PyIterable<u32>, vm: &VirtualMachine) -> PyResult<()>
15811578
ret.map_err(|err| convert_nix_error(vm, err))
15821579
}
15831580

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

@@ -1846,6 +2033,15 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: &PyObjectRef) {
18462033
extend_module!(vm, module, {
18472034
"pipe2" => ctx.new_function(os_pipe2),
18482035
});
2036+
2037+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
2038+
extend_module!(vm, module, {
2039+
"posix_spawn" => ctx.new_function(os_posix_spawn),
2040+
"posix_spawnp" => ctx.new_function(os_posix_spawnp),
2041+
"POSIX_SPAWN_OPEN" => ctx.new_int(i32::from(PosixSpawnFileActionIdentifier::Open)),
2042+
"POSIX_SPAWN_CLOSE" => ctx.new_int(i32::from(PosixSpawnFileActionIdentifier::Close)),
2043+
"POSIX_SPAWN_DUP2" => ctx.new_int(i32::from(PosixSpawnFileActionIdentifier::Dup2)),
2044+
});
18492045
}
18502046

18512047
#[cfg(windows)]

0 commit comments

Comments
 (0)