Skip to content

systemd user services, pam, and environment variables

Arseniy Zaostrovnykh edited this page Mar 30, 2024 · 6 revisions

...or, why xdg-desktop-portal-wlr doesn't work out of the box

TL;DR

You probably want to put

exec "systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP"

at the bottom of your sway config (or another compositor's equivalent), assuming the compositor itself is launched with XDG_CURRENT_DESKTOP set. Our suggestion is to launch your compositor with a wrapper script that sets this variable and any others specific to sway/wayland. Alternative approaches are discussed below. There is also a step-by-step troubleshooting guide if you don't care about the details.

First, some background

xdg-desktop-portal (xdp) is the program that keeps track of different portal implementations, their capabilities, and proxies the appropriate messages between them and other applications that use them.

xdg-desktop-portal-wlr (xdpw) is one specific portal implementation that provides screenshot and screencast implementations for wlroots compositors.

xdp looks for the environment variable XDG_CURRENT_DESKTOP and tries to find a corresponding portal implementation by looking at the installed *.portal files. Those files should all have a line that potentially matches the value of that environment variable (e.g., UseIn=sway). If no environment variable is present, xdp chooses a default implementation for each interface from the collection of all installed portals. xdp needs to be able to see this environment variable in order to choose xdpw as the implementation for the screenshot and screencast interfaces, or a different portal implementation may be used instead (and will fail, often in the form of an all black screenshot or screencast, possibly showing only the cursor).

So what is the problem then?

xdp is meant to start via dbus activation where the dbus-daemon runs the service or, if systemd is used, activates the service as a systemd user session service. Those services are not forked from your login shell and thus, don't inherit environment variables set in your .profile, .{shell}_profile, or .{shell}rc files.

Instead, if using systemd, you are advised to use one of the configs specific to the "user service environment" as documented here. This is explained in even more detail in the ArchWiki and in the Gnome documentation.

If you aren't using systemd but you are relying on d-bus activation, you should read up on dbus-launch, find out where it is being invoked, and look into setting the environment variables you need there.

This is extra confusing, because if you try to run xdp/xdpw manually from a terminal after logging in, and you've set XDG_CURRENT_DESKTOP in one of the profile or rc config files, it will likely work correctly.

That's annoying, why are user services not forked from my login shell?

It has to do with pam, and how pam is used to initialize systemd user sessions.

For example, on a default Arch Linux install, the /etc/pam.d/login file includes /etc/pam.d/system-local-login, which includes /etc/pam.d/system-login, which contains the lines:

-session   optional   pam_systemd.so
session    required   pam_env.so           user_readenv=1

You can find the pam.d config file documentation here, but in short, the first line invokes the pam_systemd.so module if present. One of the documented purposes of that pam module is to launch "[a]n instance of the system service user@.service, which runs the systemd user manager instance". It also "registers user sessions with the systemd login manager systemd-logind", which automatically handles gettys. Your getty implementation (agetty on a default Arch installation) ultimately invokes /bin/login. The systemd user session is also the parent of the dbus-daemon "session" process that handles dbus-activation.

So how should I set this environment variable?

The environment.d docs list a number of files where you can set XDG_CURRENT_DESKTOP if you are using systemd, but those aren't your only choices. You could also set them in /etc/environment, /etc/security/pam_env.conf, or possibly ~/.pam_environment, which are loaded by the pam module, pam_env.so. Its configuration is explained here. These options set environment variables either user or system-wide, even when you aren't using sway, which may not be desirable.

The recommended approach is to run systemctl --user import-environment if using systemd, or dbus-update-activation-environment --all if you aren't, any time before xdp is started, assuming that XDG_CURRENT_DESKTOP is correctly set when and where that command is run. Many users exec one of those commands in their compositor config, which seems to work nicely. This has the added benefit of being specific to your compositor, as you can set XDG_CURRENT_DESKTOP in a wrapper script that starts your compositor instead of in a user or system-wide config. This is desirable if you are also running X or other Wayland compositors where XDG_CURRENT_DESKTOP=sway doesn't make sense.