about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2016-02-03 16:55:59 -0800
committerAlex Crichton <alex@alexcrichton.com>2016-02-10 09:28:48 -0800
commitb1898db0f10f9641c7616e93499348d4fe743ddd (patch)
treec0f4add8dbc41cf4946ba211b2cff3ce1416a6cf /src/libstd/sys
parent6c4198469025bf037f59d617c5b75229546ce68a (diff)
downloadrust-b1898db0f10f9641c7616e93499348d4fe743ddd.tar.gz
rust-b1898db0f10f9641c7616e93499348d4fe743ddd.zip
std: Implement CommandExt::before_exec
This is a Unix-specific function which adds the ability to register a closure to
run pre-exec to configure the child process as required (note that these
closures are run post-fork).

cc #31398
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/unix/ext/process.rs38
-rw-r--r--src/libstd/sys/unix/process.rs15
2 files changed, 51 insertions, 2 deletions
diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs
index 97938b07f8b..96727ed6674 100644
--- a/src/libstd/sys/unix/ext/process.rs
+++ b/src/libstd/sys/unix/ext/process.rs
@@ -12,6 +12,9 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+use prelude::v1::*;
+
+use io;
 use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
 use os::unix::raw::{uid_t, gid_t};
 use process;
@@ -44,6 +47,34 @@ pub trait CommandExt {
     #[unstable(feature = "process_session_leader", reason = "recently added",
                issue = "27811")]
     fn session_leader(&mut self, on: bool) -> &mut process::Command;
+
+    /// Schedules a closure to be run just before the `exec` function is
+    /// invoked.
+    ///
+    /// The closure is allowed to return an I/O error whose OS error code will
+    /// be communicated back to the parent and returned as an error from when
+    /// the spawn was requested.
+    ///
+    /// Multiple closures can be registered and they will be called in order of
+    /// their registration. If a closure returns `Err` then no further closures
+    /// will be called and the spawn operation will immediately return with a
+    /// failure.
+    ///
+    /// # Notes
+    ///
+    /// This closure will be run in the context of the child process after a
+    /// `fork`. This primarily means that any modificatons made to memory on
+    /// behalf of this closure will **not** be visible to the parent process.
+    /// This is often a very constrained environment where normal operations
+    /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
+    /// other threads perhaps still running when the `fork` was run).
+    ///
+    /// When this closure is run, aspects such as the stdio file descriptors and
+    /// working directory have successfully been changed, so output to these
+    /// locations may not appear where intended.
+    #[unstable(feature = "process_exec", issue = "31398")]
+    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+        where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -62,6 +93,13 @@ impl CommandExt for process::Command {
         self.as_inner_mut().session_leader(on);
         self
     }
+
+    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+        where F: FnMut() -> io::Result<()> + Send + Sync + 'static
+    {
+        self.as_inner_mut().before_exec(Box::new(f));
+        self
+    }
 }
 
 /// Unix-specific extensions to `std::process::ExitStatus`
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
index ed512b834f8..7387e9def9f 100644
--- a/src/libstd/sys/unix/process.rs
+++ b/src/libstd/sys/unix/process.rs
@@ -58,6 +58,7 @@ pub struct Command {
     gid: Option<gid_t>,
     session_leader: bool,
     saw_nul: bool,
+    closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
 }
 
 impl Command {
@@ -75,6 +76,7 @@ impl Command {
             gid: None,
             session_leader: false,
             saw_nul: saw_nul,
+            closures: Vec::new(),
         }
     }
 
@@ -164,6 +166,11 @@ impl Command {
     pub fn session_leader(&mut self, session_leader: bool) {
         self.session_leader = session_leader;
     }
+
+    pub fn before_exec(&mut self,
+                       f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
+        self.closures.push(f);
+    }
 }
 
 fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
@@ -283,7 +290,7 @@ impl Process {
         Ok(())
     }
 
-    pub fn spawn(cfg: &Command,
+    pub fn spawn(cfg: &mut Command,
                  in_fd: Stdio,
                  out_fd: Stdio,
                  err_fd: Stdio) -> io::Result<Process> {
@@ -387,7 +394,7 @@ impl Process {
     // 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 exec(cfg: &Command,
+    unsafe fn exec(cfg: &mut Command,
                    in_fd: Stdio,
                    out_fd: Stdio,
                    err_fd: Stdio) -> io::Error {
@@ -497,6 +504,10 @@ impl Process {
             }
         }
 
+        for callback in cfg.closures.iter_mut() {
+            try!(callback());
+        }
+
         libc::execvp(cfg.argv[0], cfg.argv.as_ptr());
         io::Error::last_os_error()
     }