(5 月 16 日更新了更多信息,请参阅帖子末尾的 journalctl 消息)
我创建了一个 systemd 计时器和服务,目的是运行一些 bash 脚本。具体来说,我的目标是在给定时间安装外部硬盘,然后安装 veracrypt 卷,以便运行一些异地备份操作。
每个脚本在我自己的管理员用户运行时都能正常工作,例如
sudo /path/to/script.sh
但是,在 systemd 服务运行时,有些脚本可以运行,有些脚本会失败。即 mount 脚本失败,但 unmount 脚本可以成功运行。
我非常确定这要么是权限问题,要么是用户身份运行问题……但我对 systemd 还不熟悉,似乎无法找到原因。我宁愿避免使用 systemctl --user 机制,因为它依赖于一种我不熟悉的特殊“linger”类型……但如果这是唯一的解决方案,我会接受它。
该功能的高级视图是这样的......
mountup.timer --> mountup.service --> mountup.sh --> mountusb.sh
反向操作也有类似的工作流程
这是代码...
/etc/systemd.system/mountup.timer
[Unit]
Description=run the mountup.service on a given schedule
[Timer]
Unit=mountup.service
OnCalendar=*-*-* *:00,30:00
#this means any day, month, year any hour... each 00 and 30 minute
Persistent=true
[Install]
WantedBy=timers.target
/etc/systemd/system/mountup.service
[Unit]
Description = Runs the mountup.sh script
[Service]
Type = simple
User = adminuseraccount
ExecStartPre = /bin/bash -c 'echo "systemctl says...mountup service triggered at $(date)" >> /usr/local/bin/mountlog.txt'
ExecStart = /usr/local/bin/mountup.sh
/usr/local/bin/mountup.sh
#! /bin/bash
# the fancy function below pipes the echo messages through a function that preprends the timestamp, then sends the output to the logfile
logit() {
while read
do
printf "%(%Y-%m-%d %T)T %s\n" -1 "$REPLY" >> /usr/local/bin/mountlog.txt
done
}
#the line below redirects standard output to logfile, along with standard error to the same,
#after routing them through the fancy function above
exec 3>&1 1>> >(logit) 2>&1
echo "mountup.sh says ... starting mountusb.sh..." &&
/usr/local/bin/mountusb.sh &&
echo "mountup.sh says ... mountusb.sh has been run" &&
/usr/local/bin/mountusb.sh
#! /bin/bash
usbuuid=9743y934y943
mount --uuid $usbuuid /mnt/targetlocation
非常感谢,我断断续续地这样做了大约 2 周,现在终于到了放弃并了解我做错什么的时候了。
编辑:来自sudo journalctl
mountup.service 尝试的相关输出......
Starting Runs the mountup.sh script...
mountup.service: Control process exited, code=exited, status=1/FAILURE
mountup.service: Failed with result 'exit-code'.
Failed to start Runs the mountup.sh script.
sudo journalctl
与 mountdown.service 尝试相关的输出...
Starting Runs the mountdown.sh script...
Started Runs the mountdown.sh script.
root : PWD=/ ; USER=root ; COMMAND=/usr/bin/umount /mnt/targetlocation
pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
pam_unix(sudo:session): session closed for user root
mountdown.service: Main process exited, code=exited, status=32/n/a
mountdown.service: Failed with result 'exit-code'.
据我所知,umount 命令的退出代码 32 仅表示卸载卷失败,仅此而已。
答案1
首先,您应该设置一个一次性服务,因为当它完成后您的脚本就会退出:
[Unit]
Description = Runs the mountup.sh script
[Service]
Type = oneshot
User = adminuseraccount
ExecStart = /usr/local/bin/mountup.sh
RemainAfterExit=true
StandardOutput=journal
StandardError=journal
然后你就可以用journalctl -u mountup.service
有了日志,我还会放弃所有文件句柄的重定向。另外,您/usr/local/bin/mountlog.txt
需要 root 权限才能写入。通常,在那里写入是不好的做法。但同样,我根本不会使用重定向。
而且,由于您使用 链接命令&&
,因此您可以考虑执行set -e
。这样,脚本会在出错时中止。这有一些注意事项(例如,错误构造的 if-greps 可能会中止您的程序),但对于您来说,这看起来是一个不错的选择。
这不是这解决方案本身,因为我们没有看到错误,但它应该可以帮助您。
另外,我想我会把所有东西都放在一个脚本中。在其中放入一个“退出陷阱”以便sync; umount /foo/bar
之后执行,这样你就可以确保当它完成时,至少已经刷新了文件系统缓冲区并且驱动器已卸载。这样就可以安全地拔下电源。我倾向于在这些情况下调用的原因sync
也是以防万一umount
失败。
关于卸载失败:如果这是您的问题/一个问题,您可能保持该位置打开,cd
例如,如果您进入它。