Hi,

This patch adds a command-line switch to urxvtd to enable support for
systemd socket activation. The purpose of the change is to allow
configuring urxvtd as a systemd service which starts on demand. Most
Linux distros are using systemd these days so it is widely applicable.

The patch is against latest CVS. The changes have been tested against
the latest stable release, 9.22, running under NixOS 18.09. Please see
the diffs for doc/rxvtd.1.pod and doc/systemd for an explanation of
how it works. For a primer on systemd socket activation, see
http://0pointer.de/blog/projects/socket-activation.html.

An existing solution to on-demand activation is the urxvtc wrapper
script provided by the urxvtc manpage. For systems that run urxvtd as
a systemd service, this script could be adapted as follows:

    #!/bin/sh
    urxvtc "$@"
    if [ $? -eq 2 ]; then
       systemctl --user start urxvtd.service
       urxvtc "$@"
    fi

I feel the socket activation approach is preferable for the sake of
consistency on these systems. Other similar on-demand services are
likely to be using socket activation, so the user will expect urxvtd
to be configured this way. (As an admin I'd find it easier to remember
that services A, B, and C are socket activated, than that A and C are
socket-activated whereas B has a custom wrapper script.) In addition,
since systemctl exits as soon as the service daemon is launched, it
seems possible (if unlikely) to have a race condition where the second
urxvtc invocation occurs before urxvtd has created its socket, leading
to failure.

Thanks for your consideration.

Cheers,
Jacob (fishyfriend)

---
 config.h.in                |  4 +++
 configure.ac               | 20 +++++++++++++
 doc/rxvtd.1.pod            | 16 ++++++++++
 doc/systemd/README.systemd | 25 ++++++++++++++++
 doc/systemd/urxvtd.service |  8 +++++
 doc/systemd/urxvtd.socket  |  8 +++++
 src/rxvtd.C                | 61 ++++++++++++++++++++++++++++++++++++--
 7 files changed, 140 insertions(+), 2 deletions(-)
 create mode 100644 doc/systemd/README.systemd
 create mode 100644 doc/systemd/urxvtd.service
 create mode 100644 doc/systemd/urxvtd.socket

diff --git a/config.h.in b/config.h.in
index 914d6062..3ff46a1c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -15,6 +15,10 @@
 /* Define if you want bold and italic support */
 #undef ENABLE_STYLES

+/* Define if you want to enable a command-line option for systemd socket
+   activation */
+#undef ENABLE_SYSTEMD
+
 /* Define if you want your background to use the parent window background */
 #undef ENABLE_TRANSPARENCY

diff --git a/configure.ac b/configure.ac
index 0da3b596..4e23c898 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@ support_8bitctrls=no
 support_iso14755=yes
 support_styles=yes
 support_perl=yes
+support_systemd=no
 codesets=all

 dnl# --------------------------------------------------------------------------
@@ -133,6 +134,7 @@ AC_ARG_ENABLE(everything,
        support_iso14755=no
        support_styles=no
        support_perl=no
+       support_systemd=no
        codesets=
     fi
     if test x$enableval = xyes; then
@@ -161,6 +163,7 @@ AC_ARG_ENABLE(everything,
        support_iso14755=yes
        support_styles=yes
        support_perl=yes
+       support_systemd=yes
        codesets=all
     fi
   ])
@@ -413,6 +416,12 @@ AC_ARG_WITH(terminfo,
     AC_DEFINE_UNQUOTED(RXVT_TERMINFO, "$withval", Set TERMINFO value
to the value given by configure) terminfo="$withval"
   fi])

+AC_ARG_ENABLE(systemd,
+  [  --enable-systemd        enable systemd socket activation],
+  [if test x$enableval = xyes -o x$enableval = xno; then
+    support_systemd=$enableval
+  fi])
+
 if test x$support_resources = xno; then
   if test x$support_frills = xyes || test x$support_perl = xyes; then
     AC_MSG_ERROR([--disable-resources requires --disable-frills
--disable-perl])
@@ -481,6 +490,10 @@ AC_CHECK_HEADERS( \
     wchar.h \
 )

