Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Bump versions.
- Improve test suite by fixing multiple drop invocation.
- Fix signal coalescing issue.

# v0.1.5

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 32 additions & 33 deletions Development.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

All of these tests have been automated in [sanity.rs](./pid1/tests/sanity.rs).

There are some programs under the directory `examples`, that will be
used for testing this library.
The `examples` directory contains several programs used for testing this library.

- simple.rs: Program which demonstrate the usage of pid1-rs library
- zombie.rs: Creates zombie process
- sigterm_handler.rs: Program which has SIGTERM handler and exits on receiving it.
- sigterm_loop.rs: A buggy program which doesn't exit on SIGTERM
- dumb_shell.rs: Alternative shell that you can use to test your
program since bash does reaping of process.
- `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.

## Environment setup

Expand Down Expand Up @@ -46,11 +45,11 @@ Wed Sep 27 08:29:37 UTC 2023
Wed Sep 27 08:29:39 UTC 2023
```

You have to ensure that it exits with status code 0. The above test
confirms the following things:
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 the process as pid 7 which executed various processes.
- The `simple` program was executed as PID 1.
- It relaunched itself as PID 7, which then executed various child processes.

## Zombie process

Expand All @@ -71,8 +70,8 @@ Going to sleep 500 seconds
Wed Sep 27 08:35:03 UTC 2023
```

And while it's executing create a new shell run the recipe which will
create zombie process:
While it's executing, open a new shell and run the recipe to create a
zombie process:

``` shellsession
❯ just run-zombie
Expand All @@ -81,8 +80,8 @@ Process ID is 9
Parent process: going to sleep and exit
```

You could see the following logs on the `simple` process about a
process being reaped
You should see the following logs from the `simple` process, indicating
that a process has been reaped:

``` shellsession
...
Expand All @@ -92,7 +91,7 @@ Reaped pid: 15

## Child Exit code status propagation

- Run recipe `run-image`:
- Run the `run-image` recipe:

``` shellsession
❯ just run-image
Expand All @@ -111,7 +110,7 @@ Going to sleep 500 seconds
Wed Sep 27 08:35:03 UTC 2023
```

- Run recipe `exec-shell` and do the testing:
- Run the `exec-shell` recipe and perform the test:

``` shellsession
❯ just exec-shell
Expand All @@ -129,20 +128,20 @@ root 17 0.0 0.0 2460 1608 pts/1 R+ 12:34 0:00 ps -aux
/ # kill -12 7
```

- See the logs of the `run-image` recipe and find the exit status
- Check the logs of the `run-image` recipe to find the exit status
code:

``` shellsession
...
error: Recipe `run-image` failed on line 21 with exit code 140
```

And the exit code status 140 is indeed correct (128 + 12).
The exit code 140 is correct (128 + 12).

## SIGINT/SIGTERM handling

The aim is to check that the child process's SIGTERM handler is called
in case it's 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:

Expand All @@ -161,27 +160,26 @@ pid1-rs: Process not running as Pid 1: PID 7
This APP can be killed by SIGTERM (15)
```

And now send SIGTERM to the pid1:
Now, send `SIGTERM` to the PID 1 process:

``` shellsession
❯ just send-sigterm
```

Confirm from the logs that the SIGTERM handler is called and the exit
status is 0:
Confirm from the logs that the `SIGTERM` handler was called and that
the process exited with a status of 0:

``` shellsession
App got SIGTERM 15, going to exit
```

Now do the same test with `SIGINT` and you can confirm that it won't
print anything since it is not handled.
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 is for testing an application where it ignores SIGTERM and
continus on doing some work. This library should be able to force 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`:

Expand All @@ -195,7 +193,7 @@ pid1-rs: Process not running as Pid 1: PID 7
This APP ignores SIGTERM (15)
```

And now send SIGTERM to the pid1:
Now, send `SIGTERM` to the PID 1 process:

``` shellsession
❯ just send-sigterm
Expand All @@ -209,5 +207,6 @@ 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 that the child process got killed
by (137 - 128 = 9) SIGKIL as the application ignores 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`.
94 changes: 70 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,59 @@
[crates-badge-exe]: https://img.shields.io/crates/v/pid1-exe.svg
[crates-url-exe]: https://crates.io/crates/pid1-exe

pid1 handling library for proper signal and zombie reaping of the PID1
process.
A Rust library and binary for correct PID 1 signal handling and zombie
process reaping in containerized environments.

## Overview

In Unix-like systems, the process with Process ID (PID) 1 has special
responsibilities. It is the ancestor of all other processes and is
responsible for adopting orphaned processes and reaping them. When
running applications in containers, your application's process often
becomes PID 1.

Without proper handling, signals like `SIGTERM` might not be forwarded
to child processes, and zombie processes can accumulate, leading to
resource leaks. `pid1-rs` solves this by providing:

- **Signal Forwarding:** Intercepts signals like `SIGTERM` and
`SIGINT` and forwards them to its child process, allowing for
graceful shutdown.
- **Zombie Reaping:** Acts as an init process to reap orphaned child
processes, preventing zombie process accumulation.

This project provides two ways to use this functionality: as a Rust
library integrated into your application, or as a standalone binary
executable.

## Comparison with `tini`

[tini](https://github.com/krallin/tini) is a popular, minimal init for containers. `pid1-rs`
provides similar functionality with a different approach:

- The **`pid1` library** integrates directly into your Rust
application. This is its main advantage over `tini`, as you don't
need to add a separate binary to your container. The PID 1 handling
logic is compiled into your application, simplifying your
`Dockerfile` and potentially reducing image size.
- The **`pid1-exe` binary** is a direct, Rust-native alternative to
`tini`. Both serve the same purpose as a standalone init binary. If
you prefer a toolchain built in Rust or need a init for non-Rust
applications, `pid1-exe` is an excellent choice.

## Packages in this Repository

This repository consists of two packages:
- [pid1](./pid1/) crate: Library meant to be used by your Rust applications.
- [pid1-exe](./pid1-exe) crate: Binary which internally uses pid1
crate for container deployments. The binary name is `pid1`.
- [`pid1`](./pid1/): A Rust library to integrate into your application.
- [`pid1-exe`](./pid1-exe): A standalone `pid1` binary for use in any container environment.

## pid1 Library Usage
## `pid1` Library Usage

This library is used to simplify Rust deployment in a containerized
environment. Instead of using binaries like [Haskell's
pid1](https://github.com/fpco/pid1) or
[tini](https://github.com/krallin/tini) in your container, you can use
this crate directly.
environment. Instead of using binaries like [Haskell's pid1](https://github.com/fpco/pid1) or
[tini](https://github.com/krallin/tini) in your container, you can use this crate directly.

### Usage

You must ensure that the `launch` method is the first statement in
your `main` function:
Expand All @@ -34,35 +72,42 @@ use pid1::Pid1Settings;

fn main() {
Pid1Settings::new()
.enable_log(true)
.timeout(Duration::from_secs(2))
.enable_log(true) // Optional: for debugging
.timeout(Duration::from_secs(2)) // Optional: timeout for graceful shutdown
.launch()
.expect("Launch failed");
println!("Hello world");
// Rest of the logic...
}
```

You can also see various example usages [here.](./examples/) This
function is meant only for Unix systems and the above code is a no-op
in Windows.
This function is meant only for Unix systems and the above code is a no-op
on Windows.

For more examples, see the [examples](./pid1/examples/) directory.

## `pid1-exe` Binary Usage

## Using pid1 binary
You can download the `pid1` binary from the [releases page](https://github.com/fpco/pid1-rs/releases) and use
it as the `ENTRYPOINT` in your container.

You can download the `pid1` binary that is part of the [releases](https://github.com/fpco/pid1-rs/releases)
and use it in your container directly. Example:
### Docker Example

In this example, `your-application` and its arguments are passed using
`CMD`.

``` dockerfile
FROM alpine:3.14.2

ADD https://github.com/fpco/pid1-rs/releases/download/v0.1.0/pid1-x86_64-unknown-linux-musl /usr/bin/pid1

RUN chmod +x /usr/bin/pid1
ADD --chmod=755 https://github.com/fpco/pid1-rs/releases/download/v0.1.0/pid1-x86_64-unknown-linux-musl /usr/bin/pid1

ENTRYPOINT [ "pid1" ]
CMD [ "your-application", "--arg1" ]
```

Various options supported by the binary:
### Command-line Options

The `pid1` binary supports various command-line options:

``` shellsession
❯ pid1 --help
Expand All @@ -82,7 +127,8 @@ Options:
-h, --help Print help
```

---

## Development

The testing steps are documented in [Development.md](./Development.md). We also have
integration tests for the same.
The testing steps are documented in [Development.md](./Development.md).
4 changes: 2 additions & 2 deletions pid1-exe/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pid1-exe"
version = "0.1.5"
version = "0.1.6"
edition = "2021"
description = "pid1 handling library for proper signal and zombie reaping of the PID1 process"
readme = "../README.md"
Expand All @@ -21,5 +21,5 @@ clap = { version = "4.5.41", default-features = false, features = [
"help",
"std",
] }
pid1 = { version = "0.1.5", path = "../pid1" }
pid1 = { version = "0.1.6", path = "../pid1" }
signal-hook = "0.4.3"
2 changes: 1 addition & 1 deletion pid1/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pid1"
version = "0.1.5"
version = "0.1.6"
edition = "2021"
readme = "../README.md"
homepage = "https://github.com/fpco/pid1-rs"
Expand Down
Loading