2

I've created a systemd user service to run a duplicity backup job once a day at 17:00 broadly based on an article in Fedora Magazine. I'm running Fedora 28 with the Gnome desktop. My systemd timer config is:

[Unit]
Description=daily-backup timer

[Timer]
OnCalendar=*-*-* 17:00:00
Unit=daily-backup.service
Persistent=true

[Install]
WantedBy=default.target

This works well except in the case where the machine was switched off when the backup was due to run. The next time I login, the persistent option ensures the backup runs immediately. Unfortunately the backup script fails at this point because the script tries to run before the user session is active. The backup script starts by trying to mount the backup disk if its not already mounted:

#!/usr/bin/env bash

USER=$(whoami)
MOUNTPOINT="/run/media/$USER/$2"
DEVICE=$(blkid -l -o device -t LABEL="$2")

if [ ! -d "$MOUNTPOINT" ]; then
    gio mount --device $DEVICE
fi

# Call duplicity...

It appears, however, that until the user session is active, the script cannot mount the disk. I can work around this by polling for an active session, but this is a crude solution to the problem:

#!/usr/bin/env bash

USER=$(whoami)
MOUNTPOINT="/run/media/$USER/$2"
DEVICE=$(blkid -l -o device -t LABEL="$2")

# Poll for an active session
for i in {1..30}
do
    SESSION_STATE=$(loginctl show-user "$USER" -p State --value)
    [ "$SESSION_STATE" != "active" ] && sleep 1s || break
done

if [ ! -d "$MOUNTPOINT" ]; then
    gio mount --device $DEVICE
fi

It seems like there should be a way to configure my systemd user service to require an active user session but I've been unable to work out how to do that. Does anyone know if this is possible?

suilven
  • 23
  • I don't know. Have you considered adding the disk to /etc/fstab with options noauto,user? That should allow mount as any unprivileged user. This is assuming the disk isn't encrypted or something, and that the technically more lax security wouldn't bother you. I haven't tested whether you have to use mount, or whether gio mount / the udisks backend are then smart enough to let you do it from an inactive session. – sourcejedi May 08 '18 at 16:23
  • You're mixing things that shouldn't be mixed. If your cron job depends on a mount, the script shouldn't try to do the mount, systemd should do it. And then if you want the mount to auto start on a user session, that shouldn't be part of the cron job at all. That should be configuration of the mount unit (or auto-start the cron and have the systemd dependency auto-start the mount). Then you can configure your cron unit to depend on the mount unit. Once all this is cleaned up it should work. Don't have time to write a full answer right now, but this might get you in the right direction. – phemmer May 08 '18 at 17:03
  • 1
    There's no cron job at all in the question, Patrick, just a per-user timer unit and a script run by the associated per-user service unit that that timer activates. And the questioner's problem is that by the time that per-user service management, started up by a login session, has fired off the pending timer, that login session itself has not yet got around to (allowing) mounting the things that the per-user service is needing to be mounted. It would be a bad answer to go on about cron jobs. It might be a good answer to make use of the per-user GNOME VFS service unit. – JdeBP May 09 '18 at 07:56
  • 1
    @sourcejedi I will investigate the /etc/fstab option again but iirc from the last time I tried that, the backup script still failed because the gnome-keyring had not yet initialized/unlocked and that was required so that duplicity could access the GPG keys. – suilven May 09 '18 at 23:27

2 Answers2

2

It looks (from the USER=$(whoami) line in your script) that you're running this service as a non-root user (probably by setting User= in your daily-backup.service file.)

You might want to consider setting up the service and timer units as user services, run by the systemd --user manager instead.

To do that, just create daily-backup.service and daily-backup.timer under the ~$USER/.config/systemd/user/ directory. You can essentially use the files you created in the system directory, for the most part unmodified. (You can remove the reference to User= since that will be the user you're setting it up for.)

Use systemctl --user ... commands to enable and start them and to check the status. For the install section, use:

[Install]
WantedBy=default.target

Since the user manager only knows about a default.target (and not e.g. multi-user.target.)

In order to have the user session start at boot, you can enable linger, in which case it will start early and stick around:

loginctl enable-linger $USER

(See documentation for loginctl enable-linger.)

In fact, looking at the Fedora Magazine article you referred to, it seems that's what they had in mind. The "Automate with a timer" part has references to $HOME/.config/systemd/user/.

If you want to read more on systemd user units, the Arch Linux Wiki has a nice article on it.

filbranden
  • 21,751
  • 4
  • 63
  • 86
  • Hmmm, re-reading your question and it seems you already have a user service... So when you're talking about "session" I guess you mean GNOME? Have you considered using systemd-mount instead of gio? – filbranden May 09 '18 at 05:43
  • 2
    The question did say "systemd user service" in both title and first sentence. And the questioner does talk about the timer triggering, and hence the service being activated, by per-user service management starting up at first login. – JdeBP May 09 '18 at 07:46
  • @JdeBP yes I realized that on re-reading the question, but I left it around as I was unsure whether the enable-linger would help... Indeed, it seems gnome-vfs-daemon is what is required here for gio to work, but it's unclear to me what starts it... – filbranden May 09 '18 at 13:10
  • @FilipeBrandenburger I was not aware of systemd-mount so I'll investigate that option. One thing to note is that if I allow Gnome to automount the disk and remove the lines concerned with mounting the disk from the script, the script still fails because the disk is not yet mounted at /run/media/user/disk-label when the backup tries to run. Adding the disk to /etc/fstab is another option but iirc I tried that and the script still failed because the gnome-keyring had not yet initialized and that was required to access the GPG keys to run duplicity. – suilven May 09 '18 at 23:24
  • @suilven One option you might want to consider is to trigger the backup when the disk is mounted (perhaps in addition to a timer.) This came up recently, see my answer at https://unix.stackexchange.com/a/441913/281844 for more details (though not completely sure whether doing so in a user unit will work, hmmm...) – filbranden May 10 '18 at 00:18
0

tl;dr (short answer)

I believe there is no such way, at least out-of-the-box (in form of a systemd unit directive or something). However, this is certainly doable from outside of systemd.

long answer

It would be nice if systemd supported event-based triggering (in a way similar to udev's) or allowed customization of auto-generated unit files. Either idea, if implemented, would solve this problem, but both actually are long-standing shortcomings of systemd.

You may try to implement this logic yourself. If I properly understand what are you trying to actually achieve, you desire the semantics of "delaying" start-up until the session becomes active rather than "skipping" start-up if the session is inactive when the timer triggers.

Then, the basic idea would be to have a daemon that starts as a systemd user service, listens for logind's state change events on the system bus and starts/stops the timer accordingly. This should be easily doable in Python (unfortunately, bash is out of the question because there are no shell-friendly tools that allow listening for signals on D-Bus).

intelfx
  • 5,365
  • Yes I'm trying to delay startup until the session is active and all services required by my backup script are available. It does appear to me that some interaction with the D-Bus is required here. I'm not a Python developer so I'm not going to try and implement your solution but I appreciate the feedback. My crude workaround does the job and I was mostly interested to know if there was a simple win involving systemd dependencies. – suilven May 14 '18 at 11:06