Systemd service using environment variables

Hello
I need help to understand how to pass environment variables.

I use a test script based on one found on internet : https://gist.github.com/nickjacob/9909574#gistcomment-1714346

Here is my script :

[Unit]
Description=Demonstrate Bash

[Service]
User=user_install
PermissionsStartOnly=true
 
ExecStartPre=".................................."
ExecStart=/usr/bin/echo "MYVAR = ${MYVAR}"

ExecStop=

[Install]
WantedBy=multi-user.target

The following prestart command does not work ( print null value )

#Run as root
ExecStartPre=/usr/bin/bash -c "/usr/bin/systemctl set-environment MYVAR=$WINDOWMANAGER"
ExecStartPre=/usr/bin/bash -c "/usr/bin/systemctl set-environment MYVAR=$AUDIODRIVER"
ExecStartPre=/usr/bin/bash -c "/usr/bin/systemctl set-environment MYVAR=$LOG_USER"

The following prestart command are OK


ExecStartPre=/usr/bin/bash -c "/usr/bin/systemctl set-environment MYVAR=$USER"
ExecStartPre=/usr/bin/bash -c "/usr/bin/systemctl set-environment MYVAR=$LOGNAME"
ExecStartPre=/usr/bin/bash -c "/usr/bin/systemctl set-environment MYVAR=1234"

All my own variables (LOGNAME,[FONT=monospace]LOG_USER, … ) are exported and they [/FONT]are set by sourcing the file ‘system_common_general_env_var’ in /etc/profile.local

:~> echo $USER 
user_install 
:~> echo $WINDOWMANAGER 
/usr/bin/startplasma-x11 
:~> echo $AUDIODRIVER 
pulseaudio 
:~> echo $LOGNAME  
user_install 
:~> echo $LOG_USER 
[FONT=monospace]user_install[/FONT]

Result with string ‘1234’

:~>  sudo systemctl status test_unit.service 
● test_unit.service - Demonstrate Bash 
     Loaded: loaded (/etc/systemd/system/test_unit.service; disabled; vendor preset: disabled) 
     Active: inactive (dead) 

Nov 24 22:48:00 15-3-G731GV systemd[1]: Starting Demonstrate Bash... 
Nov 24 22:48:00 15-3-G731GV systemd[1]: Started Demonstrate Bash. 
Nov 24 22:48:00 15-3-G731GV echo[11374]: MYVAR = 1234 
Nov 24 22:48:00 15-3-G731GV systemd[1]: test_unit.service: Succeeded.

Result with variable $LOG_USER

[FONT=monospace]:~>  sudo systemctl status test_unit.service  
● test_unit.service - Demonstrate Bash 
     Loaded: loaded (/etc/systemd/system/test_unit.service; disabled; vendor preset: disabled) 
     Active: inactive (dead) 

Nov 24 22:44:34 15-3-G731GV systemd[1]: Starting Demonstrate Bash... 
Nov 24 22:44:34 15-3-G731GV systemd[1]: Started Demonstrate Bash. 
Nov 24 22:44:34 15-3-G731GV echo[11210]: MYVAR = user_install 
Nov 24 22:44:34 15-3-G731GV systemd[1]: test_unit.service: Succeeded.[/FONT]

Result with variable [FONT=monospace]$WINDOWMANAGER
[/FONT]

[FONT=monospace]:~>[/FONT][FONT=monospace]sudo systemctl status test_unit.service 
● test_unit.service - Demonstrate Bash 
     Loaded: loaded (/etc/systemd/system/test_unit.service; disabled; vendor preset: disabled) 
     Active: inactive (dead) 

Nov 24 22:42:56 15-3-G731GV systemd[1]: Starting Demonstrate Bash... 
Nov 24 22:42:56 15-3-G731GV systemd[1]: Started Demonstrate Bash. 
Nov 24 22:42:56 15-3-G731GV echo[10766]: [/FONT][FONT=monospace][FONT=monospace]MYVAR = 
Nov 24 22:42:56 15-3-G731GV systemd[1]: test_unit.service: Succeeded.[/FONT][/FONT]

Any comment is welcome

Hi
Use an environment file and call that in your service


EnvironmentFile=-/etc/sysconfig/blahblah

How systemd is supposed to know the values of these variables?

The following prestart command are OK

ExecStartPre=/usr/bin/bash -c “/usr/bin/systemctl set-environment MYVAR=$USER”
ExecStartPre=/usr/bin/bash -c “/usr/bin/systemctl set-environment MYVAR=$LOGNAME”

They are set by bash itself and are completely unrelated to these commands which set these variable to the “wrong” values anyway (“root” instead of “user_install”).

All my own variables (LOGNAME,[FONT=monospace]LOG_USER, … ) are exported and they [/FONT]are set by sourcing the file ‘system_common_general_env_var’ in /etc/profile.local

How is it relevant to services?

I use a test script based on one found on internet : execute arbitrary bash code/variable substitution in systemd units · GitHub

This shows complete misunderstanding about what systemctl set-environment does.

