From eef8b877fbe2c17113764850ca8deda2d8f7e5c4 Mon Sep 17 00:00:00 2001 From: Willow Barraco Date: Mon, 6 Apr 2026 15:39:21 +0200 Subject: [PATCH] Add "stopgroup" to kill the whole process group We want to give an option for the user to signal the whole process group. This help writing shell scripts as daemons, without leaving orphan process behind. We don't want to always cleanup on orphans processes. They can be intentional, and harmless. --- man/openrc-run.8 | 3 ++ sh/start-stop-daemon.sh | 1 + sh/supervise-daemon.sh | 1 + src/shared/schedules.c | 43 +++++++++++++++-------- src/shared/schedules.h | 4 +-- src/start-stop-daemon/start-stop-daemon.c | 13 +++++-- src/supervise-daemon/supervise-daemon.c | 15 +++++--- 7 files changed, 56 insertions(+), 24 deletions(-) diff --git a/man/openrc-run.8 b/man/openrc-run.8 index 436c0305c..b47f1c212 100644 --- a/man/openrc-run.8 +++ b/man/openrc-run.8 @@ -214,6 +214,8 @@ If using then using .Pa retry is preferred. +.It Ar stopgroup +Signal the whole process group when stopping the daemon. .It Ar respawn_delay Respawn delay .Xr supervise-daemon 8 @@ -296,6 +298,7 @@ retry:stop:start: s6_log_arguments:::start secbits:start:start: stopsig:stop:start:stop +stopgroup:stop:start: supervise_daemon_args::start: timeout_down:::stop timeout_kill:::stop diff --git a/sh/start-stop-daemon.sh b/sh/start-stop-daemon.sh index b87061285..9768e5613 100644 --- a/sh/start-stop-daemon.sh +++ b/sh/start-stop-daemon.sh @@ -100,6 +100,7 @@ ssd_stop() ${procname:+--name} $procname \ ${pidfile:+--pidfile} $chroot$pidfile \ ${stopsig:+--signal} $stopsig \ + ${stopgroup+--stop-group} \ ${_progress} eend $? "Failed to stop ${name:-$RC_SVCNAME}" diff --git a/sh/supervise-daemon.sh b/sh/supervise-daemon.sh index cdb5ef6f1..331b8e908 100644 --- a/sh/supervise-daemon.sh +++ b/sh/supervise-daemon.sh @@ -58,6 +58,7 @@ supervise_start() ${command_user+--user} $command_user \ ${umask+--umask} $umask \ ${notify+--notify} $notify \ + ${stopgroup+--stop-group} \ ${supervise_daemon_args-${start_stop_daemon_args}} \ $command \ -- $command_args $command_args_foreground diff --git a/src/shared/schedules.c b/src/shared/schedules.c index a32d8d3a4..88b20f750 100644 --- a/src/shared/schedules.c +++ b/src/shared/schedules.c @@ -245,13 +245,17 @@ void parse_schedule(const char *applet, const char *string, int timeout) /* return number of processes killed, -1 on error */ int do_stop(const char *applet, const char *exec, const char *const *argv, - pid_t pid, uid_t uid,int sig, bool test, bool quiet) + pid_t pid, uid_t uid, int sig, bool group, bool test, bool quiet) { RC_PIDLIST *pids; RC_PID *pi; RC_PID *np; bool killed; int nkilled = 0; + uid_t target; + char* kind = "PID"; + if (group) + kind = "GID"; if (pid > 0) pids = rc_find_pids(NULL, NULL, 0, pid); @@ -263,24 +267,28 @@ int do_stop(const char *applet, const char *exec, const char *const *argv, LIST_FOREACH_SAFE(pi, pids, entries, np) { if (test) { - einfo("Would send signal %d to PID %d", sig, pi->pid); + einfo("Would send signal %d to %s %d", sig, kind, pi->pid); nkilled++; } else { if (sig) { - syslog(LOG_DEBUG, "Sending signal %d to PID %d", sig, pi->pid); + syslog(LOG_DEBUG, "Sending signal %d to %s %d", sig, kind, pi->pid); if (!quiet) - ebeginv("Sending signal %d to PID %d", sig, pi->pid); + ebeginv("Sending signal %d to %s %d", sig, kind, pi->pid); } errno = 0; - killed = (kill(pi->pid, sig) == 0 || + if (group) + target = -pi->pid; + else + target = pi->pid; + killed = (kill(target, sig) == 0 || errno == ESRCH ? true : false); if (!quiet) eendv(killed ? 0 : 1, - "%s: failed to send signal %d to PID %d: %s", - applet, sig, pi->pid, strerror(errno)); + "%s: failed to send signal %d to %s %d: %s", + applet, kind, sig, pi->pid, strerror(errno)); else if (!killed) - syslog(LOG_ERR, "Failed to send signal %d to PID %d: %s", - sig, pi->pid, strerror(errno)); + syslog(LOG_ERR, "Failed to send signal %d to %s %d: %s", + sig, kind, pi->pid, strerror(errno)); if (!killed) { nkilled = -1; } else { @@ -297,7 +305,7 @@ int do_stop(const char *applet, const char *exec, const char *const *argv, int run_stop_schedule(const char *applet, const char *exec, const char *const *argv, - pid_t pid, uid_t uid, + pid_t pid, uid_t uid, bool group, bool test, bool progress, bool quiet) { SCHEDULEITEM *item = TAILQ_FIRST(&schedule); @@ -317,8 +325,13 @@ int run_stop_schedule(const char *applet, syslog(LOG_DEBUG, "Will stop %s", exec); } if (pid > 0) { - einfov("Will stop PID %d", pid); - syslog(LOG_DEBUG, "Will stop PID %d", pid); + if (!group) { + einfov("Will stop PID %d", pid); + syslog(LOG_DEBUG, "Will stop PID %d", pid); + } else { + einfov("Will stop GID %d", pid); + syslog(LOG_DEBUG, "Will stop GID %d", pid); + } } if (uid) { einfov("Will stop processes owned by UID %d", uid); @@ -344,8 +357,8 @@ int run_stop_schedule(const char *applet, case SC_SIGNAL: nrunning = 0; - nkilled = do_stop(applet, exec, argv, pid, uid, item->value, test, - quiet); + nkilled = do_stop(applet, exec, argv, pid, uid, item->value, group, + test, quiet); if (nkilled == 0) { if (tkilled == 0) { if (progressed) @@ -375,7 +388,7 @@ int run_stop_schedule(const char *applet, nloops++) { if ((nrunning = do_stop(applet, exec, argv, - pid, uid, 0, test, quiet)) == 0) + pid, uid, 0, group, test, quiet)) == 0) return 0; diff --git a/src/shared/schedules.h b/src/shared/schedules.h index 43cf12f77..a3dce06b4 100644 --- a/src/shared/schedules.h +++ b/src/shared/schedules.h @@ -20,10 +20,10 @@ void free_schedulelist(void); int parse_signal(const char *applet, const char *sig); void parse_schedule(const char *applet, const char *string, int timeout); int do_stop(const char *applet, const char *exec, const char *const *argv, - pid_t pid, uid_t uid,int sig, bool test, bool quiet); + pid_t pid, uid_t uid, int sig, bool group, bool test, bool quiet); int run_stop_schedule(const char *applet, const char *exec, const char *const *argv, - pid_t pid, uid_t uid, + pid_t pid, uid_t uid, bool group, bool test, bool progress, bool quiet); #endif diff --git a/src/start-stop-daemon/start-stop-daemon.c b/src/start-stop-daemon/start-stop-daemon.c index ca846657e..72bf5db45 100644 --- a/src/start-stop-daemon/start-stop-daemon.c +++ b/src/start-stop-daemon/start-stop-daemon.c @@ -96,7 +96,7 @@ enum { const char *applet = NULL; const char *extraopts = NULL; -const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:0:1:2:3:4:" \ +const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:Gg:ik:mn:op:s:tu:r:w:x:0:1:2:3:4:" \ getoptstring_COMMON; const struct option longopts[] = { { "capabilities", 1, NULL, LONGOPT_CAPABILITIES}, @@ -115,6 +115,7 @@ const struct option longopts[] = { { "env", 1, NULL, 'e'}, { "umask", 1, NULL, 'k'}, { "group", 1, NULL, 'g'}, + { "stop-group", 0, NULL, 'G'}, { "interpreted", 0, NULL, 'i'}, { "make-pidfile", 0, NULL, 'm'}, { "name", 1, NULL, 'n'}, @@ -154,6 +155,7 @@ const char * const longopts_help[] = { "Set an environment string", "Set the umask for the daemon", "Change the process group", + "Stop the whole process group", "Match process name by interpreter", "Create a pidfile", "Match process name", @@ -343,6 +345,7 @@ int main(int argc, char **argv) char *exec_file = NULL; struct passwd *pw; struct group *gr; + bool stopgroup = false; char *line = NULL; FILE *fp; size_t len; @@ -461,6 +464,10 @@ int main(int argc, char **argv) stop = true; break; + case 'G': /* --stop-group */ + stopgroup = true; + break; + case 'N': /* --nice */ if (sscanf(optarg, "%d", &nicelevel) != 1) eerrorx("%s: invalid nice level `%s'", @@ -813,7 +820,7 @@ int main(int argc, char **argv) pid = 0; } i = run_stop_schedule(applet, exec, (const char *const *)margv, - pid, uid, test, progress, false); + pid, uid, stopgroup, test, progress, false); if (i < 0) /* We failed to stop something */ @@ -1266,7 +1273,7 @@ int main(int argc, char **argv) } else pid = 0; if (do_stop(applet, exec, (const char *const *)margv, - pid, uid, 0, test, false) > 0) + pid, uid, 0, false, test, false) > 0) alive = true; } diff --git a/src/supervise-daemon/supervise-daemon.c b/src/supervise-daemon/supervise-daemon.c index 15b18930b..2c7bcbeb0 100644 --- a/src/supervise-daemon/supervise-daemon.c +++ b/src/supervise-daemon/supervise-daemon.c @@ -85,7 +85,7 @@ enum { const char *applet = NULL; const char *extraopts = NULL; -const char getoptstring[] = "A:a:D:d:e:g:I:Kk:m:N:p:R:r:s:Su:0:1:2:3" \ +const char getoptstring[] = "A:a:D:d:e:Gg:I:Kk:m:N:p:R:r:s:Su:0:1:2:3" \ getoptstring_COMMON; const struct option longopts[] = { { "healthcheck-timer", 1, NULL, 'a'}, @@ -99,6 +99,7 @@ const struct option longopts[] = { { "chdir", 1, NULL, 'd'}, { "env", 1, NULL, 'e'}, { "group", 1, NULL, 'g'}, + { "stop-group", 0, NULL, 'G'}, { "ionice", 1, NULL, 'I'}, { "stop", 0, NULL, 'K'}, { "umask", 1, NULL, 'k'}, @@ -133,6 +134,7 @@ const char * const longopts_help[] = { "Change the PWD", "Set an environment string", "Change the process group", + "Stop the whole process group", "Set an ionice class:data when starting", "Stop daemon", "Set the umask for the daemon", @@ -168,6 +170,7 @@ static int oom_score_adj = INT_MIN; static char *changeuser, *ch_root, *ch_dir; static uid_t uid = 0; static gid_t gid = 0; +static bool stopgroup = false; static int devnull_fd = -1; static int stdin_fd; static int stdout_fd; @@ -735,7 +738,7 @@ RC_NORETURN static void supervisor(char *exec, char **argv) rc_waitpid(health_pid); syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid); nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0, - false, false, true); + stopgroup, false, false, true); if (nkilled < 0) syslog(LOG_INFO, "Unable to kill %d: %s", child_pid, strerror(errno)); @@ -747,7 +750,7 @@ RC_NORETURN static void supervisor(char *exec, char **argv) alarm(0); syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid); nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0, - false, false, true); + stopgroup, false, false, true); if (nkilled > 0) syslog(LOG_INFO, "killed %d processes", nkilled); continue; @@ -1018,6 +1021,10 @@ int main(int argc, char **argv) gid = gr->gr_gid; break; + case 'G': /* -G */ + stopgroup = true; + break; + case 'k': if (parse_mode(&numask, optarg)) eerrorx("%s: invalid mode `%s'", @@ -1225,7 +1232,7 @@ int main(int argc, char **argv) pid = get_pid(applet, pidfile); if (pid != -1) if (do_stop(applet, exec, (const char * const *)argv, pid, uid, - 0, false, true) > 0) + 0, false, false, true) > 0) eerrorx("%s: %s is already running", applet, exec); if (respawn_period > 0 && respawn_delay * respawn_max > respawn_period)