Snap for 8564071 from 9a669cc36ea9c6eb3815c20a2f92f31455d613dc to mainline-conscrypt-release Change-Id: Ib08cd00f2615873b6ab50e263488dcf101cedbc4
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index a082427..ea16222 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@ { "git": { - "sha1": "9ff8c8e9d6dc8da25a7f7947f4488ceecdffa553" + "sha1": "ed1c4f6eed646608390a4130bb6de78d630a7b0f" } }
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8a05d56 --- /dev/null +++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@ +name: tests + +on: + push: + branches: + - "*" + # not on tags + pull_request: + +env: + RUSTFLAGS: "-D warnings" + RUST_BACKTRACE: "1" + +jobs: + cargo_tests: + name: ${{ matrix.os }} ${{ matrix.rust_channel }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "macOS-latest", "windows-latest"] + rust_channel: [stable, beta, nightly] + + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust_channel }} + profile: minimal + override: true + - run: cargo test
diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0d4ad53..0000000 --- a/.travis.yml +++ /dev/null
@@ -1,8 +0,0 @@ -language: rust -os: - - linux - - osx -rust: - - stable - - beta - - nightly
diff --git a/Android.bp b/Android.bp index 72212d4..60a4abd 100644 --- a/Android.bp +++ b/Android.bp
@@ -22,6 +22,8 @@ name: "libshared_child", host_supported: true, crate_name: "shared_child", + cargo_env_compat: true, + cargo_pkg_version: "1.0.0", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -29,34 +31,25 @@ ], apex_available: [ "//apex_available:platform", + "com.android.compos", "com.android.virt", ], } -rust_defaults { - name: "shared_child_defaults", +rust_test { + name: "shared_child_test_src_lib", + host_supported: true, crate_name: "shared_child", + cargo_env_compat: true, + cargo_pkg_version: "1.0.0", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, + test_options: { + unit_test: true, + }, edition: "2018", rustlibs: [ "liblibc", ], } - -rust_test_host { - name: "shared_child_host_test_src_lib", - defaults: ["shared_child_defaults"], - test_options: { - unit_test: true, - }, -} - -rust_test { - name: "shared_child_device_test_src_lib", - defaults: ["shared_child_defaults"], -} - -// dependent_library ["feature_list"] -// libc-0.2.94 "default,std"
diff --git a/Cargo.toml b/Cargo.toml index 75e890b..d13367c 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -3,17 +3,16 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "shared_child" -version = "0.3.5" +version = "1.0.0" authors = ["jacko"] description = "a library for using child processes from multiple threads" documentation = "https://docs.rs/shared_child"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 5c8ea27..2453bc6 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@ [package] name = "shared_child" -version = "0.3.5" +version = "1.0.0" authors = ["jacko"] license = "MIT" repository = "https://github.com/oconnor663/shared_child.rs"
diff --git a/METADATA b/METADATA index 2c67826..029d963 100644 --- a/METADATA +++ b/METADATA
@@ -7,13 +7,13 @@ } url { type: ARCHIVE - value: "https://static.crates.io/crates/shared_child/shared_child-0.3.5.crate" + value: "https://static.crates.io/crates/shared_child/shared_child-1.0.0.crate" } - version: "0.3.5" + version: "1.0.0" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 4 - day: 13 + year: 2022 + month: 3 + day: 1 } }
diff --git a/README.md b/README.md index 94525fa..b08106d 100644 --- a/README.md +++ b/README.md
@@ -1,4 +1,4 @@ -# shared_child.rs [](https://travis-ci.org/oconnor663/shared_child.rs) [](https://ci.appveyor.com/project/oconnor663/shared-child-rs/branch/master) [](https://crates.io/crates/shared_child) [](https://docs.rs/shared_child) +# shared_child.rs [](https://github.com/oconnor663/shared_child.rs/actions) [](https://crates.io/crates/shared_child) [](https://docs.rs/shared_child) A library for awaiting and killing child processes from multiple threads.
diff --git a/README.tpl b/README.tpl index 3ad7e39..c16ee7f 100644 --- a/README.tpl +++ b/README.tpl
@@ -1,3 +1,3 @@ -# {{crate}}.rs [](https://travis-ci.org/oconnor663/shared_child.rs) [](https://ci.appveyor.com/project/oconnor663/shared-child-rs/branch/master) [](https://crates.io/crates/shared_child) [](https://docs.rs/shared_child) +# {{crate}}.rs [](https://github.com/oconnor663/shared_child.rs/actions) [](https://crates.io/crates/shared_child) [](https://docs.rs/shared_child) {{readme}}
diff --git a/TEST_MAPPING b/TEST_MAPPING index 22f1cfe..078d2ff 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING
@@ -2,7 +2,18 @@ { "presubmit": [ { - "name": "shared_child_device_test_src_lib" + "name": "shared_child_test_src_lib" + }, + { + "name": "virtualizationservice_device_test" + } + ], + "presubmit-rust": [ + { + "name": "shared_child_test_src_lib" + }, + { + "name": "virtualizationservice_device_test" } ] }
diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 354716e..0000000 --- a/appveyor.yml +++ /dev/null
@@ -1,32 +0,0 @@ -environment: - matrix: - - TARGET: x86_64-pc-windows-msvc - VERSION: 1.31.0 - - TARGET: i686-pc-windows-msvc - VERSION: 1.31.0 - - TARGET: i686-pc-windows-gnu - VERSION: 1.31.0 - - TARGET: x86_64-pc-windows-msvc - VERSION: beta - - TARGET: i686-pc-windows-msvc - VERSION: beta - - TARGET: i686-pc-windows-gnu - VERSION: beta - - TARGET: x86_64-pc-windows-msvc - VERSION: nightly - - TARGET: i686-pc-windows-msvc - VERSION: nightly - - TARGET: i686-pc-windows-gnu - VERSION: nightly -install: - - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:VERSION}-${env:TARGET}.exe" - - rust-%VERSION%-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin - - SET PATH=%PATH%;C:\MinGW\bin - - rustc -V - - cargo -V - -build: false - -test_script: - - cargo test --verbose
diff --git a/cargo2android.json b/cargo2android.json index ac56e26..0e54308 100644 --- a/cargo2android.json +++ b/cargo2android.json
@@ -1,10 +1,11 @@ { "apex-available": [ "//apex_available:platform", + "com.android.compos", "com.android.virt" ], "dependencies": true, "device": true, "run": true, "tests": true -} \ No newline at end of file +}
diff --git a/src/lib.rs b/src/lib.rs index 58c1e0c..5c4f200 100644 --- a/src/lib.rs +++ b/src/lib.rs
@@ -62,7 +62,7 @@ //! ``` use std::io; -use std::process::{Child, Command, ExitStatus}; +use std::process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, ExitStatus}; use std::sync::{Condvar, Mutex}; mod sys; @@ -85,16 +85,36 @@ } impl SharedChild { - /// Spawn a new `SharedChild` from a `std::process::Command`. - pub fn spawn(command: &mut Command) -> io::Result<SharedChild> { + /// Spawn a new `SharedChild` from a + /// [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html). + pub fn spawn(command: &mut Command) -> io::Result<Self> { let child = command.spawn()?; - Ok(SharedChild { + Ok(Self { child: Mutex::new(child), state_lock: Mutex::new(NotWaiting), state_condvar: Condvar::new(), }) } + /// Construct a new `SharedChild` from an already spawned + /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html). + /// + /// This constructor needs to know whether `child` has already been waited on, and the only way + /// to find that out is to call `child.try_wait()` internally. If the child process is + /// currently a zombie, that call will clean it up as a side effect. The [`SharedChild::spawn`] + /// constructor doesn't need to do this. + pub fn new(mut child: Child) -> io::Result<Self> { + let state = match child.try_wait()? { + Some(status) => Exited(status), + None => NotWaiting, + }; + Ok(Self { + child: Mutex::new(child), + state_lock: Mutex::new(state), + state_condvar: Condvar::new(), + }) + } + /// Return the child process ID. pub fn id(&self) -> u32 { self.child.lock().unwrap().id() @@ -204,15 +224,46 @@ self.child.lock().unwrap().kill() } - /// Consume the `SharedChild` and return the `std::process::Child` it - /// contains. + /// Consume the `SharedChild` and return the + /// [`std::process::Child`](https://doc.rust-lang.org/std/process/struct.Child.html) + /// it contains. /// - /// We never reap the child process except through `Child::wait`, so the - /// child object's inner state is correct, even if it was waited on while it - /// was shared. + /// We never reap the child process except by calling `wait` or `try_wait` + /// on it, so the child object's inner state is correct, even if it was + /// waited on while it was shared. pub fn into_inner(self) -> Child { self.child.into_inner().unwrap() } + + /// Take the child's + /// [`stdin`](https://doc.rust-lang.org/std/process/struct.Child.html#structfield.stdin) + /// handle, if any. + /// + /// This will only return `Some` the first time it's called, and then only if the `Command` + /// that created the child was configured with `.stdin(Stdio::piped())`. + pub fn take_stdin(&self) -> Option<ChildStdin> { + self.child.lock().unwrap().stdin.take() + } + + /// Take the child's + /// [`stdout`](https://doc.rust-lang.org/std/process/struct.Child.html#structfield.stdout) + /// handle, if any. + /// + /// This will only return `Some` the first time it's called, and then only if the `Command` + /// that created the child was configured with `.stdout(Stdio::piped())`. + pub fn take_stdout(&self) -> Option<ChildStdout> { + self.child.lock().unwrap().stdout.take() + } + + /// Take the child's + /// [`stderr`](https://doc.rust-lang.org/std/process/struct.Child.html#structfield.stderr) + /// handle, if any. + /// + /// This will only return `Some` the first time it's called, and then only if the `Command` + /// that created the child was configured with `.stderr(Stdio::piped())`. + pub fn take_stderr(&self) -> Option<ChildStderr> { + self.child.lock().unwrap().stderr.take() + } } #[derive(Debug)] @@ -226,9 +277,9 @@ #[cfg(test)] mod tests { - use super::{sys, SharedChild}; - use std; - use std::process::Command; + use super::*; + use std::error::Error; + use std::process::{Command, Stdio}; use std::sync::Arc; // Python isn't available on some Unix platforms, e.g. Android, so we need this instead. @@ -244,6 +295,7 @@ cmd } + // Python isn't available on some Unix platforms, e.g. Android, so we need this instead. #[cfg(unix)] pub fn sleep_forever_cmd() -> Command { let mut cmd = Command::new("sleep"); @@ -258,6 +310,19 @@ cmd } + // Python isn't available on some Unix platforms, e.g. Android, so we need this instead. + #[cfg(unix)] + pub fn cat_cmd() -> Command { + Command::new("cat") + } + + #[cfg(not(unix))] + pub fn cat_cmd() -> Command { + let mut cmd = Command::new("python"); + cmd.arg("-c").arg(""); + cmd + } + #[test] fn test_wait() { let child = SharedChild::spawn(&mut true_cmd()).unwrap(); @@ -345,4 +410,55 @@ // But wait should succeed. child.wait().unwrap(); } + + #[test] + fn test_new() -> Result<(), Box<dyn Error>> { + // Spawn a short-lived child. + let mut command = cat_cmd(); + command.stdin(Stdio::piped()); + command.stdout(Stdio::null()); + let mut child = command.spawn()?; + let child_stdin = child.stdin.take().unwrap(); + + // Construct a SharedChild from the Child, which has not yet been waited on. The child is + // blocked on stdin, so we know it hasn't yet exited. + let mut shared_child = SharedChild::new(child).unwrap(); + assert!(matches!( + *shared_child.state_lock.lock().unwrap(), + NotWaiting, + )); + + // Now close the child's stdin. This will cause the child to exit. + drop(child_stdin); + + // Construct more SharedChild objects from the same child, in a loop. Eventually one of + // them will notice that the child has exited. + loop { + shared_child = SharedChild::new(shared_child.into_inner())?; + if let Exited(status) = &*shared_child.state_lock.lock().unwrap() { + assert!(status.success()); + return Ok(()); + } + } + } + + #[test] + fn test_takes() -> Result<(), Box<dyn Error>> { + let mut command = true_cmd(); + command.stdin(Stdio::piped()); + command.stdout(Stdio::piped()); + command.stderr(Stdio::piped()); + let shared_child = SharedChild::spawn(&mut command)?; + + assert!(shared_child.take_stdin().is_some()); + assert!(shared_child.take_stdout().is_some()); + assert!(shared_child.take_stderr().is_some()); + + assert!(shared_child.take_stdin().is_none()); + assert!(shared_child.take_stdout().is_none()); + assert!(shared_child.take_stderr().is_none()); + + shared_child.wait()?; + Ok(()) + } }
diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 400f910..abdfa48 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs
@@ -48,9 +48,6 @@ // invocation, then check for a non-zero value afterwards. // // https://github.com/opensource-apple/xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/kern/kern_exit.c#L2150-L2156 - // - // XXX: The siginfo_t struct has padding. Does that make it unsound to - // initialize it this way? siginfo = std::mem::zeroed(); libc::waitid( libc::P_PID,