通过映射到标准输入的套接字与 Systemd 服务通信
Communicating with Systemd service through socket mapped to stdin
我正在创建我的第一个后台服务,我想通过套接字与其通信。
我有以下脚本 /tmp/myservice.sh
:
#! /usr/bin/env bash
while read received_cmd
do
echo "Received command ${received_cmd}"
done
以及下面的套接字/etc/systemd/user/myservice.socket
[Unit]
Description=Socket to communicate with myservice
[Socket]
ListenSequentialPacket=/tmp/myservice.socket
以及以下服务:
[Unit]
Description=A simple service example
[Service]
ExecStart=/bin/bash /tmp/myservice.sh
StandardError=journal
StandardInput=socket
StandardOutput=socket
Type=simple
思路是了解如何与后台服务通信,这里使用unix文件套接字。当从 shell 启动并读取标准输入时,该脚本运行良好,我认为通过设置 StandardInput = "socket"
它会以相同的方式从套接字读取。
然而,当我 运行 nc -U /tmp/myservice.socket
命令 returns 时,我得到以下输出:
$ journalctl --user -u myservice
-- Logs begin at Sat 2020-10-24 17:26:25 BST, end at Thu 2020-10-29 14:00:53 GMT. --
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21941]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21942]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21943]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21944]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21945]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Start request repeated too quickly.
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Failed with result 'start-limit-hit'.
Oct 29 08:40:16 shiny systemd[1689]: Failed to start A simple service example.
我是不是误解了套接字的工作原理?为什么 read
无法从套接字读取?我是否应该使用另一种机制与我的后台服务通信(正如我所说,这是我的第一个后台服务,所以我可能会在这里做非常规的事情)?
我唯一看到的使用 shell 脚本的是 ListenStream=
而不是 ListenSequentialPacket=
。 (显然,这意味着您丢失了数据包边界,但是 shell 读取通常面向从流中读取以 \n
结尾的行,因此这通常不是问题)。
但是缺少的最重要的东西是额外的 Accept
行:
[Socket]
ListenStream=...
Accept=true
据我所知,如果没有这个,服务将被传递一个套接字,它必须首先在该套接字上进行套接字 accept()
调用,以获得实际的连接套接字(因此 read
错误) .该服务还必须处理所有进一步的连接。
通过使用Accept=true
,将为每个新连接启动一个新服务,并将通过立即可用的套接字。但是请注意,这意味着现在必须对服务进行模板化,即调用 myservice@.service
而不是 myservice.service
.
(对于数据报套接字,Accept
必须默认为 false)。参见 man systemd.socket
。
我正在创建我的第一个后台服务,我想通过套接字与其通信。
我有以下脚本 /tmp/myservice.sh
:
#! /usr/bin/env bash
while read received_cmd
do
echo "Received command ${received_cmd}"
done
以及下面的套接字/etc/systemd/user/myservice.socket
[Unit]
Description=Socket to communicate with myservice
[Socket]
ListenSequentialPacket=/tmp/myservice.socket
以及以下服务:
[Unit]
Description=A simple service example
[Service]
ExecStart=/bin/bash /tmp/myservice.sh
StandardError=journal
StandardInput=socket
StandardOutput=socket
Type=simple
思路是了解如何与后台服务通信,这里使用unix文件套接字。当从 shell 启动并读取标准输入时,该脚本运行良好,我认为通过设置 StandardInput = "socket"
它会以相同的方式从套接字读取。
然而,当我 运行 nc -U /tmp/myservice.socket
命令 returns 时,我得到以下输出:
$ journalctl --user -u myservice
-- Logs begin at Sat 2020-10-24 17:26:25 BST, end at Thu 2020-10-29 14:00:53 GMT. --
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21941]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21942]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21943]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21944]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: Started A simple service example.
Oct 29 08:40:16 shiny bash[21945]: /tmp/myservice.sh: line 3: read: read error: 0: Invalid argument
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Succeeded.
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Start request repeated too quickly.
Oct 29 08:40:16 shiny systemd[1689]: myservice.service: Failed with result 'start-limit-hit'.
Oct 29 08:40:16 shiny systemd[1689]: Failed to start A simple service example.
我是不是误解了套接字的工作原理?为什么 read
无法从套接字读取?我是否应该使用另一种机制与我的后台服务通信(正如我所说,这是我的第一个后台服务,所以我可能会在这里做非常规的事情)?
我唯一看到的使用 shell 脚本的是 ListenStream=
而不是 ListenSequentialPacket=
。 (显然,这意味着您丢失了数据包边界,但是 shell 读取通常面向从流中读取以 \n
结尾的行,因此这通常不是问题)。
但是缺少的最重要的东西是额外的 Accept
行:
[Socket]
ListenStream=...
Accept=true
据我所知,如果没有这个,服务将被传递一个套接字,它必须首先在该套接字上进行套接字 accept()
调用,以获得实际的连接套接字(因此 read
错误) .该服务还必须处理所有进一步的连接。
通过使用Accept=true
,将为每个新连接启动一个新服务,并将通过立即可用的套接字。但是请注意,这意味着现在必须对服务进行模板化,即调用 myservice@.service
而不是 myservice.service
.
(对于数据报套接字,Accept
必须默认为 false)。参见 man systemd.socket
。