daemon,native-client-api: first shot at socket-based activation.
authorKrisztian Litkey <kli@iki.fi>
Tue, 14 Jan 2014 19:47:41 +0000 (21:47 +0200)
committerKrisztian Litkey <kli@iki.fi>
Tue, 14 Jan 2014 21:42:13 +0000 (23:42 +0200)
This patch adds support for systemd socket-based activation to the
daemon/core and to the native client API plugin. The core part of
the patch adds a new command line option (--sockets/-S) that is
used to specify the order and list of configuration variables to
which the sockets passed in are assigned to. Multiple variables can
be specified as a comma-separated list the order of which must match
the order of Listen* directives in the assocaited systemd socket
file.

The native client API part changes to plugin to use any passed in
socket for the transport, or otherwise use the configured transport
address to create one.

This needs a corresponding fix to the murphy transports to work.

configure.ac
src/Makefile.am
src/daemon/config.c
src/plugins/client-api/native/native-config.h
src/plugins/client-api/native/native-server.c

index cb037a5..770d0d1 100644 (file)
@@ -259,6 +259,25 @@ AC_SUBST(FESTIVAL_ENABLED)
 AC_SUBST(FESTIVAL_CXXFLAGS)
 AC_SUBST(FESTIVAL_LIBS)
 
+# Check if systemd socket-based activation was enabled.
+AC_ARG_ENABLE(systemd,
+              [  --enable-systemd       enable systemd socket-based activation],
+             [enable_systemd=$enableval], [enable_systemd=no])
+
+if test "$enable_systemd" = "yes"; then
+    PKG_CHECK_MODULES(SYSTEMD, libsystemd-daemon)
+else
+    AC_MSG_NOTICE([systemd support is disabled.])
+fi
+
+if test "$enable_systemd" = "yes"; then
+    AC_DEFINE([SYSTEMD_ENABLED], 1, [Enable systemd socket-based activation ?])
+fi
+
+AM_CONDITIONAL(SYSTEMD_ENABLED, [test "$enable_systemd" = "yes"])
+AC_SUBST(SYSTEMD_CFLAGS)
+AC_SUBST(SYSTEMD_LIBS)
+
 # Shave by default.
 SHAVE_INIT([build-aux], [enable])
 
@@ -285,6 +304,7 @@ echo "Extra C warnings flags: $WARNING_CFLAGS"
 echo "D-Bus support: $enable_dbus"
 echo "Sphinx support: $enable_sphinx"
 echo "Festival support: $enable_festival"
+echo "systemd socket-based activation: $enable_systemd"
 
 if test "$DUMP_LIB_FLAGS" = "yes"; then
     echo "MURPHY_COMMON:"
index 2558f8d..ef7ed70 100644 (file)
@@ -38,7 +38,8 @@ srs_daemon_CFLAGS =                           \
                $(PULSE_CFLAGS)                 \
                $(PULSE_GLIB_CFLAGS)            \
                $(MURPHY_GLIB_CFLAGS)           \
-               $(GLIB_CFLAGS)
+               $(GLIB_CFLAGS)                  \
+               $(SYSTEMD_CFLAGS)
 
 srs_daemon_LDADD =                             \
                $(MURPHY_PULSE_LIBS)            \
@@ -48,6 +49,7 @@ srs_daemon_LDADD =                            \
                $(PULSE_GLIB_LIBS)              \
                $(MURPHY_GLIB_LIBS)             \
                $(GLIB_LIBS)                    \
+               $(SYSTEMD_LIBS)                 \
                -ldl
 
 srs_daemon_LDFLAGS =                           \
index 81d0e9f..a606b09 100644 (file)
 #define _GNU_SOURCE
 #include <getopt.h>
 
+#include "srs/config.h"
+
+#ifdef SYSTEMD_ENABLED
+#    include <systemd/sd-daemon.h>
+#endif
+
 #include <murphy/common/mm.h>
 #include <murphy/common/log.h>
 
@@ -139,7 +145,10 @@ static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
            "  -f, --foreground               don't daemonize\n"
            "  -h, --help                     show help on usage\n"
            "  -V, --valgrind[=VALGRIND-PATH] try to run under valgrind\n"
-           "  -W, --valgrind-full[=VALGRIND-PATH] try to run under valgrind\n",
+#ifdef SYSTEMD_ENABLED
+           "  -S, --sockets=var1[,var2...]   set sockets in by systemd\n"
+#endif
+,
            argv0, cfg, plg);
 
     if (exit_code < 0)
@@ -149,6 +158,63 @@ static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
 }
 
 
