From 6dee1a5a9f0e5a85ac294c7473c247e33b63284c Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 17 Nov 2019 22:56:13 -0800 Subject: [PATCH] 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 --- src/libstd/sys/unix/ext/process.rs | 16 +++++++++ src/libstd/sys/unix/process/process_common.rs | 23 +++++++++---- .../sys/unix/process/process_fuchsia.rs | 2 +- src/libstd/sys/unix/process/process_unix.rs | 4 +-- src/test/ui/command-argv0.rs | 33 +++++++++++++++++++ 5 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/command-argv0.rs diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 7ace95edef9fb..0e95f97486b24 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -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(&mut self, arg: S) -> &mut process::Command + where S: AsRef; } #[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(&mut self, arg: S) -> &mut process::Command + where S: AsRef + { + self.as_inner_mut().set_arg_0(arg.as_ref()); + self + } } /// Unix-specific extensions to [`process::ExitStatus`]. diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 4edd2ebf8c598..0e6f96bb22892 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -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 { &self.cwd diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs index 2b1a3ecfd70f5..486c12f9bf88a 100644 --- a/src/libstd/sys/unix/process/process_fuchsia.rs +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -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(), diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index 507dc6892613a..45e4d195f17d2 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -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 _, diff --git a/src/test/ui/command-argv0.rs b/src/test/ui/command-argv0.rs new file mode 100644 index 0000000000000..56a9fb4d39125 --- /dev/null +++ b/src/test/ui/command-argv0.rs @@ -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"); +}