From d10d4c5f223761735da5924563d1befc579f015b Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Wed, 10 Jun 2026 18:07:09 +1200 Subject: [PATCH 01/15] docs: Fix typos in CLI arg descriptions --- pid1-exe/src/cli.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index 44a4449..a05f3ac 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -14,10 +14,10 @@ use std::{error::Error, ffi::OsString, path::PathBuf}; #[derive(Parser, Debug, PartialEq)] pub(crate) struct Pid1App { - /// Specify working direcory + /// Specify working directory #[arg(short, long, value_name = "DIR")] pub(crate) workdir: Option, - /// Timeout (in seconds) to wait for child proess to exit + /// Timeout (in seconds) to wait for child process to exit #[arg(short, long, value_name = "TIMEOUT", default_value_t = 2)] pub(crate) timeout: u8, /// Turn on verbose output From 7c989365da17a8ff8e7991942f8d9ba4bf977eca Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Wed, 10 Jun 2026 18:10:23 +1200 Subject: [PATCH 02/15] chore: Format `Pid1App` fields with blank lines --- pid1-exe/src/cli.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index a05f3ac..f8f999b 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -17,24 +17,31 @@ pub(crate) struct Pid1App { /// Specify working directory #[arg(short, long, value_name = "DIR")] pub(crate) workdir: Option, + /// Timeout (in seconds) to wait for child process to exit #[arg(short, long, value_name = "TIMEOUT", default_value_t = 2)] pub(crate) timeout: u8, + /// Turn on verbose output #[arg(short, long, default_value_t = false)] pub(crate) verbose: bool, + /// Override environment variables. Can specify multiple times. #[arg(short, long, value_parser=parse_key_val::)] pub(crate) env: Vec<(OsString, OsString)>, + /// Run command with user ID #[arg(short, long, value_name = "USER_ID")] user_id: Option, + /// Run command with group ID #[arg(short, long, value_name = "GROUP_ID")] group_id: Option, + /// Process to run #[arg(required = true)] pub(crate) command: String, + /// Arguments to the process #[arg(required = false)] pub(crate) args: Vec, From e69fbad5877093a148712627a7fa06105d1f1cc4 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:38:36 +1200 Subject: [PATCH 03/15] chore: Adjust `Pid1App` for consistency - Remove implicit default attributes - Add `pub(crate)` to `user_id` + `group_id` for consistency --- pid1-exe/src/cli.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index f8f999b..4490f6f 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -23,7 +23,7 @@ pub(crate) struct Pid1App { pub(crate) timeout: u8, /// Turn on verbose output - #[arg(short, long, default_value_t = false)] + #[arg(short, long)] pub(crate) verbose: bool, /// Override environment variables. Can specify multiple times. @@ -32,18 +32,16 @@ pub(crate) struct Pid1App { /// Run command with user ID #[arg(short, long, value_name = "USER_ID")] - user_id: Option, + pub(crate) user_id: Option, /// Run command with group ID #[arg(short, long, value_name = "GROUP_ID")] - group_id: Option, + pub(crate) group_id: Option, /// Process to run - #[arg(required = true)] pub(crate) command: String, /// Arguments to the process - #[arg(required = false)] pub(crate) args: Vec, } From 88b1d2e27a75b07ab66d06e0a4f9d04c5840f313 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:58:31 +1200 Subject: [PATCH 04/15] chore(CLI): Display version and description --- pid1-exe/Cargo.toml | 3 +-- pid1-exe/src/cli.rs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pid1-exe/Cargo.toml b/pid1-exe/Cargo.toml index 74a8716..8098a38 100644 --- a/pid1-exe/Cargo.toml +++ b/pid1-exe/Cargo.toml @@ -2,14 +2,13 @@ name = "pid1-exe" version = "0.1.6" edition = "2021" -description = "pid1 handling library for proper signal and zombie reaping of the PID1 process" +description = "A Unix PID1 process wrapper for signal handling and zombie reaping" readme = "../README.md" homepage = "https://github.com/fpco/pid1-rs" repository = "https://github.com/fpco/pid1-rs" license = "MIT" keywords = ["cli", "init", "pid1", "process"] categories = ["command-line-utilities"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] name = "pid1" diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index 4490f6f..70f5fd6 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -13,6 +13,7 @@ use std::time::Duration; use std::{error::Error, ffi::OsString, path::PathBuf}; #[derive(Parser, Debug, PartialEq)] +#[command(version, about, long_about = None)] pub(crate) struct Pid1App { /// Specify working directory #[arg(short, long, value_name = "DIR")] From 3fb570f6e7bd816a7859d09909a30844bdbf00fd Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 13:59:28 +1200 Subject: [PATCH 05/15] feat(CLI): Make `--` optional --- pid1-exe/src/cli.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index 70f5fd6..cb244a1 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -40,6 +40,7 @@ pub(crate) struct Pid1App { pub(crate) group_id: Option, /// Process to run + #[arg(trailing_var_arg = true)] pub(crate) command: String, /// Arguments to the process From 0c3a281e41f0f50a5a62fd755cc540412285cd0a Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:35:57 +1200 Subject: [PATCH 06/15] refactor(CLI): Simplify parsing `--env` values --- pid1-exe/src/cli.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index cb244a1..290fa6e 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -10,7 +10,7 @@ use signal_hook::{ use std::os::unix::process::CommandExt; #[cfg(target_family = "unix")] use std::time::Duration; -use std::{error::Error, ffi::OsString, path::PathBuf}; +use std::{str::FromStr, ffi::OsString, path::PathBuf}; #[derive(Parser, Debug, PartialEq)] #[command(version, about, long_about = None)] @@ -28,8 +28,8 @@ pub(crate) struct Pid1App { pub(crate) verbose: bool, /// Override environment variables. Can specify multiple times. - #[arg(short, long, value_parser=parse_key_val::)] - pub(crate) env: Vec<(OsString, OsString)>, + #[arg(short, long, value_name = "KEY=VALUE")] + pub(crate) env: Vec, /// Run command with user ID #[arg(short, long, value_name = "USER_ID")] @@ -61,7 +61,7 @@ impl Pid1App { if let Some(group_id) = &self.group_id { child.gid(*group_id); } - for (key, value) in &self.env { + for KeyValue(key, value) in &self.env { child.env(key, value); } let pid = std::process::id(); @@ -96,16 +96,18 @@ impl Pid1App { } } -/// Parse a single key-value pair -fn parse_key_val(s: &str) -> Result<(T, U), Box> -where - T: std::str::FromStr, - T::Err: Error + Send + Sync + 'static, - U: std::str::FromStr, - U::Err: Error + Send + Sync + 'static, -{ - let pos = s - .find('=') - .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; - Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) +/// A CLI argument parsed from a `KEY=VALUE` pair. +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct KeyValue(OsString, OsString); + +impl FromStr for KeyValue { + type Err = String; + + /// Parses a CLI flag value into a [`KeyValue`] type. + fn from_str(s: &str) -> Result { + let (key, value) = s.split_once('=') + .ok_or_else(|| format!("invalid `KEY=VALUE` pair: no `=` found in `{s}`"))?; + + Ok(KeyValue(key.into(), value.into())) + } } From 88266429588e607d5c6434a7bab2454e288fa88f Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 17:56:16 +1200 Subject: [PATCH 07/15] chore: Minor revisions to `Pid1App` --- pid1-exe/src/cli.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index 290fa6e..ad8d005 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -1,4 +1,3 @@ -use clap::Parser; #[cfg(target_family = "unix")] use pid1::Pid1Settings; #[cfg(target_family = "unix")] @@ -12,15 +11,15 @@ use std::os::unix::process::CommandExt; use std::time::Duration; use std::{str::FromStr, ffi::OsString, path::PathBuf}; -#[derive(Parser, Debug, PartialEq)] +#[derive(clap::Parser, Debug, PartialEq)] #[command(version, about, long_about = None)] pub(crate) struct Pid1App { /// Specify working directory #[arg(short, long, value_name = "DIR")] pub(crate) workdir: Option, - /// Timeout (in seconds) to wait for child process to exit - #[arg(short, long, value_name = "TIMEOUT", default_value_t = 2)] + /// Grace period for stopping before escalating to SIGKILL + #[arg(short, long, value_name = "SECONDS", default_value_t = 2)] pub(crate) timeout: u8, /// Turn on verbose output @@ -32,18 +31,18 @@ pub(crate) struct Pid1App { pub(crate) env: Vec, /// Run command with user ID - #[arg(short, long, value_name = "USER_ID")] + #[arg(short, long, value_name = "USER ID")] pub(crate) user_id: Option, /// Run command with group ID - #[arg(short, long, value_name = "GROUP_ID")] + #[arg(short, long, value_name = "GROUP ID")] pub(crate) group_id: Option, /// Process to run #[arg(trailing_var_arg = true)] pub(crate) command: String, - /// Arguments to the process + /// Arguments to that process pub(crate) args: Vec, } From 10f4860e554477d455cf947777a068b8ea1f517d Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:32:15 +1200 Subject: [PATCH 08/15] chore: Don't support compiling for `windows` --- pid1-exe/src/cli.rs | 13 ++----------- pid1-exe/src/main.rs | 1 + 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index ad8d005..71b0e98 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -1,13 +1,9 @@ -#[cfg(target_family = "unix")] use pid1::Pid1Settings; -#[cfg(target_family = "unix")] use signal_hook::{ consts::{SIGCHLD, SIGINT, SIGTERM}, iterator::Signals, }; -#[cfg(target_family = "unix")] use std::os::unix::process::CommandExt; -#[cfg(target_family = "unix")] use std::time::Duration; use std::{str::FromStr, ffi::OsString, path::PathBuf}; @@ -47,10 +43,10 @@ pub(crate) struct Pid1App { } impl Pid1App { - #[cfg(target_family = "unix")] pub(crate) fn run(self) -> ! { let mut child = std::process::Command::new(&self.command); let child = child.args(&self.args[..]); + if let Some(workdir) = &self.workdir { child.current_dir(workdir); } @@ -63,6 +59,7 @@ impl Pid1App { for KeyValue(key, value) in &self.env { child.env(key, value); } + let pid = std::process::id(); if pid != 1 { let status = child.exec(); @@ -87,12 +84,6 @@ impl Pid1App { .pid1_handling(signals, child) } } - - #[cfg(target_family = "windows")] - pub(crate) fn run(self) -> ! { - eprintln!("pid1: Not supported on Windows"); - std::process::exit(1); - } } /// A CLI argument parsed from a `KEY=VALUE` pair. diff --git a/pid1-exe/src/main.rs b/pid1-exe/src/main.rs index 4922c96..d40ae09 100644 --- a/pid1-exe/src/main.rs +++ b/pid1-exe/src/main.rs @@ -1,3 +1,4 @@ +#[cfg(target_family = "unix")] mod cli; use clap::Parser; From aca116ec113d10d867b47e9936919df6c8cd2539 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:50:05 +1200 Subject: [PATCH 09/15] chore: Minor revisions for `Pid1App::run()` --- pid1-exe/src/cli.rs | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index 71b0e98..f2adca9 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -44,45 +44,44 @@ pub(crate) struct Pid1App { impl Pid1App { pub(crate) fn run(self) -> ! { - let mut child = std::process::Command::new(&self.command); - let child = child.args(&self.args[..]); + let mut cmd = std::process::Command::new(&self.command); + cmd.args(&self.args); if let Some(workdir) = &self.workdir { - child.current_dir(workdir); + cmd.current_dir(workdir); } if let Some(user_id) = &self.user_id { - child.uid(*user_id); + cmd.uid(*user_id); } if let Some(group_id) = &self.group_id { - child.gid(*group_id); + cmd.gid(*group_id); } for KeyValue(key, value) in &self.env { - child.env(key, value); + cmd.env(key, value); } let pid = std::process::id(); if pid != 1 { - let status = child.exec(); + let status = cmd.exec(); eprintln!("execvp failed with: {status:?}"); std::process::exit(1); - } else { - // Install signal handlers before launching child process - let signals = Signals::new([SIGTERM, SIGINT, SIGCHLD]).unwrap(); - let child = child.spawn(); - let child = match child { - Ok(child) => child, - Err(err) => { - eprintln!("pid1: {} spawn failed. Got error: {err}", self.command); - std::process::exit(1); - } - }; - - Pid1Settings::new() - .enable_log(self.verbose) - .timeout(Duration::from_secs(self.timeout.into())) - .pid1_handling(signals, child) } + + // CRITICAL: Install signal handlers BEFORE spawning the child. + // This prevents a race condition where a fast-failing child sends SIGCHLD + // before we are ready to catch and reap it, creating zombie processes. + let signals = Signals::new([SIGTERM, SIGINT, SIGCHLD]).unwrap(); + + let child = cmd.spawn().unwrap_or_else(|err| { + eprintln!("pid1: {} spawn failed. Got error: {err}", self.command); + std::process::exit(1); + }); + + Pid1Settings::new() + .enable_log(self.verbose) + .timeout(Duration::from_secs(self.timeout.into())) + .pid1_handling(signals, child) } } From 87425175668f4ccd8b3b827c5f32b8d401efe0a4 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:15:29 +1200 Subject: [PATCH 10/15] docs: README - Update `--help` output --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index dc0fb3d..3520bf5 100644 --- a/README.md +++ b/README.md @@ -109,22 +109,25 @@ CMD [ "your-application", "--arg1" ] The `pid1` binary supports various command-line options: -``` shellsession +```shellsession ❯ pid1 --help -Usage: +A Unix PID1 process wrapper for signal handling and zombie reaping + +Usage: pid1 [OPTIONS] [ARGS]... Arguments: Process to run - [ARGS]... Arguments to the process + [ARGS]... Arguments to that process Options: - -w, --workdir Specify working direcory - -t, --timeout Timeout (in seconds) to wait for child proess to exit [default: 2] + -w, --workdir Specify working directory + -t, --timeout Grace period for stopping before escalating to SIGKILL [default: 2] -v, --verbose Turn on verbose output - -e, --env Override environment variables. Can specify multiple times - -u, --user-id Run command with user ID - -g, --group-id Run command with group ID + -e, --env Override environment variables. Can specify multiple times + -u, --user-id Run command with user ID + -g, --group-id Run command with group ID -h, --help Print help + -V, --version Print version ``` --- From 1c184572b9dd381b0fe0e5f8b12a35b121cf8445 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:36:25 +1200 Subject: [PATCH 11/15] chore: Improve DX --- pid1-exe/Cargo.toml | 4 +++- pid1-exe/src/cli.rs | 6 ++++++ pid1-exe/src/main.rs | 13 ++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pid1-exe/Cargo.toml b/pid1-exe/Cargo.toml index 8098a38..a48f920 100644 --- a/pid1-exe/Cargo.toml +++ b/pid1-exe/Cargo.toml @@ -20,5 +20,7 @@ clap = { version = "4.5.41", default-features = false, features = [ "help", "std", ] } -pid1 = { version = "0.1.6", path = "../pid1" } signal-hook = "0.4.3" + +[target.'cfg(unix)'.dependencies] +pid1 = { version = "0.1.6", path = "../pid1" } diff --git a/pid1-exe/src/cli.rs b/pid1-exe/src/cli.rs index f2adca9..bdd1a73 100644 --- a/pid1-exe/src/cli.rs +++ b/pid1-exe/src/cli.rs @@ -43,6 +43,12 @@ pub(crate) struct Pid1App { } impl Pid1App { + /// Parses CLI arguments from the environment. + pub(crate) fn from_cli() -> Self { + use clap::Parser; + Self::parse() + } + pub(crate) fn run(self) -> ! { let mut cmd = std::process::Command::new(&self.command); cmd.args(&self.args); diff --git a/pid1-exe/src/main.rs b/pid1-exe/src/main.rs index d40ae09..e9847a5 100644 --- a/pid1-exe/src/main.rs +++ b/pid1-exe/src/main.rs @@ -1,11 +1,10 @@ -#[cfg(target_family = "unix")] +#[cfg(unix)] mod cli; -use clap::Parser; - -use crate::cli::Pid1App; - fn main() { - let cli = Pid1App::parse(); - cli.run() + #[cfg(unix)] + cli::Pid1App::from_cli().run(); + + #[cfg(not(unix))] + compile_error!("`pid1` is only compatible with Unix-like operating systems."); } From 64fe9b84952b04e77a7f025263e6cc946e297803 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:37:35 +1200 Subject: [PATCH 12/15] chore: Indent to 4 spaces --- pid1-exe/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pid1-exe/src/main.rs b/pid1-exe/src/main.rs index e9847a5..00b1f0c 100644 --- a/pid1-exe/src/main.rs +++ b/pid1-exe/src/main.rs @@ -2,9 +2,9 @@ mod cli; fn main() { - #[cfg(unix)] - cli::Pid1App::from_cli().run(); + #[cfg(unix)] + cli::Pid1App::from_cli().run(); - #[cfg(not(unix))] - compile_error!("`pid1` is only compatible with Unix-like operating systems."); + #[cfg(not(unix))] + compile_error!("`pid1` is only compatible with Unix-like operating systems."); } From 9a96890f94b57b6fb63284aa61a327f01a32ffd9 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 15 Jun 2026 19:27:38 +1200 Subject: [PATCH 13/15] tests: Remove unecessary TTY assignment --- Development.md | 12 ++++++------ justfile | 28 +++++++++++++++------------- pid1/justfile | 14 +++++++------- pid1/tests/sanity.rs | 17 ++++------------- 4 files changed, 32 insertions(+), 39 deletions(-) diff --git a/Development.md b/Development.md index fdfb29c..48afb14 100644 --- a/Development.md +++ b/Development.md @@ -35,7 +35,7 @@ PID USER TIME COMMAND ``` shellsession ❯ just test ... -docker run --name pid1rs -t pid1rstest +docker run --name pid1rs pid1rstest pid1-rs: Process running as PID 1 pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 @@ -58,7 +58,7 @@ confirms the following: ... docker rm pid1rs || exit 0 pid1rs -docker run --name pid1rs -t pid1rstest /simple --sleep +docker run --name pid1rs pid1rstest /simple --sleep pid1-rs: Process running as PID 1 pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 @@ -75,7 +75,7 @@ zombie process: ``` shellsession ❯ just run-zombie -docker exec -t pid1rs zombie +docker exec pid1rs zombie Process ID is 9 Parent process: going to sleep and exit ``` @@ -98,7 +98,7 @@ Reaped pid: 15 ... docker rm pid1rs || exit 0 pid1rs -docker run --name pid1rs -t pid1rstest /simple --sleep +docker run --name pid1rs pid1rstest /simple --sleep pid1-rs: Process running as PID 1 pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 @@ -154,7 +154,7 @@ Execute the recipe `sigterm-test`: ❯ just sigterm-test docker rm pid1rs || exit 0 pid1rs -docker run --name pid1rs -t pid1rstest sigterm_handler +docker run --name pid1rs pid1rstest sigterm_handler pid1-rs: Process running as PID 1 pid1-rs: Process not running as Pid 1: PID 7 This APP can be killed by SIGTERM (15) @@ -187,7 +187,7 @@ Execute the recipe `sigloop-test`: ❯ just sigloop-test docker rm pid1rs || exit 0 pid1rs -docker run --name pid1rs -t pid1rstest sigterm_loop +docker run --name pid1rs pid1rstest sigterm_loop pid1-rs: Process running as PID 1 pid1-rs: Process not running as Pid 1: PID 7 This APP ignores SIGTERM (15) diff --git a/justfile b/justfile index 7d190f7..1386761 100644 --- a/justfile +++ b/justfile @@ -1,36 +1,38 @@ +drun_pid1 := "docker run --rm --name pid pid1runner" + # List all recipies default: just --list --unsorted # Build pid binary build-release-binary: - cargo build --target x86_64-unknown-linux-musl --release + cargo build --bin pid1 --release --target {{ arch() }}-unknown-linux-musl # Build test container test: build-release-binary - cp target/x86_64-unknown-linux-musl/release/pid1 ./pid1-exe/etc/ - cd pid1-exe/etc && docker build . -f Dockerfile --tag pid1runner + cp target/{{ arch() }}-unknown-linux-musl/release/pid1 ./pid1-exe/etc/ + docker build --tag pid1runner ./pid1-exe/etc/ # Test docker image test-init-image: - docker run --rm --interactive --name pid pid1runner ps aux - docker run --rm --interactive --name pid pid1runner ls - docker run --rm --interactive --name pid pid1runner ls / - docker run --rm --interactive --name pid pid1runner id - docker run --rm --interactive --name pid pid1runner --workdir=/home pwd - docker run --rm --interactive --name pid pid1runner --env HELLO=WORLD --env=FOO=BYE printenv HELLO FOO + {{ drun_pid1 }} ps aux + {{ drun_pid1 }} ls + {{ drun_pid1 }} ls / + {{ drun_pid1 }} id + {{ drun_pid1 }} --workdir=/home pwd + {{ drun_pid1 }} --env HELLO=WORLD --env=FOO=BYE printenv HELLO FOO # Exec init image exec-init-image: - docker run --rm --name pid --tty --interactive pid1runner sh + docker run --rm -it --name pid pid1runner sh # Build binary for other architectures binaries clean='false': - cross build --target x86_64-unknown-linux-gnu --release + cross build --bin pid1 --release --target x86_64-unknown-linux-gnu -{{ clean }} && docker image rm ghcr.io/cross-rs/x86_64-unknown-linux-gnu:0.2.5 - cross build --target aarch64-unknown-linux-gnu --release + cross build --bin pid1 --release --target aarch64-unknown-linux-gnu -{{ clean }} && docker image rm ghcr.io/cross-rs/aarch64-unknown-linux-gnu:0.2.5 - cross build --target aarch64-unknown-linux-musl --release + cross build --bin pid1 --release --target aarch64-unknown-linux-musl -{{ clean }} && docker image rm ghcr.io/cross-rs/aarch64-unknown-linux-musl:0.2.5 # Copy binaries to artifacts directory diff --git a/pid1/justfile b/pid1/justfile index 33bb8b3..d87c6ac 100644 --- a/pid1/justfile +++ b/pid1/justfile @@ -20,32 +20,32 @@ build-image: # Run test image run-image: docker rm pid1rs || exit 0 - docker run --name pid1rs -t pid1rstest /simple --sleep 20 + docker run --name pid1rs pid1rstest /simple --sleep 20 # Run zombie in the container run-zombie: - docker exec -t pid1rs zombie + docker exec pid1rs zombie # Test test: build-image docker rm pid1rs || exit 0 - docker run --name pid1rs -t pid1rstest + docker run --name pid1rs pid1rstest cargo test # Run SIGTERM test sigterm-test: docker rm pid1rs || exit 0 - docker run --name pid1rs -t pid1rstest sigterm_handler + docker run --name pid1rs pid1rstest sigterm_handler # Send SIGTERM to container send-sigterm: - docker exec -it pid1rs kill 1 + docker exec pid1rs kill 1 # Run SIGTERM loop test sigloop-test: docker rm pid1rs || exit 0 - docker run --name pid1rs -t pid1rstest sigterm_loop + docker run --name pid1rs pid1rstest sigterm_loop -# Exec into that docker container +# Exec into the `pid1rs` docker container exec-shell: docker exec -it pid1rs bash diff --git a/pid1/tests/sanity.rs b/pid1/tests/sanity.rs index 5721dac..6e30186 100644 --- a/pid1/tests/sanity.rs +++ b/pid1/tests/sanity.rs @@ -18,7 +18,6 @@ impl Container { "run", "--name", self.name.as_str(), - "-t", self.image.as_str(), ]) .output() @@ -31,7 +30,7 @@ impl Container { impl Drop for Container { fn drop(&mut self) { - // Use rm -f to stop and remove the container. This is robust + // Use `rm -f` to stop and remove the container. This is robust // and ensures cleanup even if tests fail to stop the container. let output = Command::new("docker") .args(["rm", "-f", self.name.as_str()]) @@ -72,7 +71,6 @@ fn reaps_zombie_process() { "run", "--name", container.name.as_str(), - "-t", container.image.as_str(), "/simple", "--sleep", @@ -85,7 +83,7 @@ fn reaps_zombie_process() { let zombie_result = s.spawn(|| { let zombie_output = container - .plain_run(&["exec", "-t", container.name.as_str(), "zombie"]) + .plain_run(&["exec", container.name.as_str(), "zombie"]) .unwrap(); zombie_output }); @@ -117,7 +115,6 @@ fn child_process_status_code() { "run", "--name", container.name.as_str(), - "-t", container.image.as_str(), "/simple", "--sleep", @@ -144,7 +141,6 @@ fn child_process_status_code() { container .plain_run(&[ "exec", - "-t", container.name.as_str(), "kill", "-12", @@ -174,7 +170,6 @@ fn sigterm_handling() { "run", "--name", container.name.as_str(), - "-t", container.image.as_str(), "sigterm_handler", ]); @@ -184,7 +179,7 @@ fn sigterm_handling() { let kill_result = s.spawn(|| { std::thread::sleep(Duration::from_secs(2)); container - .plain_run(&["exec", "-t", container.name.as_str(), "kill", "1"]) + .plain_run(&["exec", container.name.as_str(), "kill", "1"]) .unwrap() }); @@ -210,7 +205,6 @@ fn sigterm_ignore() { "run", "--name", container.name.as_str(), - "-t", container.image.as_str(), "sigterm_loop", ]); @@ -220,7 +214,7 @@ fn sigterm_ignore() { let kill_result = s.spawn(|| { std::thread::sleep(Duration::from_secs(2)); container - .plain_run(&["exec", "-t", container.name.as_str(), "kill", "1"]) + .plain_run(&["exec", container.name.as_str(), "kill", "1"]) .unwrap() }); @@ -261,7 +255,6 @@ fn reaps_multiple_zombie_processes() { "run", "--name", container.name.as_str(), - "-t", container.image.as_str(), "/simple", "--sleep", @@ -294,7 +287,6 @@ fn reaps_multiple_zombie_processes() { let zombie_check_output = container .plain_run(&[ "exec", - "-t", container.name.as_str(), "sh", "-c", @@ -351,7 +343,6 @@ fn reaps_orphaned_grandchildren() { "run", "--name", container.name.as_str(), - "-t", container.image.as_str(), "/simple", "--create-grandchildren", From db229d7c49988b464f694dfde0dbbd96a6fa6487 Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 15 Jun 2026 19:29:52 +1200 Subject: [PATCH 14/15] docs: Revise development guide --- Development.md | 285 ++++++++++++++++++++++++------------------------- 1 file changed, 141 insertions(+), 144 deletions(-) diff --git a/Development.md b/Development.md index 48afb14..fdea232 100644 --- a/Development.md +++ b/Development.md @@ -1,41 +1,41 @@ # Testing -All of these tests have been automated in [sanity.rs](./pid1/tests/sanity.rs). +All of these tests have been automated in [`pid1/tests/sanity.rs`](./pid1/tests/sanity.rs). -The `examples` directory contains several programs used for testing this library. +The [`pid1/examples` directory](./pid1/examples) contains several programs used for testing this library. - `simple.rs`: A program that demonstrates the usage of the `pid1-rs` library. - `zombie.rs`: Creates a zombie process. - `sigterm_handler.rs`: A program that has a `SIGTERM` handler and exits upon receiving the signal. - `sigterm_loop.rs`: A buggy program that does not exit on `SIGTERM`. -- `dumb_shell.rs`: An alternative shell for testing, since `bash` - automatically reaps child processes. +- `dumb_shell.rs`: An alternative shell for testing, since `bash` automatically reaps child processes. ## Environment setup - Run the test program via the just recipe: `build-image` - You can get shell access to it via the recipe: `exec-shell` -``` shellsession -❯ just exec-shell -docker exec -it pid1rs sh -/ # ps aux -PID USER TIME COMMAND - 1 root 0:05 /simple - 7 root 0:00 /simple - 8 root 0:00 sh - 14 root 0:00 [date] - 15 root 0:00 ps aux -``` + ```console + $ just exec-shell + $ docker exec -it pid1rs sh + + $ ps aux + PID USER TIME COMMAND + 1 root 0:05 /simple + 7 root 0:00 /simple + 8 root 0:00 sh + 14 root 0:00 [date] + 15 root 0:00 ps aux + ``` # Tests ## Basic functionality -``` shellsession -❯ just test -... -docker run --name pid1rs pid1rstest +```console +$ just test + +$ docker run --name pid1rs pid1rstest pid1-rs: Process running as PID 1 pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 @@ -45,168 +45,165 @@ Wed Sep 27 08:29:37 UTC 2023 Wed Sep 27 08:29:39 UTC 2023 ``` -Ensure that the process exits with a status code of 0. The test above -confirms the following: +Ensure that the process exits with a status code of `0`. The test above confirms the following: - The `simple` program was executed as PID 1. - It relaunched itself as PID 7, which then executed various child processes. ## Zombie process -``` shellsession -❯ just run-image -... -docker rm pid1rs || exit 0 -pid1rs -docker run --name pid1rs pid1rstest /simple --sleep -pid1-rs: Process running as PID 1 -pid1-rs: Process not running as Pid 1: PID 7 -In the simple process, going to sleep. Process ID is 7 -Args: ["/simple", "--sleep"] -Wed Sep 27 08:34:57 UTC 2023 -Wed Sep 27 08:34:59 UTC 2023 -Wed Sep 27 08:35:01 UTC 2023 -Going to sleep 500 seconds -Wed Sep 27 08:35:03 UTC 2023 -``` +1. Run the `run-image` recipe: -While it's executing, open a new shell and run the recipe to create a -zombie process: + ```console + $ just run-image + $ docker rm pid1rs || exit 0 + pid1rs -``` shellsession -❯ just run-zombie -docker exec pid1rs zombie -Process ID is 9 -Parent process: going to sleep and exit -``` + $ docker run --name pid1rs pid1rstest /simple --sleep + pid1-rs: Process running as PID 1 + pid1-rs: Process not running as Pid 1: PID 7 + In the simple process, going to sleep. Process ID is 7 + Args: ["/simple", "--sleep"] + Wed Sep 27 08:34:57 UTC 2023 + Wed Sep 27 08:34:59 UTC 2023 + Wed Sep 27 08:35:01 UTC 2023 + Going to sleep 500 seconds + Wed Sep 27 08:35:03 UTC 2023 + ``` -You should see the following logs from the `simple` process, indicating -that a process has been reaped: +2. While it's executing, open a new shell and run the recipe to create a zombie process: -``` shellsession -... -Wed Sep 27 09:40:56 UTC 2023 -Reaped pid: 15 -``` + ```console + $ just run-zombie + + $ docker exec pid1rs zombie + Process ID is 9 + Parent process: going to sleep and exit + ``` + +3. You should see the following logs from the `simple` process, indicating that a process has been reaped: + + ```console + Wed Sep 27 09:40:56 UTC 2023 + Reaped pid: 15 + ``` ## Child Exit code status propagation -- Run the `run-image` recipe: +1. Run the `run-image` recipe: -``` shellsession -❯ just run-image -... -docker rm pid1rs || exit 0 -pid1rs -docker run --name pid1rs pid1rstest /simple --sleep -pid1-rs: Process running as PID 1 -pid1-rs: Process not running as Pid 1: PID 7 -In the simple process, going to sleep. Process ID is 7 -Args: ["/simple", "--sleep"] -Wed Sep 27 08:34:57 UTC 2023 -Wed Sep 27 08:34:59 UTC 2023 -Wed Sep 27 08:35:01 UTC 2023 -Going to sleep 500 seconds -Wed Sep 27 08:35:03 UTC 2023 -``` + ```console + $ just run-image + $ docker rm pid1rs || exit 0 + pid1rs -- Run the `exec-shell` recipe and perform the test: - -``` shellsession -❯ just exec-shell -docker exec -it pid1rs sh -/ # ps -aux -USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND -root 1 0.0 0.0 996 4 pts/0 Ss+ 12:34 0:00 /simple --sleep -root 7 0.0 0.0 976 4 pts/0 S+ 12:34 0:00 /simple --sleep -root 8 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] -root 9 0.0 0.0 1672 1048 pts/1 Ss 12:34 0:00 sh -root 14 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] -root 15 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] -root 16 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] -root 17 0.0 0.0 2460 1608 pts/1 R+ 12:34 0:00 ps -aux -/ # kill -12 7 -``` + $ docker run --name pid1rs pid1rstest /simple --sleep + pid1-rs: Process running as PID 1 + pid1-rs: Process not running as Pid 1: PID 7 + In the simple process, going to sleep. Process ID is 7 + Args: ["/simple", "--sleep"] + Wed Sep 27 08:34:57 UTC 2023 + Wed Sep 27 08:34:59 UTC 2023 + Wed Sep 27 08:35:01 UTC 2023 + Going to sleep 500 seconds + Wed Sep 27 08:35:03 UTC 2023 + ``` -- Check the logs of the `run-image` recipe to find the exit status - code: +2. Run the `exec-shell` recipe and perform the test: -``` shellsession -... -error: Recipe `run-image` failed on line 21 with exit code 140 -``` + ```console + $ just exec-shell + $ docker exec -it pid1rs sh + $ ps -aux + USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND + root 1 0.0 0.0 996 4 pts/0 Ss+ 12:34 0:00 /simple --sleep + root 7 0.0 0.0 976 4 pts/0 S+ 12:34 0:00 /simple --sleep + root 8 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] + root 9 0.0 0.0 1672 1048 pts/1 Ss 12:34 0:00 sh + root 14 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] + root 15 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] + root 16 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] + root 17 0.0 0.0 2460 1608 pts/1 R+ 12:34 0:00 ps -aux + + $ kill -12 7 + ``` + +3. Check the logs of the `run-image` recipe to find the exit status code: + + ```console + error: Recipe `run-image` failed on line 21 with exit code 140 + ``` -The exit code 140 is correct (128 + 12). + The exit code 140 is correct (128 + 12). -## SIGINT/SIGTERM handling +## SIGINT / SIGTERM handling -The goal of this test is to verify that the child process's `SIGTERM` -handler is called if it is defined. +The goal of this test is to verify that the child process's `SIGTERM` handler is called if it is defined. These are the signal codes: -- SIGINT: 2 -- SIGTERM: 15 +- `SIGINT`: 2 +- `SIGTERM`: 15 -Execute the recipe `sigterm-test`: +1. Execute the recipe `sigterm-test`: -``` shellsession -❯ just sigterm-test -docker rm pid1rs || exit 0 -pid1rs -docker run --name pid1rs pid1rstest sigterm_handler -pid1-rs: Process running as PID 1 -pid1-rs: Process not running as Pid 1: PID 7 -This APP can be killed by SIGTERM (15) -``` + ```console + $ just sigterm-test + $ docker rm pid1rs || exit 0 + pid1rs -Now, send `SIGTERM` to the PID 1 process: + $ docker run --name pid1rs pid1rstest sigterm_handler + pid1-rs: Process running as PID 1 + pid1-rs: Process not running as Pid 1: PID 7 + This APP can be killed by SIGTERM (15) + ``` -``` shellsession -❯ just send-sigterm -``` +2. Now, send `SIGTERM` to the PID 1 process: -Confirm from the logs that the `SIGTERM` handler was called and that -the process exited with a status of 0: + ```shell + just send-sigterm + ``` -``` shellsession -App got SIGTERM 15, going to exit -``` +3. Confirm from the logs that the `SIGTERM` handler was called and that the process exited with a status of `0`: + + ```console + App got SIGTERM 15, going to exit + ``` -Now, perform the same test with `SIGINT`. You can confirm that nothing -is printed, as this signal is not handled by the application. +Now, perform the same test with `SIGINT`. You can confirm that nothing is printed, +as this signal is not handled by the application. ## SIGTERM ignore -This test is for an application that ignores `SIGTERM` and continues -running. This library should be able to forcibly kill such processes. +This test is for an application that ignores `SIGTERM` and continues running. +This library should be able to forcibly kill such processes. -Execute the recipe `sigloop-test`: +1. Execute the recipe `sigloop-test`: -``` shellsession -❯ just sigloop-test -docker rm pid1rs || exit 0 -pid1rs -docker run --name pid1rs pid1rstest sigterm_loop -pid1-rs: Process running as PID 1 -pid1-rs: Process not running as Pid 1: PID 7 -This APP ignores SIGTERM (15) -``` + ```console + $ just sigloop-test + $ docker rm pid1rs || exit 0 + pid1rs -Now, send `SIGTERM` to the PID 1 process: + $ docker run --name pid1rs pid1rstest sigterm_loop + pid1-rs: Process running as PID 1 + pid1-rs: Process not running as Pid 1: PID 7 + This APP ignores SIGTERM (15) + ``` -``` shellsession -❯ just send-sigterm -``` +2. Now, send `SIGTERM` to the PID 1 process: -You will see that it would have exited: + ```shell + just send-sigterm + ``` -``` shellsession -App got SIGTERM 15, but will not exit -App got SIGTERM 15, but will not exit -error: Recipe `sigloop-test` failed on line 44 with exit code 137 -``` +3. You will see that it would have exited: + + ```console + App got SIGTERM 15, but will not exit + App got SIGTERM 15, but will not exit + error: Recipe `sigloop-test` failed on line 44 with exit code 137 + ``` -You can confirm from the status code (137) that the child process was -killed by `SIGKILL` (signal 9, exit code 128 + 9), because the -application ignored `SIGTERM`. +You can confirm from the status code (137) that the child process was killed by +`SIGKILL` (signal 9, exit code 128 + 9), because the application ignored `SIGTERM`. From f143cb3bb3d483890cf37c2d2887298b9be80fdc Mon Sep 17 00:00:00 2001 From: Brennan Kinney <5098581+polarathene@users.noreply.github.com> Date: Mon, 15 Jun 2026 22:14:14 +1200 Subject: [PATCH 15/15] docs: Revise development guide --- Development.md | 78 +++++++++++++++++--------------------------------- pid1/justfile | 18 ++++++------ 2 files changed, 36 insertions(+), 60 deletions(-) diff --git a/Development.md b/Development.md index fdea232..07c1f1a 100644 --- a/Development.md +++ b/Development.md @@ -17,14 +17,13 @@ The [`pid1/examples` directory](./pid1/examples) contains several programs used ```console $ just exec-shell - $ docker exec -it pid1rs sh + # docker exec -it pid1rs sh $ ps aux PID USER TIME COMMAND 1 root 0:05 /simple 7 root 0:00 /simple 8 root 0:00 sh - 14 root 0:00 [date] 15 root 0:00 ps aux ``` @@ -35,20 +34,17 @@ The [`pid1/examples` directory](./pid1/examples) contains several programs used ```console $ just test -$ docker run --name pid1rs pid1rstest +# docker run --rm --name pid1rs pid1rstest pid1-rs: Process running as PID 1 -pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 Args: ["/simple"] -Wed Sep 27 08:29:35 UTC 2023 -Wed Sep 27 08:29:37 UTC 2023 -Wed Sep 27 08:29:39 UTC 2023 +pid1-rs: Reaped PID 7 ``` Ensure that the process exits with a status code of `0`. The test above confirms the following: - The `simple` program was executed as PID 1. -- It relaunched itself as PID 7, which then executed various child processes. +- It relaunched itself as PID 7, before exiting and was then reaped. ## Zombie process @@ -56,19 +52,12 @@ Ensure that the process exits with a status code of `0`. The test above confirms ```console $ just run-image - $ docker rm pid1rs || exit 0 - pid1rs - $ docker run --name pid1rs pid1rstest /simple --sleep + # docker run --rm --name pid1rs pid1rstest /simple --sleep 20 pid1-rs: Process running as PID 1 - pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 - Args: ["/simple", "--sleep"] - Wed Sep 27 08:34:57 UTC 2023 - Wed Sep 27 08:34:59 UTC 2023 - Wed Sep 27 08:35:01 UTC 2023 - Going to sleep 500 seconds - Wed Sep 27 08:35:03 UTC 2023 + Args: ["/simple", "--sleep", "20"] + Going to sleep 20 seconds ``` 2. While it's executing, open a new shell and run the recipe to create a zombie process: @@ -76,7 +65,7 @@ Ensure that the process exits with a status code of `0`. The test above confirms ```console $ just run-zombie - $ docker exec pid1rs zombie + # docker exec pid1rs zombie Process ID is 9 Parent process: going to sleep and exit ``` @@ -84,54 +73,47 @@ Ensure that the process exits with a status code of `0`. The test above confirms 3. You should see the following logs from the `simple` process, indicating that a process has been reaped: ```console - Wed Sep 27 09:40:56 UTC 2023 - Reaped pid: 15 + Reaped PID: 15 ``` ## Child Exit code status propagation -1. Run the `run-image` recipe: +1. Run the `run-orphans` recipe: ```console $ just run-image - $ docker rm pid1rs || exit 0 - pid1rs - $ docker run --name pid1rs pid1rstest /simple --sleep + # docker run --rm --name pid1rs pid1rstest /simple --sleep 20 --create-grandchildren pid1-rs: Process running as PID 1 - pid1-rs: Process not running as Pid 1: PID 7 In the simple process, going to sleep. Process ID is 7 - Args: ["/simple", "--sleep"] - Wed Sep 27 08:34:57 UTC 2023 - Wed Sep 27 08:34:59 UTC 2023 - Wed Sep 27 08:35:01 UTC 2023 - Going to sleep 500 seconds - Wed Sep 27 08:35:03 UTC 2023 + Args: ["/simple", "--sleep", "20", "--create-grandchildren"] + Spawning 3 grandchildren processes that will be orphaned. + Going to sleep 20 seconds ``` 2. Run the `exec-shell` recipe and perform the test: ```console $ just exec-shell - $ docker exec -it pid1rs sh + + # docker exec -it pid1rs sh $ ps -aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND - root 1 0.0 0.0 996 4 pts/0 Ss+ 12:34 0:00 /simple --sleep - root 7 0.0 0.0 976 4 pts/0 S+ 12:34 0:00 /simple --sleep - root 8 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] - root 9 0.0 0.0 1672 1048 pts/1 Ss 12:34 0:00 sh - root 14 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] - root 15 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] - root 16 0.0 0.0 0 0 pts/0 Z+ 12:34 0:00 [date] - root 17 0.0 0.0 2460 1608 pts/1 R+ 12:34 0:00 ps -aux + root 1 0.8 0.0 1048 756 ? Ss 10:11 0:00 /simple --sleep 20 --create-grandchildren + root 7 0.0 0.0 1040 860 ? S 10:11 0:00 /simple --sleep 20 --create-grandchildren + root 8 0.0 0.0 0 0 ? Z 10:11 0:00 [sleep] + root 9 0.0 0.0 0 0 ? Z 10:11 0:00 [sleep] + root 10 0.0 0.0 0 0 ? Z 10:11 0:00 [sleep] + root 11 0.3 0.1 2900 1888 pts/0 Ss 10:11 0:00 sh + root 17 0.0 0.1 7072 3068 pts/0 R+ 10:11 0:00 ps -aux $ kill -12 7 ``` -3. Check the logs of the `run-image` recipe to find the exit status code: +3. Check the logs of the `run-orphans` recipe to find the exit status code: ```console - error: Recipe `run-image` failed on line 21 with exit code 140 + error: Recipe `run-orphans` failed on line 21 with exit code 140 ``` The exit code 140 is correct (128 + 12). @@ -149,12 +131,9 @@ These are the signal codes: ```console $ just sigterm-test - $ docker rm pid1rs || exit 0 - pid1rs - $ docker run --name pid1rs pid1rstest sigterm_handler + # docker run --rm --name pid1rs pid1rstest sigterm_handler pid1-rs: Process running as PID 1 - pid1-rs: Process not running as Pid 1: PID 7 This APP can be killed by SIGTERM (15) ``` @@ -182,12 +161,9 @@ This library should be able to forcibly kill such processes. ```console $ just sigloop-test - $ docker rm pid1rs || exit 0 - pid1rs - $ docker run --name pid1rs pid1rstest sigterm_loop + # docker run --rm --name pid1rs pid1rstest sigterm_loop pid1-rs: Process running as PID 1 - pid1-rs: Process not running as Pid 1: PID 7 This APP ignores SIGTERM (15) ``` diff --git a/pid1/justfile b/pid1/justfile index d87c6ac..f9bb5f0 100644 --- a/pid1/justfile +++ b/pid1/justfile @@ -15,12 +15,15 @@ build-image: cp ../target/x86_64-unknown-linux-musl/debug/examples/sigterm_handler etc cp ../target/x86_64-unknown-linux-musl/debug/examples/sigterm_loop etc cp ../target/x86_64-unknown-linux-musl/debug/examples/dumb_shell etc - docker build etc -f etc/Dockerfile --tag pid1rstest + docker build --tag pid1rstest ./etc/ # Run test image run-image: - docker rm pid1rs || exit 0 - docker run --name pid1rs pid1rstest /simple --sleep 20 + docker run --rm --name pid1rs pid1rstest /simple --sleep 20 + +# Run orphans image +run-orphans: + docker run --rm --name pid1rs pid1rstest /simple --sleep 20 --create-grandchildren # Run zombie in the container run-zombie: @@ -28,14 +31,12 @@ run-zombie: # Test test: build-image - docker rm pid1rs || exit 0 - docker run --name pid1rs pid1rstest + docker run --rm --name pid1rs pid1rstest cargo test # Run SIGTERM test sigterm-test: - docker rm pid1rs || exit 0 - docker run --name pid1rs pid1rstest sigterm_handler + docker run --rm --name pid1rs pid1rstest sigterm_handler # Send SIGTERM to container send-sigterm: @@ -43,8 +44,7 @@ send-sigterm: # Run SIGTERM loop test sigloop-test: - docker rm pid1rs || exit 0 - docker run --name pid1rs pid1rstest sigterm_loop + docker run --rm --name pid1rs pid1rstest sigterm_loop # Exec into the `pid1rs` docker container exec-shell: