1

Apologies in advance for any mangling of terminology, syntax, conventions, etc. I'm still a fairly novice Linux user. I am working on Steam Deck running the latest SteamOS (3.5.17).

I managed to successfully setup a startup and shutdown service using systemd which synchronizes game saves between the Steam Deck and a SAMBA shared network drive I have setup on a Raspberry Pi. I have the drive set to automount (via fstab) at "/home/deck/mnt/xtra/". Each service runs the bash script called "sync_saves.sh" at startup and shutdown respectively.

The problem is that I also want to store the script "sync_saves.sh" on the network drive and run it from there so that I can update it more easily. The script reads a config file (also on the network) that is updated frequently from various computers. I don't want them each to have their own local version because keeping them in sync manually becomes a real pain. However, while I can run the script directly from the network drive, the service doesn't run if the script is on the network drive. If there are any errors, I'm not seeing them when I run:

systemctl --user status ss_lin_startup

The two services are defined in files named ss_lin_startup.service and ss_lin_shutdown.service. Here are their contents:

ss_lin_startup.service

[Unit]
Description=SyncSaves Startup

[Service]
#ExecStart=/home/deck/mnt/xtra/scripts/SyncSaves/sync_saves.sh #network drive, doesn't work
ExecStart=/home/deck/Documents/scripts/SyncSaves/sync_saves.sh #local drive, works

[Install]
WantedBy=default.target

ss_lin_shutdown.service

[Unit]
Description=SyncSaves Shutdown

[Service]
Type=oneshot
RemainAfterExit=true
#ExecStop=/home/deck/mnt/xtra/scripts/SyncSaves/sync_saves.sh #network drive, doesn't work
ExecStop=/home/deck/Documents/scripts/SyncSaves/sync_saves.sh #local drive, works

[Install]
WantedBy=default.target

I got them working (when script is stored on local drive) by putting a link to the service files in "/home/deck/.config/systemd/user/" and running the following commands:

systemctl --user daemon-reload
systemctl --user enable ss_lin_startup.service
systemctl --user enable ss_lin_shutdown.service

If it matters, the service files are stored at "/home/deck/Documents/scripts/SyncSaves/services" linked in the folder "/home/deck/.config/systemd/user/". The Steam Deck desktop environment offers the option to drag and drop files as either move, copy or link, which is the method I used to create the links.

Is there a way to get this working? Am I going about it the right way? Alternatively, is there a way for me to automate copying the script "sync_saves.sh" from the network drive to the local drive on boot?

I've been Googling for several hours and the only hints I've come up with are as follows:

-I saw somewhere that systemd doesn't like when services are located on different partitions (from the system partition presumably), but I'm not sure if I understood that correctly or if the implications apply here or if it matters where the scripts they invoke are stored.

-In most of the discussions on this topic, the services are placed or linked in "/etc/systemd/system/" and the command to enable them is "systemctl enable your_service.service" without the --user flag and sometimes with sudo. In my initial testing on the Raspberry Pi, I used that approach, but had issues on Steam Deck.

UPDATE:

I figured out a part of the problem, but still don't have a solution. I suspected this might happen, but not sure how to remedy it. I ran

systemctl --user -l status ss_lin_startup.service

and got more details output. It says "Failed to locate executable ...sync_saves.sh". So what is likely happening (in the startup at least) is that the drive isn't mounted yet. I've seen people use different arguments in the service file to make sure the command runs after network startup (i.e. Wants=network-online.target, After=network-online.target), but when I use those I get something like "target not found".

FINAL UPDATE:

I came up with a work around which was to write an update script (which pulls the latest version of sync_saves.sh and config files from the network drive) and then a script to run the update script and then the sync script. With that it is working as intended. I'm not sure how much progress can be made here as there doesn't seem to be a lot of information out there about the idiosyncrasies of the SteamOS.

1
  • It might help if you could add the fstab entry for that network filesystem to your question (minus credentials and IP adresses/hostnames, for obvious privacy reasons) or how you got it to automount.
    – doktor5000
    Mar 12 at 19:48

2 Answers 2

