-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to optionally log #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#include <stdlib.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
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; | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,16 @@ | ||
use pid1::Pid1Settings; | ||
|
||
fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
pid1::relaunch_if_pid1()?; | ||
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::<Vec<_>>(); | ||
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(()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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,15 +12,27 @@ pub enum Error { | |
SpawnChild(std::io::Error), | ||
} | ||
|
||
pub fn relaunch_if_pid1() -> 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 { | ||
eprintln!("pid1-rs: Process running as PID 1"); | ||
} | ||
pid1_handling(Some(child)) | ||
} else { | ||
if option.log { | ||
eprintln!("pid1-rs: Process not running as Pid 1: PID {pid}"); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Debug, Default, Copy, Clone)] | ||
pub struct Pid1Settings { | ||
pub log: bool, | ||
} | ||
|
||
fn relaunch() -> Result<Child, Error> { | ||
let exe = std::env::current_exe().unwrap(); | ||
let args = std::env::args_os().skip(1).collect::<Vec<_>>(); | ||
|
@@ -31,17 +43,36 @@ fn relaunch() -> Result<Child, Error> { | |
} | ||
|
||
fn pid1_handling(child: Option<Child>) -> ! { | ||
let child = child.map(|x| x.id()); | ||
let mut signals = Signals::new([SIGTERM, SIGINT, SIGCHLD]).unwrap(); | ||
|
||
let child = child.map(|x| x.id()); | ||
struct ProcessStatus { | ||
pid: Pid, | ||
exit_code: i32, | ||
} | ||
|
||
loop { | ||
for signal in signals.pending() { | ||
for signal in signals.forever() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This fixes the busy loop issue. Earlier the above loop caused 100% cpu consumption.
This comment was marked as duplicate.
Sorry, something went wrong. |
||
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), | ||
let child_exit_status = match nix::sys::wait::wait().unwrap() { | ||
WaitStatus::Exited(pid, exit_code) => { | ||
let exit_status = ProcessStatus { pid, exit_code }; | ||
Some(exit_status) | ||
} | ||
WaitStatus::Signaled(pid, signal, _) => { | ||
let exit_status = ProcessStatus { | ||
pid, | ||
// Translate signal to exit code | ||
exit_code: signal as i32 + 128, | ||
This comment was marked as duplicate.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The process can exit because of two reasons:
In case of Unix signals, we remap it to proper exit code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get that, but you seem to be conflating the process ID and the exit status. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think renaming it from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you look over the code one more time from scratch? I think you'll see what I'm talking about: in some branches you're returning a process ID, in some an exit code, and in some a signal, but assigning all of them to a variable called pid. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have gone ahead with some renaming which will hopefully help with the confusion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sure, I will let you know once it's ready for review. Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, I see my confusion, you were completely correct. Sorry for the noise. |
||
}; | ||
Some(exit_status) | ||
} | ||
WaitStatus::Stopped(_, _) => None, | ||
WaitStatus::PtraceEvent(_, _, _) => None, | ||
WaitStatus::PtraceSyscall(_) => None, | ||
|
@@ -50,10 +81,12 @@ fn pid1_handling(child: Option<Child>) -> ! { | |
}; | ||
(|| { | ||
let child = child?; | ||
let pid = pid?; | ||
let pid = u32::try_from(pid.as_raw()).ok()?; | ||
if pid == child { | ||
graceful_shutdown() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling the abort call previously resulted in the segmentation fault issue.
This comment was marked as duplicate.
Sorry, something went wrong. |
||
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); | ||
} | ||
Some(()) | ||
})(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sample program to test that it does reap the orphan process.