Providing XDG Runtime Directories

Introduction

The intended audience for this document are application programmers and providers of init-systems for managing features of installations of the GNU/Linux operating system and POSIX-compliant operating systems in general, who are concerned with per-user temporary file management based on the XDG Base Directory Specification, [XDG].

Many applications that follow XDG guidelines and specifications expect a directory with a location specified by environment variable XDG_RUNTIME_DIR. This directory – in the following called „rundir“ – stores files that serve purposes of process communication and synchronization; it is comparable to the directory /run, but on a per-user basis, with ownership and permissions set up so that every user of a system can have such a directory, protected from access by other users.

The existence of such a directory, requirements to this directory, management of the directory’s lifecycle and its denomination by the content of a user’s XDG_RUNTIME_DIR environment variable are mandated by [XDG].

The XDG-mandated rundir Lifecycle

The mandated lifecycle of a user’s  rundir is such that

  • the directory is created at the time the user first logs in after system startup.
  • It then remains until the user is fully logged out of the system,
  • whereupon it gets completely deleted.

Should the user log in again after having been fully logged out of the system, a pristine rundir is created.

rundir lifecycle as mandated by XDG Basedir Specification.

rundir lifecycle as mandated by XDG Basedir Specification.

Existing Implementations

  • libxdg-basedir: From the description, „this library implements functions to list the directories according to the specification and provides a few higher-level functions.“ Amongst others, the X window manager „awesome“, can use this library to determine the setting of XDG_RUNTIME_DIR. „libxdg-basedir“ is not concerned with the implementation of the rundir lifecycle, as can bee seen in the sourcecode at [LIBXDG-BASEDIR].
  • pam-xdg-support: The GNU/Linux distribution Ubuntu had a PAM-based implementation of the XDG-mandated rundir lifecycle in the past, but it has been removed from the distribution on request of its author, the reason given that it had been „entiryly obsoleted by libpam-systemd“, as can be seen in the package’s bugtracker at [PAM-XDG].
  • systemd:  systemd’s libpam-systemd provides a function pam_sm_open_session() that sets XDG_RUNTIME_DIR for a user that is logging in using a variety of PAM services. The function performs no action unless sytemd’s logind is running as can be seen in the sourcecode at [SYSTEMD-PAM].

Besides those freestanding software systems listed above, some applications provide built-in support of [XDG]. An example is KDE, which claims it „supports the XDG Basedir Specification“; at the time this article is written this is stated in [KDE1]. However, at the same time, [KDE2] reveals that its implementation does not address XDG_RUNTIME_DIR at all.

The Notion Of Being „Fully Logged Out“

It is not clearly specified by [XDG] what determines the state of a user being „fully logged out“ from a system.

Amongst others, these interpretations of  the requirement of being „fully logged out“ are possible:

  1. There is no process that is running on the system and has the identity of the user.
  2. There is no process that is running on the system, has the identity of the user and
    has an attached controlling TTY.
  3. The user has no PAM session.
  4. The user has no PAM sessions of certain types („cron“ might be counted as
    „not logged in“ whereas „login“ certainly counts as „logged in“).
  5. The user has no X session.

The Linux operating system feature of namespacing system resources might complicate the interpretation even further.

In summary, it is not clear from the current wording of [XDG] which of these interpretations, if any, actually constitutes the user being „fully logged out“ of a system.

It is my belief that this unclarity has imposed a great deal of difficulty onto compliant implementations of the XDG Rundir Specification. To the extent of my awareness, the only software besides systemd’s logind that attempted lifecycle management of users‘ rundirs is Ubuntu’s „pam-xdg-support“, and it is my speculation that its development has been halted because that task turned out to be too complex – in theory, the software had to monitor all aspects that potentially influence the state of a user being „fully logged out“ as listed above – and that therefore Ubuntu abandoned it and „passed the buck“ to systemd instead.

Case Study: A Per-User Service

For „jack-autostart“, an audio service that is started by a user to provide all own audio applications with audio playback and capture facilities, i need a single runtime file that contains the pathname name of the /dev device filesystem entry of the controller device the service is configured to operate on (more information on „jack-autostart“ is available at [JACK-AUTOSTART]).

The ALSA device that jack-autostart operates on is determined at the time the user performs the configuration procedure jack-autostart-config. The ALSA device is specified in the jack-autostart configuration as  a stabilized ALSA hardware device identifier that takes the form

hw:$NAME,DEV=$DEVNUM

where $NAME is provided by ALSA as a derivate of the the identification string of the ALSA driver providing the card, and $DEVNUM is the index of the device provided by the card (which can be multiple).

An ALSA device specification that i use for my installation of jack-autostart is

hw:USB,DEV=0

meaning the first device provided by an USB headset that has been assigned a value of „USB“ for $NAME by ALSA (because it is the driver „snd-usb-audio“ that provides that card).

This form of providing stabilized identifiers as configuration to jack-autostart worked well, until i implemented hotplug event management by means of a script jack-autostart-hotplug that is executed by „udev“ on every „add“ and „remove“ action affecting an ALSA audio controller, meaning a controller of a device provided by an ALSA card.

