tl;博士(简短回答)

tl;博士(简短回答)

我创建了一个 systemd 用户服务,每天 17:00 运行一次口是心非的备份作业,大致基于 中的一篇文章软呢帽杂志。我正在运行带有 Gnome 桌面的 Fedora 28。我的 systemd 计时器配置是:

[Unit]
Description=daily-backup timer

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

[Install]
WantedBy=default.target

除非在备份运行时机器被关闭的情况下,否则这种方法效果很好。下次登录时,该persistent选项可确保备份立即运行。不幸的是,备份脚本此时失败,因为该脚本尝试在用户会话处于活动状态之前运行。备份脚本首先尝试安装备份磁盘(如果尚未安装):

#!/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...

但是,在用户会话处于活动状态之前,脚本似乎无法挂载磁盘。我可以通过轮询活动会话来解决此问题,但这是该问题的粗略解决方案:

#!/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

似乎应该有一种方法来配置我的 systemd 用户服务以需要活动用户会话,但我一直无法弄清楚如何做到这一点。有谁知道这是否可能?

答案1

看起来(从USER=$(whoami)脚本中的行来看)您正在以非 root 用户身份运行此服务(可能通过在文件User=中进行设置daily-backup.service。)

您可能需要考虑将服务和计时器单元设置为用户服务,由systemd --user管理器运行。

为此,只需在目录下创建daily-backup.service和。您基本上可以使用在系统目录中创建的文件,大部分无需修改。 (您可以删除对它的引用,因为这将是您为其设置的用户。)daily-backup.timer~$USER/.config/systemd/user/User=

使用systemctl --user ...命令启用和启动它们并检查状态。对于安装部分,使用:

[Install]
WantedBy=default.target

由于用户管理器只知道 a default.target(而不是例如multi-user.target。)

为了让用户会话在启动时启动,您可以启用 linger,在这种情况下,它将提前启动并保留下来:

loginctl enable-linger $USER

(请参阅文档loginctl enable-linger.)

事实上,看看你提到的 Fedora 杂志文章,他们似乎就是这么想的。 “使用计时器实现自动化”部分引用了$HOME/.config/systemd/user/.

如果您想了解有关 systemd 用户单元的更多信息,Arch Linux Wiki 有一个不错的内容关于它的文章

答案2

tl;博士(简短回答)

我相信没有这样的方法,至少没有开箱即用的方法(以 systemd 单元指令或其他形式)。然而,这在 systemd 外部当然是可行的。

长答案

如果 systemd 支持基于事件的触发(以类似于 udev 的方式)或允许自定义自动生成的单元文件,那就太好了。这两种想法如果实现的话,都可以解决这个问题,但这两者实际上都是 systemd 长期存在的缺点。

您可以尝试自己实现这个逻辑。如果我正确理解您想要实际实现的目标,您需要“延迟”启动直到会话变为活动状态的语义,而不是如果计时器触发时会话处于非活动状态则“跳过”启动。

然后,基本思想是有一个守护进程作为 systemd 用户服务启动,侦听系统总线上的 logind 状态更改事件并相应地启动/停止计时器。这在 Python 中应该很容易实现(不幸的是,bash 是不可能的,因为没有允许监听 D-Bus 上信号的 shell 友好工具)。

相关内容