supervise-daemon: add support for a fifo

This will allow us to signal the daemon we are supervising as well as
send other commands to the supervisor in the future.

This fixes #227.
This commit is contained in:
William Hubbs 2018-11-30 11:45:48 -06:00
parent 7f23e0461d
commit 77262c359c
2 changed files with 61 additions and 7 deletions

View File

@ -57,6 +57,11 @@
.Ar daemon .Ar daemon
.Fl r , -chroot .Fl r , -chroot
.Ar chrootpath .Ar chrootpath
.Nm
.Fl s , -signal
.Ar signal
.Fl r , -chroot
.Ar chrootpath
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
provides a consistent method of starting, stopping and restarting provides a consistent method of starting, stopping and restarting
@ -64,11 +69,8 @@ daemons. If
.Fl K , -stop .Fl K , -stop
is not provided, then we assume we are starting the daemon. is not provided, then we assume we are starting the daemon.
.Nm .Nm
only works with daemons which do not fork. Also, it uses its own pid only works with daemons which do not fork. If your daemon has options to
file, so the daemon should not write a pid file, or the pid file passed tell it not to fork, it should be configured to not fork.
to
.Nm
should not be the one the daemon writes.
.Pp .Pp
Here are the options to specify the daemon and how it should start or stop: Here are the options to specify the daemon and how it should start or stop:
.Bl -tag -width indent .Bl -tag -width indent

View File

