[RFC 2] systemd socket activation support
systemd support git-daemon's --inetd mode as well.
v2: actually test...
Signed-off-by: Shawn Landden <shawn@churchofgit.com>
---
Documentation/git-daemon.txt | 25 +++++++
Makefile | 1 +
daemon.c | 35 ++++++++--
sd-daemon.c | 152 +++++++++++++++++++++++++++++++++++++++++++
sd-daemon.h | 104 +++++++++++++++++++++++++++++
5 files changed, 311 insertions(+), 6 deletions(-)
create mode 100644 sd-daemon.c
create mode 100644 sd-daemon.h
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index a69b361..898e01f 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -20,6 +20,7 @@ SYNOPSIS
[--inetd |
[--listen=<host_or_ipaddr>] [--port=<n>]
[--user=<user> [--group=<group>]]]
+ [--systemd]
[<directory>...]
DESCRIPTION
@@ -190,6 +191,12 @@ Git configuration files in that directory are readable by `<user>`.
exiting with a zero status). It can also look at the $REMOTE_ADDR
and $REMOTE_PORT environment variables to learn about the
requestor when making this decision.
+--systemd::
+ For running git-daemon under systemd(1) which will pass
+ an open connection. This is similar to --inetd, except
+ that more than one address/port can be listened to at once
+ both through systemd and through --listen, and git-daemon doesn't get
+ invoked for every connection. For more details see systemd.socket(5).
+
The external command can optionally write a single line to its
standard output to be sent to the requestor as an error message when
@@ -304,7 +311,25 @@ selectively enable/disable services per repository::
uploadpack = false
uploadarch = true
----------------------------------------------------------------
++
+systemd configuration example:
+
+# /etc/systemd/system/git-daemon.socket
+[Unit]
+Description=Git Daemon socket
+
+[Socket]
+ListenStream=9418
+
+[Install]
+WantedBy=sockets.target
+# /etc/systemd/system/git-daemon.service
+[Unit]
+Description=Git Daemon
+[Service]
+ExecStart=/usr/lib/git-core/git-daemon --systemd --reuseaddr --base-path=/var/lib /var/lib/git
+User=gitdaemon
ENVIRONMENT
-----------
diff --git a/Makefile b/Makefile
index 5f3987f..4a813b9 100644
--- a/Makefile
+++ b/Makefile
@@ -765,6 +765,7 @@ LIB_OBJS += rerere.o
LIB_OBJS += resolve-undo.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
+LIB_OBJS += sd-daemon.o
LIB_OBJS += send-pack.o
LIB_OBJS += sequencer.o
LIB_OBJS += server-info.o
diff --git a/daemon.c b/daemon.c
index 9ee2187..e809a4c 100644
--- a/daemon.c
+++ b/daemon.c
@@ -4,6 +4,7 @@
#include "run-command.h"
#include "strbuf.h"
#include "string-list.h"
+#include "sd-daemon.h"
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
@@ -29,6 +30,7 @@ static const char daemon_usage[] =
" [--access-hook=<path>]\n"
" [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
" [--detach] [--user=<user> [--group=<group>]]\n"
+" [--systemd]\n"
" [<directory>...]";
/* List of acceptable pathname prefixes */
@@ -1176,11 +1178,22 @@ static void store_pid(const char *path)
}
static int serve(struct string_list *listen_addr, int listen_port,
- struct credentials *cred)
+ struct credentials *cred, int systemd_mode)
{
struct socketlist socklist = { NULL, 0, 0 };
- socksetup(listen_addr, listen_port, &socklist);
+ if (systemd_mode) {
+ int i;
+ int n;
+
+ n = sd_listen_fds(0);
+ ALLOC_GROW(socklist.list, socklist.nr + n, socklist.alloc);
+ for (i = 0; i < n; i++)
+ socklist.list[socklist.nr++] = SD_LISTEN_FDS_START + i;
+ }
+
+ if (listen_addr->nr > 0 || !systemd_mode)
+ socksetup(listen_addr, listen_port, &socklist);
if (socklist.nr == 0)
die("unable to allocate any listen sockets on port %u",
listen_port);
@@ -1196,7 +1209,7 @@ int main(int argc, char **argv)
{
int listen_port = 0;
struct string_list listen_addr = STRING_LIST_INIT_NODUP;
- int serve_mode = 0, inetd_mode = 0;
+ int serve_mode = 0, inetd_mode = 0, systemd_mode = 0;
const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
int detach = 0;
struct credentials *cred = NULL;
@@ -1331,6 +1344,10 @@ int main(int argc, char **argv)
informative_errors = 0;
continue;
}
+ if (!strcmp(arg, "--systemd")) {
+ systemd_mode = 1;
+ continue;
+ }
if (!strcmp(arg, "--")) {
ok_paths = &argv[i+1];
break;
@@ -1349,14 +1366,20 @@ int main(int argc, char **argv)
/* avoid splitting a message in the middle */
setvbuf(stderr, NULL, _IOFBF, 4096);
- if (inetd_mode && (detach || group_name || user_name))
- die("--detach, --user and --group are incompatible with --inetd");
+ if ((inetd_mode || systemd_mode) && (detach || group_name || user_name))
+ die("--detach, --user and --group are incompatible with --inetd and --systemd");
+
+ if (systemd_mode && inetd_mode)
+ die("--inetd is incompatible with --systemd");
if (inetd_mode && (listen_port || (listen_addr.nr > 0)))
die("--listen= and --port= are incompatible with --inetd");
else if (listen_port == 0)
listen_port = DEFAULT_GIT_PORT;
+ if (systemd_mode && !sd_booted())
+ die("--systemd passed and not invoked from systemd");
+
if (group_name && !user_name)
die("--group supplied without --user");
@@ -1395,5 +1418,5 @@ int main(int argc, char **argv)
cld_argv[i+1] = argv[i];
cld_argv[argc+1] = NULL;
- return serve(&listen_addr, listen_port, cred);
+ return serve(&listen_addr, listen_port, cred, systemd_mode);
}
diff --git a/sd-daemon.c b/sd-daemon.c
new file mode 100644
index 0000000..1d43904
--- /dev/null
+++ b/sd-daemon.c
@@ -0,0 +1,152 @@
+/* stripped down version */
+/***
+ Copyright 2010 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sd-daemon.h"
+
+#if (__GNUC__ >= 4)
+# ifdef SD_EXPORT_SYMBOLS
+/* Export symbols */
+# define _sd_export_ __attribute__ ((visibility("default")))
+# else
+/* Don't export the symbols */
+# define _sd_export_ __attribute__ ((visibility("hidden")))
+# endif
+#else
+# define _sd_export_
+#endif
+
+_sd_export_ int sd_listen_fds(int unset_environment) {
+
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+ return 0;
+#else
+ int r, fd;
+ const char *e;
+ char *p = NULL;
+ unsigned long l;
+
+ e = getenv("LISTEN_PID");
+ if (!e) {
+ r = 0;
+ goto finish;
+ }
+
+ errno = 0;
+ l = strtoul(e, &p, 10);
+
+ if (errno > 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!p || p == e || *p || l <= 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ /* Is this for us? */
+ if (getpid() != (pid_t) l) {
+ r = 0;
+ goto finish;
+ }
+
+ e = getenv("LISTEN_FDS");
+ if (!e) {
+ r = 0;
+ goto finish;
+ }
+
+ errno = 0;
+ l = strtoul(e, &p, 10);
+
+ if (errno > 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!p || p == e || *p) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
+ int flags;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (flags & FD_CLOEXEC)
+ continue;
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = (int) l;
+
+finish:
+ if (unset_environment) {
+ unsetenv("LISTEN_PID");
+ unsetenv("LISTEN_FDS");
+ }
+
+ return r;
+#endif
+}
+
+_sd_export_ int sd_booted(void) {
+
+#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
+ return 0;
+#else
+ struct stat st;
+
+ /* We test whether the runtime unit file directory has been
+ * created. This takes place in mount-setup.c, so is
+ * guaranteed to happen very early during boot. */
+
+ if (lstat("/run/systemd/system/", &st) < 0)
+ return 0;
+
+ return !!S_ISDIR(st.st_mode);
+#endif
+}
+
diff --git a/sd-daemon.h b/sd-daemon.h
new file mode 100644
index 0000000..e297955
--- /dev/null
+++ b/sd-daemon.h
@@ -0,0 +1,104 @@
+/* stripped down version */
+
+#ifndef foosddaemonhfoo
+#define foosddaemonhfoo
+
+/***
+ Copyright 2010 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ Reference implementation of a few systemd related interfaces for
+ writing daemons. These interfaces are trivial to implement. To
+ simplify porting we provide this reference implementation.
+ Applications are welcome to reimplement the algorithms described
+ here if they do not want to include these two source files.
+
+ The following functionality is provided:
+
+ - Support for logging with log levels on stderr
+ - File descriptor passing for socket-based activation
+ - Daemon startup and status notification
+ - Detection of systemd boots
+
+ You may compile this with -DDISABLE_SYSTEMD to disable systemd
+ support. This makes all those calls NOPs that are directly related to
+ systemd (i.e. only sd_is_xxx() will stay useful).
+
+ Since this is drop-in code we don't want any of our symbols to be
+ exported in any case. Hence we declare hidden visibility for all of
+ them.
+
+ You may find an up-to-date version of these source files online:
+
+ http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h
+ http://cgit.freedesktop.org/systemd/systemd/plain/src/libsystemd-daemon/sd-daemon.c
+
+ This should compile on non-Linux systems, too, but with the
+ exception of the sd_is_xxx() calls all functions will become NOPs.
+
+ See sd-daemon(3) for more information.
+*/
+
+/* The first passed file descriptor is fd 3 */
+#define SD_LISTEN_FDS_START 3
+
+/*
+ Returns how many file descriptors have been passed, or a negative
+ errno code on failure. Optionally, removes the $LISTEN_FDS and
+ $LISTEN_PID file descriptors from the environment (recommended, but
+ problematic in threaded environments). If r is the return value of
+ this function you'll find the file descriptors passed as fds
+ SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative
+ errno style error code on failure. This function call ensures that
+ the FD_CLOEXEC flag is set for the passed file descriptors, to make
+ sure they are not passed on to child processes. If FD_CLOEXEC shall
+ not be set, the caller needs to unset it after this call for all file
+ descriptors that are used.
+
+ See sd_listen_fds(3) for more information.
+*/
+int sd_listen_fds(int unset_environment);
+
+/*
+ Returns > 0 if the system was booted with systemd. Returns < 0 on
+ error. Returns 0 if the system was not booted with systemd. Note
+ that all of the functions above handle non-systemd boots just
+ fine. You should NOT protect them with a call to this function. Also
+ note that this function checks whether the system, not the user
+ session is controlled by systemd. However the functions above work
+ for both user and system services.
+
+ See sd_booted(3) for more information.
+*/
+int sd_booted(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--
2.2.1.209.g41e5f3a