Support both systemd and upstart.
authorPierre-Yves Ritschard <pyr@spootnik.org>
Fri, 7 Nov 2014 12:56:21 +0000 (13:56 +0100)
committerPierre-Yves Ritschard <pyr@spootnik.org>
Tue, 11 Nov 2014 09:24:26 +0000 (10:24 +0100)
This checks appropriate environment variables. When supervised
by either upstart or systemd, collectd will not daemonize but
will signal readyness with the appropriate method.

This allows collectd to be either configured with `expect stop`
in upstart or `Type=notify` with systemd.

The rationale for this is detailed here: http://spootnik.org/entries/2014/11/09_pid-tracking-in-modern-init-systems.html

src/daemon/collectd.c

index 2e2d821..eb5404f 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <netdb.h>
 
 #include <pthread.h>
@@ -191,7 +192,7 @@ static int change_basedir (const char *orig_dir)
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
-       
+
        dirlen = strlen (dir);
        while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
                dir[--dirlen] = '\0';
@@ -270,7 +271,7 @@ static void update_kstat (void)
 static void exit_usage (int status)
 {
        printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
-                       
+
                        "Available options:\n"
                        "  General:\n"
                        "    -C <file>       Configuration file.\n"
@@ -409,6 +410,72 @@ static int pidfile_remove (void)
 } /* static int pidfile_remove (const char *file) */
 #endif /* COLLECT_DAEMON */
 
+int notify_upstart (void)
+{
+    const char  *upstart_job = getenv("UPSTART_JOB");
+
+    if (upstart_job == NULL)
+        return 0;
+
+    if (strcmp(upstart_job, "collectd") != 0)
+        return 0;
+
+    WARNING ("supervised by upstart, will stop to signal readyness");
+    raise(SIGSTOP);
+    unsetenv("UPSTART_JOB");
+
+    return 1;
+}
+
+int notify_systemd (void)
+{
+    int                  fd = -1;
+    const char          *notifysocket = getenv("NOTIFY_SOCKET");
+    struct sockaddr_un   su;
+    struct iovec         iov;
+    struct msghdr        hdr;
+
+    if (notifysocket == NULL)
+        return 0;
+
+    if ((strchr("@/", notifysocket[0])) == NULL ||
+        strlen(notifysocket) < 2)
+        return 0;
+
+    WARNING ("supervised by systemd, will signal readyness");
+    if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+        WARNING ("cannot contact systemd socket %s", notifysocket);
+        return 0;
+    }
+
+    bzero(&su, sizeof(su));
+    su.sun_family = AF_UNIX;
+    sstrncpy (su.sun_path, notifysocket, sizeof(su.sun_path));
+
+    if (notifysocket[0] == '@')
+        su.sun_path[0] = 0;
+
+    bzero(&iov, sizeof(iov));
+    iov.iov_base = "READY=1";
+    iov.iov_len = strlen("READY=1");
+
+    bzero(&hdr, sizeof(hdr));
+    hdr.msg_name = &su;
+    hdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) +
+        strlen(notifysocket);
+    hdr.msg_iov = &iov;
+    hdr.msg_iovlen = 1;
+
+    unsetenv("NOTIFY_SOCKET");
+    if (sendmsg(fd, &hdr, MSG_NOSIGNAL) < 0) {
+        WARNING ("cannot send notification to systemd");
+        close(fd);
+        return 0;
+    }
+    close(fd);
+    return 1;
+}
+
 int main (int argc, char **argv)
 {
        struct sigaction sig_int_action;
@@ -525,7 +592,11 @@ int main (int argc, char **argv)
        sig_chld_action.sa_handler = SIG_IGN;
        sigaction (SIGCHLD, &sig_chld_action, NULL);
 
-       if (daemonize)
+    /*
+     * Only daemonize if we're not being supervised
+     * by upstart or systemd.
+     */
+       if (daemonize && notify_upstart() == 0 && notify_systemd() == 0)
        {
                if ((pid = fork ()) == -1)
                {