Skip to content

Commit 2b4af10

Browse files
authored
Rollup merge of rust-lang#66512 - jsgf:process-argv0, r=Dylan-DPC
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? I'm assuming so. Addresses: rust-lang#66510
2 parents 7520f6d + 6dee1a5 commit 2b4af10

File tree

5 files changed

+68
-10
lines changed

5 files changed

+68
-10
lines changed

src/libstd/sys/unix/ext/process.rs

+16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![stable(feature = "rust1", since = "1.0.0")]
44

5+
use crate::ffi::OsStr;
56
use crate::io;
67
use crate::os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
78
use crate::process;
@@ -103,6 +104,14 @@ pub trait CommandExt {
103104
/// cross-platform `spawn` instead.
104105
#[stable(feature = "process_exec2", since = "1.9.0")]
105106
fn exec(&mut self) -> io::Error;
107+
108+
/// Set executable argument
109+
///
110+
/// Set the first process argument, `argv[0]`, to something other than the
111+
/// default executable path.
112+
#[unstable(feature = "process_set_argv0", issue = "66510")]
113+
fn arg0<S>(&mut self, arg: S) -> &mut process::Command
114+
where S: AsRef<OsStr>;
106115
}
107116

108117
#[stable(feature = "rust1", since = "1.0.0")]
@@ -127,6 +136,13 @@ impl CommandExt for process::Command {
127136
fn exec(&mut self) -> io::Error {
128137
self.as_inner_mut().exec(sys::process::Stdio::Inherit)
129138
}
139+
140+
fn arg0<S>(&mut self, arg: S) -> &mut process::Command
141+
where S: AsRef<OsStr>
142+
{
143+
self.as_inner_mut().set_arg_0(arg.as_ref());
144+
self
145+
}
130146
}
131147

132148
/// Unix-specific extensions to [`process::ExitStatus`].

src/libstd/sys/unix/process/process_common.rs

+16-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::os::unix::prelude::*;
22

3-
use crate::ffi::{OsString, OsStr, CString};
3+
use crate::ffi::{OsString, OsStr, CString, CStr};
44
use crate::fmt;
55
use crate::io;
66
use crate::ptr;
@@ -11,10 +11,7 @@ use crate::sys_common::process::CommandEnv;
1111
use crate::collections::BTreeMap;
1212

1313
#[cfg(not(target_os = "fuchsia"))]
14-
use {
15-
crate::ffi::CStr,
16-
crate::sys::fs::OpenOptions,
17-
};
14+
use crate::sys::fs::OpenOptions;
1815

1916
use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
2017

@@ -135,8 +132,8 @@ impl Command {
135132
let program = os2c(program, &mut saw_nul);
136133
Command {
137134
argv: Argv(vec![program.as_ptr(), ptr::null()]),
135+
args: vec![program.clone()],
138136
program,
139-
args: Vec::new(),
140137
env: Default::default(),
141138
cwd: None,
142139
uid: None,
@@ -149,11 +146,19 @@ impl Command {
149146
}
150147
}
151148

149+
pub fn set_arg_0(&mut self, arg: &OsStr) {
150+
// Set a new arg0
151+
let arg = os2c(arg, &mut self.saw_nul);
152+
debug_assert!(self.argv.0.len() > 1);
153+
self.argv.0[0] = arg.as_ptr();
154+
self.args[0] = arg;
155+
}
156+
152157
pub fn arg(&mut self, arg: &OsStr) {
153158
// Overwrite the trailing NULL pointer in `argv` and then add a new null
154159
// pointer.
155160
let arg = os2c(arg, &mut self.saw_nul);
156-
self.argv.0[self.args.len() + 1] = arg.as_ptr();
161+
self.argv.0[self.args.len()] = arg.as_ptr();
157162
self.argv.0.push(ptr::null());
158163

159164
// Also make sure we keep track of the owned value to schedule a
@@ -178,6 +183,10 @@ impl Command {
178183
&self.argv.0
179184
}
180185

186+
pub fn get_program(&self) -> &CStr {
187+
&*self.program
188+
}
189+
181190
#[allow(dead_code)]
182191
pub fn get_cwd(&self) -> &Option<CString> {
183192
&self.cwd

src/libstd/sys/unix/process/process_fuchsia.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl Command {
110110
ZX_HANDLE_INVALID,
111111
FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE
112112
| FDIO_SPAWN_CLONE_ENVIRON, // this is ignored when envp is non-null
113-
self.get_argv()[0], self.get_argv().as_ptr(), envp,
113+
self.get_program().as_ptr(), self.get_argv().as_ptr(), envp,
114114
actions.len() as size_t, actions.as_ptr(),
115115
&mut process_handle,
116116
ptr::null_mut(),

src/libstd/sys/unix/process/process_unix.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ impl Command {
248248
*sys::os::environ() = envp.as_ptr();
249249
}
250250

251-
libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
251+
libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr());
252252
Err(io::Error::last_os_error())
253253
}
254254

@@ -373,7 +373,7 @@ impl Command {
373373
.unwrap_or_else(|| *sys::os::environ() as *const _);
374374
let ret = libc::posix_spawnp(
375375
&mut p.pid,
376-
self.get_argv()[0],
376+
self.get_program().as_ptr(),
377377
file_actions.0.as_ptr(),
378378
attrs.0.as_ptr(),
379379
self.get_argv().as_ptr() as *const _,

src/test/ui/command-argv0.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run-pass
2+
3+
// ignore-windows - this is a unix-specific test
4+
// ignore-cloudabi no processes
5+
// ignore-emscripten no processes
6+
// ignore-sgx no processes
7+
#![feature(process_set_argv0)]
8+
9+
use std::env;
10+
use std::os::unix::process::CommandExt;
11+
use std::process::Command;
12+
13+
fn main() {
14+
let args: Vec<_> = env::args().collect();
15+
16+
if args.len() > 1 {
17+
assert_eq!(args[1], "doing-test");
18+
assert_eq!(args[0], "i have a silly name");
19+
20+
println!("passed");
21+
return;
22+
}
23+
24+
let output =
25+
Command::new(&args[0]).arg("doing-test").arg0("i have a silly name").output().unwrap();
26+
assert!(
27+
output.stderr.is_empty(),
28+
"Non-empty stderr: {}",
29+
String::from_utf8_lossy(&output.stderr)
30+
);
31+
assert!(output.status.success());
32+
assert_eq!(output.stdout, b"passed\n");
33+
}

0 commit comments

Comments
 (0)