diff --git a/rts.tests/supervise-orphanage.exp b/rts.tests/supervise-orphanage.exp new file mode 100644 index 0000000..6380a69 --- /dev/null +++ b/rts.tests/supervise-orphanage.exp @@ -0,0 +1,7 @@ +--- supervise properly runs an orphanage +the first run +test.sv: up (pid x) x seconds, normally down, running +Caught TERM +test.sv: up (pid x) x seconds, normally down, orphanage +the second run +Caught HUP diff --git a/rts.tests/supervise-orphanage.sh b/rts.tests/supervise-orphanage.sh new file mode 100644 index 0000000..97b945d --- /dev/null +++ b/rts.tests/supervise-orphanage.sh @@ -0,0 +1,43 @@ +echo '--- supervise properly runs an orphanage' +catexe test.sv/run <output & +mv run2 run +echo the first run +exec ../../sleeper +EOF +catexe test.sv/run2 </dev/null 2>&1 +do + sleep 1 +done +if svok test.sv +then + svc -u test.sv + while [ -e test.sv/run2 ] + do + sleep 1 + done + svstat test.sv | filter_svstat + svc -t test.sv + until svstat test.sv | grep -q orphanage + do + sleep 1 + done + svstat test.sv | filter_svstat + cat test.sv/output + svc -+h test.sv + wait + cat test.sv/output +else + echo "This test fails on older Unix systems" + echo "(everything which is not Linux or FreeBSD)" + echo "as they have no subprocess reapers" +fi +rm test.sv/orphanage diff --git a/subreaper.c b/subreaper.c new file mode 100644 index 0000000..6d52d2d --- /dev/null +++ b/subreaper.c @@ -0,0 +1,34 @@ +#include "strerr.h" + +#ifdef __linux__ +#include + +#ifdef PR_SET_CHILD_SUBREAPER +#define HAS_SUBREAPER 1 + +extern int set_subreaper(void) +{ + return prctl(PR_SET_CHILD_SUBREAPER,1,0,0,0); +} + +#endif +#endif + +#ifdef __FreeBSD__ +#include +#ifdef PROC_REAP_ACQUIRE +#define HAS_SUBREAPER 1 + +extern int set_subreaper(void) +{ + return procctl(P_PID,0,PROC_REAP_ACQUIRE,NULL); +} +#endif +#endif + +#ifndef HAS_SUBREAPER +extern int set_subreaper(void) +{ + return -1; +} +#endif diff --git a/subreaper.h b/subreaper.h new file mode 100644 index 0000000..ce790b0 --- /dev/null +++ b/subreaper.h @@ -0,0 +1,8 @@ +/* Public domain. */ + +#ifndef SUBREAPER_H +#define SUBREAPER_H + +extern int set_subreaper(void); + +#endif diff --git a/supervise.8.in b/supervise.8.in index ded8fbc..6e0b18f 100644 --- a/supervise.8.in +++ b/supervise.8.in @@ -88,6 +88,14 @@ The fourth is the exit code or kill signal number, or .B 0 on start. +If the file +.IB s /orphanage +exists, +.B supervise +will wait until all ancestors of the supervised process have also exited, +before it will consider the process stopped. In the meantime, the process +is considered to be in an "orphanage" state. + .B supervise maintains status information in a binary format inside the directory .IR s\fB/supervise , diff --git a/supervise.c b/supervise.c index a792f66..6aa96bb 100644 --- a/supervise.c +++ b/supervise.c @@ -17,6 +17,7 @@ #include "iopause.h" #include "taia.h" #include "deepsleep.h" +#include "subreaper.h" #include "stralloc.h" #include "svpath.h" #include "svstatus.h" @@ -44,6 +45,7 @@ int fdok; int fdstatus; int flagexit = 0; +int flagorphanage = 0; int firstrun = 1; const char *runscript = 0; @@ -295,11 +297,18 @@ void doit(void) for (;;) { r = wait_nohang(&wstat); if (!r) break; - if ((r == -1) && (errno != error_intr)) break; - if (r == svcmain.pid) + if (flagorphanage && r == svcmain.pid) { + svcmain.flagstatus = svstatus_orphanage; + announce(); + continue; + } + if ((r == svcmain.pid) || ((svcmain.flagstatus == svstatus_orphanage) + && (r == -1) && (errno == ECHILD))) svc = &svcmain; else if (r == svclog.pid) svc = &svclog; + else if ((r == -1) && (errno != error_intr)) + break; else continue; killpid = svc->pid; @@ -444,9 +453,16 @@ int main(int argc,char **argv) if (!svpath_init()) strerr_die3sys(111,FATAL,"unable to setup control path for ",dir); + if (stat_exists("orphanage") != 0) { + flagorphanage = 1; + if (set_subreaper()) + strerr_die2sys(111,FATAL,"could not set subreaper attribute"); + } if (stat_isexec("log") > 0) { if (pipe(logpipe) != 0) strerr_die3sys(111,FATAL,"unable to create pipe for ",dir); + else if (flagorphanage) + strerr_die2sys(111,FATAL,"orphanage and log are mutually exclusive"); svclog.flagwantup = 1; } if (stat("down",&st) != -1) { diff --git a/supervise=x b/supervise=x index 2663fb1..b68d5a1 100644 --- a/supervise=x +++ b/supervise=x @@ -1,5 +1,6 @@ deepsleep.o svpath.o +subreaper.o time.a unix.a byte.a diff --git a/svstat.c b/svstat.c index c2a91b0..83aaf5c 100644 --- a/svstat.c +++ b/svstat.c @@ -84,6 +84,7 @@ static void showstatus(const char status[19], int r, int normallyup) case svstatus_running: x = ", running"; break; case svstatus_stopping: x = ", stopping"; break; case svstatus_failed: x=", failed"; break; + case svstatus_orphanage: x=", orphanage"; break; default: x = ", status unknown"; } if (pid && (want == '\000')) diff --git a/svstatus.h b/svstatus.h index 8032ebc..a5d79d3 100644 --- a/svstatus.h +++ b/svstatus.h @@ -8,6 +8,7 @@ enum svstatus { svstatus_running, svstatus_stopping, svstatus_failed, + svstatus_orphanage, }; #endif