|
| 1 | +use std::convert::TryFrom; |
1 | 2 | use std::ffi;
|
2 | 3 | use std::fs::File;
|
3 | 4 | use std::fs::OpenOptions;
|
4 | 5 | use std::io::{self, ErrorKind, Read, Write};
|
| 6 | +use std::mem::MaybeUninit; |
5 | 7 | #[cfg(unix)]
|
6 | 8 | use std::os::unix::fs::OpenOptionsExt;
|
7 | 9 | #[cfg(windows)]
|
@@ -36,8 +38,8 @@ use crate::obj::objstr::{PyString, PyStringRef};
|
36 | 38 | use crate::obj::objtuple::PyTupleRef;
|
37 | 39 | use crate::obj::objtype::PyClassRef;
|
38 | 40 | 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, |
41 | 43 | };
|
42 | 44 | use crate::vm::VirtualMachine;
|
43 | 45 |
|
@@ -1565,6 +1567,196 @@ fn os_setgroups(group_ids: PyIterable<u32>, vm: &VirtualMachine) -> PyResult<()>
|
1565 | 1567 | ret.map_err(|err| convert_nix_error(vm, err))
|
1566 | 1568 | }
|
1567 | 1569 |
|
| 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 | + |
1568 | 1760 | pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
1569 | 1761 | let ctx = &vm.ctx;
|
1570 | 1762 |
|
@@ -1829,6 +2021,15 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: &PyObjectRef) {
|
1829 | 2021 | extend_module!(vm, module, {
|
1830 | 2022 | "pipe2" => ctx.new_function(os_pipe2),
|
1831 | 2023 | });
|
| 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 | + }); |
1832 | 2033 | }
|
1833 | 2034 |
|
1834 | 2035 | #[cfg(windows)]
|
|
0 commit comments