Finish of changes for 0.4, unique non POSIX signal handling for nginx and php-fpm added
Some checks failed
Build & Upload tux-dock / build (push) Has been cancelled
Some checks failed
Build & Upload tux-dock / build (push) Has been cancelled
This commit is contained in:
parent
2557419d4d
commit
f568624956
6 changed files with 293 additions and 43 deletions
|
|
@ -6,8 +6,14 @@
|
|||
|
||||
- Fixed `tuxreaperdasm.c` on ARM64 by using the architecture-specific `O_DIRECTORY` value (`0x4000` on `aarch64`, `0x10000` on `x86_64`). The hardcoded x86_64 value caused `openat("/proc", ...)` to fail with `EINVAL`, which broke `/proc` scanning, descendant waiting, and SIGTERM forwarding on ARM64 Chromebooks/VMs.
|
||||
- Verified graceful Apache shutdown on ARM64: tuxreaperd remaps `SIGTERM` to `SIGWINCH` for Apache processes and waits for descendants to finish, allowing in-flight HTTP responses to complete before the container exits.
|
||||
- Added nginx-aware `SIGTERM`→`SIGQUIT` remapping in both `tuxreaperdgnu.c` and `tuxreaperdasm.c`. When `docker stop` sends `SIGTERM`, nginx processes (`/usr/sbin/nginx`, `/usr/local/nginx/sbin/nginx`) receive `SIGQUIT` for graceful shutdown instead of `SIGTERM`.
|
||||
- Added PHP-FPM-aware `SIGTERM`→`SIGQUIT` remapping with prefix matching (e.g., `/usr/sbin/php-fpm` matches `/usr/sbin/php-fpm8.4`) so PHP-FPM receives the correct graceful-shutdown signal when the container stops.
|
||||
- Documented that PHP-FPM graceful shutdown requires `process_control_timeout` to be set in the PHP-FPM pool configuration (e.g., `process_control_timeout = 60s`). Without it, the PHP-FPM master exits immediately on `SIGQUIT` and in-flight requests are dropped.
|
||||
- Set the container stop timeout to 60 seconds in `DockerManager::createContainer` (`--stop-timeout=60`) and raised the TUI stop default to 60 seconds, aligning Docker's grace period with `tuxreaperd`'s descendant wait so graceful shutdowns are not cut short by a premature SIGKILL.
|
||||
- Added `--debug-reaper` flag to `compile.sh` to build tuxreaperd with `TUXREAPERD_DEBUG` and run verbose tests; useful for diagnosing `/proc` scanning, signal broadcasts, and descendant waits.
|
||||
- Added adaptive TTY/non-TTY spinner to `compile.sh` for clearer feedback during the test phase.
|
||||
- Added a "Why tuxreaperd?" comparison section to `README.md` with a feature matrix and ASCII size chart contrasting `tuxreaperd` with `tini`, `dumb-init`, and `mini-init-asm`.
|
||||
- Added a "Web workload guide" section to `README.md` covering Apache, nginx, and PHP-FPM signal conventions, required PHP-FPM `process_control_timeout` configuration, container startup examples, and the `sleep.php` verification test.
|
||||
|
||||
### Version
|
||||
|
||||
|
|
|
|||
123
README.md
123
README.md
|
|
@ -18,11 +18,132 @@ It gives you a guided, keyboard-first TUI for common Docker operations without m
|
|||
- Direct `fork`/`exec` process execution for CLI-backed streaming and interactive commands.
|
||||
- Persistent container listings that retain exited containers.
|
||||
- Robust stop handling with state polling, timeout retry, and idempotent stop responses.
|
||||
- `tuxreaperd` micro init system: a tiny statically-linked C daemon mounted into created containers as PID 1, acting as a subreaper so orphaned processes never linger as zombies, broadcasting lifecycle signals (SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGUSR1, SIGUSR2) to the whole process tree via `kill(-1, sig)`, and propagating exit status. Includes architecture-specific `O_DIRECTORY` handling for `x86_64` and `aarch64`, plus Apache-aware `SIGTERM`→`SIGWINCH` remapping for graceful shutdown on both architectures. [Processes don't fear tuxreaperd!](tuxreaperdpromo.jpg)
|
||||
- `tuxreaperd` micro init system: a tiny statically-linked C daemon mounted into created containers as PID 1, acting as a subreaper so orphaned processes never linger as zombies, broadcasting lifecycle signals (SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGUSR1, SIGUSR2) to the whole process tree via `kill(-1, sig)`, and propagating exit status. Includes architecture-specific `O_DIRECTORY` handling for `x86_64` and `aarch64`, Apache-aware `SIGTERM`→`SIGWINCH`, nginx-aware `SIGTERM`→`SIGQUIT`, and PHP-FPM-aware `SIGTERM`→`SIGQUIT` remapping (with prefix matching for versioned binaries) for graceful shutdown, plus a 60-second container stop timeout so descendants have time to finish. Note: PHP-FPM graceful shutdown also requires `process_control_timeout` to be set in the PHP-FPM pool config (e.g., `process_control_timeout = 60s`). [Processes don't fear tuxreaperd!](tuxreaperdpromo.jpg)
|
||||
- About screen in-app with project/version/repository info.
|
||||
|
||||
---
|
||||
|
||||
## Why tuxreaperd?
|
||||
|
||||
Most container inits are generic: they spawn one child, forward signals, and reap zombies. That is enough for simple apps, but it breaks down for classic web engines that violate standard UNIX signal conventions:
|
||||
|
||||
- **Apache HTTPD** uses `SIGWINCH` for graceful shutdown.
|
||||
- **Nginx** uses `SIGQUIT` for graceful shutdown.
|
||||
- **PHP-FPM** uses `SIGQUIT` for graceful shutdown.
|
||||
|
||||
`tuxreaperd` encodes those quirks directly. It scans `/proc/<pid>/exe` during signal broadcast and translates `SIGTERM` into the correct graceful-shutdown signal for each daemon, then waits up to 60 seconds for worker descendants to drain.
|
||||
|
||||
| Feature / Scenario | `tini` | `dumb-init` | `mini-init-asm` | `tuxreaperd` (~4.7 KB) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Automatic per-binary signal translation | No | Manual `--rewrite` only | No | Yes (Apache / nginx / PHP-FPM) |
|
||||
| Zero-config web graceful stops | No | No | No | Yes |
|
||||
| Post-exit descendant draining | No | No | No | Yes, 60-second bounded drain |
|
||||
| Multi-pass `/proc` signal sweeps | No | No | No | Two-pass sweep |
|
||||
| Freestanding / no libc | No | No (unless musl) | Yes | Yes |
|
||||
| Restart-on-crash | No | No | Yes (`EP_RESTART_ENABLED`) | No |
|
||||
| Configurable grace → `SIGKILL` timeout | No | No | Yes (`EP_GRACE_SECONDS`) | No (fixed 60s drain) |
|
||||
|
||||
Static binary size comparison (KB):
|
||||
|
||||
```text
|
||||
tuxreaperd |## 4.7
|
||||
dumb-init (musl) |######## 20
|
||||
tini |######### 23
|
||||
mini-init-asm |################ 41
|
||||
|
||||
dumb-init (glibc static) is ~700+ KB and omitted from the chart for scale.
|
||||
```
|
||||
|
||||
`mini-init-asm` and `dumb-init` are excellent generic inits with useful extras like restart-on-crash or configurable kill timeouts, but they treat workloads as black boxes. `tuxreaperd` trades that generic flexibility for deep, zero-configuration automation of the three web-engine families that actually need signal translation.
|
||||
|
||||
---
|
||||
|
||||
## Web workload guide
|
||||
|
||||
`tuxreaperd` is built for containerized web engines that violate standard UNIX signal conventions. This guide covers Apache HTTPD, nginx, and PHP-FPM.
|
||||
|
||||
### Signal conventions
|
||||
|
||||
| Engine | Graceful shutdown signal | Fast shutdown signal |
|
||||
| --- | --- | --- |
|
||||
| Apache HTTPD | `SIGWINCH` | `SIGTERM` |
|
||||
| Nginx | `SIGQUIT` | `SIGTERM` / `SIGINT` |
|
||||
| PHP-FPM | `SIGQUIT` | `SIGTERM` |
|
||||
|
||||
When `docker stop` sends `SIGTERM` to PID 1, `tuxreaperd` inspects `/proc/<pid>/exe` and automatically translates the signal:
|
||||
|
||||
- Apache processes receive `SIGWINCH`.
|
||||
- Nginx processes receive `SIGQUIT`.
|
||||
- PHP-FPM processes (matched by the `/usr/sbin/php-fpm` prefix) receive `SIGQUIT`.
|
||||
|
||||
### Required PHP-FPM configuration
|
||||
|
||||
PHP-FPM has a known quirk: on `SIGQUIT` its master process exits immediately unless `process_control_timeout` is configured. Without it, the FastCGI socket closes and nginx drops in-flight requests.
|
||||
|
||||
Add this to your PHP-FPM pool config (e.g., `/etc/php/8.4/fpm/pool.d/www.conf`):
|
||||
|
||||
```ini
|
||||
[global]
|
||||
process_control_timeout = 60s
|
||||
```
|
||||
|
||||
Then restart PHP-FPM.
|
||||
|
||||
### Starting services inside the container
|
||||
|
||||
Create the container with tux-dock, which already injects `tuxreaperd` as PID 1 and sets `--stop-timeout=60`. Then start your web services as descendants:
|
||||
|
||||
```bash
|
||||
# Start PHP-FPM in the background
|
||||
docker exec my-container /usr/sbin/php-fpm8.4 --nodaemonize &
|
||||
|
||||
# Start nginx in the foreground (or background)
|
||||
docker exec my-container /usr/sbin/nginx -g 'daemon off;'
|
||||
```
|
||||
|
||||
Make sure the binary paths match the ones `tuxreaperd` recognizes:
|
||||
- `/usr/sbin/apache2`, `/usr/sbin/httpd`, `/usr/local/apache2/bin/httpd`
|
||||
- `/usr/sbin/nginx`, `/usr/local/nginx/sbin/nginx`
|
||||
- `/usr/sbin/php-fpm*`, `/usr/local/sbin/php-fpm*`
|
||||
|
||||
### Verification: long-running PHP request
|
||||
|
||||
Create `/var/www/html/sleep.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
ob_end_clean();
|
||||
header('Content-Type: text/plain');
|
||||
header('Cache-Control: no-cache');
|
||||
|
||||
echo "Test started at: " . date('H:i:s') . "\n";
|
||||
echo "Sleeping for 30 seconds...\n";
|
||||
flush();
|
||||
|
||||
sleep(30);
|
||||
|
||||
echo "Test finished at: " . date('H:i:s') . "\n";
|
||||
?>
|
||||
```
|
||||
|
||||
Start the request, then stop the container:
|
||||
|
||||
```bash
|
||||
curl -i http://localhost:8087/sleep.php
|
||||
# In another terminal:
|
||||
docker stop my-container
|
||||
```
|
||||
|
||||
Expected result: the full response body is printed, including the “Test finished at: …” line, with no `curl: (18) transfer closed with outstanding read data remaining` error.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
- **Connection drops early** → Verify `process_control_timeout` is set in PHP-FPM and restart it.
|
||||
- **Container stops instantly** → The container must be recreated to pick up `--stop-timeout=60`; existing containers keep their original stop timeout.
|
||||
- **PHP-FPM not detected** → Confirm the binary path starts with `/usr/sbin/php-fpm` or `/usr/local/sbin/php-fpm`.
|
||||
|
||||
---
|
||||
|
||||
## Build Requirements
|
||||
|
||||
- **C++17 or newer** compiler (e.g. `g++`, `clang++`)
|
||||
|
|
|
|||
8
main.cpp
8
main.cpp
|
|
@ -649,18 +649,18 @@ void TuxDockApp::ActionStopContainer() {
|
|||
OpenInput(
|
||||
"Stop Container",
|
||||
"Shutdown grace period in seconds for " + name +
|
||||
" (default 10, max " + std::to_string(kMaxStopTimeout) + "):",
|
||||
" (default 60, max " + std::to_string(kMaxStopTimeout) + "):",
|
||||
[this, id](bool ok, const std::string& value) {
|
||||
if (!ok) return;
|
||||
int seconds = 10;
|
||||
int seconds = 60;
|
||||
if (!value.empty()) {
|
||||
try {
|
||||
seconds = std::stoi(value);
|
||||
} catch (...) {
|
||||
seconds = 10;
|
||||
seconds = 60;
|
||||
}
|
||||
}
|
||||
if (seconds < 0) seconds = 10;
|
||||
if (seconds < 0) seconds = 60;
|
||||
if (seconds > kMaxStopTimeout) seconds = kMaxStopTimeout;
|
||||
BeginStopOperation(id, seconds);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ bool DockerManager::createContainer(const std::string& name,
|
|||
|
||||
std::vector<std::string> args{"docker", "create", "--name", name};
|
||||
|
||||
// Give the container a 60-second stop grace period so tuxreaperd can wait
|
||||
// for descendants (e.g., nginx workers) to finish gracefully before Docker
|
||||
// sends SIGKILL.
|
||||
args.push_back("--stop-timeout=60");
|
||||
|
||||
// Port mappings
|
||||
for (const auto& port : ports) {
|
||||
args.push_back("-p");
|
||||
|
|
|
|||
|
|
@ -578,8 +578,10 @@ static void my_execvp(const char *file, char *const argv[], char *const envp[])
|
|||
}
|
||||
|
||||
/* Apache hijacks SIGWINCH for graceful shutdown. When the outside world
|
||||
sends SIGTERM, we translate it to SIGWINCH for Apache processes and send
|
||||
the original signal to everyone else. */
|
||||
sends SIGTERM, we translate it to SIGWINCH for Apache processes.
|
||||
Nginx and PHP-FPM both use SIGQUIT for graceful shutdown, so we
|
||||
translate SIGTERM to SIGQUIT for those processes. Everyone else
|
||||
receives the original signal. */
|
||||
static const char * const apache_exes[] = {
|
||||
"/usr/sbin/apache2",
|
||||
"/usr/sbin/httpd",
|
||||
|
|
@ -589,6 +591,30 @@ static const char * const apache_exes[] = {
|
|||
#define APACHE_IN_SIG 15 /* SIGTERM */
|
||||
#define APACHE_OUT_SIG 28 /* SIGWINCH */
|
||||
|
||||
static const char * const nginx_exes[] = {
|
||||
"/usr/sbin/nginx",
|
||||
"/usr/local/nginx/sbin/nginx",
|
||||
0
|
||||
};
|
||||
#define NGINX_IN_SIG 15 /* SIGTERM */
|
||||
#define NGINX_OUT_SIG 3 /* SIGQUIT */
|
||||
|
||||
/* PHP-FPM installs versioned binaries like /usr/sbin/php-fpm8.4, so these
|
||||
rules use prefix matching to catch any version without maintaining a list. */
|
||||
static const char * const phpfpm_exes[] = {
|
||||
"/usr/sbin/php-fpm",
|
||||
"/usr/local/sbin/php-fpm",
|
||||
0
|
||||
};
|
||||
#define PHPFPM_IN_SIG 15 /* SIGTERM */
|
||||
#define PHPFPM_OUT_SIG 3 /* SIGQUIT */
|
||||
|
||||
struct sig_rule {
|
||||
const char *const *target_exes;
|
||||
int out_sig;
|
||||
int prefix_match; /* 0 = exact, 1 = prefix */
|
||||
};
|
||||
|
||||
#define DESCENDANT_TIMEOUT_SECONDS 60
|
||||
#define BROADCAST_SCAN_DELAY_US 100000
|
||||
#define DESCENDANT_POLL_INTERVAL_US 50000
|
||||
|
|
@ -670,9 +696,9 @@ static long parse_pid(const char *s) {
|
|||
}
|
||||
|
||||
/* Iterate /proc, read each process's /proc/<pid>/exe symlink, and send
|
||||
out_sig to matching PIDs while sending in_sig to everyone else.
|
||||
Skip PID 1 (the reaper itself). */
|
||||
static void proc_remap_signal(int in_sig, int out_sig, const char *const *target_exes) {
|
||||
the first matching rule's out_sig to matching PIDs while sending in_sig
|
||||
to everyone else. Skip PID 1 (the reaper itself). */
|
||||
static void proc_remap_signal(int in_sig, const struct sig_rule *rules, int num_rules) {
|
||||
int procfd = (int)sys_openat(AT_FDCWD, "/proc", O_RDONLY | O_DIRECTORY, 0);
|
||||
#ifdef TUXREAPERD_DEBUG
|
||||
debug_write("[tuxreaperd] proc_remap_signal openat fd=");
|
||||
|
|
@ -729,23 +755,36 @@ static void proc_remap_signal(int in_sig, int out_sig, const char *const *target
|
|||
if (linklen <= 0) continue;
|
||||
linkbuf[linklen] = '\0';
|
||||
|
||||
int matched = 0;
|
||||
for (int i = 0; target_exes[i]; i++) {
|
||||
long target_len = my_strlen(target_exes[i]);
|
||||
if (linklen == target_len && my_strcmp(linkbuf, target_exes[i]) == 0) {
|
||||
matched = 1;
|
||||
int sent_sig = in_sig;
|
||||
for (int r = 0; r < num_rules; r++) {
|
||||
int matched = 0;
|
||||
for (int i = 0; rules[r].target_exes[i]; i++) {
|
||||
long target_len = my_strlen(rules[r].target_exes[i]);
|
||||
if (rules[r].prefix_match) {
|
||||
if (linklen >= target_len &&
|
||||
my_strncmp(linkbuf, rules[r].target_exes[i], target_len) == 0) {
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (linklen == target_len &&
|
||||
my_strcmp(linkbuf, rules[r].target_exes[i]) == 0) {
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
sent_sig = rules[r].out_sig;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int sent_sig = matched ? out_sig : in_sig;
|
||||
#ifdef TUXREAPERD_DEBUG
|
||||
debug_write("[tuxreaperd] scan pid=");
|
||||
debug_write_int(pid);
|
||||
debug_write(" exe=");
|
||||
debug_write(linkbuf);
|
||||
debug_write(" matched=");
|
||||
debug_write_int(matched);
|
||||
debug_write(" sig=");
|
||||
debug_write_int(sent_sig);
|
||||
debug_write("\n");
|
||||
|
|
@ -764,17 +803,15 @@ static void sleep_us(long us) {
|
|||
sys_nanosleep(&req, 0);
|
||||
}
|
||||
|
||||
static void broadcast_signal(int in_sig, int out_sig, const char *const *target_exes) {
|
||||
static void broadcast_signal(int in_sig, const struct sig_rule *rules, int num_rules) {
|
||||
#ifdef TUXREAPERD_DEBUG
|
||||
debug_write("[tuxreaperd] broadcasting in_sig=");
|
||||
debug_write_int(in_sig);
|
||||
debug_write(" out_sig=");
|
||||
debug_write_int(out_sig);
|
||||
debug_write("\n");
|
||||
#endif
|
||||
proc_remap_signal(in_sig, out_sig, target_exes);
|
||||
proc_remap_signal(in_sig, rules, num_rules);
|
||||
sleep_us(BROADCAST_SCAN_DELAY_US);
|
||||
proc_remap_signal(in_sig, out_sig, target_exes);
|
||||
proc_remap_signal(in_sig, rules, num_rules);
|
||||
}
|
||||
|
||||
static int count_descendants(void) {
|
||||
|
|
@ -872,10 +909,30 @@ static void handle_pending_signals(void) {
|
|||
debug_write_int(sig);
|
||||
debug_write("\n");
|
||||
#endif
|
||||
if (sig == APACHE_IN_SIG) {
|
||||
broadcast_signal(APACHE_IN_SIG, APACHE_OUT_SIG, apache_exes);
|
||||
if (sig == APACHE_IN_SIG || sig == NGINX_IN_SIG || sig == PHPFPM_IN_SIG) {
|
||||
struct sig_rule rules[3];
|
||||
int num_rules = 0;
|
||||
if (sig == APACHE_IN_SIG) {
|
||||
rules[num_rules].target_exes = apache_exes;
|
||||
rules[num_rules].out_sig = APACHE_OUT_SIG;
|
||||
rules[num_rules].prefix_match = 0;
|
||||
num_rules++;
|
||||
}
|
||||
if (sig == NGINX_IN_SIG) {
|
||||
rules[num_rules].target_exes = nginx_exes;
|
||||
rules[num_rules].out_sig = NGINX_OUT_SIG;
|
||||
rules[num_rules].prefix_match = 0;
|
||||
num_rules++;
|
||||
}
|
||||
if (sig == PHPFPM_IN_SIG) {
|
||||
rules[num_rules].target_exes = phpfpm_exes;
|
||||
rules[num_rules].out_sig = PHPFPM_OUT_SIG;
|
||||
rules[num_rules].prefix_match = 1;
|
||||
num_rules++;
|
||||
}
|
||||
broadcast_signal(sig, rules, num_rules);
|
||||
} else {
|
||||
broadcast_signal(sig, sig, apache_exes);
|
||||
broadcast_signal(sig, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ static void debug(const char *fmt, ...) {
|
|||
/* Apache hijacks SIGWINCH (window resize) for graceful shutdown. Yes,
|
||||
really. So when the outside world sends SIGTERM, we translate it to
|
||||
SIGWINCH for anything whose /proc/<pid>/exe smells like apache2.
|
||||
Other processes receive the original signal unchanged. */
|
||||
Nginx and PHP-FPM both use SIGQUIT for graceful shutdown, so we
|
||||
translate SIGTERM to SIGQUIT for those processes. Other processes
|
||||
receive the original signal unchanged. */
|
||||
static const char * const apache_exes[] = {
|
||||
"/usr/sbin/apache2",
|
||||
"/usr/sbin/httpd",
|
||||
|
|
@ -38,6 +40,30 @@ static const char * const apache_exes[] = {
|
|||
#define APACHE_IN_SIG SIGTERM
|
||||
#define APACHE_OUT_SIG SIGWINCH
|
||||
|
||||
static const char * const nginx_exes[] = {
|
||||
"/usr/sbin/nginx",
|
||||
"/usr/local/nginx/sbin/nginx",
|
||||
0
|
||||
};
|
||||
#define NGINX_IN_SIG SIGTERM
|
||||
#define NGINX_OUT_SIG SIGQUIT
|
||||
|
||||
/* PHP-FPM installs versioned binaries like /usr/sbin/php-fpm8.4, so these
|
||||
rules use prefix matching to catch any version without maintaining a list. */
|
||||
static const char * const phpfpm_exes[] = {
|
||||
"/usr/sbin/php-fpm",
|
||||
"/usr/local/sbin/php-fpm",
|
||||
0
|
||||
};
|
||||
#define PHPFPM_IN_SIG SIGTERM
|
||||
#define PHPFPM_OUT_SIG SIGQUIT
|
||||
|
||||
struct sig_rule {
|
||||
const char *const *target_exes;
|
||||
int out_sig;
|
||||
int prefix_match; /* 0 = exact, 1 = prefix */
|
||||
};
|
||||
|
||||
/* How long to keep the container alive after the main child exits, waiting
|
||||
for remaining descendants (e.g., Apache workers) to finish gracefully. */
|
||||
#define DESCENDANT_TIMEOUT_SECONDS 60
|
||||
|
|
@ -76,7 +102,7 @@ static void proxy_signal_handler(int sig) {
|
|||
}
|
||||
}
|
||||
|
||||
static void proc_remap_signal(int in_sig, int out_sig, const char *const *target_exes) {
|
||||
static void proc_remap_signal(int in_sig, const struct sig_rule *rules, int num_rules) {
|
||||
DIR *proc = opendir("/proc");
|
||||
if (!proc) {
|
||||
debug("[tuxreaperd] opendir(/proc) failed: %s", strerror(errno));
|
||||
|
|
@ -100,18 +126,33 @@ static void proc_remap_signal(int in_sig, int out_sig, const char *const *target
|
|||
if (linklen <= 0) continue;
|
||||
linkbuf[linklen] = '\0';
|
||||
|
||||
int matched = 0;
|
||||
for (int i = 0; target_exes[i]; i++) {
|
||||
size_t target_len = strlen(target_exes[i]);
|
||||
if ((size_t)linklen == target_len && strcmp(linkbuf, target_exes[i]) == 0) {
|
||||
matched = 1;
|
||||
int sent_sig = in_sig;
|
||||
for (int r = 0; r < num_rules; r++) {
|
||||
int matched = 0;
|
||||
for (int i = 0; rules[r].target_exes[i]; i++) {
|
||||
size_t target_len = strlen(rules[r].target_exes[i]);
|
||||
if (rules[r].prefix_match) {
|
||||
if ((size_t)linklen >= target_len &&
|
||||
strncmp(linkbuf, rules[r].target_exes[i], target_len) == 0) {
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((size_t)linklen == target_len &&
|
||||
strcmp(linkbuf, rules[r].target_exes[i]) == 0) {
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
sent_sig = rules[r].out_sig;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int sent_sig = matched ? out_sig : in_sig;
|
||||
debug("[tuxreaperd] scan pid=%ld exe=%s matched=%d sending sig=%d",
|
||||
pid, linkbuf, matched, sent_sig);
|
||||
debug("[tuxreaperd] scan pid=%ld exe=%s sending sig=%d",
|
||||
pid, linkbuf, sent_sig);
|
||||
if (kill((pid_t)pid, sent_sig) < 0) {
|
||||
debug("[tuxreaperd] kill(%ld, %d) failed: %s", pid, sent_sig, strerror(errno));
|
||||
}
|
||||
|
|
@ -120,11 +161,11 @@ static void proc_remap_signal(int in_sig, int out_sig, const char *const *target
|
|||
closedir(proc);
|
||||
}
|
||||
|
||||
static void broadcast_signal(int in_sig, int out_sig, const char *const *target_exes) {
|
||||
debug("[tuxreaperd] broadcasting in_sig=%d out_sig=%d", in_sig, out_sig);
|
||||
proc_remap_signal(in_sig, out_sig, target_exes);
|
||||
static void broadcast_signal(int in_sig, const struct sig_rule *rules, int num_rules) {
|
||||
debug("[tuxreaperd] broadcasting in_sig=%d", in_sig);
|
||||
proc_remap_signal(in_sig, rules, num_rules);
|
||||
usleep(BROADCAST_SCAN_DELAY_US);
|
||||
proc_remap_signal(in_sig, out_sig, target_exes);
|
||||
proc_remap_signal(in_sig, rules, num_rules);
|
||||
}
|
||||
|
||||
static int count_descendants(void) {
|
||||
|
|
@ -156,10 +197,30 @@ static void handle_pending_signals(void) {
|
|||
if (!(pending & (sig_atomic_t)(1U << (sig - 1)))) continue;
|
||||
|
||||
debug("[tuxreaperd] handling pending signal %d", sig);
|
||||
if (sig == APACHE_IN_SIG) {
|
||||
broadcast_signal(APACHE_IN_SIG, APACHE_OUT_SIG, apache_exes);
|
||||
if (sig == APACHE_IN_SIG || sig == NGINX_IN_SIG || sig == PHPFPM_IN_SIG) {
|
||||
struct sig_rule rules[3];
|
||||
int num_rules = 0;
|
||||
if (sig == APACHE_IN_SIG) {
|
||||
rules[num_rules].target_exes = apache_exes;
|
||||
rules[num_rules].out_sig = APACHE_OUT_SIG;
|
||||
rules[num_rules].prefix_match = 0;
|
||||
num_rules++;
|
||||
}
|
||||
if (sig == NGINX_IN_SIG) {
|
||||
rules[num_rules].target_exes = nginx_exes;
|
||||
rules[num_rules].out_sig = NGINX_OUT_SIG;
|
||||
rules[num_rules].prefix_match = 0;
|
||||
num_rules++;
|
||||
}
|
||||
if (sig == PHPFPM_IN_SIG) {
|
||||
rules[num_rules].target_exes = phpfpm_exes;
|
||||
rules[num_rules].out_sig = PHPFPM_OUT_SIG;
|
||||
rules[num_rules].prefix_match = 1;
|
||||
num_rules++;
|
||||
}
|
||||
broadcast_signal(sig, rules, num_rules);
|
||||
} else {
|
||||
broadcast_signal(sig, sig, apache_exes);
|
||||
broadcast_signal(sig, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue