Skip to content

Commit dc1e7a5

Browse files
authored
Rollup merge of rust-lang#48624 - bdrewery:freebsd-posix-spawn, r=alexcrichton
Command: Support posix_spawn() on FreeBSD/OSX/GNU Linux
2 parents 82e2f36 + 8e0faf7 commit dc1e7a5

File tree

6 files changed

+193
-28
lines changed

6 files changed

+193
-28
lines changed

src/libstd/sys/unix/net.rs

+5-28
Original file line numberDiff line numberDiff line change
@@ -383,42 +383,19 @@ impl IntoInner<c_int> for Socket {
383383
// believe it's thread-safe).
384384
#[cfg(target_env = "gnu")]
385385
fn on_resolver_failure() {
386+
use sys;
387+
386388
// If the version fails to parse, we treat it the same as "not glibc".
387-
if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
388-
if let Some(version) = parse_glibc_version(version_str) {
389-
if version < (2, 26) {
390-
unsafe { libc::res_init() };
391-
}
389+
if let Some(version) = sys::os::glibc_version() {
390+
if version < (2, 26) {
391+
unsafe { libc::res_init() };
392392
}
393393
}
394394
}
395395

396396
#[cfg(not(target_env = "gnu"))]
397397
fn on_resolver_failure() {}
398398

399-
#[cfg(target_env = "gnu")]
400-
fn glibc_version_cstr() -> Option<&'static CStr> {
401-
weak! {
402-
fn gnu_get_libc_version() -> *const libc::c_char
403-
}
404-
if let Some(f) = gnu_get_libc_version.get() {
405-
unsafe { Some(CStr::from_ptr(f())) }
406-
} else {
407-
None
408-
}
409-
}
410-
411-
// Returns Some((major, minor)) if the string is a valid "x.y" version,
412-
// ignoring any extra dot-separated parts. Otherwise return None.
413-
#[cfg(target_env = "gnu")]
414-
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
415-
let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
416-
match (parsed_ints.next(), parsed_ints.next()) {
417-
(Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
418-
_ => None
419-
}
420-
}
421-
422399
#[cfg(all(test, taget_env = "gnu"))]
423400
mod test {
424401
use super::*;

src/libstd/sys/unix/os.rs

+32
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,35 @@ pub fn getpid() -> u32 {
546546
pub fn getppid() -> u32 {
547547
unsafe { libc::getppid() as u32 }
548548
}
549+
550+
#[cfg(target_env = "gnu")]
551+
pub fn glibc_version() -> Option<(usize, usize)> {
552+
if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
553+
parse_glibc_version(version_str)
554+
} else {
555+
None
556+
}
557+
}
558+
559+
#[cfg(target_env = "gnu")]
560+
fn glibc_version_cstr() -> Option<&'static CStr> {
561+
weak! {
562+
fn gnu_get_libc_version() -> *const libc::c_char
563+
}
564+
if let Some(f) = gnu_get_libc_version.get() {
565+
unsafe { Some(CStr::from_ptr(f())) }
566+
} else {
567+
None
568+
}
569+
}
570+
571+
// Returns Some((major, minor)) if the string is a valid "x.y" version,
572+
// ignoring any extra dot-separated parts. Otherwise return None.
573+
#[cfg(target_env = "gnu")]
574+
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
575+
let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
576+
match (parsed_ints.next(), parsed_ints.next()) {
577+
(Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
578+
_ => None
579+
}
580+
}

src/libstd/sys/unix/process/process_common.rs

+3
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ impl Command {
184184
let maybe_env = self.env.capture_if_changed();
185185
maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
186186
}
187+
pub fn env_saw_path(&self) -> bool {
188+
self.env.have_changed_path()
189+
}
187190

188191
pub fn setup_io(&self, default: Stdio, needs_stdin: bool)
189192
-> io::Result<(StdioPipes, ChildPipes)> {

src/libstd/sys/unix/process/process_unix.rs

+118
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ impl Command {
3434
}
3535

3636
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
37+
38+
if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
39+
return Ok((ret, ours))
40+
}
41+
3742
let (input, output) = sys::pipe::anon_pipe()?;
3843

3944
let pid = unsafe {
@@ -229,6 +234,119 @@ impl Command {
229234
libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
230235
io::Error::last_os_error()
231236
}
237+
238+
#[cfg(not(any(target_os = "macos", target_os = "freebsd",
239+
all(target_os = "linux", target_env = "gnu"))))]
240+
fn posix_spawn(&mut self, _: &ChildPipes, _: Option<&CStringArray>)
241+
-> io::Result<Option<Process>>
242+
{
243+
Ok(None)
244+
}
245+
246+
// Only support platforms for which posix_spawn() can return ENOENT
247+
// directly.
248+
#[cfg(any(target_os = "macos", target_os = "freebsd",
249+
all(target_os = "linux", target_env = "gnu")))]
250+
fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
251+
-> io::Result<Option<Process>>
252+
{
253+
use mem;
254+
use sys;
255+
256+
if self.get_cwd().is_some() ||
257+
self.get_gid().is_some() ||
258+
self.get_uid().is_some() ||
259+
self.env_saw_path() ||
260+
self.get_closures().len() != 0 {
261+
return Ok(None)
262+
}
263+
264+
// Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
265+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
266+
{
267+
if let Some(version) = sys::os::glibc_version() {
268+
if version < (2, 24) {
269+
return Ok(None)
270+
}
271+
} else {
272+
return Ok(None)
273+
}
274+
}
275+
276+
let mut p = Process { pid: 0, status: None };
277+
278+
struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);
279+
280+
impl Drop for PosixSpawnFileActions {
281+
fn drop(&mut self) {
282+
unsafe {
283+
libc::posix_spawn_file_actions_destroy(&mut self.0);
284+
}
285+
}
286+
}
287+
288+
struct PosixSpawnattr(libc::posix_spawnattr_t);
289+
290+
impl Drop for PosixSpawnattr {
291+
fn drop(&mut self) {
292+
unsafe {
293+
libc::posix_spawnattr_destroy(&mut self.0);
294+
}
295+
}
296+
}
297+
298+
unsafe {
299+
let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
300+
let mut attrs = PosixSpawnattr(mem::uninitialized());
301+
302+
libc::posix_spawnattr_init(&mut attrs.0);
303+
libc::posix_spawn_file_actions_init(&mut file_actions.0);
304+
305+
if let Some(fd) = stdio.stdin.fd() {
306+
cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
307+
fd,
308+
libc::STDIN_FILENO))?;
309+
}
310+
if let Some(fd) = stdio.stdout.fd() {
311+
cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
312+
fd,
313+
libc::STDOUT_FILENO))?;
314+
}
315+
if let Some(fd) = stdio.stderr.fd() {
316+
cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
317+
fd,
318+
libc::STDERR_FILENO))?;
319+
}
320+
321+
let mut set: libc::sigset_t = mem::uninitialized();
322+
cvt(libc::sigemptyset(&mut set))?;
323+
cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
324+
&set))?;
325+
cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
326+
cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
327+
&set))?;
328+
329+
let flags = libc::POSIX_SPAWN_SETSIGDEF |
330+
libc::POSIX_SPAWN_SETSIGMASK;
331+
cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;
332+
333+
let envp = envp.map(|c| c.as_ptr())
334+
.unwrap_or(*sys::os::environ() as *const _);
335+
let ret = libc::posix_spawnp(
336+
&mut p.pid,
337+
self.get_argv()[0],
338+
&file_actions.0,
339+
&attrs.0,
340+
self.get_argv().as_ptr() as *const _,
341+
envp as *const _,
342+
);
343+
if ret == 0 {
344+
Ok(Some(p))
345+
} else {
346+
Err(io::Error::from_raw_os_error(ret))
347+
}
348+
}
349+
}
232350
}
233351

