diff options
Diffstat (limited to 'tests/ui/command')
-rw-r--r-- | tests/ui/command/command-argv0.rs | 30 | ||||
-rw-r--r-- | tests/ui/command/command-create-pidfd.rs | 56 | ||||
-rw-r--r-- | tests/ui/command/command-current-dir.rs | 50 | ||||
-rw-r--r-- | tests/ui/command/command-exec.rs | 104 | ||||
-rw-r--r-- | tests/ui/command/command-pre-exec.rs | 106 | ||||
-rw-r--r-- | tests/ui/command/command-setgroups.rs | 26 | ||||
-rw-r--r-- | tests/ui/command/command-uid-gid.rs | 32 | ||||
-rw-r--r-- | tests/ui/command/issue-10626.rs | 26 |
8 files changed, 430 insertions, 0 deletions
diff --git a/tests/ui/command/command-argv0.rs b/tests/ui/command/command-argv0.rs new file mode 100644 index 000000000..b782a4fd3 --- /dev/null +++ b/tests/ui/command/command-argv0.rs @@ -0,0 +1,30 @@ +// run-pass + +// ignore-windows - this is a unix-specific test +// ignore-emscripten no processes +// ignore-sgx no processes +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"); +} diff --git a/tests/ui/command/command-create-pidfd.rs b/tests/ui/command/command-create-pidfd.rs new file mode 100644 index 000000000..4df443c66 --- /dev/null +++ b/tests/ui/command/command-create-pidfd.rs @@ -0,0 +1,56 @@ +// run-pass +// only-linux - pidfds are a linux-specific concept + +#![feature(linux_pidfd)] +#![feature(rustc_private)] + +extern crate libc; + +use std::io::Error; +use std::os::linux::process::{ChildExt, CommandExt}; +use std::process::Command; + +fn has_clone3() -> bool { + let res = unsafe { libc::syscall(libc::SYS_clone3, 0, 0) }; + let err = (res == -1) + .then(|| Error::last_os_error()) + .expect("probe syscall should not succeed"); + + // If the `clone3` syscall is not implemented in the current kernel version it should return an + // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and + // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of + // that we need to check for *both* `ENOSYS` and `EPERM`. + // + // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning + // to update their filtering to return `ENOSYS` in a future release: + // + // https://github.com/moby/moby/issues/42680 + // + err.raw_os_error() != Some(libc::ENOSYS) && err.raw_os_error() != Some(libc::EPERM) +} + +fn main() { + // pidfds require the clone3 syscall + if !has_clone3() { + return; + } + + // We don't assert the precise value, since the standard library + // might have opened other file descriptors before our code runs. + let _ = Command::new("echo") + .create_pidfd(true) + .spawn() + .unwrap() + .pidfd().expect("failed to obtain pidfd"); + + let _ = Command::new("echo") + .create_pidfd(false) + .spawn() + .unwrap() + .pidfd().expect_err("pidfd should not have been created when create_pid(false) is set"); + + let _ = Command::new("echo") + .spawn() + .unwrap() + .pidfd().expect_err("pidfd should not have been created"); +} diff --git a/tests/ui/command/command-current-dir.rs b/tests/ui/command/command-current-dir.rs new file mode 100644 index 000000000..5d06fcdeb --- /dev/null +++ b/tests/ui/command/command-current-dir.rs @@ -0,0 +1,50 @@ +// run-pass +// ignore-emscripten no processes +// ignore-sgx no processes +// ignore-fuchsia Needs directory creation privilege + +use std::env; +use std::fs; +use std::path::Path; +use std::process::Command; + +fn main() { + // Checks the behavior of current_dir when used with a relative exe path. + let me = env::current_exe().unwrap(); + if matches!(env::args().skip(1).next().as_deref(), Some("current-dir")) { + let cwd = env::current_dir().unwrap(); + assert_eq!(cwd.file_name().unwrap(), "bar"); + std::process::exit(0); + } + let exe = me.file_name().unwrap(); + let cwd = me.parent().unwrap(); + eprintln!("cwd={:?}", cwd); + // Change directory to where the executable is located, since this test + // fundamentally needs to use relative paths. In some cases (like + // remote-test-server), the current_dir can be somewhere else, so make + // sure it is something we can use. We assume we can write to this + // directory. + env::set_current_dir(&cwd).unwrap(); + let foo = cwd.join("foo"); + let bar = cwd.join("bar"); + fs::create_dir_all(&foo).unwrap(); + fs::create_dir_all(&bar).unwrap(); + fs::copy(&me, foo.join(exe)).unwrap(); + + // Unfortunately this is inconsistent based on the platform, see + // https://github.com/rust-lang/rust/issues/37868. On Windows, + // it is relative *before* changing the directory, and on Unix + // it is *after* changing the directory. + let relative_exe = if cfg!(windows) { + Path::new("foo").join(exe) + } else { + Path::new("../foo").join(exe) + }; + + let status = Command::new(relative_exe) + .arg("current-dir") + .current_dir("bar") + .status() + .unwrap(); + assert!(status.success()); +} diff --git a/tests/ui/command/command-exec.rs b/tests/ui/command/command-exec.rs new file mode 100644 index 000000000..032dad184 --- /dev/null +++ b/tests/ui/command/command-exec.rs @@ -0,0 +1,104 @@ +// run-pass + +#![allow(stable_features)] +// ignore-windows - this is a unix-specific test +// ignore-pretty issue #37199 +// ignore-emscripten no processes +// ignore-sgx no processes +// ignore-fuchsia no execvp syscall provided + +#![feature(process_exec)] + +use std::env; +use std::os::unix::process::CommandExt; +use std::process::Command; + +fn main() { + let mut args = env::args(); + let me = args.next().unwrap(); + + if let Some(arg) = args.next() { + match &arg[..] { + "test1" => println!("passed"), + + "exec-test1" => { + let err = Command::new(&me).arg("test1").exec(); + panic!("failed to spawn: {}", err); + } + + "exec-test2" => { + Command::new("/path/to/nowhere").exec(); + println!("passed"); + } + + "exec-test3" => { + Command::new(&me).arg("bad\0").exec(); + println!("passed"); + } + + "exec-test4" => { + Command::new(&me).current_dir("/path/to/nowhere").exec(); + println!("passed"); + } + + "exec-test5" => { + env::set_var("VARIABLE", "ABC"); + Command::new("definitely-not-a-real-binary").env("VARIABLE", "XYZ").exec(); + assert_eq!(env::var("VARIABLE").unwrap(), "ABC"); + println!("passed"); + } + + "exec-test6" => { + let err = Command::new("echo").arg("passed").env_clear().exec(); + panic!("failed to spawn: {}", err); + } + + "exec-test7" => { + let err = Command::new("echo").arg("passed").env_remove("PATH").exec(); + panic!("failed to spawn: {}", err); + } + + _ => panic!("unknown argument: {}", arg), + } + return + } + + let output = Command::new(&me).arg("exec-test1").output().unwrap(); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + + let output = Command::new(&me).arg("exec-test2").output().unwrap(); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + + let output = Command::new(&me).arg("exec-test3").output().unwrap(); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + + let output = Command::new(&me).arg("exec-test4").output().unwrap(); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + + let output = Command::new(&me).arg("exec-test5").output().unwrap(); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + + if cfg!(target_os = "linux") { + let output = Command::new(&me).arg("exec-test6").output().unwrap(); + println!("{:?}", output); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + + let output = Command::new(&me).arg("exec-test7").output().unwrap(); + println!("{:?}", output); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"passed\n"); + } +} diff --git a/tests/ui/command/command-pre-exec.rs b/tests/ui/command/command-pre-exec.rs new file mode 100644 index 000000000..d366c5ffb --- /dev/null +++ b/tests/ui/command/command-pre-exec.rs @@ -0,0 +1,106 @@ +// run-pass +// revisions: mir thir +// [thir]compile-flags: -Zthir-unsafeck + +#![allow(stable_features)] +// ignore-windows - this is a unix-specific test +// ignore-emscripten no processes +// ignore-sgx no processes +// ignore-fuchsia no execvp syscall +#![feature(process_exec, rustc_private)] + +extern crate libc; + +use std::env; +use std::io::Error; +use std::os::unix::process::CommandExt; +use std::process::Command; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; + +fn main() { + if let Some(arg) = env::args().nth(1) { + match &arg[..] { + "test1" => println!("hello2"), + "test2" => assert_eq!(env::var("FOO").unwrap(), "BAR"), + "test3" => assert_eq!(env::current_dir().unwrap().to_str().unwrap(), "/"), + "empty" => {} + _ => panic!("unknown argument: {}", arg), + } + return; + } + + let me = env::current_exe().unwrap(); + + let output = unsafe { + Command::new(&me) + .arg("test1") + .pre_exec(|| { + println!("hello"); + Ok(()) + }) + .output() + .unwrap() + }; + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert_eq!(output.stdout, b"hello\nhello2\n"); + + let output = unsafe { + Command::new(&me) + .arg("test3") + .pre_exec(|| { + env::set_current_dir("/").unwrap(); + Ok(()) + }) + .output() + .unwrap() + }; + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert!(output.stdout.is_empty()); + + let output = unsafe { + Command::new(&me) + .arg("bad") + .pre_exec(|| Err(Error::from_raw_os_error(102))) + .output() + .unwrap_err() + }; + assert_eq!(output.raw_os_error(), Some(102)); + + let pid = unsafe { libc::getpid() }; + assert!(pid >= 0); + let output = unsafe { + Command::new(&me) + .arg("empty") + .pre_exec(move || { + let child = libc::getpid(); + assert!(child >= 0); + assert!(pid != child); + Ok(()) + }) + .output() + .unwrap() + }; + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert!(output.stdout.is_empty()); + + let mem = Arc::new(AtomicUsize::new(0)); + let mem2 = mem.clone(); + let output = unsafe { + Command::new(&me) + .arg("empty") + .pre_exec(move || { + assert_eq!(mem2.fetch_add(1, Ordering::SeqCst), 0); + Ok(()) + }) + .output() + .unwrap() + }; + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + assert!(output.stdout.is_empty()); + assert_eq!(mem.load(Ordering::SeqCst), 0); +} diff --git a/tests/ui/command/command-setgroups.rs b/tests/ui/command/command-setgroups.rs new file mode 100644 index 000000000..aff67f91b --- /dev/null +++ b/tests/ui/command/command-setgroups.rs @@ -0,0 +1,26 @@ +// run-pass +// ignore-windows - this is a unix-specific test +// ignore-emscripten +// ignore-sgx +// ignore-musl - returns dummy result for _SC_NGROUPS_MAX + +#![feature(rustc_private)] +#![feature(setgroups)] + +extern crate libc; +use std::process::Command; +use std::os::unix::process::CommandExt; + +fn main() { + #[cfg(unix)] + run() +} + +#[cfg(unix)] +fn run() { + let max_ngroups = unsafe { libc::sysconf(libc::_SC_NGROUPS_MAX) }; + let max_ngroups = max_ngroups as u32 + 1; + let vec: Vec<u32> = (0..max_ngroups).collect(); + let p = Command::new("/bin/id").groups(&vec[..]).spawn(); + assert!(p.is_err()); +} diff --git a/tests/ui/command/command-uid-gid.rs b/tests/ui/command/command-uid-gid.rs new file mode 100644 index 000000000..aa4e2f5b8 --- /dev/null +++ b/tests/ui/command/command-uid-gid.rs @@ -0,0 +1,32 @@ +// run-pass +// ignore-android +// ignore-emscripten +// ignore-sgx +// ignore-fuchsia no '/bin/sh', '/bin/ls' + +#![feature(rustc_private)] + +fn main() { + #[cfg(unix)] + run() +} + +#[cfg(unix)] +fn run() { + extern crate libc; + use std::process::Command; + use std::os::unix::prelude::*; + + let mut p = Command::new("/bin/sh") + .arg("-c").arg("true") + .uid(unsafe { libc::getuid() }) + .gid(unsafe { libc::getgid() }) + .spawn().unwrap(); + assert!(p.wait().unwrap().success()); + + // if we're already root, this isn't a valid test. Most of the bots run + // as non-root though (android is an exception). + if unsafe { libc::getuid() != 0 } { + assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); + } +} diff --git a/tests/ui/command/issue-10626.rs b/tests/ui/command/issue-10626.rs new file mode 100644 index 000000000..696a2dd16 --- /dev/null +++ b/tests/ui/command/issue-10626.rs @@ -0,0 +1,26 @@ +// run-pass +// ignore-emscripten no processes +// ignore-sgx no processes + +// Make sure that if a process doesn't have its stdio/stderr descriptors set up +// that we don't die in a large ball of fire + +use std::env; +use std::process::{Command, Stdio}; + +pub fn main () { + let args: Vec<String> = env::args().collect(); + if args.len() > 1 && args[1] == "child" { + for _ in 0..1000 { + println!("hello?"); + } + for _ in 0..1000 { + println!("hello?"); + } + return; + } + + let mut p = Command::new(&args[0]); + p.arg("child").stdout(Stdio::null()).stderr(Stdio::null()); + println!("{:?}", p.spawn().unwrap().wait()); +} |