Skip to content

Commit

Permalink
fix process termination when svscan runs as init
Browse files Browse the repository at this point in the history
1. svscan.c: send SIGTERM twice followed by SIGKILL to terminate
   logging and then all remaining processes
2. supervise.c: Fixed termination of processes with -G option
   when no processes found in a process group
3. supervise.c: die on two SIGTERM if PPID is 1 and supervise is
   a logger
  • Loading branch information
mbhangui committed Mar 1, 2024
1 parent 5d0fae7 commit b40e480
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 39 deletions.
5 changes: 5 additions & 0 deletions daemontools-x/doc/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ Release 1.1.3-1.1 Start 27/04/2023 End 01/01/2024
19. multilog.c: BUG: fix wrong argument passed to open_read() in finish()
- 26/02/2024
20. systemd.in: replace /etc/init.d/svscan with /usr/bin/qmailctl for ExeStop
- 28/02/2024
21. svscan.c: send SIGTERM twice followed by SIGKILL to terminate logging and
then remaining processes
22. supervise.c: Fixed termination of processes with -G option when no
processes found in a process group

* Sun Apr 23 2023 17:05:00 +0000 Manvendra Bhangui <daemontools@indimail.org> 1.1.2-1.1%{?dist}
Release 1.1.2-1.1 Start 20/02/2023 End 23/04/2023
Expand Down
40 changes: 26 additions & 14 deletions daemontools-x/supervise.8
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,35 @@ fork themselves into background, which some consider bad software design.
If you want to monitor such a daemon, set the sticky bit on ./run. This
makes \fBsupervise\fR go into \fBsubreaper\fR mode using \fBprctl\fR(2)
\fBPR_SET_CHILD_SUBREAPER\fR on Linux or \fBprocctl\fR(2)
\fBPROC_REAP_ACQUIRE\fR on FreeBSD. In subpreaper mode or If the
\fBPROC_REAP_ACQUIRE\fR on FreeBSD. In subpreaper mode or when the
environment variable \fBSETPGID\fR is set, ./run will have it's process
Group ID set to the value of it's PID. Setting the process Group ID is
required to monitor ./run reliably when ./run has a command which double
forks. It is also required in such cases to make \fBsvc\fR(8) command
operate reliably for such double forked daemon/commands in ./run.
operate and control \fBsupervise\fR reliably such double forked
daemon/commands in ./run.

If the file \fIs\fR/down exists, \fBsupervise\fR(8) does not start ./run
immediately. You can use \fBsvc\fR(8) to start ./run or to give other
commands to \fBsupervise\fR(8). \fBsupervise\fR uses
\fIs\fR/\fIsupervise\fR/\/\fIcontrol\fR fifo to read these commands.

On receipt of \fBSIGTERM\fR, \fBsupervise\fR sends \fBSIGTERM\fR followed
by \fBSIGCONT\fR to its child. It uses \fBkillpg\fR(3) to send the signal
if runnning in supreaper mode or when \fBSETPGID\fR environment variable is
set. It uses \fBkill\fR(2) to send signals when not running in subreaper
mod and \fBSETPGID\fR environment variable is not set. \fBsupervise\fR
requires to \fBSIGTERM\fR signals to get terminated when started by
\fBsvscan\fR(8) running as PID 1.

if the file \fIs\fR/shutdown exists \fBsupervise\fR(8) executes
\fIshutdown\fR when asked to exit.

if the file \fIs\fR/alert exists \fBsupervise\fR(8) executes \fIalert\fR
whenever run exits. The pid of the process that exited is passed as the
first argument to \fIalert\fR. The exit value or signal (if killed by
signal) is passed as the second argument to \fIalert\fR. The third argument
is either \fIexited\fR or \fIstopped\fR / \fIsignalled\fR.
is either of the strings \fIexited\fR or \fIstopped\fR / \fIsignalled\fR.

The last argument to ./run, ./shutdown and ./alert is full path of \fIs\fR.

