1 // run-pass 2 // only-linux - pidfds are a linux-specific concept 3 4 #![feature(linux_pidfd)] 5 #![feature(rustc_private)] 6 7 extern crate libc; 8 9 use std::io::Error; 10 use std::os::linux::process::{ChildExt, CommandExt}; 11 use std::process::Command; 12 has_clone3() -> bool13fn has_clone3() -> bool { 14 let res = unsafe { libc::syscall(libc::SYS_clone3, 0, 0) }; 15 let err = (res == -1) 16 .then(|| Error::last_os_error()) 17 .expect("probe syscall should not succeed"); 18 19 // If the `clone3` syscall is not implemented in the current kernel version it should return an 20 // `ENOSYS` error. Docker also blocks the whole syscall inside unprivileged containers, and 21 // returns `EPERM` (instead of `ENOSYS`) when a program tries to invoke the syscall. Because of 22 // that we need to check for *both* `ENOSYS` and `EPERM`. 23 // 24 // Note that Docker's behavior is breaking other projects (notably glibc), so they're planning 25 // to update their filtering to return `ENOSYS` in a future release: 26 // 27 // https://github.com/moby/moby/issues/42680 28 // 29 err.raw_os_error() != Some(libc::ENOSYS) && err.raw_os_error() != Some(libc::EPERM) 30 } 31 main()32fn main() { 33 // pidfds require the clone3 syscall 34 if !has_clone3() { 35 return; 36 } 37 38 // We don't assert the precise value, since the standard library 39 // might have opened other file descriptors before our code runs. 40 let _ = Command::new("echo") 41 .create_pidfd(true) 42 .spawn() 43 .unwrap() 44 .pidfd().expect("failed to obtain pidfd"); 45 46 let _ = Command::new("echo") 47 .create_pidfd(false) 48 .spawn() 49 .unwrap() 50 .pidfd().expect_err("pidfd should not have been created when create_pid(false) is set"); 51 52 let _ = Command::new("echo") 53 .spawn() 54 .unwrap() 55 .pidfd().expect_err("pidfd should not have been created"); 56 } 57