![systemd 忽略单元文件中的 ExecStop,将其作为 ExecStart 的一部分运行](https://linux22.com/image/892072/systemd%20%E5%BF%BD%E7%95%A5%E5%8D%95%E5%85%83%E6%96%87%E4%BB%B6%E4%B8%AD%E7%9A%84%20ExecStop%EF%BC%8C%E5%B0%86%E5%85%B6%E4%BD%9C%E4%B8%BA%20ExecStart%20%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%E8%BF%90%E8%A1%8C.png)
我正在尝试让几个 VirtualBox 映像在启动时自动启动,并在主机关闭前正确关闭。我有一个 bash 脚本,/usr/local/bin/vmctl.sh
它使用对 VBoxManage 的调用来处理客户机映像的启动和停止。启动调用非常简单 - 它只是遍历一个映像列表并调用VBoxManage startvm --type headless "<imgname>"
然后退出 0。停止调用循环遍历列表并调用VBoxManage controlvm "<imgname>" acpipowerbutton
然后循环直到VBoxManage list runningvms
返回一个空列表或 60 秒后才退出 0。从命令行运行脚本非常有效。
我已经在以下位置设置了一个单元文件/lib/systemd/system/vmctl.service
:
[Unit]
Description=VirtualBox Control
After=virtualbox.service
[Service]
ExecStart=/usr/local/bin/vmctl.sh start
ExecStop=/usr/local/bin/vmctl.sh stop
[Install]
WantedBy=multi-user.target
当我运行 时systemctl start vmctl.service
,它会调用启动和停止行。当我调用 时,systemctl stop vmctl.service
系统日志中有一个条目显示,Stopped VirtualBox Control
但它不执行任何操作。
我对 systemd 完全是个新手。我最近将这个 Ubuntu 机器升级到了 16.04。我很确定这种行为有一个简单的解释,只是我没有看到。
谢谢!
根据马克的建议进行更新:
我确认了语法systemd-analyze verify /etc/systemd/system/vmctl.service
(将文件移到那里后 - 感谢您的提示)。然后我按照您的建议更改了 ExecStart 和 ExecStop,运行systemctl daemon-reload
后仍然看到相同的行为。日志显示调用时两者都在执行systemctl start vmctl
,但运行时均未执行systemctl stop vmctl
:
# journalctl -u vmctl | tail
.
.
.
Apr 06 19:28:18 macmi10-builder systemd[1]: Started VirtualBox Control.
Apr 06 19:28:18 macmi10-builder echo[13901]: I started
Apr 06 19:28:18 macmi10-builder echo[13904]: I stopped
Apr 06 19:28:33 macmi10-builder systemd[1]: Stopped VirtualBox Control.
答案1
正如其他人提到的,问题在于vmctl.sh
立即退出。与@Christophe的回答相反,分叉很可能不会起作用,因为vmctl.sh
很可能不会分叉。您需要的是一个oneshot
带有的服务RemainAfterExit=true
。如果您将其更改为oneshot
,您将获得完全相同的行为。该RemainAfterExit
部分告诉它即使在ExecStart
退出后,服务仍应被视为正在运行,因此它不应该运行ExecStop
(s)。
答案2
你的systemd
语法是正确的。你的问题出在其他地方。
首先,您可以使用以下命令确认语法本身是否正确:
systemd-analyze verify /path/to/your/vmctl.service
其次,尝试替换以下几行:
ExecStart=/bin/echo "I started"
ExecStop=/bin/echo "I stopped"
运行systemctl start vmctl
或后systemctl stop vmctl
,使用journalctl -u vmctl
检查日志。我希望您确认systemd
运行了正确的命令。
也/lib/systemd/system
可用于包管理 systemd 文件。人类修改和手动管理的文件应该放在/etc/systemd/system
答案3
服务单元的默认类型设置为类型=简单,当配置了 ExecStart= 的进程是服务的主进程时使用。这样的单元将等待 ExecStart 指定的进程返回,然后通过运行 ExecStop 指定的进程来停用。在您的例子中,这将在虚拟机启动后立即发生(这不是您想要的)。
类型=分叉当 ExecStart 指定的进程预计在启动完成后退出,而其子进程继续在后台运行时,使用此方法。这是传统 UNIX 守护进程的行为,也是您的建议选择。ExecStop 指定的进程将在服务崩溃或执行“systemctl stop vmctl”命令时运行。
因此你的单元文件应该是:
[Unit]
Description=VirtualBox Control
After=virtualbox.service
[Service]
Type=forking
ExecStart=/usr/local/bin/vmctl.sh start
ExecStop=/usr/local/bin/vmctl.sh stop
[Install]
WantedBy=multi-user.target