Skip to content

Commit

Permalink
Add unix::process::CommandExt::arg0
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jsgf committed Nov 18, 2019
1 parent 361791b commit 3de7bd2
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src/libstd/sys/unix/ext/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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")]
Expand All @@ -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`].
Expand Down
16 changes: 14 additions & 2 deletions src/libstd/sys/unix/process/process_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,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,
Expand All @@ -149,11 +149,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
Expand All @@ -178,6 +186,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
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/sys/unix/process/process_fuchsia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
4 changes: 2 additions & 2 deletions src/libstd/sys/unix/process/process_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

Expand Down Expand Up @@ -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 _,
Expand Down
33 changes: 33 additions & 0 deletions src/test/ui/command-argv0.rs
Original file line number Diff line number Diff line change
@@ -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");
}

0 comments on commit 3de7bd2

Please sign in to comment.