234352
////////////////////////////////////////////////////////////////////////////////

src/libstd/sys_common/process.rs

+12
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ impl EnvKey for DefaultEnvKey {}
4747
#[derive(Clone, Debug)]
4848
pub struct CommandEnv<K> {
4949
clear: bool,
50+
saw_path: bool,
5051
vars: BTreeMap<K, Option<OsString>>
5152
}
5253

5354
impl<K: EnvKey> Default for CommandEnv<K> {
5455
fn default() -> Self {
5556
CommandEnv {
5657
clear: false,
58+
saw_path: false,
5759
vars: Default::default()
5860
}
5961
}
@@ -108,9 +110,11 @@ impl<K: EnvKey> CommandEnv<K> {
108110

109111
// The following functions build up changes
110112
pub fn set(&mut self, key: &OsStr, value: &OsStr) {
113+
self.maybe_saw_path(&key);
111114
self.vars.insert(key.to_owned().into(), Some(value.to_owned()));
112115
}
113116
pub fn remove(&mut self, key: &OsStr) {
117+
self.maybe_saw_path(&key);
114118
if self.clear {
115119
self.vars.remove(key);
116120
} else {
@@ -121,4 +125,12 @@ impl<K: EnvKey> CommandEnv<K> {
121125
self.clear = true;
122126
self.vars.clear();
123127
}
128+
pub fn have_changed_path(&self) -> bool {
129+
self.saw_path || self.clear
130+
}
131+
fn maybe_saw_path(&mut self, key: &OsStr) {
132+
if !self.saw_path && key == "PATH" {
133+
self.saw_path = true;
134+
}
135+
}
124136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-cloudabi no processes
12+
// ignore-emscripten no processes
13+
14+
use std::io::ErrorKind;
15+
use std::process::Command;
16+
17+
fn main() {
18+
assert_eq!(Command::new("nonexistent")
19+
.spawn()
20+
.unwrap_err()
21+
.kind(),
22+
ErrorKind::NotFound);
23+
}

0 commit comments

Comments
 (0)