// And at this point we've reached a special time in the life of the
// child. The child must now be considered hamstrung and unable to
// do anything other than syscalls really. Consider the following
// scenario:
//
//      1. Thread A of process 1 grabs the malloc() mutex
//      2. Thread B of process 1 forks(), creating thread C
//      3. Thread C of process 2 then attempts to malloc()
//      4. The memory of process 2 is the same as the memory of
//         process 1, so the mutex is locked.
//
// This situation looks a lot like deadlock, right? It turns out
// that this is what pthread_atfork() takes care of, which is
// presumably implemented across platforms. The first thing that
// threads to *before* forking is to do things like grab the malloc
// mutex, and then after the fork they unlock it.
//
// Despite this information, libnative's spawn has been witnessed to
// deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
// all collected backtraces point at malloc/free traffic in the
// child spawned process.
//
// For this reason, the block of code below should contain 0
// invocations of either malloc of free (or their related friends).
//
// As an example of not having malloc/free traffic, we don't close
// this file descriptor by dropping the FileDesc (which contains an
// allocation). Instead we just close it manually. This will never
// have the drop glue anyway because this code never returns (the
// child will either exec() or invoke libc::exit)
And at this point we've reached a special time in the life of the
child. The child must now be considered hamstrung and unable to
do anything other than syscalls really. Consider the following
scenario.

1. Thread A of process 1 grabs the malloc() mutex
2. Thread B of process 1 forks(), creating thread C
3. Thread C of process 2 then attempts to malloc()
4. The memory of process 2 is the same as the memory of
process 1, so the mutex is locked.

This situation looks a lot like deadlock, right. It turns out
that this is what pthread_atfork() takes care of, which is
presumably implemented across platforms. The first thing that
threads to *before* forking is to do things like grab the malloc
mutex, and then after the fork they unlock it.

Despite this information, libnative's spawn has been witnessed to
deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
all collected backtraces point at malloc/free traffic in the
child spawned process.

For this reason, the block of code below should contain 0
invocations of either malloc of free (or their related friends).