+if test x$support_systemd = xyes; then
+   AC_CHECK_HEADER(systemd/sd-daemon.h)
+fi
+
 AC_CACHE_CHECK([for XLIB_ILLEGAL_ACCESS], rxvt_cv_xlib_illegal_access,
 [AC_COMPILE_IFELSE(
    [AC_LANG_PROGRAM([
@@ -582,6 +595,13 @@ TTY_GROUP_CHECK
 dnl# --------------------------------------------------------------------------
 dnl# now add and remove other stuff
 dnl# --------------------------------------------------------------------------
+if test x$support_systemd = xyes; then
+  RXVT_CHECK_MODULES(SYSTEMD, systemd, [
+    LIBS="$LIBS -lsystemd"
+  ], [AC_MSG_ERROR(systemd not found)])
+  AC_DEFINE(ENABLE_SYSTEMD, 1, Define if you want to enable a
command-line option for systemd socket activation)
+fi
+
 support_image=no
 if test x$support_inheritpixmap = xyes || test x$support_pixbuf = xyes; then
   support_image=yes
diff --git a/doc/rxvtd.1.pod b/doc/rxvtd.1.pod
index 08d6b2b6..56bbe2b8 100644
--- a/doc/rxvtd.1.pod
+++ b/doc/rxvtd.1.pod
@@ -81,6 +81,22 @@ listening sockets for additional protocols.
 The code is currently executed I<before> creating the normal listening
 sockets: this might change in future versions.

+=item B<-a>, B<--activate>
+
+Support systemd socket activation (requires systemd support to be enabled
+when compiling B<@@RXVT_NAME@@d>).
+
+This option causes urxvtd to check on startup whether systemd passed in a
+listening socket. If such a socket is found, urxvtd will use that
socket instead
+of creating its own. Otherwise, it will fall back to creating a new socket
+as normal.
+
+Note that the filesystem path used by systemd for the socket should match
+the value of I<RXVT_SOCKET> in the user environment (or the default socket
+path if I<RXVT_SOCKET> is unset; see "ENVIRONMENT" below).  This is to
+ensure that B<@@RXVT_NAME@@c> can find the daemon. See I<doc/systemd>
+for more information about how to configure urxvtd as a systemd service.
+
 =back

 =head1 EXAMPLES
diff --git a/doc/systemd/README.systemd b/doc/systemd/README.systemd
new file mode 100644
index 00000000..1fcd345c
--- /dev/null
+++ b/doc/systemd/README.systemd
@@ -0,0 +1,25 @@
+README for systemd support
+--------------------------
+
+rxvt-unicode includes support for running the terminal daemon (urxvtd) as a
+socket-activated service under systemd. To enable socket activation, build
+rxvt-unicode with the "--with-systemd" configure flag and invoke urxvtd with
+the "--activate"/"-a" command line option. (Consult the man page for more
+details regarding the behavior of this option.)
+
+You will also need to set up socket and service unit files for urxvtd. For most
+systemd setups, it should suffice to copy the example unit files in this
+directory into /etc/systemd/user after applying any customizations for your
+particular situation. Then run:
+
+    systemctl --user enable --now urxvtd.socket
+
+Finally, be sure that RXVT_SOCKET is set correctly in the environment of each
+user who will run urxvtc. The example unit files use the path string
+"%t/urxvtd-socket"; for user services, this evaluates to
+"$XDG_RUNTIME_DIR/urxvt-socket". Thus, in your user's shell profile, you could
+include the line
+
+    export RXVT_SOCKET="$XDG_RUNTIME_DIR/urxvt-socket"
+
+to ensure that urxvtc can find the daemon.
diff --git a/doc/systemd/urxvtd.service b/doc/systemd/urxvtd.service
new file mode 100644
index 00000000..a62bcf48
--- /dev/null
+++ b/doc/systemd/urxvtd.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=urxvt terminal daemon
+
+[Service]
+Environment=RXVT_SOCKET=%t/urxvtd-socket
+ExecStart=/usr/bin/urxvtd -o -a
+Restart=on-failure
+RestartSec=5s
diff --git a/doc/systemd/urxvtd.socket b/doc/systemd/urxvtd.socket
new file mode 100644
index 00000000..d6b61f9a
--- /dev/null
+++ b/doc/systemd/urxvtd.socket
@@ -0,0 +1,8 @@
+[Unit]
+Description=socket for urxvtd, the urxvt terminal daemon
+
+[Socket]
+ListenStream=%t/urxvtd-socket
+
+[Install]
+WantedBy=graphical-session.target
diff --git a/src/rxvtd.C b/src/rxvtd.C
index 50261c62..dc225c9c 100644
--- a/src/rxvtd.C
+++ b/src/rxvtd.C
@@ -41,6 +41,10 @@
 # include <sys/mman.h>
 #endif

+#if ENABLE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
 #include <errno.h>

 #include "rxvt.h"
@@ -75,7 +79,14 @@ struct unix_listener {

   void accept_cb (ev::io &w, int revents); ev::io accept_ev;

+  // create listener on a new socket
   unix_listener (const char *sockname);
+
+  // create listener on an existing socket
+  unix_listener (int descriptor);
+
+  // copy constructor
+  unix_listener (const unix_listener &other);
 };

 unix_listener::unix_listener (const char *sockname)
@@ -123,6 +134,23 @@ unix_listener::unix_listener (const char *sockname)
   accept_ev.start (fd, ev::READ);
 }

+unix_listener::unix_listener (int descriptor)
+{
+  accept_ev.set<unix_listener, &unix_listener::accept_cb> (this);
+
+  fd = descriptor;
+
+  fcntl (fd, F_SETFD, FD_CLOEXEC);
+  fcntl (fd, F_SETFL, O_NONBLOCK);
+
+  accept_ev.start (fd, ev::READ);
+}
+
+unix_listener::unix_listener (const unix_listener &other)
+{
+  fd = other.fd;
+}
+
 void unix_listener::accept_cb (ev::io &w, int revents)
 {
   int fd2 = accept (fd, 0, 0);
@@ -236,6 +264,9 @@ main (int argc, char *argv[])
 #if ENABLE_MLOCK
   static char opt_lock;
 #endif
+#if ENABLE_SYSTEMD
+  static char opt_activate;
+#endif

   for (int i = 1; i < argc; i++)
     {
@@ -252,6 +283,10 @@ main (int argc, char *argv[])
 #if ENABLE_PERL
       else if (!strcmp (argv [i], "-e") || !strcmp (argv [i], "--eval"))
         opt_eval = argv [++i];
+#endif
+#if ENABLE_SYSTEMD
+      else if (!strcmp (argv [i], "-a") || !strcmp (argv [i], "--activate"))
+        opt_activate = 1;
 #endif
       else
         {
@@ -276,13 +311,35 @@ main (int argc, char *argv[])
       displays.get (dpy ? dpy : ":0"); // move string logic into
rxvt_display maybe?

   char *sockname = rxvt_connection::unix_sockname ();
-  unix_listener l (sockname);
+  char supplied_fd = 0;
+
+#if ENABLE_SYSTEMD
+  // optionally look for a socket file passed in by systemd.
+  if (opt_activate)
+    {
+      int n = sd_listen_fds (1);
+      if (n > 1)
+        {
+          fputs ("too many file descriptors received, aborting.\n", stderr);
+          exit (EXIT_FAILURE);
+        }
+      else if (n == 1)
+        supplied_fd = SD_LISTEN_FDS_START + 0;
+    }
+#endif
+
+  unix_listener l = supplied_fd ?
+    unix_listener (supplied_fd) :
+    unix_listener (sockname);

   chdir ("/");

   if (!opt_quiet)
     {
-      printf ("rxvt-unicode daemon listening on %s.\n", sockname);
+      if (supplied_fd)
+        puts ("rxvt-unicode daemon listening on received socket.");
+      else
+        printf ("rxvt-unicode daemon listening on %s.\n", sockname);
       fflush (stdout);
     }

-- 
2.18.1

_______________________________________________
rxvt-unicode mailing list
[email protected]
http://lists.schmorp.de/mailman/listinfo/rxvt-unicode

Reply via email to