In case of a hotplug remove event affecting a sound controller device, the script, running as „root“ user, iterates over all users that currently run a jackd process. Assuming the identity of each user, the script executes the management script jack-autostart, for example, if „udev“ found that /dev/snd/pcmC4D0c has gone away:

jack-autostart hotplug remove /dev/snd/pcmC4D0c

The script jack-autostart, running as the affected user, now needs to resolve this /dev filesystem entry back into the stabilized identifier as described above. It can not do so from the ALSA runtime state, because the device is gone – no ALSA routine can provide the information necessary to determine the stable hardware identifier of a device that is not present on the system.

The solution i found was to let jack-autostart have the full pathname of the device filesystem entry stored at time of its „start“ action; the procedure can be outlined as follows:

runfile=$rundir/jack-autostart.run
hw=hw:USB,DEV=0
dev=$(find-dev-for-hw $hw)
echo $dev > $runfile

In consequence, when a hotplug remove event of sound device $dev occurs, jack-autostart hotplug remove $dev is able to determine if the service currently running is affected by the remove event by interpreting the content of $runfile.

In summary,

  • jack-autostart start wants to create a file $rundir/jack-autostart.run that contains a device filesystem entry of the currently configured ALSA hardware device.
  • jack-autostart hotplug remove $dev wants to read from this file in case of a hotplug remove event.

Note that „udev“ can execute jack-autostart hotplug remove $dev whenever a hotplug remove event occurs; this is not immediately related to the user being logged in or partially or fully logged out, it is instead related to a jackd process being run with the identity of the user, and – as described above – it is unclear if that condition constitutes a user being „logged in“.

Abandoning XDG Basedir Compliance

In short, i cannot work with this.

  • The rundir lifecycle mandated by [XDG] is potentially incompatible with my application.
  • Having a rundir with a lifecycle as mandated by [XDG] requires me to make my software depend on extensive changes to the init system as imposed by systemd, it also requires me to run a modified PAM setup as imposed by libpam-systemd and an additional login-management process provided by logind. It also further deepens a site’s dependency on the D-BUS RPC system.

With the recently observed rate of adoption of systemd into popular distributions of GNU/Linux, it is to be expected that many sites will implement per-user rundirs as mandated by [XDG]. This will cause problems for applications that want to remain XDG compliant without wanting to depend on systemd’s logind.

I therefore took the liberty of implementing an alternative rundir lifecycle that is explicitly not XDG compliant. It is intended for operation of XDG compliant applications on systems that do not have systemd’s logind enabled. It comes in the form of a software „xdg-compat“ that provides user processes with a runtime directory and a helper executable that sets the environment variable XDG_RUNTIME_DIR to the full pathname of that directory.

„xdg-compat“ Components

The software includes the following facilities:

  • A service script, executed during the start and shutdown of a system as the „root“ user, which prepares the parent directory of all users‘ rundirs at start and removes all users‘ rundirs at shutdown.
  • A setuid executable, running as a dedicated system user „xdg-compat“, equipped with the capability CAP_CHOWN, that has permissions to, in the parent directory described above, create per-user rundirs, set ownership of a rundir to that of the user and set permissions of a rundir to 0700.
  • An X session hook, that, if enabled, performs the rundir check and creation at the start of a user’s X session by executing the setuid helper executable described above.
  • Configuration files that define
    • the naming of the rundir parent directory and
    • the naming of individual rundirs and
    • a routine that performs check and creation of a rundir; this routine is executed by the setuid executable described above.

The „xdg-compat“ rundir Lifecycle

The rundir lifecycle as implemented by „xdg-compat“ can be paraphrased as follows:

  • As soon as she logs on to the system, create a user’s rundir unless it already exists.
  • When the system is shut down, delete the rundir.
rundir lifecycle as implemented by "xdg-compat".

rundir lifecycle as implemented by „xdg-compat“.

After the user has logged in for the first time, XDG-compliant applications that are executed by the user will encounter a rundir denominated in a compliant fashion by XDG_RUNTIME_DIR, it will however not be emptied, removed or recreated as part of a „full logout/login cycle“ as mandated by [XDG].

If a system maintainer wanted to implement XDG compliance, she can either enable systemd’s logind or implement an own deletion mechanism for rundirs according to the site’s interpretation of a user being „fully logged out“. This mechanism of deletion of an existing rundir can be integrated with configuration scripts provided by „xdg-compat“. A system-maintainer can either redefine the procedure that is testing for an existing rundir and creating it if neccessary, or she can modify the procedure that is executed at the start of an X session and perform a reset of the rundir at that time.

If a user wanted to accomplish the XDG mandated behavior of having a cleared rundir when he logs in to the system, without the system providing this behavior,  he could accomplish this in terms of an X session, by adding a script that does this, to the autostart-routines of the X session manager or window manager of his choice.

„xdg-compat“ Availability

The software is currently under active development. The most recent sourcecode is available at [XDG-COMPAT].

Links