(index<- )        ./librustuv/process.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2013 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  use libc::c_int;
  12  use libc;
  13  use std::io::IoError;
  14  use std::io::process;
  15  use std::ptr;
  16  use std::rt::rtio::RtioProcess;
  17  use std::rt::task::BlockedTask;
  18  
  19  use homing::{HomingIO, HomeHandle};
  20  use pipe::PipeWatcher;
  21  use super::{UvHandle, UvError, uv_error_to_io_error,
  22              wait_until_woken_after, wakeup};
  23  use uvio::UvIoFactory;
  24  use uvll;
  25  
  26  pub struct Process {
  27      handle: *uvll::uv_process_t,
  28      home: HomeHandle,
  29  
  30      /// Task to wake up (may be null) for when the process exits
  31      to_wake: Option<BlockedTask>,
  32  
  33      /// Collected from the exit_cb
  34      exit_status: Option<process::ProcessExit>,
  35  }
  36  
  37  impl Process {
  38      /// Spawn a new process inside the specified event loop.
  39      ///
  40      /// Returns either the corresponding process object or an error which
  41      /// occurred.
  42      pub fn spawn(io_loop&mut UvIoFactory, configprocess::ProcessConfig)
  43                  -> Result<(Box<Process>, Vec<Option<PipeWatcher>>), UvError>
  44      {
  45          let cwd = config.cwd.map(|s| s.to_c_str());
  46          let mut io = vec![config.stdin, config.stdout, config.stderr];
  47          for slot in config.extra_io.iter() {
  48              io.push(*slot);
  49          }
  50          let mut stdio = Vec::<uvll::uv_stdio_container_t>::with_capacity(io.len());
  51          let mut ret_io = Vec::with_capacity(io.len());
  52          unsafe {
  53              stdio.set_len(io.len());
  54              for (slot, other) in stdio.iter().zip(io.iter()) {
  55                  let io = set_stdio(slot as *uvll::uv_stdio_container_t, other,
  56                                     io_loop);
  57                  ret_io.push(io);
  58              }
  59          }
  60  
  61          let ret = with_argv(config.program, config.args, |argv| {
  62              with_env(config.env, |envp| {
  63                  let mut flags = 0;
  64                  if config.uid.is_some() {
  65                      flags |= uvll::PROCESS_SETUID;
  66                  }
  67                  if config.gid.is_some() {
  68                      flags |= uvll::PROCESS_SETGID;
  69                  }
  70                  if config.detach {
  71                      flags |= uvll::PROCESS_DETACHED;
  72                  }
  73                  let options = uvll::uv_process_options_t {
  74                      exit_cb: on_exit,
  75                      file: unsafe { *argv },
  76                      args: argv,
  77                      env: envp,
  78                      cwd: match cwd {
  79                          Some(ref cwd) => cwd.with_ref(|p| p),
  80                          None => ptr::null(),
  81                      },
  82                      flags: flags as libc::c_uint,
  83                      stdio_count: stdio.len() as libc::c_int,
  84                      stdio: stdio.as_ptr(),
  85                      uid: config.uid.unwrap_or(0) as uvll::uv_uid_t,
  86                      gid: config.gid.unwrap_or(0) as uvll::uv_gid_t,
  87                  };
  88  
  89                  let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
  90                  let process = box Process {
  91                      handle: handle,
  92                      home: io_loop.make_handle(),
  93                      to_wake: None,
  94                      exit_status: None,
  95                  };
  96                  match unsafe {
  97                      uvll::uv_spawn(io_loop.uv_loop(), handle, &options)
  98                  } {
  99                      0 => Ok(process.install()),
 100                      err => Err(UvError(err)),
 101                  }
 102              })
 103          });
 104  
 105          match ret {
 106              Ok(p) => Ok((p, ret_io)),
 107              Err(e) => Err(e),
 108          }
 109      }
 110  
 111      pub fn kill(pidlibc::pid_t, signumint) -> Result<(), UvError> {
 112          match unsafe {
 113              uvll::uv_kill(pid as libc::c_int, signum as libc::c_int)
 114          } {
 115              0 => Ok(()),
 116              n => Err(UvError(n))
 117          }
 118      }
 119  }
 120  
 121  extern fn on_exit(handle: *uvll::uv_process_t,
 122                    exit_status: i64,
 123                    term_signallibc::c_int) {
 124      let p&mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
 125  
 126      assert!(p.exit_status.is_none());
 127      p.exit_status = Some(match term_signal {
 128          0 => process::ExitStatus(exit_status as int),
 129          n => process::ExitSignal(n as int),
 130      });
 131  
 132      if p.to_wake.is_none() { return }
 133      wakeup(&mut p.to_wake);
 134  }
 135  
 136  unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t,
 137                      io: &process::StdioContainer,
 138                      io_loop: &mut UvIoFactory) -> Option<PipeWatcher> {
 139      match *io {
 140          process::Ignored => {
 141              uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE);
 142              None
 143          }
 144          process::InheritFd(fd) => {
 145              uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD);
 146              uvll::set_stdio_container_fd(dst, fd);
 147              None
 148          }
 149          process::CreatePipe(readable, writable) => {
 150              let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int;
 151              if readable {
 152                  flags |= uvll::STDIO_READABLE_PIPE as libc::c_int;
 153              }
 154              if writable {
 155                  flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int;
 156              }
 157              let pipe = PipeWatcher::new(io_loop, false);
 158              uvll::set_stdio_container_flags(dst, flags);
 159              uvll::set_stdio_container_stream(dst, pipe.handle());
 160              Some(pipe)
 161          }
 162      }
 163  }
 164  
 165  /// Converts the program and arguments to the argv array expected by libuv
 166  fn with_argv<T>(prog: &str, args: &[~str], f: |**libc::c_char-> T) -> T {
 167      // First, allocation space to put all the C-strings (we need to have
 168      // ownership of them somewhere
 169      let mut c_strs = Vec::with_capacity(args.len() + 1);
 170      c_strs.push(prog.to_c_str());
 171      for arg in args.iter() {
 172          c_strs.push(arg.to_c_str());
 173      }
 174  
 175      // Next, create the char** array
 176      let mut c_args = Vec::with_capacity(c_strs.len() + 1);
 177      for s in c_strs.iter() {
 178          c_args.push(s.with_ref(|p| p));
 179      }
 180      c_args.push(ptr::null());
 181      f(c_args.as_ptr())
 182  }
 183  
 184  /// Converts the environment to the env array expected by libuv
 185  fn with_env<T>(envOption<&[(~str, ~str)]>, f: |**libc::c_char-> T) -> T {
 186      let env = match env {
 187          Some(s) => s,
 188          None => { return f(ptr::null()); }
 189      };
 190      // As with argv, create some temporary storage and then the actual array
 191      let mut envp = Vec::with_capacity(env.len());
 192      for &(ref key, ref value) in env.iter() {
 193          envp.push(format!("{}={}", *key, *value).to_c_str());
 194      }
 195      let mut c_envp = Vec::with_capacity(envp.len() + 1);
 196      for s in envp.iter() {
 197          c_envp.push(s.with_ref(|p| p));
 198      }
 199      c_envp.push(ptr::null());
 200      f(c_envp.as_ptr())
 201  }
 202  
 203  impl HomingIO for Process {
 204      fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
 205  }
 206  
 207  impl UvHandle<uvll::uv_process_t> for Process {
 208      fn uv_handle(&self) -> *uvll::uv_process_t { self.handle }
 209  }
 210  
 211  impl RtioProcess for Process {
 212      fn id(&self) -> libc::pid_t {
 213          unsafe { uvll::process_pid(self.handle) as libc::pid_t }
 214      }
 215  
 216      fn kill(&mut self, signalint) -> Result<(), IoError> {
 217          let _m = self.fire_homing_missile();
 218          match unsafe {
 219              uvll::uv_process_kill(self.handle, signal as libc::c_int)
 220          } {
 221              0 => Ok(()),
 222              err => Err(uv_error_to_io_error(UvError(err)))
 223          }
 224      }
 225  
 226      fn wait(&mut self) -> process::ProcessExit {
 227          // Make sure (on the home scheduler) that we have an exit status listed
 228          let _m = self.fire_homing_missile();
 229          match self.exit_status {
 230              Some(..) => {}
 231              None => {
 232                  // If there's no exit code previously listed, then the
 233                  // process's exit callback has yet to be invoked. We just
 234                  // need to deschedule ourselves and wait to be reawoken.
 235                  wait_until_woken_after(&mut self.to_wake, &self.uv_loop(), || {});
 236                  assert!(self.exit_status.is_some());
 237              }
 238          }
 239  
 240          self.exit_status.unwrap()
 241      }
 242  }
 243  
 244  impl Drop for Process {
 245      fn drop(&mut self) {
 246          let _m = self.fire_homing_missile();
 247          assert!(self.to_wake.is_none());
 248          self.close();
 249      }
 250  }