0
  • if SELINUX is in enforcing mode which is the default, (then in RHEL 8.9 for example) selinux will prevent a service from running unless that /home/deck/mnt/xtra/scripts/SyncSaves/sync_saves.sh file is labeled with a selinux context that allows it to run. I asked asked about this and there doesn't seem to be any hard convention, so unless you are into writing your own selinux rules I just label it as bin_t.
  • for SELINUX other options are to
    • set it to permissive mode real time by doing setenforce 0 at the prompt and then troubleshoot if the problem immediately goes away, if so then it is selinux blocking it
    • you can observe the selinux blocking in action by doing a tail -f /var/log/audit/audit.log and watch the printout when it happens
    • set selinux to permissive mode globally by editing /etc/selinux/config then rebooting; it is advised to not set it to disabled unless you know what you are doing as that can cause problems; permissive is basically disabled == allow everything but also print what would've been a problem in audit.log.

systemd service won't run from network drive

for /home/deck/Documents/scripts/SyncSaves/sync_saves.sh if that mount is mounted as noexec in /etc/fstab then the sync_saves.sh script, or any script or any file for that matter, will not execute. Do a mount | grep home and see if a noexec is present as a mount option. Also make sure the .sh file being called out from the .service file has execute permissions for the user that will be running it.

I saw somewhere that systemd doesn't like when services are located on different partitions.

The service is that file that is recognized by systemctl list-unit-files after doing a daemon-reload.... your ss_lin_startup.service file. Whatever that service file calls via execstart or execstop or whatever else, any file called to execute has to have a selinux label per selinux rules that will be permitted to run when selinux is enforcing, and the bin_t label is one of them. The location of this file does not matter except if it is under an nfs mount that has the noexec mount option.

Also make sure your sync_saves.sh has a #!/bin/bash as the first line, if it doesn't my experience is it won't run when being called from the .service file.

8
  • Running "semanage fcontext -a -t bin_t "/etc/systemd/system/ss_lin_startup.service" returns "bash: semanage: command not found", same with restorecon.
    – imfm
    Mar 4 at 22:05
  • Steam Deck does not appear to have a /etc/selinux/config or even an /etc/selinux folder. The closest I can find is a file called Kconfig in the directory "/usr/lib/modules/6.1.52-valve16-1-neptune-61/build/security/selinux", which looks like it is part of a build process.
    – imfm
    Mar 4 at 22:22
  • There's also no /var/log/audit/audit.log
    – imfm
    Mar 4 at 22:22
  • then it is likely a file permission problem, as I just noticed u said works when located on local disk but not from nfs mount location. Also make sure the nfs mount has happened before this service is run, i assume it would be because of WantedBy=default.target
    – ron
    Mar 4 at 22:52
  • for troubleshooting simply edit your sync_saves.sh to do something simple like echo "hello" >> /hello.txt; and do service ss_lin_startup start until you observe it working; run it under root first, then transition the file and service permissions to whatever user; get it working while the system is already booted and running then go to getting it working on boot.
    – ron
    Mar 4 at 22:55
0

For the main part of the question, on how to run a systemd service from a network filesystem, there are basically two issues. One is adding a condition to the systemd unit to tell it that it needs a given path/mountpoint to be available. The other is how systemd actually mounts that path/mountpoint in the first place, as you need to depend on that.

  • systemd unit dependency on path

You should try adding this dependency on the network filesystem path via the RequiresMountsFor= option. This should also work for hierarchical mounts (/home is probably a separate filesystem, and /home/deck/mnt/xtra is the network filesystem mounted below that from your explanation)

Quoting the description:

Takes a space-separated list of absolute paths. Automatically adds dependencies of type Requires= and After= for all mount units required to access the specified path.

Mount points marked with noauto are not mounted automatically through local-fs.target, but are still honored for the purposes of this option, i.e. they will be pulled in by this unit.

So try that by adding

RequiresMountsFor=/home/deck/mnt/xtra/scripts/SyncSaves/sync_saves.sh

to your ss_lin_startup.service. You can do that easily by running systemctl edit --full --user ss_lin_startup.service which will let you edit your systemd service and it should also do a systemctl daemon-reload --user afterwards.

  • how systemd mounts entries from /etc/fstab

All the entries in /etc/fstab are parsed by systemd via systemd-fstab-generator which creates so-called .mount units for each of them on every boot. For network filesystems you should always add _netdev as an option to the /etc/fstab entry as this is parsed by systemd-fstab-generator which adds a dependency to remote-fs.target. This is usually* reliable enough to make systemd wait for the network to be available, before mounting that network filesystem.

--- * Usually meaning that there is a distinction between network started and network online, i.e. that one of the started network connections is actually able to reach anything. Although that by itself is a pretty complex topic and probably outside the scope of this question/answer.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .