Skip to content

Lightweight signal forwarder for daemonizing programs

License

Notifications You must be signed in to change notification settings

ZentriaMC/pidproxy

Repository files navigation

pidproxy

Lightweight signal forwarder for daemonizing programs.

Inspired from supervisor project's pidproxy script

Features

  • Signal rewriting
  • Monitoring and signal processes using pidfd_open(2) & pidfd_send_signal(2)
  • Small (When compiled statically with musl libc, and packed with upx, binary is about 36 kilobytes on x86_64) - Ideal for containers.

Usage

USAGE: pidproxy [options] <path to pid file> <argv>

pidproxy supports following optional arguments:

  • -g - Whether to send signal to a process group or not.
  • -r from:to - Rewrite a signal. Can be specified multiple times to rewrite multiple signals. Example: -r 15:1
  • -U uid/username - Run target program as user. Can be either uid or username. Example: -U game
  • -G gid/group - Override the group which program should run as. Can be either gid or group name. Example: -G game
  • -E path-to-program - An external program to run after monitored process exits.

Usage within containers

Since pidproxy does not do process reaping like normal PID 1 would do, then I strongly recommend to launch pidproxy using an init process.

Here are few suggestions:

Exit hook

Following environment variables will be exposed to the external program:

  • PIDPROXY_PID_FILE - PID file initially supplied to the pidproxy.
  • PIDPROXY_EXIT_CODE - pidproxy's own exit code.
  • PIDPROXY_PID - pidproxy's PID.

Building

Run make to get statically compiled binary. You'll get best results using musl libc (Using musl-gcc wrapper or zig cc is sufficient)

To use musl-gcc, do CC=musl-gcc make
To use zig cc, do CC="zig cc -target x86_64-linux-musl" (see available targets using zig targets | jq '.libc')

Makefile also provides pidproxy.upx target, which produces packed executable using upx (packed executable is usually ~50% smaller than original).

Building against glibc

Note that building against glibc isn't generally necessary, unless your target users/groups come only from NSS (e.g systemd DynamicUser) and not via standard /etc/passwd and/or /etc/group, or you just do not want to set up musl.

Building functional static pidproxy binary with glibc might be outright impossible with systems running older systemd (< 246).

See issue comment @ htop-dev/htop for solutions - TL;DR update systemd to version >= 246 (easiest and most reliable).

GDB backtrace
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7c87bf1 in _nss_systemd_is_blocked () from /usr/lib/libnss_systemd.so.2
(gdb) bt
#0  0x00007ffff7c87bf1 in _nss_systemd_is_blocked () from /usr/lib/libnss_systemd.so.2
#1  0x00007ffff7c8e577 in _nss_systemd_getgrgid_r () from /usr/lib/libnss_systemd.so.2
#2  0x00000000004410a9 in getgrgid_r ()
#3  0x0000000000401a00 in resolve_gid (gid=<optimized out>, name=<optimized out>) at user.c:153

If upgrading or rebuilding systemd is not possible, then you are probably better off building non-static pidproxy.

Notes

  1. Monitoring processes using pidfd_open(2) is not available in Docker containers before Docker engine version 20.10, because older default seccomp profile does not allow pidfd syscalls. pidproxy will fall back to polling the PID, but that has possible PID race on busier containers, and makes pidproxy exit bit later than monitored process.
  2. Using -g flag to send a signal to the process group makes pidproxy use kill(2) instead of pidfd_send_signal(2), which has possible PID race (on busier containers).

License

GNU General Public License version 3

About

Lightweight signal forwarder for daemonizing programs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published