systemd 服务中的 /dev/stderr

systemd 服务中的 /dev/stderr

我正在编写一个应该启动 LTE 连接的服务。

但是我用来连接的实用程序(sakis3g)正在写入/dev/stderr,这在 systemd 中不可用,并且日志充满了

Cannot create /dev/stderr: No such device or address

有什么方法可以解决这个问题吗?

不幸的是,更改实用程序脚本/二进制文件不是一个选项。

编辑:这是服务:

[Unit]
Description=LTE
After=syslog.target network-online.target
Before=openvpn-client@client
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/mav/LTE/
ExecStartPre=/sbin/ip link set wwan0 down
ExecStart=/opt/mav/LTE/sakis3g connect -g --sudo
ExecStop=/opt/mav/LTE/sakis3g disconnect --sudo

[Install]
WantedBy=multi-user.target

并且它在这种状态下工作。但是,当我从更详细的调试输出标志开始时sakis3g-g就会stderr出现错误。

答案1

如果没有 stderr,您将不会看到该错误。

在这里,错误是关于无法打开/dev/stderr文件,在 Linux 上是指向 fd 2 上打开的实际文件的符号链接(而在其他系统上,打开 /dev/stderr 就像执行 a 一样dup(2))。

这里的问题可能是 fd 2 在套接字(inet TCP、Unix 流或其他)上打开,而您不能打开open()套接字文件。

如果你运行:

sudo lsof -aU +E -d 2

在使用的系统上,systemd您会注意到大多数服务的 fd 2 是到systemd-journald.

作为解决方法,您可以将其启动为:

bash -o pipefail -c '{ /opt/mav/LTE/sakis3g connect -g --sudo 2>&1 >&3 3>&- | cat >&2 3>&-; } 3>&1'

也就是说,使 stderr 成为管道(to cat)而不是套接字,cat将管道上接收到的内容转发到原始 stderr (套接字),并确保我们使用该pipefail选项保留命令的退出状态。

无论如何,其来源sakis3g是可用的并且sakis3g初始化文件是一个 bash 脚本,它恰好执行以下操作:

echo text >> /dev/stderr

代替:

echo test >&2

甚至还有一些> /dev/stderr反而>> /dev/stderr更错误的。它没有tee -a /dev/stderr | other cmd合法使用,/dev/stderr也不能与套接字上的 stderr 一起使用,因此很容易修复。

您可能希望通过在以下地址提出问题来让他们知道该问题:https://github.com/Trixarian/sakis3g-source/issues

答案2

tee我在尝试在服务内进行标准输出时遇到了类似的问题。虽然这不是OP的确切问题另一个答案称其为“合理使用/dev/stderr”,所以我觉得值得在这里发布,以防未来有人需要它。

也就是说我有以下内容:

command1 | tee /proc/$$/fd/1 | command2

在这种情况下,/proc/$$/fd/1始终是 shell 的/dev/stdout,因此它通常在管道中工作。尽管拒绝打开套接字,但它仍然存在相同的问题。

我想出的解决方案是tee通过进程替换来交换:

command1 | tee >(command2)

这还没有完全实现,因为现在 Bash 不会等待command2退出。为了解决这个问题,我们可以使用以下方法这个另一个答案

{ { command1 | tee >(command2); } 3>&1 >&4 4>&- | cat; } 4>&1

相关内容