1

I have a simple need to run a systemd user service with access to all of the environment variables provided by the user DBus session. Here's my example unit:

[Unit]
Description=Environment Demo

[Service]
Type=simple
Environment=DISPLAY=:0
ExecStart=/bin/bash -c 'env > shell.env.sh'
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=default.target

The keys exported are:

_
DISPLAY
HOME
LANG
LOGNAME
MANAGERPID
PATH
PWD
SHELL
SHLVL
USER
XDG_RUNTIME_DIR

This is a far cry from the full list of environment variables present if I launch a desktop application from my tray or from my launcher (I'm on elementary OS Loki aka Ubuntu 16.04 xenial). If I launch my terminal emulator (pantheon-terminal) and I get a sorted list of my environment variables, I get the following:

_
DBUS_SESSION_BUS_ADDRESS
DEFAULTS_PATH
DESKTOP_SESSION
DISPLAY
EDITOR
GDM_LANG
GDMSESSION
GIO_LAUNCHED_DESKTOP_FILE
GIO_LAUNCHED_DESKTOP_FILE_PID
GNOME_DESKTOP_SESSION_ID
GPG_TTY
GSETTINGS_SCHEMA_DIR
GTK_CSD
GTK_MODULES
HOME
LANG
LANGUAGE
LESSCLOSE
LESSOPEN
LOGNAME
LS_COLORS
MANDATORY_PATH
PANTHEON_TERMINAL_ID
PATH
PROMPT_COMMAND
PWD
QT_ACCESSIBILITY
QT_IM_MODULE
QT_LINUX_ACCESSIBILITY_ALWAYS_ON
QT_STYLE_OVERRIDE
SESSION_MANAGER
SHELL
SHLVL
SSH_AGENT_PID
SSH_AUTH_SOCK
TERM
USER
VTE_VERSION
XAUTHORITY
XDG_CONFIG_DIRS
XDG_CURRENT_DESKTOP
XDG_DATA_DIRS
XDG_GREETER_DATA_DIR
XDG_MENU_PREFIX
XDG_RUNTIME_DIR
XDG_SEAT
XDG_SEAT_PATH
XDG_SESSION_DESKTOP
XDG_SESSION_ID
XDG_SESSION_PATH
XDG_SESSION_TYPE
XDG_VTNR
XMODIFIERS

To make things more clear:

diff --git a/systemd-user.env.txt b/pantheon-terminal.env.txt
index c684056..f6d0685 100644
--- a/systemd-user.env.txt
+++ b/pantheon-terminal.env.txt
@@ -1,12 +1,54 @@
 _
+DBUS_SESSION_BUS_ADDRESS
+DEFAULTS_PATH
+DESKTOP_SESSION
 DISPLAY
+EDITOR
+GDM_LANG
+GDMSESSION
+GIO_LAUNCHED_DESKTOP_FILE
+GIO_LAUNCHED_DESKTOP_FILE_PID
+GNOME_DESKTOP_SESSION_ID
+GPG_TTY
+GSETTINGS_SCHEMA_DIR
+GTK_CSD
+GTK_MODULES
 HOME
 LANG
+LANGUAGE
+LESSCLOSE
+LESSOPEN
 LOGNAME
-MANAGERPID
+LS_COLORS
+MANDATORY_PATH
+PANTHEON_TERMINAL_ID
 PATH
+PROMPT_COMMAND
 PWD
+QT_ACCESSIBILITY
+QT_IM_MODULE
+QT_LINUX_ACCESSIBILITY_ALWAYS_ON
+QT_STYLE_OVERRIDE
+SESSION_MANAGER
 SHELL
 SHLVL
+SSH_AGENT_PID
+SSH_AUTH_SOCK
+TERM
 USER
+VTE_VERSION
+XAUTHORITY
+XDG_CONFIG_DIRS
+XDG_CURRENT_DESKTOP
+XDG_DATA_DIRS
+XDG_GREETER_DATA_DIR
+XDG_MENU_PREFIX
 XDG_RUNTIME_DIR
+XDG_SEAT
+XDG_SEAT_PATH
+XDG_SESSION_DESKTOP
+XDG_SESSION_ID
+XDG_SESSION_PATH
+XDG_SESSION_TYPE
+XDG_VTNR
+XMODIFIERS

There are somewhere around 30 more environment variables when starting something this way.


My use case is this: I want to be able to start processes with a full environment like in the context of launching my terminal application.

How can I expose a full(er) environment to my systemd user daemons?

Naftuli Kay
  • 39,676

3 Answers3

1

You may find some help in the ArchLinux wiki page which discusses setting the environment for user Units. In particular,

systemctl --user import-environment 

will export all the current environment variables into your systemd user environment. You can provide an explicit list of variables instead. You can check by running

systemctl --user show-environment

before and after. There is also

systemctl --user set-environment MYVAR=myvalue ...
systemctl --user unset-environment MYVAR ...

See the systemctl man page. The wiki also mentions a dbus specific alternative with which I had less success:

dbus-update-activation-environment --systemd --all
meuh
  • 51,383
1

My solution takes into account @meuh's solution above and some digging.

Step one is to create a dummy user unit called dbus-environment.service:

[Unit]
Description=Environment Imported Target

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/true

Next step is to create a Bash script:

#!/bin/bash

systemctl --user import-environment
systemctl --user start dbus-environment.service

Next, add this script to your window manager's startup applications which are guaranteed to run within the DBUS session and the window manager, X, etc.

On session login, the script will be called, importing everything into the systemd user daemon.

Next, for all units depending on these environment variables, simply have them depend on dbus-environment.service:

[Unit]
Description=Duplicity Backup Service
Requires=dbus-environment.service
After=dbus-environment.service

[Service]
Type=oneshot
ExecStart=/home/naftuli/.local/bin/duply home backup

At this point, when this service starts, it is guaranteed to have the environment imported. Perhaps I should be using a target rather than a service, but that exercise will have to wait.

Naftuli Kay
  • 39,676
0

@Naftuli: Would it not have been better to instead use something like this in all your services unit files?

ExecStartPre=\usr\bin\sh -c "systemctl --user import-environment"
peterh
  • 9,731
Robin
  • 1
  • That would not solve it. The current execution context is systemd, which doesn't have the display manager environment variables. Executing a shell will not have it automatically inherit these environment variables. You need something that is guaranteed to inherit these variables, so a normal shell won't have them. – Naftuli Kay Jul 30 '18 at 14:30
  • Thanks! I am a newbie. Still trying to understand systemd. I am actually also stuck in a similar situation where in I would like to import all environment variable available to the user to the system service daemon.. There is no possibility for me to use a script to import these variables. Have you come across another solution so that all the variables which have been set for the users becomes visible to service daemon? Thanks! – Robin Jul 30 '18 at 14:37
  • dbus-launch might be able to do something. I was originally crawling the /proc/PID/env of the dbus session to get its address and then call dbus-launch – Naftuli Kay Jul 30 '18 at 14:39