@ -68,7 +68,7 @@ static struct pam_conv conv = { NULL, NULL};
const char *applet = NULL; const char *applet = NULL;
const char *extraopts = NULL; const char *extraopts = NULL;
const char *getoptstring = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:Su:1:2:3" \ const char *getoptstring = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:s:Su:1:2:3" \
getoptstring_COMMON; getoptstring_COMMON;
const struct option longopts[] = { const struct option longopts[] = {
{ "healthcheck-timer", 1, NULL, 'a'}, { "healthcheck-timer", 1, NULL, 'a'},
@ -86,6 +86,7 @@ const struct option longopts[] = {
{ "respawn-period", 1, NULL, 'P'}, { "respawn-period", 1, NULL, 'P'},
{ "retry", 1, NULL, 'R'}, { "retry", 1, NULL, 'R'},
{ "chroot", 1, NULL, 'r'}, { "chroot", 1, NULL, 'r'},
{ "signal", 1, NULL, 's'},
{ "start", 0, NULL, 'S'}, { "start", 0, NULL, 'S'},
{ "user", 1, NULL, 'u'}, { "user", 1, NULL, 'u'},
{ "stdout", 1, NULL, '1'}, { "stdout", 1, NULL, '1'},
@ -109,6 +110,7 @@ const char * const longopts_help[] = {
"Set respawn time period", "Set respawn time period",
"Retry schedule to use when stopping", "Retry schedule to use when stopping",
"Chroot to this directory", "Chroot to this directory",
"Send a signal to the daemon",
"Start daemon", "Start daemon",
"Change the process user", "Change the process user",
"Redirect stdout to file", "Redirect stdout to file",
@ -142,6 +144,8 @@ static int respawn_count = 0;
static int respawn_delay = 0; static int respawn_delay = 0;
static int respawn_max = 10; static int respawn_max = 10;
static int respawn_period = 5; static int respawn_period = 5;
static char *fifopath = NULL;
static int fifo_fd = 0;
static char *pidfile = NULL; static char *pidfile = NULL;
static char *svcname = NULL; static char *svcname = NULL;
@ -439,10 +443,14 @@ static void child_process(char *exec, char **argv)
static void supervisor(char *exec, char **argv) static void supervisor(char *exec, char **argv)
{ {
FILE *fp; FILE *fp;
char buf[2048];
char cmd[2048];
int count;
int health_status; int health_status;
int healthcheck_respawn; int healthcheck_respawn;
int i; int i;
int nkilled; int nkilled;
int sig_send;
pid_t health_pid; pid_t health_pid;
pid_t wait_pid; pid_t wait_pid;
sigset_t old_signals; sigset_t old_signals;
@ -489,9 +497,29 @@ static void supervisor(char *exec, char **argv)
alarm(healthcheckdelay); alarm(healthcheckdelay);
else if (healthchecktimer) else if (healthchecktimer)
alarm(healthchecktimer); alarm(healthchecktimer);
fifo_fd = open(fifopath, O_RDONLY |O_NONBLOCK);
while (!exiting) { while (!exiting) {
healthcheck_respawn = 0; healthcheck_respawn = 0;
wait_pid = waitpid(child_pid, &i, 0); wait_pid = waitpid(child_pid, &i, WNOHANG);
memset(buf, 0, sizeof(buf));
if (fifo_fd >= 0) {
count = read(fifo_fd, buf, sizeof(buf) - 1);
if (count != -1)
buf[count] = 0;
}
if (strlen(buf) > 0) {
syslog(LOG_DEBUG, "Received %s from fifo", buf);
if (strncasecmp(buf, "sig", 3) == 0) {
if ((sscanf(buf, "%s %d", cmd, &sig_send) == 2)
&& (sig_send >= 0 && sig_send < NSIG)) {
syslog(LOG_INFO, "Sending signal %d to %d", sig_send,
child_pid);
if (kill(child_pid, sig_send) == -1)
syslog(LOG_ERR, "Unable to send signal %d to %d",
sig_send, child_pid);
}
}
}
if (do_healthcheck) { if (do_healthcheck) {
do_healthcheck = 0; do_healthcheck = 0;
alarm(0); alarm(0);
@ -578,6 +606,8 @@ static void supervisor(char *exec, char **argv)
if (pidfile && exists(pidfile)) if (pidfile && exists(pidfile))
unlink(pidfile); unlink(pidfile);
if (fifopath && exists(fifopath))
unlink(fifopath);
if (svcname) { if (svcname) {
rc_service_daemon_set(svcname, exec, (const char *const *)argv, rc_service_daemon_set(svcname, exec, (const char *const *)argv,
pidfile, false); pidfile, false);
@ -595,6 +625,7 @@ int main(int argc, char **argv)
bool start = false; bool start = false;
bool stop = false; bool stop = false;
bool reexec = false; bool reexec = false;
bool sendsig = false;
char *exec = NULL; char *exec = NULL;
char *retry = NULL; char *retry = NULL;
int sig = SIGTERM; int sig = SIGTERM;
@ -700,6 +731,10 @@ int main(int argc, char **argv)
eerrorx("Invalid respawn-period value '%s'", optarg); eerrorx("Invalid respawn-period value '%s'", optarg);
break; break;
case 's': /* --signal */
sig = parse_signal(applet, optarg);
sendsig = true;
break;
case 'S': /* --start */ case 'S': /* --start */
start = true; start = true;
break; break;
@ -818,6 +853,10 @@ int main(int argc, char **argv)
umask(numask); umask(numask);
xasprintf(&pidfile, "/var/run/supervise-%s.pid", svcname); xasprintf(&pidfile, "/var/run/supervise-%s.pid", svcname);
xasprintf(&fifopath, "%s/supervise-%s.ctl", RC_SVCDIR, svcname);
if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST)
eerrorx("%s: unable to create control fifo: %s",
applet, strerror(errno));
if (reexec) { if (reexec) {
str = rc_service_value_get(svcname, "argc"); str = rc_service_value_get(svcname, "argc");
@ -989,5 +1028,18 @@ int main(int argc, char **argv)
rc_service_mark(svcname, RC_SERVICE_STOPPED); rc_service_mark(svcname, RC_SERVICE_STOPPED);
} }
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} else if (sendsig) {
fifo_fd = open(fifopath, O_WRONLY |O_NONBLOCK);
if (fifo_fd < 0)
eerrorx("%s: unable to open control fifo %s", applet, strerror(errno));
xasprintf(&str, "sig %d", sig);
x = write(fifo_fd, str, strlen(str));
if (x == -1) {
free(tmp);
eerrorx("%s: error writing to control fifo: %s", applet,
strerror(errno));
}
free(tmp);
exit(EXIT_SUCCESS);
} }
} }