(index<- )        ./libnative/io/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 2012-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 std::io;
  12  use libc::{pid_t, c_void, c_int};
  13  use libc;
  14  use std::os;
  15  use std::ptr;
  16  use std::rt::rtio;
  17  use p = std::io::process;
  18  
  19  use super::IoResult;
  20  use super::file;
  21  
  22  #[cfg(windows)] use std::cast;
  23  #[cfg(windows)] use std::strbuf::StrBuf;
  24  #[cfg(not(windows))] use super::retry;
  25  
  26  /**
  27   * A value representing a child process.
  28   *
  29   * The lifetime of this value is linked to the lifetime of the actual
  30   * process - the Process destructor calls self.finish() which waits
  31   * for the process to terminate.
  32   */
  33  pub struct Process {
  34      /// The unique id of the process (this should never be negative).
  35      pid: pid_t,
  36  
  37      /// A handle to the process - on unix this will always be NULL, but on
  38      /// windows it will be a HANDLE to the process, which will prevent the
  39      /// pid being re-used until the handle is closed.
  40      handle: *(),
  41  
  42      /// None until finish() is called.
  43      exit_code: Option<p::ProcessExit>,
  44  
  45      /// Manually delivered signal
  46      exit_signal: Option<int>,
  47  }
  48  
  49  impl Process {
  50      /// Creates a new process using native process-spawning abilities provided
  51      /// by the OS. Operations on this process will be blocking instead of using
  52      /// the runtime for sleeping just this current task.
  53      ///
  54      /// # Arguments
  55      ///
  56      /// * prog - the program to run
  57      /// * args - the arguments to pass to the program, not including the program
  58      ///          itself
  59      /// * env - an optional environment to specify for the child process. If
  60      ///         this value is `None`, then the child will inherit the parent's
  61      ///         environment
  62      /// * cwd - an optionally specified current working directory of the child,
  63      ///         defaulting to the parent's current working directory
  64      /// * stdin, stdout, stderr - These optionally specified file descriptors
  65      ///     dictate where the stdin/out/err of the child process will go. If
  66      ///     these are `None`, then this module will bind the input/output to an
  67      ///     os pipe instead. This process takes ownership of these file
  68      ///     descriptors, closing them upon destruction of the process.
  69      pub fn spawn(configp::ProcessConfig)
  70          -> Result<(Process, Vec<Option<file::FileDesc>>), io::IoError>
  71      {
  72          // right now we only handle stdin/stdout/stderr.
  73          if config.extra_io.len() > 0 {
  74              return Err(super::unimpl());
  75          }
  76  
  77          fn get_io(iop::StdioContainer, ret&mut Vec<Option<file::FileDesc>>)
  78              -> (Option<os::Pipe>, c_int)
  79          {
  80              match io {
  81                  p::Ignored => { ret.push(None); (None, -1) }
  82                  p::InheritFd(fd) => { ret.push(None); (None, fd) }
  83                  p::CreatePipe(readable, _writable) => {
  84                      let pipe = os::pipe();
  85                      let (theirs, ours) = if readable {
  86                          (pipe.input, pipe.out)
  87                      } else {
  88                          (pipe.out, pipe.input)
  89                      };
  90                      ret.push(Some(file::FileDesc::new(ours, true)));
  91                      (Some(pipe), theirs)
  92                  }
  93              }
  94          }
  95  
  96          let mut ret_io = Vec::new();
  97          let (in_pipe, in_fd) = get_io(config.stdin, &mut ret_io);
  98          let (out_pipe, out_fd) = get_io(config.stdout, &mut ret_io);
  99          let (err_pipe, err_fd) = get_io(config.stderr, &mut ret_io);
 100  
 101          let env = config.env.map(|a| a.to_owned());
 102          let cwd = config.cwd.map(|a| Path::new(a));
 103          let res = spawn_process_os(config, env, cwd.as_ref(), in_fd, out_fd,
 104                                     err_fd);
 105  
 106          unsafe {
 107              for pipe in in_pipe.iter() { let _ = libc::close(pipe.input); }
 108              for pipe in out_pipe.iter() { let _ = libc::close(pipe.out); }
 109              for pipe in err_pipe.iter() { let _ = libc::close(pipe.out); }
 110          }
 111  
 112          match res {
 113              Ok(res) => {
 114                  Ok((Process {
 115                          pid: res.pid,
 116                          handle: res.handle,
 117                          exit_code: None,
 118                          exit_signal: None,
 119                      },
 120                      ret_io))
 121              }
 122              Err(e) => Err(e)
 123          }
 124      }
 125  
 126      pub fn kill(pidlibc::pid_t, signumint) -> IoResult<()> {
 127          unsafe { killpid(pid, signum) }
 128      }
 129  }
 130  
 131  impl rtio::RtioProcess for Process {
 132      fn id(&self) -> pid_t { self.pid }
 133  
 134      fn wait(&mut self) -> p::ProcessExit {
 135          match self.exit_code {
 136              Some(code) => code,
 137              None => {
 138                  let code = waitpid(self.pid);
 139                  // On windows, waitpid will never return a signal. If a signal
 140                  // was successfully delivered to the process, however, we can
 141                  // consider it as having died via a signal.
 142                  let code = match self.exit_signal {
 143                      None => code,
 144                      Some(signal) if cfg!(windows) => p::ExitSignal(signal),
 145                      Some(..) => code,
 146                  };
 147                  self.exit_code = Some(code);
 148                  code
 149              }
 150          }
 151      }
 152  
 153      fn kill(&mut self, signumint) -> Result<(), io::IoError> {
 154          // On linux (and possibly other unices), a process that has exited will
 155          // continue to accept signals because it is "defunct". The delivery of
 156          // signals will only fail once the child has been reaped. For this
 157          // reason, if the process hasn't exited yet, then we attempt to collect
 158          // their status with WNOHANG.
 159          if self.exit_code.is_none() {
 160              match waitpid_nowait(self.pid) {
 161                  Some(code) => { self.exit_code = Some(code); }
 162                  None => {}
 163              }
 164          }
 165  
 166          // if the process has finished, and therefore had waitpid called,
 167          // and we kill it, then on unix we might ending up killing a
 168          // newer process that happens to have the same (re-used) id
 169          match self.exit_code {
 170              Some(..) => return Err(io::IoError {
 171                  kind: io::OtherIoError,
 172                  desc: "can't kill an exited process",
 173                  detail: None,
 174              }),
 175              None => {}
 176          }
 177  
 178          // A successfully delivered signal that isn't 0 (just a poll for being
 179          // alive) is recorded for windows (see wait())
 180          match unsafe { killpid(self.pid, signum) } {
 181              Ok(()) if signum == 0 => Ok(()),
 182              Ok(()) => { self.exit_signal = Some(signum); Ok(()) }
 183              Err(e) => Err(e),
 184          }
 185      }
 186  }
 187  
 188  impl Drop for Process {
 189      fn drop(&mut self) {
 190          free_handle(self.handle);
 191      }
 192  }
 193  
 194  #[cfg(windows)]
 195  unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
 196      let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
 197                                     libc::PROCESS_QUERY_INFORMATION,
 198                                     libc::FALSE, pid as libc::DWORD);
 199      if handle.is_null() {
 200          return Err(super::last_error())
 201      }
 202      let ret = match signal {
 203          // test for existence on signal 0
 204          0 => {
 205              let mut status = 0;
 206              let ret = libc::GetExitCodeProcess(handle, &mut status);
 207              if ret == 0 {
 208                  Err(super::last_error())
 209              } else if status != libc::STILL_ACTIVE {
 210                  Err(io::IoError {
 211                      kind: io::OtherIoError,
 212                      desc: "process no longer alive",
 213                      detail: None,
 214                  })
 215              } else {
 216                  Ok(())
 217              }
 218          }
 219          io::process::PleaseExitSignal | io::process::MustDieSignal => {
 220              let ret = libc::TerminateProcess(handle, 1);
 221              super::mkerr_winbool(ret)
 222          }
 223          _ => Err(io::IoError {
 224              kind: io::OtherIoError,
 225              desc: "unsupported signal on windows",
 226              detail: None,
 227          })
 228      };
 229      let _ = libc::CloseHandle(handle);
 230      return ret;
 231  }
 232  
 233  #[cfg(not(windows))]
 234  unsafe fn killpid(pidpid_t, signal: int) -> Result<(), io::IoError> {
 235      let r = libc::funcs::posix88::signal::kill(pid, signal as c_int);
 236      super::mkerr_libc(r)
 237  }
 238  
 239  struct SpawnProcessResult {
 240      pid: pid_t,
 241      handle: *(),
 242  }
 243  
 244  #[cfg(windows)]
 245  fn spawn_process_os(config: p::ProcessConfig,
 246                      env: Option<~[(~str, ~str)]>,
 247                      dir: Option<&Path>,
 248                      in_fd: c_int, out_fd: c_int,
 249                      err_fd: c_int) -> IoResult<SpawnProcessResult> {
 250      use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
 251      use libc::consts::os::extra::{
 252          TRUE, FALSE,
 253          STARTF_USESTDHANDLES,
 254          INVALID_HANDLE_VALUE,
 255          DUPLICATE_SAME_ACCESS
 256      };
 257      use libc::funcs::extra::kernel32::{
 258          GetCurrentProcess,
 259          DuplicateHandle,
 260          CloseHandle,
 261          CreateProcessA
 262      };
 263      use libc::funcs::extra::msvcrt::get_osfhandle;
 264  
 265      use std::mem;
 266  
 267      if config.gid.is_some() || config.uid.is_some() {
 268          return Err(io::IoError {
 269              kind: io::OtherIoError,
 270              desc: "unsupported gid/uid requested on windows",
 271              detail: None,
 272          })
 273      }
 274  
 275      unsafe {
 276  
 277          let mut si = zeroed_startupinfo();
 278          si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
 279          si.dwFlags = STARTF_USESTDHANDLES;
 280  
 281          let cur_proc = GetCurrentProcess();
 282  
 283          if in_fd != -1 {
 284              let orig_std_in = get_osfhandle(in_fd) as HANDLE;
 285              if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
 286                  fail!("failure in get_osfhandle: {}", os::last_os_error());
 287              }
 288              if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
 289                                 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
 290                  fail!("failure in DuplicateHandle: {}", os::last_os_error());
 291              }
 292          }
 293  
 294          if out_fd != -1 {
 295              let orig_std_out = get_osfhandle(out_fd) as HANDLE;
 296              if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
 297                  fail!("failure in get_osfhandle: {}", os::last_os_error());
 298              }
 299              if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
 300                                 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
 301                  fail!("failure in DuplicateHandle: {}", os::last_os_error());
 302              }
 303          }
 304  
 305          if err_fd != -1 {
 306              let orig_std_err = get_osfhandle(err_fd) as HANDLE;
 307              if orig_std_err == INVALID_HANDLE_VALUE as HANDLE {
 308                  fail!("failure in get_osfhandle: {}", os::last_os_error());
 309              }
 310              if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
 311                                 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
 312                  fail!("failure in DuplicateHandle: {}", os::last_os_error());
 313              }
 314          }
 315  
 316          let cmd = make_command_line(config.program, config.args);
 317          let mut pi = zeroed_process_information();
 318          let mut create_err = None;
 319  
 320          // stolen from the libuv code.
 321          let mut flags = 0;
 322          if config.detach {
 323              flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
 324          }
 325  
 326          with_envp(env, |envp| {
 327              with_dirp(dir, |dirp| {
 328                  cmd.with_c_str(|cmdp| {
 329                      let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
 330                                                   ptr::mut_null(), ptr::mut_null(), TRUE,
 331                                                   flags, envp, dirp, &mut si,
 332                                                   &mut pi);
 333                      if created == FALSE {
 334                          create_err = Some(super::last_error());
 335                      }
 336                  })
 337              })
 338          });
 339  
 340          if in_fd != -1 { assert!(CloseHandle(si.hStdInput) != 0); }
 341          if out_fd != -1 { assert!(CloseHandle(si.hStdOutput) != 0); }
 342          if err_fd != -1 { assert!(CloseHandle(si.hStdError) != 0); }
 343  
 344          match create_err {
 345              Some(err) => return Err(err),
 346              None => {}
 347          }
 348  
 349          // We close the thread handle because we don't care about keeping the
 350          // thread id valid, and we aren't keeping the thread handle around to be
 351          // able to close it later. We don't close the process handle however
 352          // because std::we want the process id to stay valid at least until the
 353          // calling code closes the process handle.
 354          assert!(CloseHandle(pi.hThread) != 0);
 355  
 356          Ok(SpawnProcessResult {
 357              pid: pi.dwProcessId as pid_t,
 358              handle: pi.hProcess as *()
 359          })
 360      }
 361  }
 362  
 363  #[cfg(windows)]
 364  fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
 365      libc::types::os::arch::extra::STARTUPINFO {
 366          cb: 0,
 367          lpReserved: ptr::mut_null(),
 368          lpDesktop: ptr::mut_null(),
 369          lpTitle: ptr::mut_null(),
 370          dwX: 0,
 371          dwY: 0,
 372          dwXSize: 0,
 373          dwYSize: 0,
 374          dwXCountChars: 0,
 375          dwYCountCharts: 0,
 376          dwFillAttribute: 0,
 377          dwFlags: 0,
 378          wShowWindow: 0,
 379          cbReserved2: 0,
 380          lpReserved2: ptr::mut_null(),
 381          hStdInput: ptr::mut_null(),
 382          hStdOutput: ptr::mut_null(),
 383          hStdError: ptr::mut_null()
 384      }
 385  }
 386  
 387  #[cfg(windows)]
 388  fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
 389      libc::types::os::arch::extra::PROCESS_INFORMATION {
 390          hProcess: ptr::mut_null(),
 391          hThread: ptr::mut_null(),
 392          dwProcessId: 0,
 393          dwThreadId: 0
 394      }
 395  }
 396  
 397  #[cfg(windows)]
 398  fn make_command_line(prog: &str, args: &[~str]) -> ~str {
 399      let mut cmd = StrBuf::new();
 400      append_arg(&mut cmd, prog);
 401      for arg in args.iter() {
 402          cmd.push_char(' ');
 403          append_arg(&mut cmd, *arg);
 404      }
 405      return cmd.into_owned();
 406  
 407      fn append_arg(cmd: &mut StrBuf, arg: &str) {
 408          let quote = arg.chars().any(|c| c == ' ' || c == '\t');
 409          if quote {
 410              cmd.push_char('"');
 411          }
 412          for i in range(0u, arg.len()) {
 413              append_char_at(cmd, arg, i);
 414          }
 415          if quote {
 416              cmd.push_char('"');
 417          }
 418      }
 419  
 420      fn append_char_at(cmd: &mut StrBuf, arg: &str, i: uint) {
 421          match arg[i] as char {
 422              '"' => {
 423                  // Escape quotes.
 424                  cmd.push_str("\\\"");
 425              }
 426              '\\' => {
 427                  if backslash_run_ends_in_quote(arg, i) {
 428                      // Double all backslashes that are in runs before quotes.
 429                      cmd.push_str("\\\\");
 430                  } else {
 431                      // Pass other backslashes through unescaped.
 432                      cmd.push_char('\\');
 433                  }
 434              }
 435              c => {
 436                  cmd.push_char(c);
 437              }
 438          }
 439      }
 440  
 441      fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
 442          while i < s.len() && s[i] as char == '\\' {
 443              i += 1;
 444          }
 445          return i < s.len() && s[i] as char == '"';
 446      }
 447  }
 448  
 449  #[cfg(unix)]
 450  fn spawn_process_os(configp::ProcessConfig,
 451                      envOption<~[(~str, ~str)]>,
 452                      dirOption<&Path>,
 453                      in_fdc_int, out_fdc_int,
 454                      err_fdc_int) -> IoResult<SpawnProcessResult> {
 455      use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
 456      use libc::funcs::bsd44::getdtablesize;
 457      use io::c;
 458  
 459      mod rustrt {
 460          extern {
 461              pub fn rust_unset_sigprocmask();
 462          }
 463      }
 464  
 465      #[cfg(target_os = "macos")]
 466      unsafe fn set_environ(envp: *c_void) {
 467          extern { fn _NSGetEnviron() -> *mut *c_void; }
 468  
 469          *_NSGetEnviron() = envp;
 470      }
 471      #[cfg(not(target_os = "macos"))]
 472      unsafe fn set_environ(envp: *c_void) {
 473          extern { static mut environ: *c_void; }
 474          environ = envp;
 475      }
 476  
 477      unsafe fn set_cloexec(fdc_int) {
 478          let ret = c::ioctl(fd, c::FIOCLEX);
 479          assert_eq!(ret, 0);
 480      }
 481  
 482      let dirp = dir.map(|p| p.to_c_str());
 483      let dirp = dirp.as_ref().map(|c| c.with_ref(|p| p)).unwrap_or(ptr::null());
 484  
 485      with_envp(env, proc(envp) {
 486          with_argv(config.program, config.args, proc(argv) unsafe {
 487              let pipe = os::pipe();
 488              let mut input = file::FileDesc::new(pipe.input, true);
 489              let mut output = file::FileDesc::new(pipe.out, true);
 490  
 491              set_cloexec(output.fd());
 492  
 493              let pid = fork();
 494              if pid < 0 {
 495                  fail!("failure in fork: {}", os::last_os_error());
 496              } else if pid > 0 {
 497                  drop(output);
 498                  let mut bytes = [0, ..4];
 499                  return match input.inner_read(bytes) {
 500                      Ok(4) => {
 501                          let errno = (bytes[0] << 24) as i32 |
 502                                      (bytes[1] << 16) as i32 |
 503                                      (bytes[2] <<  8) as i32 |
 504                                      (bytes[3] <<  0) as i32;
 505                          Err(io::IoError::from_errno(errno as uint, false))
 506                      }
 507                      Err(e) => {
 508                          assert!(e.kind == io::BrokenPipe ||
 509                                  e.kind == io::EndOfFile,
 510                                  "unexpected error: {}", e);
 511                          Ok(SpawnProcessResult {
 512                              pid: pid,
 513                              handle: ptr::null()
 514                          })
 515                      }
 516                      Ok(..) => fail!("short read on the cloexec pipe"),
 517                  };
 518              }
 519              // And at this point we've reached a special time in the life of the
 520              // child. The child must now be considered hamstrung and unable to
 521              // do anything other than syscalls really. Consider the following
 522              // scenario:
 523              //
 524              //      1. Thread A of process 1 grabs the malloc() mutex
 525              //      2. Thread B of process 1 forks(), creating thread C
 526              //      3. Thread C of process 2 then attempts to malloc()
 527              //      4. The memory of process 2 is the same as the memory of
 528              //         process 1, so the mutex is locked.
 529              //
 530              // This situation looks a lot like deadlock, right? It turns out
 531              // that this is what pthread_atfork() takes care of, which is
 532              // presumably implemented across platforms. The first thing that
 533              // threads to *before* forking is to do things like grab the malloc
 534              // mutex, and then after the fork they unlock it.
 535              //
 536              // Despite this information, libnative's spawn has been witnessed to
 537              // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
 538              // all collected backtraces point at malloc/free traffic in the
 539              // child spawned process.
 540              //
 541              // For this reason, the block of code below should contain 0
 542              // invocations of either malloc of free (or their related friends).
 543              //
 544              // As an example of not having malloc/free traffic, we don't close
 545              // this file descriptor by dropping the FileDesc (which contains an
 546              // allocation). Instead we just close it manually. This will never
 547              // have the drop glue anyway because this code never returns (the
 548              // child will either exec() or invoke libc::exit)
 549              let _ = libc::close(input.fd());
 550  
 551              fn fail(output&mut file::FileDesc) -> ! {
 552                  let errno = os::errno();
 553                  let bytes = [
 554                      (errno << 24) as u8,
 555                      (errno << 16) as u8,
 556                      (errno <<  8) as u8,
 557                      (errno <<  0) as u8,
 558                  ];
 559                  assert!(output.inner_write(bytes).is_ok());
 560                  unsafe { libc::_exit(1) }
 561              }
 562  
 563              rustrt::rust_unset_sigprocmask();
 564  
 565              if in_fd == -1 {
 566                  let _ = libc::close(libc::STDIN_FILENO);
 567              } else if retry(|| dup2(in_fd, 0)) == -1 {
 568                  fail(&mut output);
 569              }
 570              if out_fd == -1 {
 571                  let _ = libc::close(libc::STDOUT_FILENO);
 572              } else if retry(|| dup2(out_fd, 1)) == -1 {
 573                  fail(&mut output);
 574              }
 575              if err_fd == -1 {
 576                  let _ = libc::close(libc::STDERR_FILENO);
 577              } else if retry(|| dup2(err_fd, 2)) == -1 {
 578                  fail(&mut output);
 579              }
 580              // close all other fds
 581              for fd in range(3, getdtablesize()).rev() {
 582                  if fd != output.fd() {
 583                      let _ = close(fd as c_int);
 584                  }
 585              }
 586  
 587              match config.gid {
 588                  Some(u) => {
 589                      if libc::setgid(u as libc::gid_t) != 0 {
 590                          fail(&mut output);
 591                      }
 592                  }
 593                  None => {}
 594              }
 595              match config.uid {
 596                  Some(u) => {
 597                      // When dropping privileges from root, the `setgroups` call will
 598                      // remove any extraneous groups. If we don't call this, then
 599                      // even though our uid has dropped, we may still have groups
 600                      // that enable us to do super-user things. This will fail if we
 601                      // aren't root, so don't bother checking the return value, this
 602                      // is just done as an optimistic privilege dropping function.
 603                      extern {
 604                          fn setgroups(ngroupslibc::c_int,
 605                                       ptr*libc::c_void) -> libc::c_int;
 606                      }
 607                      let _ = setgroups(0, 0 as *libc::c_void);
 608  
 609                      if libc::setuid(u as libc::uid_t) != 0 {
 610                          fail(&mut output);
 611                      }
 612                  }
 613                  None => {}
 614              }
 615              if config.detach {
 616                  // Don't check the error of setsid because it fails if we're the
 617                  // process leader already. We just forked so it shouldn't return
 618                  // error, but ignore it anyway.
 619                  let _ = libc::setsid();
 620              }
 621              if !dirp.is_null() && chdir(dirp) == -1 {
 622                  fail(&mut output);
 623              }
 624              if !envp.is_null() {
 625                  set_environ(envp);
 626              }
 627              let _ = execvp(*argv, argv);
 628              fail(&mut output);
 629          })
 630      })
 631  }
 632  
 633  #[cfg(unix)]
 634  fn with_argv<T>(prog: &str, args: &[~str], cb: proc(**libc::c_char) -> T) -> T {
 635      // We can't directly convert `str`s into `*char`s, as someone needs to hold
 636      // a reference to the intermediary byte buffers. So first build an array to
 637      // hold all the ~[u8] byte strings.
 638      let mut tmps = Vec::with_capacity(args.len() + 1);
 639  
 640      tmps.push(prog.to_c_str());
 641  
 642      for arg in args.iter() {
 643          tmps.push(arg.to_c_str());
 644      }
 645  
 646      // Next, convert each of the byte strings into a pointer. This is
 647      // technically unsafe as the caller could leak these pointers out of our
 648      // scope.
 649      let mut ptrsVec<_> = tmps.iter().map(|tmp| tmp.with_ref(|buf| buf)).collect();
 650  
 651      // Finally, make sure we add a null pointer.
 652      ptrs.push(ptr::null());
 653  
 654      cb(ptrs.as_ptr())
 655  }
 656  
 657  #[cfg(unix)]
 658  fn with_envp<T>(envOption<~[(~str, ~str)]>, cb: proc(*c_void) -> T) -> T {
 659      // On posixy systems we can pass a char** for envp, which is a
 660      // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to
 661      // have a temporary buffer to hold the intermediary `~[u8]` byte strings.
 662      match env {
 663          Some(env) => {
 664              let mut tmps = Vec::with_capacity(env.len());
 665  
 666              for pair in env.iter() {
 667                  let kv = format!("{}={}", *pair.ref0(), *pair.ref1());
 668                  tmps.push(kv.to_c_str());
 669              }
 670  
 671              // Once again, this is unsafe.
 672              let mut ptrsVec<*libc::c_char> = tmps.iter()
 673                                                     .map(|tmp| tmp.with_ref(|buf| buf))
 674                                                     .collect();
 675              ptrs.push(ptr::null());
 676  
 677              cb(ptrs.as_ptr() as *c_void)
 678          }
 679          _ => cb(ptr::null())
 680      }
 681  }
 682  
 683  #[cfg(windows)]
 684  fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: |*mut c_void| -> T) -> T {
 685      // On win32 we pass an "environment block" which is not a char**, but
 686      // rather a concatenation of null-terminated k=v\0 sequences, with a final
 687      // \0 to terminate.
 688      match env {
 689          Some(env) => {
 690              let mut blk = Vec::new();
 691  
 692              for pair in env.iter() {
 693                  let kv = format!("{}={}", *pair.ref0(), *pair.ref1());
 694                  blk.push_all(kv.as_bytes());
 695                  blk.push(0);
 696              }
 697  
 698              blk.push(0);
 699  
 700              cb(blk.as_mut_ptr() as *mut c_void)
 701          }
 702          _ => cb(ptr::mut_null())
 703      }
 704  }
 705  
 706  #[cfg(windows)]
 707  fn with_dirp<T>(d: Option<&Path>, cb: |*libc::c_char| -> T) -> T {
 708      match d {
 709        Some(dir) => dir.with_c_str(|buf| cb(buf)),
 710        None => cb(ptr::null())
 711      }
 712  }
 713  
 714  #[cfg(windows)]
 715  fn free_handle(handle: *()) {
 716      assert!(unsafe {
 717          libc::CloseHandle(cast::transmute(handle)) != 0
 718      })
 719  }
 720  
 721  #[cfg(unix)]
 722  fn free_handle(_handle: *()) {
 723      // unix has no process handle object, just a pid
 724  }
 725  
 726  #[cfg(unix)]
 727  fn translate_status(statusc_int) -> p::ProcessExit {
 728      #[cfg(target_os = "linux")]
 729      #[cfg(target_os = "android")]
 730      mod imp {
 731          pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
 732          pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
 733          pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
 734      }
 735  
 736      #[cfg(target_os = "macos")]
 737      #[cfg(target_os = "freebsd")]
 738      mod imp {
 739          pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
 740          pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
 741          pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
 742      }
 743  
 744      if imp::WIFEXITED(status) {
 745          p::ExitStatus(imp::WEXITSTATUS(status) as int)
 746      } else {
 747          p::ExitSignal(imp::WTERMSIG(status) as int)
 748      }
 749  }
 750  
 751  /**
 752   * Waits for a process to exit and returns the exit code, failing
 753   * if there is no process with the specified id.
 754   *
 755   * Note that this is private to avoid race conditions on unix where if
 756   * a user calls waitpid(some_process.get_id()) then some_process.finish()
 757   * and some_process.destroy() and some_process.finalize() will then either
 758   * operate on a none-existent process or, even worse, on a newer process
 759   * with the same id.
 760   */
 761  fn waitpid(pidpid_t) -> p::ProcessExit {
 762      return waitpid_os(pid);
 763  
 764      #[cfg(windows)]
 765      fn waitpid_os(pid: pid_t) -> p::ProcessExit {
 766          use libc::types::os::arch::extra::DWORD;
 767          use libc::consts::os::extra::{
 768              SYNCHRONIZE,
 769              PROCESS_QUERY_INFORMATION,
 770              FALSE,
 771              STILL_ACTIVE,
 772              INFINITE,
 773              WAIT_FAILED
 774          };
 775          use libc::funcs::extra::kernel32::{
 776              OpenProcess,
 777              GetExitCodeProcess,
 778              CloseHandle,
 779              WaitForSingleObject
 780          };
 781  
 782          unsafe {
 783  
 784              let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
 785                                        FALSE,
 786                                        pid as DWORD);
 787              if process.is_null() {
 788                  fail!("failure in OpenProcess: {}", os::last_os_error());
 789              }
 790  
 791              loop {
 792                  let mut status = 0;
 793                  if GetExitCodeProcess(process, &mut status) == FALSE {
 794                      assert!(CloseHandle(process) != 0);
 795                      fail!("failure in GetExitCodeProcess: {}", os::last_os_error());
 796                  }
 797                  if status != STILL_ACTIVE {
 798                      assert!(CloseHandle(process) != 0);
 799                      return p::ExitStatus(status as int);
 800                  }
 801                  if WaitForSingleObject(process, INFINITE) == WAIT_FAILED {
 802                      assert!(CloseHandle(process) != 0);
 803                      fail!("failure in WaitForSingleObject: {}", os::last_os_error());
 804                  }
 805              }
 806          }
 807      }
 808  
 809      #[cfg(unix)]
 810      fn waitpid_os(pidpid_t) -> p::ProcessExit {
 811          use libc::funcs::posix01::wait;
 812          let mut status = 0 as c_int;
 813          match retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) {
 814              -1 => fail!("unknown waitpid error: {}", super::last_error()),
 815              _ => translate_status(status),
 816          }
 817      }
 818  }
 819  
 820  fn waitpid_nowait(pidpid_t) -> Option<p::ProcessExit> {
 821      return waitpid_os(pid);
 822  
 823      // This code path isn't necessary on windows
 824      #[cfg(windows)]
 825      fn waitpid_os(_pid: pid_t) -> Option<p::ProcessExit> { None }
 826  
 827      #[cfg(unix)]
 828      fn waitpid_os(pidpid_t) -> Option<p::ProcessExit> {
 829          use libc::funcs::posix01::wait;
 830          let mut status = 0 as c_int;
 831          match retry(|| unsafe {
 832              wait::waitpid(pid, &mut status, libc::WNOHANG)
 833          }) {
 834              n if n == pid => Some(translate_status(status)),
 835              0 => None,
 836              n => fail!("unknown waitpid error `{}`: {}", n, super::last_error()),
 837          }
 838      }
 839  }
 840  
 841  #[cfg(test)]
 842  mod tests {
 843  
 844      #[test] #[cfg(windows)]
 845      fn test_make_command_line() {
 846          use super::make_command_line;
 847          assert_eq!(
 848              make_command_line("prog", ["aaa".to_owned(), "bbb".to_owned(), "ccc".to_owned()]),
 849              "prog aaa bbb ccc".to_owned()
 850          );
 851          assert_eq!(
 852              make_command_line("C:\\Program Files\\blah\\blah.exe", ["aaa".to_owned()]),
 853              "\"C:\\Program Files\\blah\\blah.exe\" aaa".to_owned()
 854          );
 855          assert_eq!(
 856              make_command_line("C:\\Program Files\\test", ["aa\"bb".to_owned()]),
 857              "\"C:\\Program Files\\test\" aa\\\"bb".to_owned()
 858          );
 859          assert_eq!(
 860              make_command_line("echo", ["a b c".to_owned()]),
 861              "echo \"a b c\"".to_owned()
 862          );
 863      }
 864  }


