我有一个 Red Hat Kickstart 流程,它通过向状态服务器发出 POST 请求来报告关键点的进度。
%pre
在和期间这很好%post
,但是当它们之间发生实际构建时,这是一个信息黑洞。
我编写了一个简单的 shell 片段,用于报告已安装的软件包数量,以粗略了解进度。我已将以下内容放入%pre
:
%pre
## various other stuff here, all works fine ##
cat > /tmp/rpm_watcher.sh << EOF_RPM
PREV=-1
while true
do
COUNT="\$(rpm -qa | wc -l)"
if [ \${COUNT} -ne \${PREV} ] ; then
/bin/wget --post-data " ${Hostname} : Package count \${COUNT}" ${builddest}/log
PREV=\${COUNT}
fi
sleep 15
done
EOF_RPM
/bin/sh /tmp/rpm_watcher.sh &
disown -a
%end
但是,当我从上面将其作为后台任务启动时%pre
,它会挂起等待脚本结束 -%pre
永远不会完成(如果我终止生成的脚本%pre
完成并且构建正确开始)。
我无法使用,因为它在预安装环境中不可用,使用和 也是nohup
如此。at now
screen
我尝试使用disown -a
,它是可用的;这似乎成功地否认了该进程(这样它就属于 PID 1),但它仍然挂起等待脚本完成。
谁能给我提供替代方案吗?
答案1
你已经非常接近解决方案了。 Anaconda(安装程序)是用 Python 编写的,因此我深入研究了代码。
最终,它执行这样的脚本:
rc = iutil.execWithRedirect(self.interp, ["/tmp/%s" % os.path.basename(path)],
stdin = messages, stdout = messages, stderr = messages,
root = scriptRoot)
进一步挖掘,您可以找到“iutil.py”中定义的 iutil.execWithRedirect。该函数最终使用 subprocess.Popen (Python 内置)来执行命令。它还非常努力地从 %pre 脚本中获取 STDOUT 和 STDERR 的内容。
代码如下所示:
#prepare tee proceses
proc_std = tee(pstdout, stdout, program_log.info, command)
proc_err = tee(perrout, stderr, program_log.error, command)
#start monitoring the outputs
proc_std.start()
proc_err.start()
proc = subprocess.Popen([command] + argv, stdin=stdin,
stdout=pstdin,
stderr=perrin,
preexec_fn=chroot, cwd=root,
env=env)
proc.wait()
ret = proc.returncode
#close the input ends of pipes so we get EOF in the tee processes
os.close(pstdin)
os.close(perrin)
#wait for the output to be written and destroy them
proc_std.join()
del proc_std
proc_err.join()
del proc_err
因此,利用您所拥有的,您可以通过分叉到后台来绕过 proc.wait() 和 os.close 调用。
proc_std和proc_err是重复调用的线程阅读线在 STDOUT 和 STDERR 上。他们继续读取直到遇到 EOF。由于您的脚本从 %pre 脚本继承了 STDOUT 和 STDERR 套接字,因此它们永远不会遇到 EOF。然后 Ananconda 挂起,等待读取 STDOUT 的线程退出(在“proc_std.join()”行上),但这从未发生。
这是一个非常令人困惑的问题,但最终是一个非常简单的解决方案。代替:
/bin/sh /tmp/rpm_watcher.sh &
使用
/bin/sh /tmp/rpm_watcher.sh > /dev/null 2>&1 < /dev/null &
这可确保您的脚本不会继承 STDOUT 和 STDERR,因此 Anaconda 不会挂起并且安装可以继续。
答案2
如果您将脚本中的 while 循环设置为背景,而不是让脚本背景化,如下所示:
while true
do
COUNT="\$(rpm -qa | wc -l)"
if [ \${COUNT} -ne \${PREV} ] ; then
/bin/wget --post-data " ${Hostname} : Package count \${COUNT}" ${builddest}/log
PREV=\${COUNT}
fi
sleep 15
done &
done
请注意,我在循环行的末尾添加了“&”号while
。
参考
答案3
也许 Anaconda 的实现已经发展到打破了所提出的解决方案。我始终无法否认这个过程。
您现在可以在 Anaconda 环境中使用systemd
,这对我有用(适应问题):
cat > /etc/systemd/system/rpm-watcher.service << EOF
[Service]
Type=simple
ExecStart=/bin/sh /tmp/rpm_watcher.sh
EOF
systemctl daemon-reload
systemctl start rpm-watcher
答案4
嗯,有时以“ at now
”开头对我来说很合适
现在立即返回 - 并且启动的进程在后台运行......
atd
当然必须运行:)
例如
/bin/echo "/custom/scripte/test.sh" | /usr/bin/at now