From acf1c506aeea5f4279c47f1ebd2bb64b5f13b068 Mon Sep 17 00:00:00 2001 From: Sibi Prabakaran Date: Tue, 26 Sep 2023 08:54:25 +0530 Subject: [PATCH 1/5] Log via option --- examples/simple.rs | 4 +++- justfile | 2 +- src/lib.rs | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 8c4d7f8..7d6a915 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,5 +1,7 @@ +use pid1::Pid1Opt; + fn main() -> Result<(), Box> { - pid1::relaunch_if_pid1()?; + pid1::relaunch_if_pid1(Pid1Opt { log: true})?; let id = std::process::id(); println!("In the simple process, going to sleep. Process ID is {id}"); let args = std::env::args().collect::>(); diff --git a/justfile b/justfile index 6cd466c..b89269b 100644 --- a/justfile +++ b/justfile @@ -2,7 +2,7 @@ default: just --list --unsorted -# Basic test +# Test test: cargo build --target x86_64-unknown-linux-musl --example simple cp target/x86_64-unknown-linux-musl/debug/examples/simple etc diff --git a/src/lib.rs b/src/lib.rs index 3c724e7..a8a63fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,15 +12,32 @@ pub enum Error { SpawnChild(std::io::Error), } -pub fn relaunch_if_pid1() -> Result<(), Error> { +pub fn relaunch_if_pid1(option: Pid1Opt) -> Result<(), Error> { if std::process::id() == 1 { let child = relaunch()?; + if option.log { + println!("Process running as pid 1"); + } pid1_handling(Some(child)) } else { + if option.log { + eprintln!("Process not running as pid 1"); + } Ok(()) } } +#[derive(Debug, Copy, Clone)] +pub struct Pid1Opt { + pub log: bool +} + +impl Default for Pid1Opt { + fn default() -> Self { + Self { log: false } + } +} + fn relaunch() -> Result { let exe = std::env::current_exe().unwrap(); let args = std::env::args_os().skip(1).collect::>(); From b20689992704b9bce8a7bb4d3a248c9c48d2b659 Mon Sep 17 00:00:00 2001 From: Sibi Prabakaran Date: Tue, 26 Sep 2023 08:52:07 +0530 Subject: [PATCH 2/5] Fix segmentaion fault and add log option --- etc/Dockerfile | 6 +++++ examples/simple.rs | 10 +++++--- justfile | 14 +++++++---- src/lib.rs | 61 +++++++++++++++++++++++++++++----------------- 4 files changed, 61 insertions(+), 30 deletions(-) diff --git a/etc/Dockerfile b/etc/Dockerfile index 661dede..a37fcde 100644 --- a/etc/Dockerfile +++ b/etc/Dockerfile @@ -1,5 +1,11 @@ FROM alpine +RUN apk update && apk add gcc musl-dev procps + COPY simple /simple +COPY zombie.c /zombie.c + +RUN gcc /zombie.c + CMD ["/simple"] diff --git a/examples/simple.rs b/examples/simple.rs index 7d6a915..7e7967d 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,14 +1,18 @@ -use pid1::Pid1Opt; +use pid1::Pid1Settings; fn main() -> Result<(), Box> { - pid1::relaunch_if_pid1(Pid1Opt { log: true})?; + pid1::relaunch_if_pid1(Pid1Settings { log: true})?; let id = std::process::id(); println!("In the simple process, going to sleep. Process ID is {id}"); let args = std::env::args().collect::>(); println!("Args: {args:?}"); + + for _ in 0..4 { + std::thread::sleep(std::time::Duration::from_secs(5)); std::process::Command::new("date").spawn().unwrap(); } - std::thread::sleep(std::time::Duration::from_secs(5)); + + Ok(()) } diff --git a/justfile b/justfile index b89269b..ce162be 100644 --- a/justfile +++ b/justfile @@ -4,8 +4,12 @@ default: # Test test: - cargo build --target x86_64-unknown-linux-musl --example simple - cp target/x86_64-unknown-linux-musl/debug/examples/simple etc - docker build etc --tag pid1rstest - # Commenting this out to make the test pass - # docker run --rm -t pid1rstest + cargo build --target x86_64-unknown-linux-musl --example simple + cp target/x86_64-unknown-linux-musl/debug/examples/simple etc + docker build etc -f etc/Dockerfile --tag pid1rstest + docker rm pid1rs || exit 0 + docker run --name pid1rs -t pid1rstest + +# Exec into that docker container +exec-shell: + docker exec -it pid1rs sh diff --git a/src/lib.rs b/src/lib.rs index a8a63fa..c7b44e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ use std::process::Child; -use nix::sys::wait::WaitStatus; +use nix::{sys::wait::WaitStatus, unistd::Pid}; use signal_hook::{ consts::{SIGCHLD, SIGINT, SIGTERM}, iterator::Signals, @@ -12,32 +12,27 @@ pub enum Error { SpawnChild(std::io::Error), } -pub fn relaunch_if_pid1(option: Pid1Opt) -> Result<(), Error> { - if std::process::id() == 1 { +pub fn relaunch_if_pid1(option: Pid1Settings) -> Result<(), Error> { + let pid = std::process::id(); + if pid == 1 { let child = relaunch()?; if option.log { - println!("Process running as pid 1"); + println!("pid1-rs: Process running as PID 1"); } - pid1_handling(Some(child)) + pid1_handling( Some(child)) } else { if option.log { - eprintln!("Process not running as pid 1"); + eprintln!("pid1-rs: Process not running as Pid 1: PID {pid}"); } Ok(()) } } -#[derive(Debug, Copy, Clone)] -pub struct Pid1Opt { - pub log: bool +#[derive(Debug, Default, Copy, Clone)] +pub struct Pid1Settings { + pub log: bool, } -impl Default for Pid1Opt { - fn default() -> Self { - Self { log: false } - } -} - fn relaunch() -> Result { let exe = std::env::current_exe().unwrap(); let args = std::env::args_os().skip(1).collect::>(); @@ -47,18 +42,38 @@ fn relaunch() -> Result { .map_err(Error::SpawnChild) } -fn pid1_handling(child: Option) -> ! { - let child = child.map(|x| x.id()); +fn pid1_handling(child : Option) -> ! { let mut signals = Signals::new([SIGTERM, SIGINT, SIGCHLD]).unwrap(); + + let child = child.map(|x| x.id()); +======= + struct ExitStatus { + pid: Pid, + exit_code: i32, + } + loop { - for signal in signals.pending() { + for signal in signals.forever() { if signal == SIGTERM || signal == SIGINT { + // TODO: Should forward these signals to the + // application and then force kill the application + // after certain time. graceful_shutdown(); } if signal == SIGCHLD { let pid = match nix::sys::wait::wait().unwrap() { - WaitStatus::Exited(pid, _) => Some(pid), - WaitStatus::Signaled(pid, _, _) => Some(pid), + WaitStatus::Exited(pid, exit_code) => { + let exit_status = ExitStatus { pid, exit_code }; + Some(exit_status) + } + WaitStatus::Signaled(pid, signal, _) => { + let exit_status = ExitStatus { + pid, + // Translate signal to exit code + exit_code: signal as i32 + 128, + }; + Some(exit_status) + } WaitStatus::Stopped(_, _) => None, WaitStatus::PtraceEvent(_, _, _) => None, WaitStatus::PtraceSyscall(_) => None, @@ -67,10 +82,12 @@ fn pid1_handling(child: Option) -> ! { }; (|| { let child = child?; - let pid = pid?; + let child_exit_status = pid?; + let pid = child_exit_status.pid; let pid = u32::try_from(pid.as_raw()).ok()?; if pid == child { - graceful_shutdown() + // Propagate child exit status code + std::process::exit(child_exit_status.exit_code); } Some(()) })(); From 7555b4dbaec1fb3e1bb68cc97fc13b8322546be4 Mon Sep 17 00:00:00 2001 From: Sibi Prabakaran Date: Tue, 26 Sep 2023 08:56:49 +0530 Subject: [PATCH 3/5] Add test zombie program --- etc/zombie.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 etc/zombie.c diff --git a/etc/zombie.c b/etc/zombie.c new file mode 100644 index 0000000..947a732 --- /dev/null +++ b/etc/zombie.c @@ -0,0 +1,24 @@ +#include +#include +#include + +int main() +{ + // fork() creates child process identical to parent + int pid = fork(); + + // if pid is greater than 0 than it is parent process + // if pid is 0 then it is child process + // if pid is -ve , it means fork() failed to create child process + + // Parent process + if (pid > 0) + sleep(20); + + // Child process + else { + exit(0); + } + + return 0; +} From f5a1e42b10405e87f7000ce35c1c908377ccbeb1 Mon Sep 17 00:00:00 2001 From: Sibi Prabakaran Date: Tue, 26 Sep 2023 08:57:47 +0530 Subject: [PATCH 4/5] cargo fmt --- examples/simple.rs | 4 +--- src/lib.rs | 7 +++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 7e7967d..01f66b7 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,18 +1,16 @@ use pid1::Pid1Settings; fn main() -> Result<(), Box> { - pid1::relaunch_if_pid1(Pid1Settings { log: true})?; + pid1::relaunch_if_pid1(Pid1Settings { log: true })?; let id = std::process::id(); println!("In the simple process, going to sleep. Process ID is {id}"); let args = std::env::args().collect::>(); println!("Args: {args:?}"); - for _ in 0..4 { std::thread::sleep(std::time::Duration::from_secs(5)); std::process::Command::new("date").spawn().unwrap(); } - Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index c7b44e8..810d4c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,12 +14,12 @@ pub enum Error { pub fn relaunch_if_pid1(option: Pid1Settings) -> Result<(), Error> { let pid = std::process::id(); - if pid == 1 { + if pid == 1 { let child = relaunch()?; if option.log { println!("pid1-rs: Process running as PID 1"); } - pid1_handling( Some(child)) + pid1_handling(Some(child)) } else { if option.log { eprintln!("pid1-rs: Process not running as Pid 1: PID {pid}"); @@ -42,11 +42,10 @@ fn relaunch() -> Result { .map_err(Error::SpawnChild) } -fn pid1_handling(child : Option) -> ! { +fn pid1_handling(child: Option) -> ! { let mut signals = Signals::new([SIGTERM, SIGINT, SIGCHLD]).unwrap(); let child = child.map(|x| x.id()); -======= struct ExitStatus { pid: Pid, exit_code: i32, From 418aa18d520f4f9e57085d4bb0c1804761480888 Mon Sep 17 00:00:00 2001 From: Sibi Prabakaran Date: Tue, 26 Sep 2023 11:07:19 +0530 Subject: [PATCH 5/5] Adderss some of the comments --- src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 810d4c2..4311aa2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ pub fn relaunch_if_pid1(option: Pid1Settings) -> Result<(), Error> { if pid == 1 { let child = relaunch()?; if option.log { - println!("pid1-rs: Process running as PID 1"); + eprintln!("pid1-rs: Process running as PID 1"); } pid1_handling(Some(child)) } else { @@ -46,7 +46,7 @@ fn pid1_handling(child: Option) -> ! { let mut signals = Signals::new([SIGTERM, SIGINT, SIGCHLD]).unwrap(); let child = child.map(|x| x.id()); - struct ExitStatus { + struct ProcessStatus { pid: Pid, exit_code: i32, } @@ -60,13 +60,13 @@ fn pid1_handling(child: Option) -> ! { graceful_shutdown(); } if signal == SIGCHLD { - let pid = match nix::sys::wait::wait().unwrap() { + let child_exit_status = match nix::sys::wait::wait().unwrap() { WaitStatus::Exited(pid, exit_code) => { - let exit_status = ExitStatus { pid, exit_code }; + let exit_status = ProcessStatus { pid, exit_code }; Some(exit_status) } WaitStatus::Signaled(pid, signal, _) => { - let exit_status = ExitStatus { + let exit_status = ProcessStatus { pid, // Translate signal to exit code exit_code: signal as i32 + 128, @@ -81,10 +81,10 @@ fn pid1_handling(child: Option) -> ! { }; (|| { let child = child?; - let child_exit_status = pid?; - let pid = child_exit_status.pid; - let pid = u32::try_from(pid.as_raw()).ok()?; - if pid == child { + let child_exit_status = child_exit_status?; + let child_pid = child_exit_status.pid; + let child_pid = u32::try_from(child_pid.as_raw()).ok()?; + if child_pid == child { // Propagate child exit status code std::process::exit(child_exit_status.exit_code); }