1

On Arch Linux, I am trying to create a systemd service which runs this Bash script:

#!/bin/bash

/bin/xbindkeys &
/bin/setxkbmap -layout gb

With some online guidance I then made a file named myfirst.service in /etc/systemd/system to store my service. Here are the contents of the file:

[Unit]
Description=Command Service

[Service]
ExecStart=/etc/startupjobscript
User=user1
Type=oneshot
Restart=on-abort
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

The service is supposed to execute /etc/startupjobscript, which is the Bash script. After this I enabled and started the service with:

systemctl enable myfirst
systemctl start myfirst

But for some reason it doesn't work and shows that it has failed to load and run at all.

EDIT: here is what sudo systemctl status myfirst give me:

Warning: The unit file, source configuration file or drop-ins of myfirst.service changed on disk. Run 'systemctl daemon-reload' to reload units.
● myfirst.service - Command Service
     Loaded: loaded (/etc/systemd/system/myfirst.service; enabled; vendor preset: disabled)
     Active: failed (Result: exit-code) since Wed 2020-08-26 21:07:02 BST; 18min ago
    Process: 332 ExecStart=/etc/startupjobscript (code=exited, status=255/EXCEPTION)
   Main PID: 332 (code=exited, status=255/EXCEPTION)

Aug 26 21:07:01 archlinux systemd[1]: Starting Command Service...
Aug 26 21:07:02 archlinux startupjobscript[337]: Cannot open display "default display"
Aug 26 21:07:02 archlinux systemd[1]: myfirst.service: Main process exited, code=exited, status=255/EXCEPTION
Aug 26 21:07:02 archlinux systemd[1]: myfirst.service: Failed with result 'exit-code'.
Aug 26 21:07:02 archlinux systemd[1]: Failed to start Command Service.

10
  • Did you run systemctl daemon-reload? – Panki Aug 26 '20 at 16:55
  • I see you've put your script in /etc/. Normally, things in /etc are configuration files with permission 644. Check if your script has execute permissions (755) -- plus I recommend sticking it in /usr/local/bin or /usr/local/sbin. If it's not execute permissions, what is the output of systemctl status myfirst? – Stewart Aug 26 '20 at 16:58
  • Since your service works with X, it might be useful to use WantedBy=graphical.target instead of multi-user.target to ensure the X server is started before you try interfacing with it. I don't think this is the problem, but systemctl status output should tell us. – Stewart Aug 26 '20 at 17:11
  • I see multiple issues: 1) X utilities need a DISPLAY to act upon; it's a variable set in the environment of the user(s) who are running an X instance, it's not available to a system systemd unit. 2) X utilities will likely fail until the graphical environment is up and running for the user that runs them, which will most likely only happen at a later moment than multi-user.target or graphical.target or the user's default.target. You may want to detail what you're trying to achieve, users here may suggest better approaches. – fra-san Aug 26 '20 at 19:38
  • @Panki Yes i have and it hasnt fixed anything – printf Aug 26 '20 at 20:19
0

When running anything that talks to the X-server, systemd services get a little tricky. There are two main methods to do it:

  1. Run a user service
  2. Set $DISPLAY and avoid starting until graphical.target is reached.

To run this as a user-service, move myfirst.service to ~/.config/systemd/system/. Then remove User= and set WantedBy= to default.target. default.target is reached when the user logs in. Some day there will be a graphical-session.target which will be reached when a user is logged into a desktop environment, but that's not available yet. Instead we are triggering off of default.target which is reached when a user logs in. If you're using a display manager, then you probably have an x-session before then.

[Unit]
Description=Command Service
After=default.target

[Service]
ExecStart=/bin/xbindkeys
ExecStartPost=/bin/setxkbmap -layout gb
Type=simple
Restart=on-abort

[Install]
WantedBy=default.target

Be sure to use --user when working with this unit such as:

systemctl --user daemon-reload
systemctl --user start myfirst.service
systemctl --user enable myfirst.service

To keep this as a system service, leave the file in /etc/systemd/system/ and you'll need to set environment so it can connect to the X-server. Finally, be sure to make it WantedBy=graphical.target. This ensures it doesn't try to connect to the X-server before the X-server is started. However I'm not sure that it will always succeed because I'm not sure that the .Xauthority is valid until you log in.

[Unit]
Description=Command Service
After=graphical.target

[Service]
ExecStart=/bin/xbindkeys
ExecStartPost=/bin/setxkbmap -layout gb
Type=simple
User=user1
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/user1/.Xauthority
Restart=on-abort

[Install]
WantedBy=graphical.target

The system-wide service thing gets a little weird if you have multiple users or multiple displays. It's recommended to do the user-specific thing. If you are in your desktop environment, you can echo $DISPLAY and echo $XAUTHORITY to figure out what to set those variables to.


Once you make these changes, you'll need to tell systemd to read them before you try your next start. Run:

systemctl daemon-reload

If you've been experimenting, be sure to systemctl disable myfirst.service first, then when you are sure systemctl start myfirst.service works, then you can systemctl enable myfirst.service. enable will create a symbolic link in default.target or graphical.target so that when you reboot, your service gets called. To avoid making a mess with those links during your experimentation state, just don't enable it until you're ready to test reboots.


Note, I do have a strange suspicion about that /bin/xbindkeys & line in your bash script. Does xbindkeys normally run forever? If so, you might want to change your ExecStart= to:

ExecStart=/bin/xbindkeys
ExecStartPost=/bin/setxkbmap -layout gb
Type=simple

I've made these changes to the suggested units above already.

This will ensure that systemd keeps track of the process directly instead of forking. Because Type= is not set to forking, I suspect that the bash script will end and systemd won't keep track of the PID of xbindkeys leaving it as an orphaned PID and no way to stop it. If you leave xbindkeys as part of the ExecStart then stopping the service will terminate xbindkeys (which I think it will fail to do right now). In that case you'll also want to set Type=simple because oneshot won't be applicable anymore.

8
  • Hi i tried to move to the file to ~/.config/systemd/system/ which does not exist in my system keep in mind im using arch linux – printf Aug 26 '20 at 21:01
  • @Qasim That directory is expected not to exist by default, it is safe to just create it. – fra-san Aug 26 '20 at 21:09
  • I'm afraid both of these approaches are not going to work (tried on Arch with SDDM and Xorg). I guess there isn't yet any X server listening for commands from user1 when default.target or graphical.target are reached. – fra-san Aug 26 '20 at 21:49
  • What error are you getting? Can you update your question with your latest *.service and systemctl status? Also, do these commands work if you run them from your own bash environment? – Stewart Aug 27 '20 at 5:37
  • @Stewart I'm gonna try this out and see if it works and get back to you – printf Aug 27 '20 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.