+static int set_passed_sockets(srs_context_t *srs, const char *variables)
+{
+#ifdef SYSTEMD_ENABLED
+    const char *b, *e;
+    char        key[256], val[64];
+    int         nfd, i, n;
+    size_t      len;
+
+    nfd = sd_listen_fds(0);
+
+    if (nfd <= 0)
+        return nfd;
+
+    i = 0;
+    b = variables;
+    while (b && *b) {
+        while (*b == ',' || *b == ' ' || *b == '\t')
+            b++;
+
+        if (!*b)
+            return 0;
+
+        if (i >= nfd)
+            return 0;
+
+        if ((e = strchr(b, ',')) != NULL)
+            len = e - b;
+        else
+            len = strlen(b);
+
+        if (len >= sizeof(key)) {
+            errno = EOVERFLOW;
+            return -1;
+        }
+
+        strncpy(key, b, len);
+        key[len] = '\0';
+
+        n = snprintf(val, sizeof(val), "%d", SD_LISTEN_FDS_START + i);
+
+        if (n < 0 || n >= sizeof(val))
+            return -1;
+
+        srs_set_config(srs, key, val);
+
+        b = e;
+        i++;
+    }
+
+    return 0;
+#else
+    errno = EOPNOTSUPP;
+    return -1;
+#endif
+}
+
+
 static void config_load_plugins(srs_context_t *srs, char *plugins)
 {
     char name[PATH_MAX], *p, *n;
@@ -284,7 +350,7 @@ static void config_parse_file(srs_context_t *srs, char *path)
 void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
                           char **envp)
 {
-#   define OPTIONS "c:P:L:l:t:B:s:fvd:DhV"
+#   define OPTIONS "c:P:L:l:t:B:s:fvd:DhS:V"
     struct option options[] = {
         { "config-file"  , required_argument, NULL, 'c' },
         { "plugin-dir"   , required_argument, NULL, 'P' },
@@ -297,6 +363,7 @@ void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
         { "list-debug"   , no_argument      , NULL, 'D' },
         { "foreground"   , no_argument      , NULL, 'f' },
         { "valgrind"     , optional_argument, NULL, 'V' },
+        { "sockets"      , required_argument, NULL, 'S' },
         { "help"         , no_argument      , NULL, 'h' },
         { NULL, 0, NULL, 0 }
     };
@@ -395,6 +462,13 @@ void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
             valgrind(optarg, argc, argv, optind, saved_argc, saved_argv, envp);
             break;
 
+#ifdef SYSTEMD_ENABLED
+        case 'S':
+            SAVE_OPTARG("-S", optarg);
+            set_passed_sockets(srs, optarg);
+            break;
+#endif
+
         case 'h':
             SAVE_OPT("-h");
             help++;
index 796d039..7dafc45 100644 (file)
 
 /** Configuration key for specifying the transport address. */
 #define CONFIG_ADDRESS  "native.address"
+#define CONFIG_SOCKET   "native.socket"
 
 /** Default transport address. */
 /*#define DEFAULT_ADDRESS "unxs:@/srs/native-client"*/
 #define DEFAULT_ADDRESS "tcp4:127.0.0.1:4100"
-
+#define DEFAULT_SOCKET  -1
 
 
 #endif /* __SRS_NATIVE_CLIENT_CONFIG_H__ */
index b4e688d..2f3d069 100644 (file)
@@ -56,6 +56,7 @@
 typedef struct {
     srs_plugin_t    *self;               /* our plugin instance */
     const char      *address;            /* our transport address */
+    int              sock;               /* or existing transport socket */
     mrp_transport_t *lt;                 /* transport we listen on */
     mrp_list_hook_t  clients;            /* connected clients */
     int              next_id;            /* next client id */
@@ -429,18 +430,28 @@ static int transport_setup(server_t *s)
     mrp_sockaddr_t  addr;
     socklen_t       alen;
     const char     *type, *opt, *val;
-    int             flags;
+    int             flags, state, sock;
     void           *typemap;
 
     alen = mrp_transport_resolve(NULL, s->address, &addr, sizeof(addr), &type);
 
     if (alen < 0) {
-        mrp_log_error("Failed to resolve transport address '%s'.", s->address);
+        mrp_log_error("Failed to resolve transport address '%s'.",
+                      s->address);
         goto fail;
     }
 
-    flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_MODE_NATIVE;
-    s->lt = mrp_transport_create(srs->ml, type, &evt, s, flags);
+    flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK |  \
+        MRP_TRANSPORT_MODE_NATIVE;
+
+    if (s->sock < 0)
+        s->lt = mrp_transport_create(srs->ml, type, &evt, s, flags);
+    else {
+        state = MRP_TRANSPORT_LISTENED;
+        sock  = s->sock;
+        s->lt = mrp_transport_create_from(srs->ml, type, &sock, &evt,
+                                          s, flags, state);
+    }
 
     if (s->lt == NULL) {
         mrp_log_error("Failed to create transport for native clients.");
@@ -457,14 +468,21 @@ static int transport_setup(server_t *s)
         goto fail;
     }
 
-    if (mrp_transport_bind(s->lt, &addr, alen) &&
-        mrp_transport_listen(s->lt, 0)) {
-        mrp_log_info("Listening on transport '%s'...", s->address);
+    if (s->sock < 0) {
+        if (mrp_transport_bind(s->lt, &addr, alen) &&
+            mrp_transport_listen(s->lt, 0)) {
+            mrp_log_info("Listening on transport '%s'...", s->address);
+
+            return TRUE;
+        }
+        else
+            mrp_log_error("Failed to bind/listen transport.");
+    }
+    else {
+        mrp_log_info("Using passed in socket fd %d...", s->sock);
 
         return TRUE;
     }
-    else
-        mrp_log_error("Failed to bind/listen transport.");
 
  fail:
     if (s->lt) {
@@ -513,7 +531,12 @@ static int config_native(srs_plugin_t *plugin, srs_cfg_t *cfg)
     mrp_debug("configure native client interface plugin");
 
     s->address = srs_get_string_config(cfg, CONFIG_ADDRESS, DEFAULT_ADDRESS);
-    mrp_log_info("Using native client transport address: '%s'.", s->address);
+    s->sock    = srs_get_int32_config (cfg, CONFIG_SOCKET , DEFAULT_SOCKET);
+
+    if (s->sock < 0)
+        mrp_log_info("Using native client transport: '%s'.", s->address);
+    else
+        mrp_log_info("Using native client socket: %d.", s->sock);
 
     return TRUE;
 }