libnative/io/process.rs:77:8-77:8 -fn- definition:
        fn get_io(io: p::StdioContainer, ret: &mut Vec<Option<file::FileDesc>>)
            -> (Option<os::Pipe>, c_int)
        {
references:- 3
96:         let mut ret_io = Vec::new();
97:         let (in_pipe, in_fd) = get_io(config.stdin, &mut ret_io);
98:         let (out_pipe, out_fd) = get_io(config.stdout, &mut ret_io);
99:         let (err_pipe, err_fd) = get_io(config.stderr, &mut ret_io);


libnative/io/process.rs:233:21-233:21 -fn- definition:
unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
    let r = libc::funcs::posix88::signal::kill(pid, signal as c_int);
    super::mkerr_libc(r)
references:- 2
126:     pub fn kill(pid: libc::pid_t, signum: int) -> IoResult<()> {
127:         unsafe { killpid(pid, signum) }
128:     }
--
179:         // alive) is recorded for windows (see wait())
180:         match unsafe { killpid(self.pid, signum) } {
181:             Ok(()) if signum == 0 => Ok(()),


libnative/io/process.rs:238:1-238:1 -struct- definition:
struct SpawnProcessResult {
    pid: pid_t,
    handle: *(),
references:- 2
453:                     in_fd: c_int, out_fd: c_int,
454:                     err_fd: c_int) -> IoResult<SpawnProcessResult> {
455:     use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
--
510:                                 "unexpected error: {}", e);
511:                         Ok(SpawnProcessResult {
512:                             pid: pid,


libnative/io/process.rs:32:4-32:4 -struct- definition:
 */
pub struct Process {
    /// The unique id of the process (this should never be negative).
references:- 5
69:     pub fn spawn(config: p::ProcessConfig)
70:         -> Result<(Process, Vec<Option<file::FileDesc>>), io::IoError>
71:     {
--
188: impl Drop for Process {
189:     fn drop(&mut self) {


libnative/io/process.rs:726:13-726:13 -fn- definition:
fn translate_status(status: c_int) -> p::ProcessExit {
    #[cfg(target_os = "linux")]
    #[cfg(target_os = "android")]
references:- 2
833:         }) {
834:             n if n == pid => Some(translate_status(status)),
835:             0 => None,


libnative/io/process.rs:551:12-551:12 -fn- definition:
            fn fail(output: &mut file::FileDesc) -> ! {
                let errno = os::errno();
                let bytes = [
references:- 7
572:             } else if retry(|| dup2(out_fd, 1)) == -1 {
573:                 fail(&mut output);
574:             }
--
589:                     if libc::setgid(u as libc::gid_t) != 0 {
590:                         fail(&mut output);
591:                     }
--
621:             if !dirp.is_null() && chdir(dirp) == -1 {
622:                 fail(&mut output);
623:             }
--
627:             let _ = execvp(*argv, argv);
628:             fail(&mut output);
629:         })