仅当脚本条件为 True 时才运行 systemd 服务

仅当脚本条件为 True 时才运行 systemd 服务

我有一个 systemd 服务,可以写入 LCD 屏幕,如下所示;

[Unit]
Description=LCD Screen


[Service]
Type=simple
User=admin
WorkingDirectory=/lcd
ExecStart=sudo /run_lcd.py
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=lcd-service
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

我只希望在液晶显示屏连接到机器时运行该服务。由于面板是通过串行端口连接的,唯一了解的方法是将特定消息发送到 LCD 屏幕,如果连接,LCD 屏幕将返回一个值。如果我在脚本中运行它,有没有办法获取脚本的输出并让服务根据输出运行?

我看过systemd 中的条件和断言但它没有显示做我想做的事情的选项。任何帮助,将不胜感激。

答案1

在埃德加的回答中,ExecStartPre=还不错,但如果它阻止了你的执行ExecStart=,你的服务就会被标记为failed

你也可以尝试一下ExecCondition=。这种行为就像是ExecStartPre=和 的Condition*=混合体。

退出代码 行为
0 机组继续启动
1-254 开始被放弃,单元继续inactive (dead)
255 开始被放弃,单位变成failed

这是一个简单的例子:

$ cat condition.service 
[Service]
ExecCondition=/bin/false
ExecStart=/bin/sleep 20

$ systemctl --user start  condition.service
$ systemctl --user status condition.service
● condition.service
     Loaded: loaded 
     Active: inactive (dead)

Starting condition.service...
condition.service: Control process exited, code=exited, status=1/FAILURE
condition.service: Skipped due to 'exec-condition'.

你的restart=always提出了一个问题。如果ExecCondition=禁止启动,它将再次尝试...永远...(RestartSec=防止它达到启动限制)。这可能不是你想要的。

我会用restart=on-failure.如果您的服务返回EXIT_SUCCESS,那么它可能已完成其工作,您可能不需要要求它再次完成该工作。但是,如果遇到错误,始终可以return EXIT_FAILURE;重新启动。 ExecCondition=使这成为可能。

使用 时ExecStartPre=,如果您的脚本尝试禁止服务,您的服务将失败退出。这意味着restart=on-failure您无法使用,并且restart=on-successrestart=no是您唯一的选择。


其他一些提示:

  • User=admin
    • 您正在sudo脚本中使用。这不仅依赖于NOPASSWD:您的 sudoers,而且还提升adminroot.如果您确实需要 root 权限来运行此脚本,则只需以 root 身份直接运行该服务并删除此行即可。
  • ExecStart=sudo /run_lcd.py
    • run_lcd.py通常不应安装到/.如果这是您编写的(或手动安装的)脚本,请将其放入/usr/local/bin.当需要在新硬件上重新创建服务器时,这样的组织确实可以帮助您找到“所有本地内容”。
  • Type=simple
    • 我认为明确一点总没有坏处,但这是 的默认值Type=。我不会费心添加这个,因为它只会让文件变得混乱。
  • Standard{Output,Error}=syslog
    • syslog选项已从 systemd 中删除。它可能仍然可以向后兼容,但已从文档。删除此行以使用默认的日志(它将根据需要转发)。文档(检查了 Debian Buster)曾经说过:

    系统日志除了日志之外,还将标准输出连接到 syslog(3) 系统 syslog 服务。请注意,日志守护进程通常配置为将其收到的所有内容转发到系统日志,在这种情况下,此选项与日志没有什么不同。

  • RestartSec=10
    • 这是人们用来避免达到重新启动限制的一种技巧。它对于在multi-user.target启动后启动图形应用程序的人特别有用。更好的解决方案是仅在资源准备就绪后才启动您的设备。使用WantedBy=graphical.target通常是正确的解决方案。 After=graphical.target还可以确保服务在一切准备就绪之前不会启动。由于您的服务似乎与图形相关,我怀疑这些会有所帮助。不管怎样,一旦你最终有了一个表现良好的服务,RestartSet=应该是没有必要的。

把它放在一起我会使用:

[Unit]
Description=LCD Screen
After=graphical.target

[Service]
WorkingDirectory=/lcd
ExecCondition=/usr/bin/python3 /usr/local/bin/checkScreen.py
ExecStart=/usr/local/bin/run_lcd.py
SyslogIdentifier=lcd-service
Restart=on-failure

[Install]
WantedBy=graphical.target

答案2

您必须更改Restart=alwaysRestart=on-success,正如@Panki所说,您应该使用ExecStartPre.

所以我建议你使用另一个脚本(我假设是Python)来检查屏幕是否已连接,例如一个简单的Python脚本是:

/checkScreen.py

#you will have to add before this lines the code
#used for getting the status of screen

if screenIsConnected:
    exit(0)
else:
    exit(1)

您的 systemd 服务将变成:

[Unit]
Description=LCD Screen

[Service]
Type=simple
User=admin
WorkingDirectory=/lcd
ExecStartPre=/usr/bin/python3 /checkScreen.py
ExecStart=sudo /run_lcd.py
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=lcd-service
Restart=on-success
RestartSec=10

[Install]
WantedBy=multi-user.target

相关内容