Fix #355: regression stopping a process and its group

In 91a9c83 a regression was introduced that affects the way Finit stops
a supervised process and its process group.

Instead of sending SIGTERM to the process, delegating the responsibility
to that process to inform any children it may have, Finit as of 91a9c83
sends SIGTERM to the entire process group.  For SIGKILL this is fine,
SIGKILL only runs as cleanup and as a last ditch effort if the process
doesn't respond to SIGTERM.

This regression, introduced in v3.2, directly affects services like
avahi-autoipd that have forked off children that it needs to tell to
exit cleanly before it returns. With the patch in question these
children are never allowed to complete, which in turn causes lingering
169.254 link-local addresses on interfaces.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2023-05-14 09:27:09 +02:00
parent 8a32ec5c92
commit 712f68f9da
+13 -6
View File
@@ -173,7 +173,7 @@ static int service_script_del(pid_t pid)
if (ptr->pid != pid)
continue;
dbg("Collected service %s script PID %d.", svc_ident(ptr->svc, NULL, 0), pid);
dbg("Collected service %s script PID %d, killing process group.", svc_ident(ptr->svc, NULL, 0), pid);
service_timeout_cancel(ptr->svc);
kill(-ptr->pid, SIGKILL);
TAILQ_REMOVE(&svc_assoc_list, ptr, link);
@@ -865,7 +865,7 @@ static void service_kill(svc_t *svc)
return;
}
dbg("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
dbg("%s: Sending SIGKILL to process group %d", pid_get_name(svc->pid, NULL, 0), svc->pid);
logit(LOG_CONSOLE | LOG_NOTICE, "Stopping %s[%d], sending SIGKILL ...",
svc_ident(svc, NULL, 0), svc->pid);
if (runlevel != 1)
@@ -969,9 +969,14 @@ int service_stop(svc_t *svc)
if (!svc_is_sysv(svc)) {
if (svc->pid > 1) {
/* Kill all children in the same proess group, e.g. logit */
rc = kill(-svc->pid, svc->sighalt);
dbg("kill(-%d, %d) => rc %d, errno %d", svc->pid, svc->sighalt, rc, errno);
/*
* Send SIGTERM to parent process of process group, not to the
* entire group. This gives the process time to properly stop
* and/or forward TERM to its children. If it does not respond
* in within a reasonable timeout we SIGKILL the entire group.
*/
rc = kill(svc->pid, svc->sighalt);
dbg("kill(%d, %d) => rc %d, errno %d", svc->pid, svc->sighalt, rc, errno);
/* PID lost or forking process never really started */
if (rc == -1 && (errno == ESRCH || errno == ENOENT)) {
service_cleanup(svc);
@@ -1837,6 +1842,8 @@ void service_monitor(pid_t lost, int status)
dbg("collected script %s(%d), normal exit: %d, signaled: %d, exit code: %d",
svc->state == SVC_CLEANUP_STATE ? svc->post_script : svc->pre_script,
lost, WIFEXITED(status), WIFSIGNALED(status), WEXITSTATUS(status));
/* Kill all children in the same proess group, e.g. logit */
dbg("Killing lingering children in same process group ...");
kill(-svc->pid, SIGKILL);
goto done;
@@ -1858,8 +1865,8 @@ void service_monitor(pid_t lost, int status)
logit(LOG_CONSOLE | LOG_NOTICE, "Stopped %s[%d]", svc_ident(svc, NULL, 0), lost);
}
/* Terminate any children in the same proess group, e.g. logit */
dbg("Killing lingering children in same process group ...");
kill(-svc->pid, SIGKILL);
/* Try removing PID file (in case service does not clean up after itself) */