Hi all,

Based on [0] and in particular:

"Despite it being against the "spirit" of systemd, this is perhaps the
cleanest and most reliable: have systemd poll whatever service the
daemon is supposed to provide. For example, if the service is starting
sshd on port 22, systemd could repeatedly try connecting to port 22,
with exponential backoff, until it succeeds. This approach requires no
modification to existing daemons, and if implemented correctly, would
have minimal cost (only at daemon start time) in cpu load and startup latency."

I added a fifo listener mode for respawn.  It will sit there waiting for
the specified fifo to become readable and it will restart the given program.
It discards all input on the fifo.

The monitoring tool does not exist yet but it can be written separately
and then simply do a dummy write to the respawn fifo.

Rate-limiting might have to happen somewhere but I'd like to keep respawn
dumb.

The number of writes do not correspond 1-1 with the number of invocations.
If the monitoring program writes too fast, the writes will most
probably be coalesced as far as the respawn tool is concerned.  So it is
likely for N writes very close together that it will trigger only 1
invocation of the program.

What do you think?

Cheers,
sin

[0] http://ewontfix.com/15/
>From bcb98c9736725eccfdaf23dfb53cbebc2d7973be Mon Sep 17 00:00:00 2001
From: sin <s...@2f30.org>
Date: Mon, 21 Apr 2014 11:48:09 +0100
Subject: [PATCH] Implement fifo listener mode for respawn

---
 respawn.1 |  8 +++++++-
 respawn.c | 44 +++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/respawn.1 b/respawn.1
index f9d68a1..05ad86b 100644
--- a/respawn.1
+++ b/respawn.1
@@ -2,7 +2,7 @@
 .SH NAME
 \fBrespawn\fR - Spawn the given command repeatedly
 .SH SYNOPSIS
-\fBrespawn\fR [\fB-d\fI N\fR] \fIcmd\fR [\fIargs...\fR]
+\fBrespawn\fR [\fB-l\fI fifo\fR] [\fB-d\fI N\fR] \fIcmd\fR [\fIargs...\fR]
 .SH DESCRIPTION
 \fBrespawn\fR spawns the given \fIcmd\fR in a new session
 repeatedly.
@@ -10,3 +10,9 @@ repeatedly.
 .TP
 \fB-d\fR
 Set the delay between invocations of \fIcmd\fR.  It defaults to 0.
+.TP
+\fB-l\fR
+Listen on the specified \fIfifo\fR for writes.  For each write
+spawn a new instance of \fIcmd\fR.  This can be used in conjunction
+with a process supervisor to restart a particular program.  The \fB-l\fR
+and \fB-d\fR options are incompatible.  All writes are discarded.
diff --git a/respawn.c b/respawn.c
index 453c534..30ca01d 100644
--- a/respawn.c
+++ b/respawn.c
@@ -1,6 +1,12 @@
 /* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
 #include <errno.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -9,20 +15,28 @@
 static void
 usage(void)
 {
-       eprintf("usage: respawn [-d N] cmd [args...]\n");
+       eprintf("usage: respawn [-l fifo] [-d N] cmd [args...]\n");
 }
 
 int
 main(int argc, char *argv[])
 {
+       char *fifo = NULL;
+       unsigned int delay = 0;
        pid_t pid;
+       char buf[BUFSIZ];
        int savederrno;
-       unsigned int delay = 0;
+       int fd;
+       ssize_t n;
+       fd_set rdfd;
 
        ARGBEGIN {
        case 'd':
                delay = estrtol(EARGF(usage()), 0);
                break;
+       case 'l':
+               fifo = EARGF(usage());
+               break;
        default:
                usage();
        } ARGEND;
@@ -30,7 +44,30 @@ main(int argc, char *argv[])
        if(argc < 1)
                usage();
 
+       if (fifo && delay > 0)
+               usage();
+
+       if (fifo) {
+               fd = open(fifo, O_RDWR | O_NONBLOCK);
+               if (fd < 0)
+                       eprintf("open %s:", fifo);
+       }
+
        while (1) {
+               if (fifo) {
+                       FD_ZERO(&rdfd);
+                       FD_SET(fd, &rdfd);
+                       n = select(fd + 1, &rdfd, NULL, NULL, NULL);
+                       if (n < 0)
+                               eprintf("select:");
+                       if (n == 0 || FD_ISSET(fd, &rdfd) == 0)
+                               continue;
+                       while ((n = read(fd, buf, sizeof(buf))) > 0)
+                               ;
+                       if (n < 0)
+                               if (errno != EAGAIN)
+                                       eprintf("read %s:", fifo);
+               }
                pid = fork();
                if (pid < 0)
                        eprintf("fork:");
@@ -47,7 +84,8 @@ main(int argc, char *argv[])
                        waitpid(pid, NULL, 0);
                        break;
                }
-               sleep(delay);
+               if (!fifo)
+                       sleep(delay);
        }
        /* not reachable */
        return EXIT_SUCCESS;
-- 
1.9.2

Reply via email to