As an example of not having malloc/free traffic, we don't close
this file descriptor by dropping the FileDesc (which contains an
allocation). Instead we just close it manually. This will never
have the drop glue anyway because this code never returns (the
child will either exec() or invoke libc::exit)
And at this point we ' ve reached a special time in the life of the child . The child must now be considered hamstrung and unable to do anything other than syscalls really . Consider the following scenario . 1 . Thread A of process 1 grabs the malloc () mutex 2 . Thread B of process 1 forks () creating thread C 3 . Thread C of process 2 then attempts to malloc () 4 . The memory of process 2 is the same as the memory of process 1 so the mutex is locked . This situation looks a lot like deadlock right . It turns out that this is what pthread_atfork () takes care of which is presumably implemented across platforms . The first thing that threads to * before * forking is to do things like grab the malloc mutex and then after the fork they unlock it . Despite this information libnative ' s spawn has been witnessed to deadlock on both OSX and FreeBSD . I ' m not entirely sure why but all collected backtraces point at malloc / free traffic in the child spawned process . For this reason the block of code below should contain 0 invocations of either malloc of free ( or their related friends ) . As an example of not having malloc / free traffic we don ' t close this file descriptor by dropping the FileDesc ( which contains an allocation ) . Instead we just close it manually . This will never have the drop glue anyway because this code never returns ( the child will either exec () or invoke libc :: exit )
unsafe fn child_after_fork(cfg: &Command,
                               mut output: AnonPipe,
                               argv: *const *const libc::c_char,
                               envp: *const libc::c_void,
                               dirp: *const libc::c_char,
                               in_fd: Stdio,
                               out_fd: Stdio,
                               err_fd: Stdio) -> ! {
        fn fail(output: &mut AnonPipe) -> ! {
            let errno = sys::os::errno() as u32;
            let bytes = [
                (errno >> 24) as u8,
                (errno >> 16) as u8,
                (errno >>  8) as u8,
                (errno >>  0) as u8,
                CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
            ];
            // pipe I/O up to PIPE_BUF bytes should be atomic, and then we want
            // to be sure we *don't* run at_exit destructors as we're being torn
            // down regardless
            assert!(output.write(&bytes).is_ok());
            unsafe { libc::_exit(1) }
        }
        let setup = |src: Stdio, dst: c_int| {
            match src {
                Stdio::Inherit => true,
                Stdio::Raw(fd) => cvt_r(|| libc::dup2(fd, dst)).is_ok(),
                // If a stdio file descriptor is set to be ignored, we open up
                // /dev/null into that file descriptor. Otherwise, the first
                // file descriptor opened up in the child would be numbered as
                // one of the stdio file descriptors, which is likely to wreak
                // havoc.
                Stdio::None => {
                    let mut opts = OpenOptions::new();
                    opts.read(dst == libc::STDIN_FILENO);
                    opts.write(dst != libc::STDIN_FILENO);
                    let devnull = CStr::from_ptr(b"/dev/null\0".as_ptr()
                                                    as *const _);
                    if let Ok(f) = File::open_c(devnull, &opts) {
                        cvt_r(|| libc::dup2(f.fd().raw(), dst)).is_ok()
                    } else {
                        false
                    }
                }
            }
        };
        if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
        if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
        if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
        if let Some(u) = cfg.gid {
            if libc::setgid(u as libc::gid_t) != 0 {
                fail(&mut output);
            }
        }
        if let Some(u) = cfg.uid {
            // When dropping privileges from root, the `setgroups` call
            // will remove any extraneous groups. If we don't call this,
            // then even though our uid has dropped, we may still have
            // groups that enable us to do super-user things. This will
            // fail if we aren't root, so don't bother checking the
            // return value, this is just done as an optimistic
            // privilege dropping function.
            let _ = c::setgroups(0, ptr::null());
            if libc::setuid(u as libc::uid_t) != 0 {
                fail(&mut output);
            }
        }
        if cfg.session_leader {
            // Don't check the error of setsid because it fails if we're the
            // process leader already. We just forked so it shouldn't return
            // error, but ignore it anyway.
            let _ = libc::setsid();
        }
        if !dirp.is_null() && libc::chdir(dirp) == -1 {
            fail(&mut output);
        }
        if !envp.is_null() {
            *sys::os::environ() = envp as *const _;
        }
        // Reset signal handling so the child process starts in a
        // standardized state. libstd ignores SIGPIPE, and signal-handling
        // libraries often set a mask. Child processes inherit ignored
        // signals and the signal mask from their parent, but most
        // UNIX programs do not reset these things on their own, so we
        // need to clean things up now to avoid confusing the program
        // we're about to run.
        let mut set: c::sigset_t = mem::uninitialized();
        if c::sigemptyset(&mut set) != 0 ||
           c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 ||
           libc::funcs::posix01::signal::signal(
               libc::SIGPIPE, mem::transmute(c::SIG_DFL)
           ) == mem::transmute(c::SIG_ERR) {
            fail(&mut output);
        }
        let _ = libc::execvp(*argv, argv);
        fail(&mut output)
    }
