summaryrefslogtreecommitdiffabout
path: root/src
Unidiff
Diffstat (limited to 'src') (more/less context) (show whitespace changes)
-rw-r--r--src/com_start.c10
-rw-r--r--src/genrc.858
-rw-r--r--src/genrc.c41
-rw-r--r--src/genrc.h10
-rw-r--r--src/sentinel.c190
5 files changed, 291 insertions, 18 deletions
diff --git a/src/com_start.c b/src/com_start.c
index 5744e39..3a9dffc 100644
--- a/src/com_start.c
+++ b/src/com_start.c
@@ -40,15 +40,19 @@ sigchld(int sig)
40 40
41int 41int
42timedwaitpid(pid_t pid, int *status) 42timedwaitpid(pid_t pid, int *status)
43{ 43{
44 struct timeval now, stoptime, ttw; 44 struct timeval now, stoptime, ttw;
45 int rc = -1; 45 int rc = -1;
46 SIGHANDLER oldsig; 46 struct sigaction act, oldact;
47
48 act.sa_handler = sigchld;
49 act.sa_flags = 0;
50 sigemptyset(&act.sa_mask);
51 sigaction(SIGCHLD, &act, &oldact);
47 52
48 oldsig = signal(SIGCHLD, sigchld);
49 gettimeofday(&stoptime, NULL); 53 gettimeofday(&stoptime, NULL);
50 stoptime.tv_sec += genrc_timeout; 54 stoptime.tv_sec += genrc_timeout;
51 while (1) { 55 while (1) {
52 pid_t p; 56 pid_t p;
53 57
54 p = waitpid(pid, status, WNOHANG); 58 p = waitpid(pid, status, WNOHANG);
@@ -70,13 +74,13 @@ timedwaitpid(pid_t pid, int *status)
70 system_error(errno, "select"); 74 system_error(errno, "select");
71 break; 75 break;
72 } 76 }
73 } 77 }
74 78
75 } 79 }
76 signal(SIGCHLD, oldsig); 80 sigaction(SIGCHLD, &oldact, NULL);
77 if (rc) { 81 if (rc) {
78 kill(pid, SIGKILL); 82 kill(pid, SIGKILL);
79 } 83 }
80 return rc; 84 return rc;
81} 85}
82 86
diff --git a/src/genrc.8 b/src/genrc.8
index 00522ee..959a00e 100644
--- a/src/genrc.8
+++ b/src/genrc.8
@@ -10,13 +10,13 @@
10.\" but WITHOUT ANY WARRANTY; without even the implied warranty of 10.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
11.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12.\" GNU General Public License for more details. 12.\" GNU General Public License for more details.
13.\" 13.\"
14.\" You should have received a copy of the GNU General Public License 14.\" You should have received a copy of the GNU General Public License
15.\" along with genrc. If not, see <http://www.gnu.org/licenses/>. 15.\" along with genrc. If not, see <http://www.gnu.org/licenses/>.
16.TH GENRC 8 "May 17, 2018" "GENRC" "Genrc User Manual" 16.TH GENRC 8 "May 20, 2018" "GENRC" "Genrc User Manual"
17.SH NAME 17.SH NAME
18genrc \- generic system initialization script helper 18genrc \- generic system initialization script helper
19.SH SYNOPSIS 19.SH SYNOPSIS
20.nh 20.nh
21.na 21.na
22\fBgenrc\fR\ 22\fBgenrc\fR\
@@ -33,12 +33,14 @@ genrc \- generic system initialization script helper
33 [\fB\-\-group=\fIGROUP\fR[,\fIGROUP\fR...]]\ 33 [\fB\-\-group=\fIGROUP\fR[,\fIGROUP\fR...]]\
34 [\fB\-\-help\fR]\ 34 [\fB\-\-help\fR]\
35 [\fB\-\-no\-reload\fR]\ 35 [\fB\-\-no\-reload\fR]\
36 [\fB\-\-pid\-from=\fISOURCE\fR]\ 36 [\fB\-\-pid\-from=\fISOURCE\fR]\
37 [\fB\-\-pidfile=\fIPIDFILE\fR]\ 37 [\fB\-\-pidfile=\fIPIDFILE\fR]\
38 [\fB\-\-program=\fIPROGRAM\fR]\ 38 [\fB\-\-program=\fIPROGRAM\fR]\
39 [\fB\-\-restart\-on\-exit=\fR[\fB!\fR]\fISTATUS\fR[\fB,\fISTATUS\fR...]]\
40 [\fB\-\-restart\-on\-signal=\fR[\fB!\fR]\fISIG\fR[\fB,\fISIG\fR...]]\
39 [\fB\-\-sentinel\fR]\ 41 [\fB\-\-sentinel\fR]\
40 [\fB\-\-signal\-reload=\fISIG\fR]\ 42 [\fB\-\-signal\-reload=\fISIG\fR]\
41 [\fB\-\-signal\-stop=\fISIG\fR]\ 43 [\fB\-\-signal\-stop=\fISIG\fR]\
42 [\fB\-\-timeout=\fISECONDS\fR]\ 44 [\fB\-\-timeout=\fISECONDS\fR]\
43 [\fB\-\-user=\fIUSER\fR]\ 45 [\fB\-\-user=\fIUSER\fR]\
44 [\fB\-\-usage\fR]\ 46 [\fB\-\-usage\fR]\
@@ -98,12 +100,36 @@ with the priority \fBinfo\fR and the error with the priority
98.PP 100.PP
99If the \fB\-\-create\-pidfile=\fIFILENAME\fR option is given together with 101If the \fB\-\-create\-pidfile=\fIFILENAME\fR option is given together with
100\fB\-\-sentinel\fR, the PID of the subsidiary command will be stored 102\fB\-\-sentinel\fR, the PID of the subsidiary command will be stored
101in \fIFILE\fR. The file will be unlinked after the subsidiary command 103in \fIFILE\fR. The file will be unlinked after the subsidiary command
102terminates. Unless the \fB\-\-pid\-from\fR option is given, 104terminates. Unless the \fB\-\-pid\-from\fR option is given,
103\fB\-\-pid\-from=FILE:\fIFILENAME\fR will be assumed. 105\fB\-\-pid\-from=FILE:\fIFILENAME\fR will be assumed.
106.PP
107In sentinel mode, it is possible to restart the program if it
108terminates with a specific exit code or on a specific signal. This is
109controlled by the \fB\-\-restart\-on\-exit\fR and
110\fB\-\-restart\-on\-signal\fR options. Use this feature to ensure the
111service provided by the program won't get terminated because of
112hitting a bug or encountering an unforeseen external condition. For
113example, the following two options will ensure that the program will
114be terminated only if it exits with status 0 or it is terminated by
115SIGTERM or SIGQUIT signal:
116.EX
117--restart-on-exit='!0' --restart-on-signal='!TERM,QUIT'
118.EE
119.PP
120If restarts are requested, \fBgenrc\fR will control how often it has
121to restart the program using the same algorithm as
122.B init (8).
123Namely, if the program is restarted more than 10 times within two
124minutes, \fBgenrc\fR will disable subsequent restarts for the next
1255 minutes. If the \fB\-\-create\-pidfile\fR option was used, the
126PID of the controlling \fBgenrc\fR process will be stored in the
127file during that interval. If the \fBSIGHUP\fR signal is delivered
128during the sleep interval, the sleep will be broken prematurely and
129the program restarted again.
104.SS status 130.SS status
105In \fBstatus\fR mode \fBgenrc\fR verifies if the \fICOMMAND\fR is 131In \fBstatus\fR mode \fBgenrc\fR verifies if the \fICOMMAND\fR is
106already running and outputs its status on the standard output. To this 132already running and outputs its status on the standard output. To this
107effect, it uses an abstraction called \fIPID source\fR, which allows 133effect, it uses an abstraction called \fIPID source\fR, which allows
108it to determine the PID of the program by its name of command line. 134it to determine the PID of the program by its name of command line.
109.PP 135.PP
@@ -185,15 +211,43 @@ Makes \fBreload\fR equivalent to \fBrestart\fR.
185\fB\-p\fR, \fB\-\-program=\fIPROGRAM\fR 211\fB\-p\fR, \fB\-\-program=\fIPROGRAM\fR
186Name of the program to run. 212Name of the program to run.
187.TP 213.TP
188\fB\-P\fR, \fB\-\-pid\-from=\fISOURCE\fR 214\fB\-P\fR, \fB\-\-pid\-from=\fISOURCE\fR
189Where to look for PIDs of the running programs. 215Where to look for PIDs of the running programs.
190.TP 216.TP
217\fB\-\-restart\-on\-exit=\fR[\fB!\fR]\fISTATUS\fR[\fB,\fISTATUS\fR...]
218This option takes effect when used together with
219\fB\-\-sentinel\fR. If the program terminates with one of status
220codes listed as the argument to this option, it will be immediately
221restarted. The exclamation mark at the start of the list inverts the
222set, e.g. \fB\-\-restart\-on\-exit='!0,1'\fR means restart unless the
223program exit code is 0 or 1. Note the use of quotation to prevent the
224\fB!\fR from being interpreted by the shell.
225.TP
226\fB\-\-restart\-on\-signal=\fR[\fB!\fR]\fISIG\fR[\fB,\fISIG\fR...]
227This option takes effect when used together with
228\fB\-\-sentinel\fR. If the program terminates due to receiving one of
229the signals from this list, it will be immediately restarted. Each
230\fISIG\fR is either a signal number, or a signal name, as listed in
231.BR signal (7).
232The \fBSIG\fR prefix can be omitted from the signal name. Names are
233case-insensitive. Thus, \fB1\fR, \fBHUP\fR, \fBSIGHUP\fR, and
234\fBsighup\fR all stand for the same signal.
235.sp
236The exclamation mark at the start of the list complements the signal
237set, so that e.g. \fB\-\-restart\-on\-signal='!TERM,QUIT,INT'\fR will
238restart the program unless it terminates on one of the listed signals.
239.TP
191\fB\-\-sentinel\fR 240\fB\-\-sentinel\fR
192\fIPROGRAM\fR runs in foreground; disconnect from the controlling 241\fIPROGRAM\fR runs in foreground; disconnect from the controlling
193terminal, run it and act as a sentinel. 242terminal, start it and run in background until it terminates. The
243program's stdout and stderr are sent to the syslog facility
244\fBdaemon\fR, priorities \fBinfo\fR and \fBerr\fR, correspondingly.
245
246See the options \fB\-\-restart\-on\-exit\fR and
247\fB\-\-restart\-on\-signal\fR for details on how to restart the program.
194.TP 248.TP
195\fB\-\-signal\-reload=\fISIG\fR 249\fB\-\-signal\-reload=\fISIG\fR
196Signal to send on reload (default: \fBSIGHUP\fR). Setting it to 0 is 250Signal to send on reload (default: \fBSIGHUP\fR). Setting it to 0 is
197equivalent to \fB\-\-no\-reload\fR. 251equivalent to \fB\-\-no\-reload\fR.
198.TP 252.TP
199\fB\-\-signal\-stop=\fISIG\fR 253\fB\-\-signal\-stop=\fISIG\fR
diff --git a/src/genrc.c b/src/genrc.c
index ae3070d..9052987 100644
--- a/src/genrc.c
+++ b/src/genrc.c
@@ -22,13 +22,15 @@ int genrc_verbose;
22enum { 22enum {
23 OPT_USAGE = 256, 23 OPT_USAGE = 256,
24 OPT_VERSION, 24 OPT_VERSION,
25 OPT_SIGNAL_RELOAD, 25 OPT_SIGNAL_RELOAD,
26 OPT_NO_RELOAD, 26 OPT_NO_RELOAD,
27 OPT_SIGNAL_STOP, 27 OPT_SIGNAL_STOP,
28 OPT_CREATE_PIDFILE 28 OPT_CREATE_PIDFILE,
29 OPT_RESTART_ON_EXIT,
30 OPT_RESTART_ON_SIGNAL,
29}; 31};
30 32
31struct option longopts[] = { 33struct option longopts[] = {
32 { "help", no_argument, 0, 'h' }, 34 { "help", no_argument, 0, 'h' },
33 { "usage", no_argument, 0, OPT_USAGE }, 35 { "usage", no_argument, 0, OPT_USAGE },
34 { "command", required_argument, 0, 'c' }, 36 { "command", required_argument, 0, 'c' },
@@ -42,12 +44,14 @@ struct option longopts[] = {
42 { "sentinel", no_argument, 0, 'S' }, 44 { "sentinel", no_argument, 0, 'S' },
43 { "create-pidfile", required_argument, 0, OPT_CREATE_PIDFILE }, 45 { "create-pidfile", required_argument, 0, OPT_CREATE_PIDFILE },
44 { "version", no_argument, 0, OPT_VERSION }, 46 { "version", no_argument, 0, OPT_VERSION },
45 { "verbose", no_argument, 0, 'v' }, 47 { "verbose", no_argument, 0, 'v' },
46 { "user", required_argument, 0, 'u' }, 48 { "user", required_argument, 0, 'u' },
47 { "group", required_argument, 0, 'g' }, 49 { "group", required_argument, 0, 'g' },
50 { "restart-on-exit", required_argument, 0, OPT_RESTART_ON_EXIT },
51 { "restart-on-signal", required_argument, 0, OPT_RESTART_ON_SIGNAL },
48 { NULL } 52 { NULL }
49}; 53};
50char shortopts[] = "c:hF:g:P:p:St:u:v"; 54char shortopts[] = "c:hF:g:P:p:St:u:v";
51 55
52struct sigdefn { 56struct sigdefn {
53 char const *sig_name; 57 char const *sig_name;
@@ -124,22 +128,28 @@ is_numeric_str(char const *s)
124 s++; 128 s++;
125 } 129 }
126 return 1; 130 return 1;
127} 131}
128 132
129int 133int
130sig_name_to_str(char const *s) 134str_to_int(char const *s)
131{ 135{
132 if (is_numeric_str(s)) {
133 char *end; 136 char *end;
134 unsigned long n; 137 unsigned long n;
135 errno = 0; 138 errno = 0;
136 n = strtoul(s, &end, 10); 139 n = strtoul(s, &end, 10);
137 if (errno || *end || n > UINT_MAX) 140 if (errno || *end || n > UINT_MAX)
138 return -1; 141 return -1;
139 return n; 142 return n;
143}
144
145int
146str_to_sig(char const *s)
147{
148 if (is_numeric_str(s)) {
149 return str_to_int(s);
140 } else { 150 } else {
141 struct sigdefn *sd; 151 struct sigdefn *sd;
142 152
143 for (sd = sigdefn; sd->sig_name; sd++) { 153 for (sd = sigdefn; sd->sig_name; sd++) {
144 if (s[0] == 's' || s[0] == 'S') { 154 if (s[0] == 's' || s[0] == 'S') {
145 if (strcasecmp(sd->sig_name, s) == 0) 155 if (strcasecmp(sd->sig_name, s) == 0)
@@ -180,23 +190,32 @@ char const *help_msg[] = {
180 " run with this group(s) privileges", 190 " run with this group(s) privileges",
181 "", 191 "",
182 "Additional configuration:", 192 "Additional configuration:",
183 "", 193 "",
184 " -t, --timeout=SECONDS time to wait for the program to start up or", 194 " -t, --timeout=SECONDS time to wait for the program to start up or",
185 " terminate", 195 " terminate",
186 " --sentinel PROGRAM runs in foreground; disconnect from the",
187 " controlling terminal, run it and act as a sentinel",
188 " -P, --pid-from=SOURCE where to look for PIDs of the running programs", 196 " -P, --pid-from=SOURCE where to look for PIDs of the running programs",
189 " -F, --pidfile=NAME name of the PID file", 197 " -F, --pidfile=NAME name of the PID file",
190 " (same as --pid-from=FILE:NAME)", 198 " (same as --pid-from=FILE:NAME)",
191 " --signal-reload=SIG signal to send on reload (default: SIGHUP)", 199 " --signal-reload=SIG signal to send on reload (default: SIGHUP)",
192 " setting to 0 is equivalent to --no-reload", 200 " setting to 0 is equivalent to --no-reload",
193 " --no-reload makes reload equivalent to restart", 201 " --no-reload makes reload equivalent to restart",
194 " --signal-stop=SIG signal to send in order to terminate the program", 202 " --signal-stop=SIG signal to send in order to terminate the program",
195 " (default: SIGTERM)", 203 " (default: SIGTERM)",
196 "", 204 "",
205 "Sentinel mode:",
206 "",
207 " --sentinel PROGRAM runs in foreground; disconnect from the",
208 " controlling terminal, run it and act as a sentinel",
209 " --restart-on-exit=[!]CODE[,...]",
210 " restart the program if it exits with one of the",
211 " listed status codes",
212 " --restart-on-signal=[!]SIG[,...]",
213 " restart the program if it terminates on one of the",
214 " listed signals",
215 "",
197 "Informational options:", 216 "Informational options:",
198 "", 217 "",
199 " -h, --help display this help list", 218 " -h, --help display this help list",
200 " --usage display short usage information", 219 " --usage display short usage information",
201 " --version display program version and exist", 220 " --version display program version and exist",
202 "", 221 "",
@@ -269,12 +288,14 @@ char const *usage_msg[] = {
269 "[--group GROUP[,GROUP...]]", 288 "[--group GROUP[,GROUP...]]",
270 "[--help]", 289 "[--help]",
271 "[--no-reload]", 290 "[--no-reload]",
272 "[--pid-from=SOURCE]", 291 "[--pid-from=SOURCE]",
273 "[--pidfile=PIDFILE]", 292 "[--pidfile=PIDFILE]",
274 "[--program=PROGRAM]", 293 "[--program=PROGRAM]",
294 "[--restart-on-exit=[!]CODE[,...]]",
295 "[--restart-on-signal=[!]SIG[,...]]",
275 "[--sentinel]", 296 "[--sentinel]",
276 "[--signal-reload=SIG]", 297 "[--signal-reload=SIG]",
277 "[--signal-stop=SIG]", 298 "[--signal-stop=SIG]",
278 "[--timeout=SECONDS]", 299 "[--timeout=SECONDS]",
279 "[--usage]", 300 "[--usage]",
280 "[--user=USER]", 301 "[--user=USER]",
@@ -419,12 +440,18 @@ main(int argc, char **argv)
419 case 't': 440 case 't':
420 setenv("GENRC_TIMEOUT", optarg, 1); 441 setenv("GENRC_TIMEOUT", optarg, 1);
421 break; 442 break;
422 case 'S': 443 case 'S':
423 setenv("GENRC_SENTINEL", "1", 1); 444 setenv("GENRC_SENTINEL", "1", 1);
424 break; 445 break;
446 case OPT_RESTART_ON_EXIT:
447 add_restart_condition(RESTART_ON_EXIT, optarg);
448 break;
449 case OPT_RESTART_ON_SIGNAL:
450 add_restart_condition(RESTART_ON_SIGNAL, optarg);
451 break;
425 case OPT_NO_RELOAD: 452 case OPT_NO_RELOAD:
426 no_reload = 1; 453 no_reload = 1;
427 break; 454 break;
428 case OPT_SIGNAL_RELOAD: 455 case OPT_SIGNAL_RELOAD:
429 setenv("GENRC_SIGNAL_RELOAD", optarg, 1); 456 setenv("GENRC_SIGNAL_RELOAD", optarg, 1);
430 break; 457 break;
@@ -447,21 +474,21 @@ main(int argc, char **argv)
447 if ((p = getenv("GENRC_PROGRAM")) != NULL) 474 if ((p = getenv("GENRC_PROGRAM")) != NULL)
448 genrc_program = p; 475 genrc_program = p;
449 476
450 if (no_reload) 477 if (no_reload)
451 genrc_no_reload = 1; 478 genrc_no_reload = 1;
452 else if ((p = getenv("GENRC_SIGNAL_RELOAD")) != NULL) { 479 else if ((p = getenv("GENRC_SIGNAL_RELOAD")) != NULL) {
453 genrc_signal_reload = sig_name_to_str(p); 480 genrc_signal_reload = str_to_sig(p);
454 if (genrc_signal_reload == -1) 481 if (genrc_signal_reload == -1)
455 usage_error("%s: invalid signal number", p); 482 usage_error("%s: invalid signal number", p);
456 else if (genrc_signal_reload == 0) 483 else if (genrc_signal_reload == 0)
457 genrc_no_reload = 1; 484 genrc_no_reload = 1;
458 } 485 }
459 486
460 if ((p = getenv("GENRC_SIGNAL_STOP")) != NULL) { 487 if ((p = getenv("GENRC_SIGNAL_STOP")) != NULL) {
461 genrc_signal_stop = sig_name_to_str(p); 488 genrc_signal_stop = str_to_sig(p);
462 if (genrc_signal_stop <= 0) 489 if (genrc_signal_stop <= 0)
463 usage_error("%s: invalid signal number", p); 490 usage_error("%s: invalid signal number", p);
464 } 491 }
465 492
466 if ((p = getenv("GENRC_TIMEOUT")) != NULL) { 493 if ((p = getenv("GENRC_TIMEOUT")) != NULL) {
467 char *end; 494 char *end;
diff --git a/src/genrc.h b/src/genrc.h
index 9842016..c6ee57b 100644
--- a/src/genrc.h
+++ b/src/genrc.h
@@ -60,12 +60,15 @@ void pidlist_kill(PIDLIST *plist, int sig);
60 60
61pid_t strtopid(char const *str); 61pid_t strtopid(char const *str);
62 62
63int pid_is_running(pid_t pid); 63int pid_is_running(pid_t pid);
64 64
65void runas(void); 65void runas(void);
66int str_to_sig(char const *);
67int str_to_int(char const *);
68
66 69
67enum { 70enum {
68 MATCH_REGEX, /* extended POSIX regexp match (default) */ 71 MATCH_REGEX, /* extended POSIX regexp match (default) */
69 MATCH_PCRE, /* PCRE match (not implemented) */ 72 MATCH_PCRE, /* PCRE match (not implemented) */
70 MATCH_GLOB, /* glob pattern match */ 73 MATCH_GLOB, /* glob pattern match */
71 MATCH_EXACT, /* exact match */ 74 MATCH_EXACT, /* exact match */
@@ -106,12 +109,19 @@ int match_regex(PROCSCANBUF buf, char const *arg);
106 109
107void match_pcre_init(PROCSCANBUF buf, char const *pattern); 110void match_pcre_init(PROCSCANBUF buf, char const *pattern);
108void match_pcre_free(PROCSCANBUF buf); 111void match_pcre_free(PROCSCANBUF buf);
109int match_pcre(PROCSCANBUF buf, char const *arg); 112int match_pcre(PROCSCANBUF buf, char const *arg);
110 113
111 114
115enum {
116 RESTART_ON_EXIT,
117 RESTART_ON_SIGNAL
118};
119
120void add_restart_condition(int type, char const *arg);
121
112 122
113struct genrc_pid_closure { 123struct genrc_pid_closure {
114 char const *name; 124 char const *name;
115 int (*pid)(struct genrc_pid_closure *, PIDLIST *); 125 int (*pid)(struct genrc_pid_closure *, PIDLIST *);
116}; 126};
117 127
diff --git a/src/sentinel.c b/src/sentinel.c
index 59b89cc..33d3e06 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -5,12 +5,13 @@ This is free software: you are free to change and redistribute it.
5There is NO WARRANTY, to the extent permitted by law. 5There is NO WARRANTY, to the extent permitted by law.
6*/ 6*/
7#include "genrc.h" 7#include "genrc.h"
8#include <unistd.h> 8#include <unistd.h>
9#include <fcntl.h> 9#include <fcntl.h>
10#include <syslog.h> 10#include <syslog.h>
11#include <time.h>
11 12
12static void 13static void
13xpipe(int p[2]) 14xpipe(int p[2])
14{ 15{
15 if (pipe(p)) { 16 if (pipe(p)) {
16 system_error(errno, "pipe"); 17 system_error(errno, "pipe");
@@ -28,12 +29,19 @@ write_pid_file(pid_t pid)
28 fp = fopen(genrc_create_pidfile, "w"); 29 fp = fopen(genrc_create_pidfile, "w");
29 fprintf(fp, "%lu\n", (unsigned long)pid); 30 fprintf(fp, "%lu\n", (unsigned long)pid);
30 fclose(fp); 31 fclose(fp);
31 } 32 }
32} 33}
33 34
35static void
36unlink_pid_file(void)
37{
38 if (genrc_create_pidfile)
39 unlink(genrc_create_pidfile);
40}
41
34#define LOGBUFSIZE 1024 42#define LOGBUFSIZE 1024
35 43
36struct log_buffer { 44struct log_buffer {
37 char buf[LOGBUFSIZE]; 45 char buf[LOGBUFSIZE];
38 size_t pos; 46 size_t pos;
39 int prio; 47 int prio;
@@ -65,12 +73,98 @@ log_buffer_read(int fd, struct log_buffer *lb)
65 lb->pos = 0; 73 lb->pos = 0;
66 } else 74 } else
67 lb->pos++; 75 lb->pos++;
68 } 76 }
69} 77}
70 78
79struct restart_cond {
80 struct restart_cond *next;
81 int type;
82 int negate;
83 int numc;
84 int numv[1];
85};
86
87struct restart_cond *restart_head, *restart_tail;
88
89static int
90restart_on(int type, int num)
91{
92 struct restart_cond *cond;
93
94 for (cond = restart_head; cond; cond = cond->next) {
95 if (cond->type == type) {
96 int result = cond->negate;
97 int i;
98 for (i = 0; i < cond->numc; i++) {
99 if (cond->numv[i] == num) {
100 result = !result;
101 break;
102 }
103 }
104 if (result)
105 return 1;
106 }
107 }
108 return 0;
109}
110
111typedef int (*RESTART_STON)(char const *);
112
113static RESTART_STON restart_ston[] = { str_to_int, str_to_sig };
114static char const *restart_what[] = { "exit status", "signal" };
115
116void
117add_restart_condition(int type, char const *arg)
118{
119 struct wordsplit ws;
120 size_t i;
121 int negate = 0;
122 struct restart_cond *cond;
123 RESTART_STON ston = restart_ston[type];
124
125 if (arg[0] == '!') {
126 negate = 1;
127 arg++;
128 }
129
130 ws.ws_delim = ",";
131 ws.ws_error = genrc_error;
132 if (wordsplit(arg, &ws,
133 WRDSF_NOCMD
134 | WRDSF_NOVAR
135 | WRDSF_DELIM
136 | WRDSF_ENOMEMABRT
137 | WRDSF_SHOWERR
138 | WRDSF_ERROR))
139 exit(1);
140
141 if (ws.ws_wordc == 0)
142 usage_error("empty restart condition");
143
144 cond = xmalloc(sizeof(*cond)
145 + (ws.ws_wordc - 1) * sizeof(cond->numv[0]));
146 cond->next = NULL;
147 cond->type = type;
148 cond->negate = negate;
149 cond->numc = ws.ws_wordc;
150 for (i = 0; i < ws.ws_wordc; i++) {
151 int n = ston(ws.ws_wordv[i]);
152 if (n == -1)
153 usage_error("bad %s: %s", restart_what[type],
154 ws.ws_wordv[i]);
155 cond->numv[i] = n;
156 }
157
158 if (restart_tail)
159 restart_tail->next = cond;
160 else
161 restart_head = cond;
162 restart_tail = cond;
163}
164
71void 165void
72wait_loop(pid_t child, int out, int err) 166wait_loop(pid_t child, int out, int err)
73{ 167{
74 fd_set rdset; 168 fd_set rdset;
75 int nfd = (out > err ? out : err) + 1; 169 int nfd = (out > err ? out : err) + 1;
76 struct log_buffer obuf, ebuf; 170 struct log_buffer obuf, ebuf;
@@ -80,26 +174,30 @@ wait_loop(pid_t child, int out, int err)
80 log_buffer_init(&ebuf, LOG_ERR); 174 log_buffer_init(&ebuf, LOG_ERR);
81 175
82 while (1) { 176 while (1) {
83 int rc, status; 177 int rc, status;
84 178
85 if (waitpid(child, &status, WNOHANG) == child) { 179 if (waitpid(child, &status, WNOHANG) == child) {
86 if (genrc_create_pidfile) 180 write_pid_file(getpid());
87 unlink(genrc_create_pidfile);
88 if (WIFEXITED(status)) { 181 if (WIFEXITED(status)) {
182 int code = WEXITSTATUS(status);
89 syslog(LOG_INFO, "%s exited with status %d", 183 syslog(LOG_INFO, "%s exited with status %d",
90 genrc_program, WEXITSTATUS(status)); 184 genrc_program, code);
91 _exit(WEXITSTATUS(status)); 185 if (restart_on(RESTART_ON_EXIT, code))
186 return;
92 } else if (WIFSIGNALED(status)) { 187 } else if (WIFSIGNALED(status)) {
93 char const *coremsg = ""; 188 char const *coremsg = "";
189 int sig = WTERMSIG(status);
94#ifdef WCOREDUMP 190#ifdef WCOREDUMP
95 if (WCOREDUMP(status)) 191 if (WCOREDUMP(status))
96 coremsg = " (core dumped)"; 192 coremsg = " (core dumped)";
97#endif 193#endif
98 syslog(LOG_INFO, "%s terminated on signal %d%s", 194 syslog(LOG_INFO, "%s terminated on signal %d%s",
99 genrc_program, WTERMSIG(status), coremsg); 195 genrc_program, sig, coremsg);
196 if (restart_on(RESTART_ON_SIGNAL, sig))
197 return;
100 } else if (WIFSTOPPED(status)) { 198 } else if (WIFSTOPPED(status)) {
101 syslog(LOG_INFO, "%s stopped on signal %d", 199 syslog(LOG_INFO, "%s stopped on signal %d",
102 genrc_program, WSTOPSIG(status)); 200 genrc_program, WSTOPSIG(status));
103 } else { 201 } else {
104 syslog(LOG_INFO, "%s terminated; status %d", 202 syslog(LOG_INFO, "%s terminated; status %d",
105 genrc_program, rc); 203 genrc_program, rc);
@@ -125,13 +223,14 @@ wait_loop(pid_t child, int out, int err)
125 log_buffer_read(out, &obuf); 223 log_buffer_read(out, &obuf);
126 } 224 }
127 if (FD_ISSET(err, &rdset)) { 225 if (FD_ISSET(err, &rdset)) {
128 log_buffer_read(err, &ebuf); 226 log_buffer_read(err, &ebuf);
129 } 227 }
130 } 228 }
131 _exit(1); 229 unlink_pid_file();
230 _exit(0);
132} 231}
133 232
134pid_t 233pid_t
135start_command(int p[]) 234start_command(int p[])
136{ 235{
137 int errpipe[2], outpipe[2]; 236 int errpipe[2], outpipe[2];
@@ -176,17 +275,83 @@ start_command(int p[])
176 275
177 p[0] = outpipe[0]; 276 p[0] = outpipe[0];
178 p[1] = errpipe[0]; 277 p[1] = errpipe[0];
179 return pid; 278 return pid;
180} 279}
181 280
281/* Restart rate control */
282static int volatile hup_received;
283
284static void
285sighup(int sig)
286{
287 hup_received++;
288}
289
290/* Consider the number of restarts during this interval */
291#define TESTTIME 2*60
292/* Stop respawning and go to sleep if it exceeds this number */
293#define MAXSPAWN 10
294/* Sleep that much seconds, then retry */
295#define SLEEPTIME 5*60
296
297struct ratectl {
298 time_t start_time; /* Start of the test interval */
299 unsigned failcount; /* Number of restarts done so far */
300};
301
302static void
303check_failure_rate(struct ratectl *rate)
304{
305 time_t now;
306 struct timeval start, stop, ttw;
307
308 time(&now);
309 if (rate->start_time + TESTTIME > now)
310 rate->failcount++;
311 else {
312 rate->failcount = 0;
313 rate->start_time = now;
314 }
315
316 if (rate->failcount > MAXSPAWN) {
317 syslog(LOG_NOTICE,
318 "%s respawning too fast; disabled for %d minutes",
319 genrc_program, SLEEPTIME / 60);
320
321 gettimeofday(&stop, NULL);
322 stop.tv_sec += SLEEPTIME;
323 while (1) {
324 gettimeofday(&start, NULL);
325 if (timercmp(&start, &stop, >=))
326 break;
327 timersub(&stop, &start, &ttw);
328 if (select(0, NULL, NULL, NULL, &ttw) < 0) {
329 if (errno == EINTR) {
330 if (hup_received) {
331 hup_received = 0;
332 break;
333 }
334 } else {
335 system_error(errno, "select");
336 break;
337 }
338 }
339 }
340
341 rate->failcount = 0;
342 }
343}
344
182int 345int
183sentinel(void) 346sentinel(void)
184{ 347{
185 pid_t pid; 348 pid_t pid;
186 int p[2]; 349 int p[2];
350 struct ratectl ctl;
351 struct sigaction act;
187 352
188 /* Detach from the controlling terminal */ 353 /* Detach from the controlling terminal */
189 pid = fork(); 354 pid = fork();
190 if (pid == -1) { 355 if (pid == -1) {
191 system_error(errno, "fork"); 356 system_error(errno, "fork");
192 return -1; 357 return -1;
@@ -202,14 +367,27 @@ sentinel(void)
202 exit(1); 367 exit(1);
203 } 368 }
204 if (pid) 369 if (pid)
205 _exit(0); 370 _exit(0);
206 371
207 /* Grand-child */ 372 /* Grand-child */
373 act.sa_handler = sighup;
374 act.sa_flags = 0;
375 sigemptyset(&act.sa_mask);
376 sigaction(SIGHUP, &act, NULL);
377
378 ctl.start_time = 0;
379 ctl.failcount = 0;
380 while (1) {
208 pid = start_command(p); 381 pid = start_command(p);
209 if (pid == -1) 382 if (pid == -1)
210 _exit(127); 383 _exit(127);
384 if (pid == 0)
385 break;
211 wait_loop(pid, p[0], p[1]); 386 wait_loop(pid, p[0], p[1]);
387 check_failure_rate(&ctl);
388 syslog(LOG_INFO, "restarting %s", genrc_program);
389 }
212 _exit(1); 390 _exit(1);
213} 391}
214 392
215 393

Return to:

Send suggestions and report system problems to the System administrator.