Expand All @@ -62,15 +71,15 @@ maintains status information in a binary format in the directory
files it needs in \fIs\fR or if another copy of \fBsupervise\fR(8) is already
running in \fIs\fR. Once \fBsupervise\fR(8) is successfully running, it will
not exit unless it is killed or specifically asked to exit. On a successful
startup \fBsupervise\fR(8) it opens the fifo
\fIs\fR/\fIsupervise\fR/\fIok\fR in O_RDONLY|O_NDELAY mode. You can use
\fBsvok\fR(8) to check whether \fBsupervise\fR(8) is successfully running.
You can use \fBsvscan\fR(8) to reliably start a collection of
\fBsupervise\fR(8) processes. \fBsvscan\fR mirrors the service directory in
/run or /var/run directory (whichever is found first). So /run/\fIs\fR will
be analogous to /service/\fIs\fR. When started by \fBsvscan\fR, error
messages printed by \fBsupervise\fR(8) will go the standard error output of
\fBsvscan\fR(8) process.
startup \fBsupervise\fR(8) opens the fifo \fIs\fR/\fIsupervise\fR/\fIok\fR
in O_RDONLY|O_NDELAY mode. You can use \fBsvok\fR(8) to check whether
\fBsupervise\fR(8) is successfully running. You can use \fBsvscan\fR(8) to
reliably start a collection of \fBsupervise\fR(8) processes. \fBsvscan\fR
mirrors the service directory in \fI/run\fR or \fI/var/run\fR directory
(whichever is found first). So /run/\fIs\fR will be analogous to
/service/\fIs\fR. When started by \fBsvscan\fR, error messages printed by
\fBsupervise\fR(8) will go the standard error output of \fBsvscan\fR(8)
process.

\fBsupervise\fR(8) can wait for another service by having a file named
\fIs\fR/\fIwait\fR. This file has two lines. The first line is time \fIt\fR
Expand Down Expand Up @@ -101,12 +110,15 @@ returns, it means \fBsupervise\fR(8) is running.
\fIs\fR/\fIsupervise\fR/\fIup\fR - clients can open this in write mode
(O_WRONLY) to test if service \fIs\fR is up. Any client that opens this
fifo will block until the service \fIs\fR is up. If write returns, it means
service \fBs\fR(8) is running.
service \fIs\fR is running.
.PP
\fBsupervise\fR logs informational, warning and error messages to
descriptor 2. Informational messages can be turned on by setting the
environment variable \fBVERBOSE\fR. Warning messages can be turned off by
setting the environment variable \fBSILENT\fR.
setting the environment variable \fBSILENT\fR. If you are using
\fBsvscan\fR for service startup, as setup for indimail-mta, you can set
environment variables for \fBsupervise\fR in
\fI/service/.svscan/variables\fR directory.

.SH SEE ALSO
svc(8),
Expand Down
30 changes: 22 additions & 8 deletions daemontools-x/supervise.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*- $Id: supervise.c,v 1.38 2024-02-09 00:31:40+05:30 Cprogrammer Exp mbhangui $ */
/*- $Id: supervise.c,v 1.39 2024-03-01 15:27:07+05:30 Cprogrammer Exp mbhangui $ */
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
Expand Down Expand Up @@ -42,6 +42,7 @@ static int flagwant = 1;
static int flagwantup = 1;
static int flagpaused; /*- defined if (pid) */
static int fddir;
static int logger = 0;
static int verbose = 0, silent = 0;
static char flagfailed;
static unsigned long scan_interval = 60;
Expand Down Expand Up @@ -125,10 +126,14 @@ do_kill(int prgrp, int *siglist, char **signame)