That does not help people.
For sure, If I had your expertise, I wouldn’t post that thread.
With a little effort on your part perhaps you could explain to me why some variables are set and others not.

I don’t know.
But I I base my guess from this

ExecStartPre=-/bin/bash -c "/bin/systemctl set-environment USB_NIC=$(basename -a /sys/class/net/* | grep enx.*)"

in this : Automatically add Linux NIC to namespace at system boot (e.g. used for Ethernet USB dongles with dynamic identifier) · GitHub

But why it knows theses :

A few brief explanations are welcome.

Somethings like that :

EnvironmentFile=/some/env/file
ExecStartPre=/bin/bash -c 'echo foo=bar > /some/env/file'
ExecStart=/some/command  # sees bar as value of $foo

Use “grep -r Environment /usr/lib/systemd/ /etc/systemd/” to search for examples on your machine.

Hi
No, just put the variables in a file it will use them if specified…

Have a look at my htop service sysconfig file GitHub - malcolmlewis/systemd-htop-service: This is a systemd service to start htop on a virtual terminal (aka /dev/ttyN).

Nice.
Thank you

If you want dynamically work with tty9, you must change dynamically HTOP_TTY=“12” to HTOP_TTY=“9” in sysconfig.htop ?

Hi
Yes for the config, however, that would also require a change in the udev rule as well… I didn’t want it any lower in case the system requires those tty’s…

No problem, it was just a complementary question relative to my understanding.

You must have some reasons to use this command. If you explain your reasons it is possible to point out where they are wrong.

But I I base my guess from this

ExecStartPre=-/bin/bash -c "/bin/systemctl set-environment USB_NIC=$(basename -a /sys/class/net/* | grep enx.*)"

This command does not contain variable expansion so you compare apples and oranges. To use value of variable this value must be previously set. Each service starts with clean environment where no variables are present except those exported by systemd.

But why it knows theses

I already answered this. “It” (if “it” means “systemd”) does not know these (values of these variables). Those variables are set by bash itself when it starts.

To the OP:
http://www.catb.org/~esr/faqs/smart-questions.html#goal

The reason is the same reason as told in this thread : https://forums.opensuse.org/showthread.php/534151-How-to-check-environment-variables-for-a-systemd-process

And I found your comment in post #6:

tr '\0' '
' < /proc/$PID/environ

So I build a systemd service for testing.

[Unit]
#
#file : test_unit_read_env_var.service
#
Description=STORE SYSTEMD UNIT ENV VARIABLES TO FILE
After=syslog.target network.target

[Service]
Type=oneshot
RemainAfterExit=yes

ExecStart=-/bin/bash -c "/usr/bin/tr '\0' '
' >  /tmp/test_unit_read_env_var_list.txt  < /proc/$(/bin/systemctl status test_unit_read_env_var.service | /bin/sed -n 's/.* PID: \(^ ]*\).*/\1/p')/environ"

[Install]
WantedBy=multi-user.target

I got this error :

**●** test_unit_read_env_var.service - STORE SYSTEMD UNIT ENV VARIABLES TO FILE 
     Loaded: loaded (/etc/systemd/system/test_unit_read_env_var.service; disabled; vendor preset: disabled) 
     Active: **active (exited)** since Mon 2021-11-29 20:17:55 CET; 5ms ago 
    Process: 8402 ExecStart=/bin/bash -c /usr/bin/tr '\0' '
' >  /tmp/test_unit_read_env_var_list.txt  < /proc/$(/bin/systemctl status test_unit_read_env_var.service | /bin/sed -n 's/.* PID: \(^ ]*\).*>
   Main PID: 8402 (code=exited, status=1/FAILURE) 

nov. 29 20:17:55 15-3-G731GV systemd[1]: Starting STORE SYSTEMD UNIT ENV VARIABLES TO FILE... 
nov. 29 20:17:55 15-3-G731GV bash[8403]: /bin/bash: /proc/$(/bin/systemctl status test_unit_read_env_var.service | /bin/sed -n 's/.* PID: \(^ ]*\).*/\1/p')/environ: ambiguous redirect 
nov. 29 20:17:55 15-3-G731GV systemd[1]: Finished STORE SYSTEMD UNIT ENV VARIABLES TO FILE.

Any help is welcome.

That is rather strange way to code

ExecStart=/usr/bin/env
nov. 29 20:17:55 15-3-G731GV bash[8403]: /bin/bash: /proc/$(/bin/systemctl status test_unit_read_env_var.service | /bin/sed -n 's/.* PID: \(^ ]*\).*/\1/p')/environ: ambiguous redirect 

If you actually look at your systemctl status output, you will see two PID: strings that are matched by your sed expression.

To my experience the above is extreme PITA. I prefer the following:

**erlangen:~ #** systemctl cat packagekit-background.service  
**# /etc/systemd/system/packagekit-background.service**
[Unit] 
Description=Script to update the system with PackageKit 

[Service] 
ExecStart=/usr/local/PackageKit/packagekit-background.sh 
**erlangen:~ #**