Package: rxvt-unicode
Version: 9.22-1+b1

|urxvtd| can be run as a per-user service under the nosh toolset's service manager and under systemd. It does not have the problems that some X11 terminal emulators have with needing a |DISPLAY| environment variable in the dæmon. But it is not perfect. In particular, one cannot use the service managers' abilities to pass the listening socket in as an already open file descriptor, using (in the systemd case for example) per-user socket and service units containing things along the lines of:

   [Service]
   RuntimeDirectory=urxvt
   RuntimeDirectoryMode=0700
   ExecStart=/usr/local/bin/urxvtd

   [Socket]
   ListenStream=%t/urxvt/daemon
   SocketMode=0600

What happens in practice is that |urxvtd| ignores the passed-in file descriptor for a listening socket and blithely opens its own, second, listening socket file descriptor.

The attached patch enhances |urxvtd| to interoperate with these service management subsystems, allowing it to be passed the listening socket in this way. It also updates |urxvtc| and the doco keeping them in step.

A possible further enhancement to the Debian package, not in this patch, would have it install pre-made per-user units for systemd in |/usr/lib/systemd/user/urxvtd.socket| and |/usr/lib/systemd/user/urxvtd.service| , fleshing out the aforegiven and allowing users to manage the urxvt dæmon with |systemctl --user|. I leave that fairly trivial exercise to someone else. I have already added automatically-made per-user service bundles to the nosh service management allowing users to manage the urxvt dæmon with |system-control --user|.

Notes:

 *

   This is neither Linux-specific nor systemd-specific, and should
   /not/ be hidden behind a systemd configuration option.  I actually
   developed and tested this on FreeBSD invoking the urxvtd service
   from the nosh per-user service manager.

 *

   The patch adds support for the |XDG_RUNTIME_DIR| environment
   variable from the Freedesktop _Base Directory Specification_, which
   enables using features like |RuntimeDirectory| and |%t| in systemd
   units like the aforegiven.

 *

   Runtime directories are by definition private to individual users
   and machines, and so when a runtime directory is used the socket
   name is fixed as ||${XDG_RUNTIME_DIR}/urxvt/|daemon| and does not
vary by host name or unnecessarily repeat the software name. (Compare the behaviour of other softwares which used to use compound
   names in |/tmp|, such as |dbus-daemon| using just
   |${XDG_RUNTIME_DIR}/bus| for example.)   Per convention the |urxvt|
   subdirectory does not have a hidden name (in contrast to
   |${HOME}/.urxvt/|).

 *

   The manual pages are updated to reflect the additional environment
   variables that are recognized, and to show which takes precedence.

--- ./src/rxvtdaemon.C.orig     2014-12-13 01:00:23.000000000 +0000
+++ ./src/rxvtdaemon.C  2018-12-22 11:01:36.726728469 +0000
@@ -40,6 +40,22 @@
 
   if (!path)
     {
+      const char * run = getenv ("XDG_RUNTIME_DIR");
+      if (run)
+       {
+         snprintf (name, PATH_MAX, "%s/urxvt", run);
+         mkdir (name, 0755);
+
+         // The Base Directory Specification guarantees that the runtime 
directory is private to both user and machine.
+         // So there is no need to differentiate amongst sockets by host name; 
making it simpler for socket units.
+         // Conventionally, subdirectories here do not use hidden names, 
either.
+         snprintf (name, PATH_MAX, "%s/urxvt/daemon", run);
+         path = name;
+       }
+    }
+
+  if (!path)
+    {
       struct utsname u;
       uname (&u);
 
--- ./src/rxvtd.C.orig  2014-12-26 22:34:13.000000000 +0000
+++ ./src/rxvtd.C       2018-12-22 11:01:36.726728469 +0000
@@ -75,13 +75,18 @@
 
   void accept_cb (ev::io &w, int revents); ev::io accept_ev;
 
-  unix_listener (const char *sockname);
+  unix_listener (int sockfd);
+  static int open (const char * sockname);
 };
 
-unix_listener::unix_listener (const char *sockname)
+unix_listener::unix_listener (int sockfd) : fd(sockfd)
 {
   accept_ev.set<unix_listener, &unix_listener::accept_cb> (this);
+  accept_ev.start (fd, ev::READ);
+}
 
+int unix_listener::open (const char * sockname)
+{
   sockaddr_un sa;
 
   if (strlen (sockname) >= sizeof(sa.sun_path))
@@ -90,6 +95,7 @@
       exit (EXIT_FAILURE);
     }
 