for (i = siglist, j = 0; *i != -1; i++, j++) {
if (grandchild || prgrp) {
if ((r = killpg(childpid, *i)) == -1 && errno != error_srch) {
strnum1[fmt_ulong(strnum1, getpid())] = 0;
strnum2[fmt_ulong(strnum2, childpid)] = 0;
strerr_warn8(warn.s, "pid ", strnum1, " killpg ", strnum2, " signal ", signame[j], ": ", &strerr_sys);
if ((r = killpg(childpid, *i)) == -1) {
if (errno == error_srch && kill(childpid, *i) == -1) {
if (errno == error_srch) {
strnum1[fmt_ulong(strnum1, getpid())] = 0;
strnum2[fmt_ulong(strnum2, childpid)] = 0;
strerr_warn8(warn.s, "pid ", strnum1, " killpg, kill ", strnum2, " signal ", signame[j], ": ", &strerr_sys);
}
}
}
if (verbose && !r) {
strnum1[fmt_ulong(strnum1, getpid())] = 0;
Expand All @@ -154,17 +159,21 @@ static void
sigterm()
{
int siglist[] = {-1, -1, -1};
char *signame[] = {0, 0};
char *signame[] = {0, 0}, *p;
static int got_term;

flagexit = 1;
if (childpid) {
p = env_get("SV_PWD");
if (p && logger && getppid() == 1 && !got_term++)
return;
flagwant = 0;
flagwantup = 1;
siglist[0] = SIGTERM;
siglist[1] = -1;
signame[0] = "SIGTERM";
do_kill(1, siglist, signame);
}
flagexit = 1;
}

void
Expand Down Expand Up @@ -1000,6 +1009,7 @@ main(int argc, char **argv)
strerr_die1x(111, "supervise: out of memory");
} else
if (argc == 3) {
logger = 1;
if (!stralloc_copys(&fatal, argv[2]) || !stralloc_catb(&fatal, "/log: supervise: fatal: ", 24))
strerr_die1x(111, "supervise: out of memory");
if (!stralloc_copys(&warn, argv[2]) || !stralloc_catb(&warn, "/log: supervise: warning: ", 26))
Expand Down Expand Up @@ -1111,13 +1121,17 @@ main(int argc, char **argv)
void
getversion_supervise_c()
{
static char *x = "$Id: supervise.c,v 1.38 2024-02-09 00:31:40+05:30 Cprogrammer Exp mbhangui $";
static char *x = "$Id: supervise.c,v 1.39 2024-03-01 15:27:07+05:30 Cprogrammer Exp mbhangui $";

x++;
}

/*
* $Log: supervise.c,v $
* Revision 1.39 2024-03-01 15:27:07+05:30 Cprogrammer
* die on two SIGTERM if PPID is 1 and supervise is a logger
* Fixed termination of processes with -G option when no processes found in a process group
*
* Revision 1.38 2024-02-09 00:31:40+05:30 Cprogrammer
* replaced chown with fchown
*
Expand Down
10 changes: 9 additions & 1 deletion daemontools-x/svscan.9
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,15 @@ require. If running as PID 1, \fBsvscan\fR will immediately restart a
\fBsupervise\fR process if it dies, rather waiting for 60 (or SCANINTERVAL)
seconds. This behaviour can be enabled as default by setting \fBSCANNOW\fR
environment variable. Setting this variable enables a signal handler for
\fBSIGCHILD\fR.
\fBSIGCHILD\fR. If running as PID 1, \fBsvscan\fR will relay \fBSIGTERM\fR
to all children when it gets \fBSIGTERM\fR. By default, it will send
\fBSIGTERM\fR twice - first immediately and second one after 30 secs
followed by \fBSIGKILL\fR after another 30 secs. The default of 30 seconds
can be changed by setting \fBKILLWAIT1\fR and \fBKILLWAIT2\fR environment
variables. Instead of \fBKILLWAIT1\fR, \fBKILLWAIT2\fR you can set
\fBKILLWAIT\fR environment variable to have second \fBSIGTERM\fR after
\fBKILLWAIT\fR/2 secs and \fBSIGKILL\fR after \fBKILLWAIT\fR secs. Setting
\fBKILLWAIT\fR overrides \fBKILLWAIT1\fR and \fBKILLWAIT2\fR.

If \fBINITCMD\fR environment variable is defined and set to an empty string
and if \fI@servicedir@\fR/.\fIsvscan\fR/\fIrun\fR exists and has the
Expand Down
Loading

0 comments on commit b40e480

Please sign in to comment.