Add unix::process::CommandExt::arg0

This allows argv[0] to be overridden on the executable's command-line. This also makes the program
executed independent of argv[0].

Does Fuchsia have the same semantics?

Addresses: #66510
This commit is contained in:
Jeremy Fitzhardinge 2019-11-17 22:56:13 -08:00 committed by Jeremy Fitzhardinge
parent 361791bb5f
commit 6dee1a5a9f
5 changed files with 68 additions and 10 deletions

View File

@ -2,6 +2,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
use crate::ffi::OsStr;
use crate::io;
use crate::os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
use crate::process;
@ -103,6 +104,14 @@ pub trait CommandExt {
/// cross-platform `spawn` instead.
#[stable(feature = "process_exec2", since = "1.9.0")]
fn exec(&mut self) -> io::Error;
/// Set executable argument
///
/// Set the first process argument, `argv[0]`, to something other than the
/// default executable path.
#[unstable(feature = "process_set_argv0", issue = "66510")]
fn arg0<S>(&mut self, arg: S) -> &mut process::Command
where S: AsRef<OsStr>;
}
#[stable(feature = "rust1", since = "1.0.0")]
@ -127,6 +136,13 @@ impl CommandExt for process::Command {
fn exec(&mut self) -> io::Error {
self.as_inner_mut().exec(sys::process::Stdio::Inherit)
}
fn arg0<S>(&mut self, arg: S) -> &mut process::Command
where S: AsRef<OsStr>
{
self.as_inner_mut().set_arg_0(arg.as_ref());
self
}
}
/// Unix-specific extensions to [`process::ExitStatus`].

View File

@ -1,6 +1,6 @@
use crate::os::unix::prelude::*;
use crate::ffi::{OsString, OsStr, CString};
use crate::ffi::{OsString, OsStr, CString, CStr};
use crate::fmt;
use crate::io;
use crate::ptr;
@ -11,10 +11,7 @@ use crate::sys_common::process::CommandEnv;
use crate::collections::BTreeMap;
#[cfg(not(target_os = "fuchsia"))]
use {
crate::ffi::CStr,
crate::sys::fs::OpenOptions,
};
use crate::sys::fs::OpenOptions;
use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
@ -135,8 +132,8 @@ impl Command {
let program = os2c(program, &mut saw_nul);
Command {
argv: Argv(vec![program.as_ptr(), ptr::null()]),
args: vec![program.clone()],
program,
args: Vec::new(),
env: Default::default(),
cwd: None,
uid: None,
@ -149,11 +146,19 @@ impl Command {
}
}
pub fn set_arg_0(&mut self, arg: &OsStr) {
// Set a new arg0
let arg = os2c(arg, &mut self.saw_nul);
debug_assert!(self.argv.0.len() > 1);
self.argv.0[0] = arg.as_ptr();
self.args[0] = arg;
}
pub fn arg(&mut self, arg: &OsStr) {
// Overwrite the trailing NULL pointer in `argv` and then add a new null
// pointer.
let arg = os2c(arg, &mut self.saw_nul);
self.argv.0[self.args.len() + 1] = arg.as_ptr();
self.argv.0[self.args.len()] = arg.as_ptr();
self.argv.0.push(ptr::null());
// Also make sure we keep track of the owned value to schedule a
@ -178,6 +183,10 @@ impl Command {
&self.argv.0
}
pub fn get_program(&self) -> &CStr {
&*self.program
}
#[allow(dead_code)]
pub fn get_cwd(&self) -> &Option<CString> {
&self.cwd

View File

@ -110,7 +110,7 @@ impl Command {
ZX_HANDLE_INVALID,
FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE
| FDIO_SPAWN_CLONE_ENVIRON, // this is ignored when envp is non-null
self.get_argv()[0], self.get_argv().as_ptr(), envp,
self.get_program().as_ptr(), self.get_argv().as_ptr(), envp,
actions.len() as size_t, actions.as_ptr(),
&mut process_handle,
ptr::null_mut(),

View File

@ -248,7 +248,7 @@ impl Command {
*sys::os::environ() = envp.as_ptr();
}
libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr());
Err(io::Error::last_os_error())
}
@ -373,7 +373,7 @@ impl Command {
.unwrap_or_else(|| *sys::os::environ() as *const _);
let ret = libc::posix_spawnp(
&mut p.pid,
self.get_argv()[0],
self.get_program().as_ptr(),
file_actions.0.as_ptr(),
attrs.0.as_ptr(),
self.get_argv().as_ptr() as *const _,

View File

@ -0,0 +1,33 @@
// run-pass
// ignore-windows - this is a unix-specific test
// ignore-cloudabi no processes
// ignore-emscripten no processes
// ignore-sgx no processes
#![feature(process_set_argv0)]
use std::env;
use std::os::unix::process::CommandExt;
use std::process::Command;
fn main() {
let args: Vec<_> = env::args().collect();
if args.len() > 1 {
assert_eq!(args[1], "doing-test");
assert_eq!(args[0], "i have a silly name");
println!("passed");
return;
}
let output =
Command::new(&args[0]).arg("doing-test").arg0("i have a silly name").output().unwrap();
assert!(
output.stderr.is_empty(),
"Non-empty stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(output.status.success());
assert_eq!(output.stdout, b"passed\n");
}