+  int fd;
   if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
     {
       perror ("unable to create listening socket");
@@ -120,7 +126,7 @@
       exit (EXIT_FAILURE);
     }
 
-  accept_ev.start (fd, ev::READ);
+  return fd;
 }
 
 void unix_listener::accept_cb (ev::io &w, int revents)
@@ -224,6 +230,48 @@
     return err ();
 }
 
+enum { LISTEN_SOCKET_FILENO = 3 };
+
+// This mechanism is available on operating systems other than Linux and from 
softwares other than systemd.
+static int get_pre_opened_listen_fd()
+{
+  const char * old;
+
+  const char * listen_pid (getenv ("LISTEN_PID"));
+  if (!listen_pid)
+    return -1;
+  old = listen_pid;
+  unsigned long lp(strtoul (listen_pid, const_cast<char **>(&listen_pid), 0));
+  if (*listen_pid || old == listen_pid)
+    return -1;
+
+  const int pid (getpid ());
+  if (0 > pid)
+    return -1;
+  if (static_cast<unsigned long>(pid) != lp)
+    return -1;
+
+  const char * listen_fds (getenv ("LISTEN_FDS"));
+  if (!listen_fds)
+    return -1;
+  old = listen_fds;
+  unsigned long lf(strtoul (listen_fds, const_cast<char **>(&listen_fds), 0));
+  if (*listen_fds || old == listen_fds)
+    return -1;
+
+  if (INT_MAX < lf || lf < 1)
+    return -1;
+  int n = static_cast<int>(lf);
+
+  unsetenv ("LISTEN_PID");
+  unsetenv ("LISTEN_FDS");
+  unsetenv ("LISTEN_FDNAMES");
+  for (int i(0U); i < n; ++i)
+    fcntl (LISTEN_SOCKET_FILENO + i, F_SETFD, FD_CLOEXEC);
+
+  return LISTEN_SOCKET_FILENO;
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -275,18 +323,23 @@
     if (const char *dpy = getenv ("DISPLAY"))
       displays.get (dpy ? dpy : ":0"); // move string logic into rxvt_display 
maybe?
 
-  char *sockname = rxvt_connection::unix_sockname ();
-  unix_listener l (sockname);
-
-  chdir ("/");
-
-  if (!opt_quiet)
+  int sockfd = get_pre_opened_listen_fd();
+  if (-1 == sockfd)
     {
-      printf ("rxvt-unicode daemon listening on %s.\n", sockname);
-      fflush (stdout);
+      char *sockname = rxvt_connection::unix_sockname ();
+      sockfd = unix_listener::open (sockname);
+      if (!opt_quiet)
+       rxvt_log ("%s: daemon listening on %s.\n", argv[0], sockname);
+      free (sockname);
+    } else {
+      fcntl (sockfd, F_SETFL, O_NONBLOCK);     // in case the socket unit is 
wrong
+      if (!opt_quiet)
+       rxvt_log ("%s: daemon using pre-opened listening fd %d.\n", argv[0], 
sockfd);
     }
 
-  free (sockname);
+  unix_listener l (sockfd);
+
+  chdir ("/");
 
   pid_t pid = 0;
   if (opt_fork)
--- ./doc/rxvtd.1.pod.orig      2014-12-26 22:51:35.000000000 +0000
+++ ./doc/rxvtd.1.pod   2018-12-22 11:10:29.570692003 +0000
@@ -36,8 +36,10 @@
 
 =item B<-q>, B<--quiet>
 
-Normally, B<@@RXVT_NAME@@d> outputs the message C<< rxvt-unicode daemon
-listening on <path> >> after binding to its control socket. This option
+Normally, B<@@RXVT_NAME@@d> outputs the message C<< @@RXVT_NAME@@d: daemon
+listening on <path> >> after binding to its control socket, or the
+message C<< @@RXVT_NAME@@d: daemon using pre-opened listening fd <n> >>
+if it is using an already opened socket. This option
 will suppress this message (errors and warnings will still be logged).
 
 =item B<-o>, B<--opendisplay>
@@ -98,12 +100,31 @@
 
 =over 4
 
+=item B<LISTEN_PID>, B<LISTEN_FDS>, B<LISTEN_FDNAMES>
+
+@@RXVT_NAME@@d takes an already-open file descriptor for its listening
+socket if these environment variables are set in accordance with the
+systemd conventions (which are also employed by some other tools).  In
+this case, it does not create a listening socket itself.
+
 =item B<RXVT_SOCKET>
 
-Both B<@@RXVT_NAME@@c> and B<@@RXVT_NAME@@d> use the environment
-variable F<RXVT_SOCKET> to create a listening socket and to contact
-the @@RXVT_NAME@@d, respectively. If the variable is missing then
-F<<< $HOME/.urxvt/urxvtd-I<< <nodename> >> >>> is used.
+@@RXVT_NAME@@d uses the environment variable F<RXVT_SOCKET> if
+present, to create a listening socket.  The variable must specify the
+absolute path of the local-address-family socket to create.
+
+=item B<XDG_RUNTIME_DIR>
+
+If the F<RXVT_SOCKET> variable is missing, @@RXVT_NAME@@d uses the
+environment variable F<XDG_RUNTIME_DIR> to create the listening
+socket, naming it F<<< $XDG_RUNTIME_DIR/urxvt/daemon >>>.
+
+=item B<HOME>
+
+If both of those environment variables are missing,
+F<<< $HOME/.urxvt/urxvtd-I<< <nodename> >> >>> is used, falling
+back to F<<< /tmp/.urxvt/urxvtd-I<< <nodename> >> >>> if even that environment
+variable is missing.
 
 =item B<DISPLAY>
 
--- ./doc/rxvtc.1.man.in.orig   2016-01-23 20:09:22.000000000 +0000
+++ ./doc/rxvtc.1.man.in        2018-12-22 11:10:29.596692206 +0000
@@ -185,11 +185,21 @@
 started directly.
 .IP "\fB\s-1RXVT_SOCKET\s0\fR" 4
 .IX Item "RXVT_SOCKET"
-Both @@RXVT_NAME@@c and @@RXVT_NAME@@d use the environment variable
-\&\fI\s-1RXVT_SOCKET\s0\fR to create a listening socket and to contact the
-@@RXVT_NAME@@d, respectively. If the variable is missing,
-\&\fI\f(CI$HOME\fI/.urxvt/urxvtd\-\fI<nodename>\fI\fR is used.  The variable 
must
-specify the absolute path of the socket to create.
+@@RXVT_NAME@@c uses the environment variable \&\fI\s-1RXVT_SOCKET\s0\fR, if
+present, to contact the @@RXVT_NAME@@d.  The variable must specify the
+absolute path of the local-address-family socket to connect to.
+.IP "\fB\s-1XDG_RUNTIME_DIR\s0\fR" 4
+.IX Item "XDG_RUNTIME_DIR"
+If the \&\fI\s-1RXVT_SOCKET\s0\fR variable is missing, @@RXVT_NAME@@c uses the
+environment variable \fB\s-1XDG_RUNTIME_DIR\s0\fR to contact the
+@@RXVT_NAME@@d, connecting to the socket named
+\&\fI\f(CI$XDG_RUNTIME_DIR\fI/urxvt/daemon\fR.
+.IP "\fB\s-1HOME\s0\fR" 4
+.IX Item "HOME"
+If both of those environment variables are missing,
+\&\fI\f(CI$HOME\fI/.urxvt/urxvtd\-\fI<nodename>\fI\fR is used, falling
+back to \&\fI/tmp/.urxvt/urxvtd\-\fI<nodename>\fI\fR if even that environment
+variable is missing.
 .SH "SEE ALSO"
 .IX Header "SEE ALSO"
 @@RXVT_NAME@@(7), @@RXVT_NAME@@d(1)
--- ./doc/rxvtd.1.man.in.orig   2016-01-23 20:09:22.000000000 +0000
+++ ./doc/rxvtd.1.man.in        2018-12-22 11:10:29.549693349 +0000
@@ -169,8 +169,10 @@
 options is not yet supported.
 .IP "\fB\-q\fR, \fB\-\-quiet\fR" 4
 .IX Item "-q, --quiet"
-Normally, \fB@@RXVT_NAME@@d\fR outputs the message \f(CW\*(C`rxvt\-unicode 
daemon
-listening on <path>\*(C'\fR after binding to its control socket. This option
+Normally, \fB@@RXVT_NAME@@d\fR outputs the message \f(CW\*(C`@@RXVT_NAME@@d: 
daemon
+listening on <path>\*(C'\fR after binding to its control socket, or the
+message \f(CW\*(C`@@RXVT_NAME@@d: daemon using pre-opened listening fd 
<n>\*(C'\fR
+if it is using an already opened socket. This option
 will suppress this message (errors and warnings will still be logged).
 .IP "\fB\-o\fR, \fB\-\-opendisplay\fR" 4
 .IX Item "-o, --opendisplay"
@@ -223,12 +225,30 @@
 \&\fB@@RXVT_NAME@@d\fR is killed.
 .SH "ENVIRONMENT"
 .IX Header "ENVIRONMENT"
+.IP 
"\fB\s-1LISTEN_PID\s0\fR,\fB\s-1LISTEN_FDS\s0\fR,\fB\s-1LISTEN_FDNAMES\s0\fR," 4
+.IX Item "LISTEN_PID"
+.IX Item "LISTEN_FDS"
+.IX Item "LISTEN_FDNAMES"
+@@RXVT_NAME@@d takes an already-open file descriptor for its listening
+socket if these environment variables are set in accordance with the
+systemd conventions (which are also employed by some other tools).  In
+this case, it does not create a listening socket itself.
 .IP "\fB\s-1RXVT_SOCKET\s0\fR" 4
 .IX Item "RXVT_SOCKET"
-Both \fB@@RXVT_NAME@@c\fR and \fB@@RXVT_NAME@@d\fR use the environment
-variable \fI\s-1RXVT_SOCKET\s0\fR to create a listening socket and to contact
-the @@RXVT_NAME@@d, respectively. If the variable is missing then
-\&\fI\f(CI$HOME\fI/.urxvt/urxvtd\-\fI<nodename>\fI\fR is used.
+@@RXVT_NAME@@d uses the environment variable \&\fI\s-1RXVT_SOCKET\s0\fR, if
+present, to create a listening socket.  The variable must specify the
+absolute path of the local-address-family socket to create.
+.IP "\fB\s-1XDG_RUNTIME_DIR\s0\fR" 4
+.IX Item "XDG_RUNTIME_DIR"
+If the \&\fI\s-1RXVT_SOCKET\s0\fR variable is missing, @@RXVT_NAME@@d uses the
+environment variable \fB\s-1XDG_RUNTIME_DIR\s0\fR to create the listening
+socket, naming it \&\fI\f(CI$XDG_RUNTIME_DIR\fI/urxvt/daemon\fR.
+.IP "\fB\s-1HOME\s0\fR" 4
+.IX Item "HOME"
+If both of those environment variables are missing,
+\&\fI\f(CI$HOME\fI/.urxvt/urxvtd\-\fI<nodename>\fI\fR is used, falling
+back to \&\fI/tmp/.urxvt/urxvtd\-\fI<nodename>\fI\fR if even that environment
+variable is missing.
 .IP "\fB\s-1DISPLAY\s0\fR" 4
 .IX Item "DISPLAY"
 Only used when the \f(CW\*(C`\-\-opendisplay\*(C'\fR option is specified. Must 
contain a
--- ./doc/rxvtc.1.pod.orig      2014-11-16 12:22:34.000000000 +0000
+++ ./doc/rxvtc.1.pod   2018-12-22 11:01:36.727728234 +0000
@@ -52,11 +52,23 @@
 
 =item B<RXVT_SOCKET>
 
-Both @@RXVT_NAME@@c and @@RXVT_NAME@@d use the environment variable
-F<RXVT_SOCKET> to create a listening socket and to contact the
-@@RXVT_NAME@@d, respectively. If the variable is missing,
-F<<< $HOME/.urxvt/urxvtd-I<< <nodename> >> >>> is used.  The variable must
-specify the absolute path of the socket to create.
+@@RXVT_NAME@@c uses the environment variable F<RXVT_SOCKET> if
+present, to contact the @@RXVT_NAME@@d.  The variable must specify the
+absolute path of the local-address-family socket to connect to.
+
+=item B<XDG_RUNTIME_DIR>
+
+If the F<RXVT_SOCKET> variable is missing, @@RXVT_NAME@@c uses the
+environment variable F<XDG_RUNTIME_DIR> to contact the
+@@RXVT_NAME@@d, connecting to the socket named
+F<<< $XDG_RUNTIME_DIR/urxvt/daemon >>>.
+
+=item B<HOME>
+
+If both of those environment variables are missing,
+F<<< $HOME/.urxvt/urxvtd-I<< <nodename> >> >>> is used, falling
+back to F<<< /tmp/.urxvt/urxvtd-I<< <nodename> >> >>> if even that environment
+variable is missing.
 
 =back
 

Reply via email to