如何在网络关闭之前触发挂起的 systemd 单元?

如何在网络关闭之前触发挂起的 systemd 单元?

(在 Stock Debian 测试中又称为拉伸又称为 9,所以我有一个常规的 systemd+logind+NetworkManager+GNOME 堆栈)

我有一对脚本想要在启动/关闭和恢复/挂起时运行。该脚本运行时需要网络存在。我尝试使用以下脚本进行此操作:

[Unit]
Description=Yamaha Reciever power
Requires=network-online.target
After=network-online.target
Before=sleep.target
Conflicts=sleep.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/av-up
ExecStop=/usr/local/bin/av-down
RemainAfterExit=yes

[Install]
WantedBy=graphical.target

这在启动/关闭时可以正常工作,但是在挂起期间它会运行网络已关闭,因此出现故障。

我已确定其原因是 systemd 下的挂起方式:

  1. 挂起是由程序(例如systemctl suspend)向登录发送 dbus 调用来启动的。
  2. 然后,Logind 向监听的任何人发送一个PrepareToShutdown dbus 信号。
  3. 然后,Logind 向 systemd 发送 StartUnit dbus 调用以运行 suspend.target 单元。

NetworkManager 监听PrepareToShutdown,因此在(2)处删除网络,而当systemd实际上在(3)处开始挂起时,我的单元被触发。 NetworkManager 对登录保持“禁止”锁定,以确保它在 (3) 之前关闭网络。 (旁注:使用 systemd 控制挂起/恢复顺序之类的东西似乎很疯狂,只是为了用登录来颠覆它,使东西绕过这个)

当网络仍在运行时,触发程序在挂起/恢复时运行的正确方法是什么?

我应该使用 NetworkManager 预关闭脚​​本吗?如果是这样,如果网络出现故障,我该如何阻止它触发,但我不是暂停?

有没有办法提前挂起进程?

有没有办法让 NetworkManager 保持网络运行更长时间?

注意:这不同于如何编写一个在网络中断之前触发的 Systemd 单元正如我所说的暂停/恢复。

答案1

灵感来自https://unix.stackexchange.com/a/139664/160746我将启动/停止脚本升级为完整的守护进程,并使其监听PrepareToShutdown 信号。这是一场与 NetworkManager 的每次启动/停止竞赛,但它似乎在我的系统上可靠地工作。

我已将我的代码和 systemd 单元上传到https://github.com/davidn/av

答案2

根据systemd 挂起文档,以及systemctl 手册页 systemctl suspend激活suspend.target.

systemctl list-dependencies suspend.target --after --all显示suspend.target调用systemctl-suspend.servicethen sleep.target。这意味着当您systemctl suspend致电默认操作顺序是:

suspend.target
|-systemd-suspend.service
  |-sleep.target

如果你放置了Before=sleep.target,那么你的操作顺序可能是:

suspend.target
|-systemd-suspend.service
  |-[custom service]
    |-sleep.target

所以你的服务正在运行 systemd-suspend.service做它的事情,这可能是你的问题。


您可以添加到您的服务文件中以获得正确的结果

Before=systemd-suspend.service

拨打电话后,systemctl daemon-reload您应该能够systemctl list-dependencies suspend.target --after --all看到您的服务出现在suspend.target和之间systemd-suspend.service。您的最终操作顺序应该是:

suspend.target
|-[custom service]
  |-systemd-suspend.service
    |-sleep.target

答案3

我有工作解决方案。对我来说这不是最好的,但也不是其他作品中最好的。将类似以下内容放入 /lib/systemd/system-sleep/tv_on_off :

#!/bin/sh
# should be placed at 
# /lib/systemd/system-sleep

if [ "${1}" = "pre" ]; then
   # about to suspend no network here
   systemctl restart network-manager.service
   nm-online

   # code that requires network
   /usr/local/bin/av-down

elif [ "${1}" = "post" ]; then
   # about to resume 
   systemctl restart network-manager.service
   nm-online

   # code that requires network
   /usr/local/bin/av-up
fi

我已经尝试过 @David 提到的 systemd 和 dbus 方法。即使使用 dbus 消息处理网络,也会在我的代码运行之前关闭。或者并行。

答案4

根据一个回复从 systemd-devel 邮件列表来看,在使用网络管理器时单独使用 systemd 是不可能的,因为网络管理器会自行侦听睡眠信号。解决方法是将您需要遇到的情况放入:

/etc/NetworkManager/dispatcher.d/pre-down.d/

请注意,每当接口出现故障时,这都会触发脚本,这在我的机器上没问题,但在您的机器上可能不行。人们或许可以检查 D-Bus 暂停信号是否已发出,但我还没有对此进行探讨。有一张关于此的票证网络管理器 gitlab 跟踪器

相关内容