我正在运行一个完全忽略 SIGTERM 的游戏服务器程序(SRCDS)。彻底关闭服务器的唯一方法是交互式地键入“quit”。
有什么方法可以将此程序包装在 bash 脚本中,该脚本将捕获 SIGTERM 并将“退出”发送到程序的 STDIN?否则,在正常操作中,包装器应该转发 STDIN 和 STDOUT,就好像根本不存在一样。
我想要实现的目标的图表:
普通手术:
--- STDIN ---> | | --- STDIN ---> | |
| Bash Script | | Server Program |
<-- STDOUT --- | | <-- STDOUT --- | |
SIGTERM 发送:
"quit"
--- STDIN ---> | | --- STDIN ---> | |
-- SIGTERM --> | Bash Script | | Server Program |
<-- STDOUT --- | | <-- STDOUT --- | |
答案1
这取决于该程序是否期望从终端运行。如果是的话,我认为你不能这样做bash
;你应该使用类似的东西expect
。例子:
包装器.期望:
puts "<<< pid=[pid] >>>"
spawn {*}$argv
trap {send "quit\n"} SIGTERM
interact
以 bc 为例:
expect wrapper.expect bc -q
<<< pid=4771 >>>
spawn bc -q
kill 4771
来自另一个窗口的A将导致expect
向 发送quit
一行bc
。请注意,这4771
是进程的 pid expect
,而不是bc
子进程的 pid。
另一种选择是使用ioctl在当前终端中TIOCSTI
注入该行;quit
但同样,您将无法从 shell 中执行此操作;你仍然需要一些小的 C(或 perl、python 等)程序来调用ioctl()
(并且TIOCSTI
已经在 OpenBSD 等系统中被删除)。
答案2
尝试这个:
#!/bin/bash
coproc cat -n # replace 'cat -n' with actual server program to be launched
# first some necessary file-descriptors fiddling
exec {srv_input}>&${COPROC[1]}-
exec {srv_output}<&${COPROC[0]}-
# background commands to relay normal stdin/stdout activity
cat <&0 >&${srv_input} &
cat <&${srv_output} >&1 &
# set signal handler up
term_received=false ; trap 'term_received=true' SIGTERM
# endless loop waiting for events
while true; do
# wait for server to exit or sigterm received
wait ${COPROC_PID}
exit_status=$?
# if sigterm received:
if [ $exit_status -gt 128 ] && $term_received ; then
# kill proxy command relaying stdin to server
kill %2
# send quit to server's stdin
echo $'\n'quit >&${srv_input}
# close server's stdin
exec {srv_input}<&-
# wait for actual server to exit
wait ${COPROC_PID}
exit $?
# something else happened: kill proxy commands and exit with server's own exit status
else
kill %2
kill %3
fi
exit $exit_status
done
如果这个脚本回答了您的问题,我将添加更完整的解释