if-up.d 启动一个脚本的两个实例

if-up.d 启动一个脚本的两个实例

案件
在我们的 Ubuntu Xenial(16.04)站上,我托管了一个用 Python 编写的机器人,该机器人连接到我们的 IRC 服务器,该服务器设置为在/etc/network/if-up.d/

尽管脚本设置为仅运行一个实例,但我们的机器人却连接了两次,并且如果其用户名已被占用,它必须关闭当前连接并使用下一个用户名重新连接。

故障排除
我曾经ps aux | grep bot_name.py$确定问题不在脚本级别,但由于某种原因,当 Ubuntu 连接到互联网时,该脚本的两个实例正在运行。

我在脚本级别创建了一个解决方法,用于检查有多少个脚本实例在本地运行,如果实例数量除一之外,它将关闭该脚本。

问题
哪些原因if-up.d/可能导致启动存储在其目录中的单个脚本的两个实例?

if-up.d/脚本
中的脚本if-up.d/是一个 sh 脚本,它包装了对 python 的调用,因此它会等待 irc 的超时时间然后连接:

sleep 269
sh -c "python /dir/to/bot.py >> /dir/to/log" & disown

其他来源
到目前为止,我已经看到了这个问题的镜像这里在不同的环境中,有人使用 Python 脚本发送电子邮件,并且由于脚本启动了多个实例,收件人收到了多封电子邮件。

我也能找到这个问题在16.04服务器故障有人遇到过类似的问题,列出的答案表明它可能运行了两次,因为有一个实例是为 IPv4 和 IPv6 启动的,但我不确定这是否真的是根本原因,或者如何指定您只想使用 IPv4 并放弃 IPv6 调用(如果是这种情况)。

在我们的 3 个案例中,Ubuntu 16.04 似乎是共同点,这让我怀疑这是否是一个错误。

答案1

除了检查 中的接口之外$IFACE,您可能还想检查 中的地址系列$ADDRFAM。IPv4 为“inet”,IPv6 为“inet6”。

中的脚本/etc/network/if-*.d不仅针对每个接口运行,而且对于每个地址系列也适用如果已配置(IPv4 和 IPv6)。

例如,如果您只需要脚本针对特定接口运行并且仅针对其 IPv4 地址运行,则可以从如下操作开始:

[ "$IFACE"   = "eth0" ] || exit 0  # we only want interface "eth0"
[ "$ADDRFAM" = "inet" ] || exit 0  # we only want IPv4 (not "inet6" for IPv6)

答案2

进一步研究后16.04 if-up.d/,我发现有人在运行 Debian 7.3 时遇到了类似的问题,并找到了答案。这不是一个错误,我遇到的问题在此处进行了解释@Søren Løvborg 的回答

一般来说,ifup/ifdown 不能保证你的脚本只运行一次。因此你的脚本必须是幂等的;也就是说,包含自己的逻辑,不建立已经存在的隧道。当然,最好的方法是通过实际检查隧道是否已经存在来启动脚本:

# Exit if network device "tun0" exists.
ip link show dev tun0 >/dev/null 2>&1 && exit 0

此外,if-up.d 等脚本针对每个启动的接口运行。正如 Jeff 正确观察到的那样,if-*.d 脚本没有接收任何参数来指定启动哪个接口。
相反,根据接口(5)手册页:

   There  exists  for  each  of  the  above  mentioned options a directory
   /etc/network/if-<option>.d/ [...]

   All of these commands have access to the  following  environment  vari‐
   ables.

   IFACE  physical name of the interface being processed

   LOGICAL
          logical name of the interface being processed

  [... and more ...]

解决方案是启动脚本并检查正确的 IFACE 值,例如:

# Exit if we're not starting "eth0".
[ "$IFACE" = 'eth0' ] || exit 0

否则,该脚本将针对每个网络设备(包括 lo)运行,此时建立隧道可能为时过早。

也就是说,我的问题是lo除了我的eth0连接之外,它还执行接口的脚本。我采取了其中一个步骤,即使用逻辑对脚本进行编程,使其不运行多次,而只运行一次,而无需调整代码,只需在执行前指定要使用的接口即可。

如果我想返回并在 sh 级别解决这个问题,我只需要用此行更新我的脚本,因为我想要使用的接口是我的以太网连接

[ "$IFACE" = 'eth0' ] || exit 0
sleep 269 
sh -c "python /dir/to/bot.py >> /dir/to/log" & disown

相关内容