Bash 通过映射到stdin的套接字与Systemd服务通信

Bash 通过映射到stdin的套接字与Systemd服务通信,bash,sockets,systemd,Bash,Sockets,Systemd,我正在创建我的第一个后台服务,我想通过套接字与它通信 我有以下脚本/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 m

我正在创建我的第一个后台服务,我想通过套接字与它通信

我有以下脚本
/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启动并读取stdin时,脚本运行良好,我认为通过设置
StandardInput=“socket”
它将以相同的方式从socket读取

然而,当我运行
nc-U/tmp/myservice.socket
时,命令会立即返回,我有以下输出:

$ 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.

我是否误解了插座的工作原理?为什么
读取
无法从套接字读取?我是否应该使用另一种机制与我的后台服务进行通信(正如我所说,这是我的第一个后台服务,因此我可能会在这里做一些非常规的事情)?

我唯一看到的使用shell脚本的方法是
ListenStream=
,而不是
ListenSequentialPacket=
。(显然,这意味着您将丢失数据包边界,但shell读取通常是从流中读取以
\n
结尾的行,因此这通常不是问题)

但是缺少的最重要的东西是额外的
Accept
行:

[Socket]
ListenStream=...
Accept=true
据我所知,如果不这样做,服务将被传递一个套接字,它必须首先在该套接字上执行socket
accept()
调用,以获取实际的连接套接字(因此出现
读取
错误)。服务还必须处理所有其他连接

通过使用
Accept=true
,将为每个新连接启动一个新服务,并将其传递给立即可用的套接字。但是,请注意,这意味着现在必须对服务进行模板化,即调用
myservice@.service
而不是
myservice.service


(对于数据报套接字,
Accept
必须默认为false)。请参见
man-systemd.socket

谢谢您的回答。如果我不想在每个连接上启动一个新的服务(比如说myservice管理一个我不想重置的状态),有没有办法实现与“接受”相同的效果但是,如果每次客户端连接到套接字时都不生成新服务,那么有一个新的systemd实用程序
systemd socket proxyd
可以为您执行accept,但我不确定如何使用它。它的手册页中有示例。也许你可以用fifo代替套接字,
listenfo
,但我目前对此也不太了解。