From 8f07f87c9068df97cdbed8060e50e258763e313c Mon Sep 17 00:00:00 2001
From: fishyfriend <fishyfriend@users.noreply.github.com>
Date: Fri, 30 Nov 2018 17:20:41 -0500
Subject: [PATCH 1/2] Add support for systemd socket activation

---
 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


From 25b40933a5bba31061f94ac2c71a247292a7a1ea Mon Sep 17 00:00:00 2001
From: fishyfriend <fishyfriend@users.noreply.github.com>
Date: Mon, 3 Dec 2018 20:26:11 -0500
Subject: [PATCH 2/2] Use int for supplied file descriptor

---
 src/rxvtd.C | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/rxvtd.C b/src/rxvtd.C
index dc225c9c..ed5a7768 100644
--- a/src/rxvtd.C
+++ b/src/rxvtd.C
@@ -311,7 +311,7 @@ main (int argc, char *argv[])
       displays.get (dpy ? dpy : ":0"); // move string logic into rxvt_display maybe?
 
   char *sockname = rxvt_connection::unix_sockname ();
-  char supplied_fd = 0;
+  int supplied_fd = 0;
 
 #if ENABLE_SYSTEMD
   // optionally look for a socket file passed in by systemd.
-- 
2.18.1

