我有一个 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-success
或restart=no
是您唯一的选择。
其他一些提示:
User=admin
- 您正在
sudo
脚本中使用。这不仅依赖于NOPASSWD:
您的 sudoers,而且还提升admin
到root
.如果您确实需要 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=always
为Restart=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