unsafe fn child_after_fork ( cfg : & Command , mut output : AnonPipe , argv : * const * const libc :: c_char , envp : * const libc :: c_void , dirp : * const libc :: c_char , in_fd : Stdio , out_fd : Stdio , err_fd : Stdio ) -> ! { fn fail ( output : & mut AnonPipe ) -> ! { let errno = sys :: os :: errno ( ) as u32 ; let bytes = [ ( errno >> 24 ) as u8 , ( errno >> 16 ) as u8 , ( errno >> 8 ) as u8 , ( errno >> 0 ) as u8 , CLOEXEC_MSG_FOOTER [ 0 ] , CLOEXEC_MSG_FOOTER [ 1 ] , CLOEXEC_MSG_FOOTER [ 2 ] , CLOEXEC_MSG_FOOTER [ 3 ] ] ; // pipe I/O up to PIPE_BUF bytes should be atomic, and then we want // to be sure we *don't* run at_exit destructors as we're being torn // down regardless assert ! ( output write ( bytes ) is_ok ( ) ) ; unsafe { libc :: _exit ( 1 ) } } let setup = | src : Stdio , dst : c_int | { match src { Stdio :: Inherit => true , Stdio :: Raw ( fd ) => cvt_r ( | | libc :: dup2 ( fd , dst ) ) . is_ok ( ) , // If a stdio file descriptor is set to be ignored, we open up // /dev/null into that file descriptor. Otherwise, the first // file descriptor opened up in the child would be numbered as // one of the stdio file descriptors, which is likely to wreak // havoc. Stdio :: None => { let mut opts = OpenOptions :: new ( ) ; opts . read ( dst == libc :: STDIN_FILENO ) ; opts . write ( dst != libc :: STDIN_FILENO ) ; let devnull = CStr :: from_ptr ( b" \0 " . as_ptr ( ) as * const _ ) ; if let Ok ( f ) = File :: open_c ( devnull , & opts ) { cvt_r ( | | libc :: dup2 ( f . fd ( ) . raw ( ) , dst ) ) . is_ok ( ) } else { false } } } } ; if ! setup ( in_fd , libc :: STDIN_FILENO ) { fail ( & mut output ) } if ! setup ( out_fd , libc :: STDOUT_FILENO ) { fail ( & mut output ) } if ! setup ( err_fd , libc :: STDERR_FILENO ) { fail ( & mut output ) } if let Some ( u ) = cfg . gid { if libc :: setgid ( u as libc :: gid_t ) != 0 { fail ( & mut output ) ; } } if let Some ( u ) = cfg . uid { // When dropping privileges from root, the `setgroups` call // will remove any extraneous groups. If we don't call this, // then even though our uid has dropped, we may still have // groups that enable us to do super-user things. This will // fail if we aren't root, so don't bother checking the // return value, this is just done as an optimistic // privilege dropping function. let _ = c :: setgroups ( 0 , ptr :: null ( ) ) ; if libc :: setuid ( u as libc :: uid_t ) != 0 { fail ( & mut output ) ; } } if cfg . session_leader { // Don't check the error of setsid because it fails if we're the // process leader already. We just forked so it shouldn't return // error, but ignore it anyway. let _ = libc :: setsid ( ) ; } if ! dirp . is_null ( ) && libc :: chdir ( dirp ) == - 1 { fail ( & mut output ) ; } if ! envp . is_null ( ) { * sys :: os :: environ ( ) = envp as * const _ ; } // Reset signal handling so the child process starts in a // standardized state. libstd ignores SIGPIPE, and signal-handling // libraries often set a mask. Child processes inherit ignored // signals and the signal mask from their parent, but most // UNIX programs do not reset these things on their own, so we // need to clean things up now to avoid confusing the program // we're about to run. let mut set : c :: sigset_t = mem :: uninitialized ( ) ; if c :: sigemptyset ( & mut set ) != 0 || c :: pthread_sigmask ( c :: SIG_SETMASK , & set , ptr :: null_mut ( ) ) != 0 || libc :: funcs :: posix01 :: signal :: signal ( libc :: SIGPIPE , mem :: transmute ( c :: SIG_DFL ) ) == mem :: transmute ( c :: SIG_ERR ) { fail ( & mut output ) ; } let _ = libc :: execvp ( * argv , argv ) ; fail ( & mut output ) }
And at this point we've reached a special time in the life of the
child.
And at this point we ' ve reached a special time in the life of the child .
cfg
&Command
output
AnonPipe
argv
*const *const libc::c_char
envp
*const libc::c_void
dirp
*const libc::c_char
in_fd
Stdio
out_fd
Stdio
err_fd
Stdio