about summary refs log tree commit diff
path: root/src/libnative
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-02-06 22:38:05 -0800
committerAlex Crichton <alex@alexcrichton.com>2014-02-16 16:01:03 -0800
commit553b7e67d70dbf72dd705616acaa496a20fba765 (patch)
tree4274ee5030cea6f05dbbb23f75c1c9eb4e58c2f1 /src/libnative
parent13dc521861f6c3be909ba9317fde50f946e9f85d (diff)
downloadrust-553b7e67d70dbf72dd705616acaa496a20fba765.tar.gz
rust-553b7e67d70dbf72dd705616acaa496a20fba765.zip
Allow configuration of uid/gid/detach on processes
This just copies the libuv implementation for libnative which seems reasonable
enough (uid/gid fail on windows).

Closes #12082
Diffstat (limited to 'src/libnative')
-rw-r--r--src/libnative/io/process.rs95
1 files changed, 74 insertions, 21 deletions
diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs
index b796535371f..affa3ebf544 100644
--- a/src/libnative/io/process.rs
+++ b/src/libnative/io/process.rs
@@ -98,8 +98,8 @@ impl Process {
 
         let env = config.env.map(|a| a.to_owned());
         let cwd = config.cwd.map(|a| Path::new(a));
-        let res = spawn_process_os(config.program, config.args, env,
-                                   cwd.as_ref(), in_fd, out_fd, err_fd);
+        let res = spawn_process_os(config, env, cwd.as_ref(), in_fd, out_fd,
+                                   err_fd);
 
         unsafe {
             for pipe in in_pipe.iter() { let _ = libc::close(pipe.input); }
@@ -180,7 +180,7 @@ struct SpawnProcessResult {
 }
 
 #[cfg(windows)]
-fn spawn_process_os(prog: &str, args: &[~str],
+fn spawn_process_os(config: p::ProcessConfig,
                     env: Option<~[(~str, ~str)]>,
                     dir: Option<&Path>,
                     in_fd: c_int, out_fd: c_int,
@@ -202,6 +202,14 @@ fn spawn_process_os(prog: &str, args: &[~str],
 
     use std::mem;
 
+    if config.gid.is_some() || config.uid.is_some() {
+        return Err(io::IoError {
+            kind: io::OtherIoError,
+            desc: "unsupported gid/uid requested on windows",
+            detail: None,
+        })
+    }
+
     unsafe {
 
         let mut si = zeroed_startupinfo();
@@ -237,16 +245,23 @@ fn spawn_process_os(prog: &str, args: &[~str],
             fail!("failure in DuplicateHandle: {}", os::last_os_error());
         }
 
-        let cmd = make_command_line(prog, args);
+        let cmd = make_command_line(config.program, config.args);
         let mut pi = zeroed_process_information();
         let mut create_err = None;
 
+        // stolen from the libuv code.
+        let mut flags = 0;
+        if config.detach {
+            flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
+        }
+
         with_envp(env, |envp| {
             with_dirp(dir, |dirp| {
                 cmd.with_c_str(|cmdp| {
                     let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
                                                  ptr::mut_null(), ptr::mut_null(), TRUE,
-                                                 0, envp, dirp, &mut si, &mut pi);
+                                                 flags, envp, dirp, &mut si,
+                                                 &mut pi);
                     if created == FALSE {
                         create_err = Some(super::last_error());
                     }
@@ -364,7 +379,7 @@ fn make_command_line(prog: &str, args: &[~str]) -> ~str {
 }
 
 #[cfg(unix)]
-fn spawn_process_os(prog: &str, args: &[~str],
+fn spawn_process_os(config: p::ProcessConfig,
                     env: Option<~[(~str, ~str)]>,
                     dir: Option<&Path>,
                     in_fd: c_int, out_fd: c_int,
@@ -372,7 +387,6 @@ fn spawn_process_os(prog: &str, args: &[~str],
     use std::libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
     use std::libc::funcs::bsd44::getdtablesize;
     use std::libc::c_ulong;
-    use std::unstable::intrinsics;
 
     mod rustrt {
         extern {
@@ -441,22 +455,34 @@ fn spawn_process_os(prog: &str, args: &[~str],
         }
         drop(input);
 
+        fn fail(output: &mut file::FileDesc) -> ! {
+            let errno = os::errno();
+            let bytes = [
+                (errno << 24) as u8,
+                (errno << 16) as u8,
+                (errno <<  8) as u8,
+                (errno <<  0) as u8,
+            ];
+            assert!(output.inner_write(bytes).is_ok());
+            unsafe { libc::_exit(1) }
+        }
+
         rustrt::rust_unset_sigprocmask();
 
         if in_fd == -1 {
             let _ = libc::close(libc::STDIN_FILENO);
         } else if retry(|| dup2(in_fd, 0)) == -1 {
-            fail!("failure in dup2(in_fd, 0): {}", os::last_os_error());
+            fail(&mut output);
         }
         if out_fd == -1 {
             let _ = libc::close(libc::STDOUT_FILENO);
         } else if retry(|| dup2(out_fd, 1)) == -1 {
-            fail!("failure in dup2(out_fd, 1): {}", os::last_os_error());
+            fail(&mut output);
         }
         if err_fd == -1 {
             let _ = libc::close(libc::STDERR_FILENO);
         } else if retry(|| dup2(err_fd, 2)) == -1 {
-            fail!("failure in dup3(err_fd, 2): {}", os::last_os_error());
+            fail(&mut output);
         }
         // close all other fds
         for fd in range(3, getdtablesize()).rev() {
@@ -465,9 +491,44 @@ fn spawn_process_os(prog: &str, args: &[~str],
             }
         }
 
+        match config.gid {
+            Some(u) => {
+                if libc::setgid(u as libc::gid_t) != 0 {
+                    fail(&mut output);
+                }
+            }
+            None => {}
+        }
+        match config.uid {
+            Some(u) => {
+                // 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.
+                extern {
+                    fn setgroups(ngroups: libc::c_int,
+                                 ptr: *libc::c_void) -> libc::c_int;
+                }
+                let _ = setgroups(0, 0 as *libc::c_void);
+
+                if libc::setuid(u as libc::uid_t) != 0 {
+                    fail(&mut output);
+                }
+            }
+            None => {}
+        }
+        if config.detach {
+            // 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();
+        }
+
         with_dirp(dir, |dirp| {
             if !dirp.is_null() && chdir(dirp) == -1 {
-                fail!("failure in chdir: {}", os::last_os_error());
+                fail(&mut output);
             }
         });
 
@@ -476,17 +537,9 @@ fn spawn_process_os(prog: &str, args: &[~str],
                 set_environ(envp);
             }
         });
-        with_argv(prog, args, |argv| {
+        with_argv(config.program, config.args, |argv| {
             let _ = execvp(*argv, argv);
-            let errno = os::errno();
-            let bytes = [
-                (errno << 24) as u8,
-                (errno << 16) as u8,
-                (errno <<  8) as u8,
-                (errno <<  0) as u8,
-            ];
-            assert!(output.inner_write(bytes).is_ok());
-            intrinsics::abort();
+            fail(&mut output);
         })
     }
 }