|
| 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)]
|
@@ -41,13 +43,8 @@ use crate::pyobject::{
|
41 | 43 | };
|
42 | 44 | use crate::vm::VirtualMachine;
|
43 | 45 |
|
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)] |
51 | 48 | use crate::pyobject::PyIterable;
|
52 | 49 |
|
53 | 50 | #[cfg(windows)]
|
@@ -1581,6 +1578,196 @@ fn os_setgroups(group_ids: PyIterable<u32>, vm: &VirtualMachine) -> PyResult<()>
|
1581 | 1578 | ret.map_err(|err| convert_nix_error(vm, err))
|
1582 | 1579 | }
|
1583 | 1580 |
|
| 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 | + |
1584 | 1771 | pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
1585 | 1772 | let ctx = &vm.ctx;
|
1586 | 1773 |
|
@@ -1846,6 +2033,15 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: &PyObjectRef) {
|
1846 | 2033 | extend_module!(vm, module, {
|
1847 | 2034 | "pipe2" => ctx.new_function(os_pipe2),
|
1848 | 2035 | });
|
| 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 | + }); |
1849 | 2045 | }
|
1850 | 2046 |
|
1851 | 2047 | #[cfg(windows)]
|
|
0 commit comments