7

I have a Red Hat Kickstart process which reports its progress at key points via a POST request to a status server.

This is fine during %pre and %post, but when the actual build is taking place between them, it's an informational black hole.

I've written a simple shell snippet that reports on the number of packages installed to give a rough idea of progress. I've placed the following in %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

However, when I launch this as a background task from %pre as above, it hangs waiting for the script to end -- %pre never completes (if I kill the spawned script %pre completes and the build proper starts).

I can't use nohup as it isn't available in the pre-installation environment, the same goes for using at now and screen.

I've attempted to use disown -a, which is available; this seems to successfully disown the process (such that it's owned by PID 1) but still it hangs waiting for the script to finish.

Can anyone offer me an alternative?

bxm
  • 4,561
  • 1
  • 20
  • 21
  • How do you start it? Did you end your `%pre` section with `%end`? – Chris Jun 04 '13 at 11:09
  • With `/bin/sh /tmp/rpm_watcher.sh &` ... yes, there's an `%end` tag. –  Jun 04 '13 at 11:31
  • Would it be a problem to kill the background task from the script when it reaches its end? Or you put the parent script's PID into the background script and check in each round whether `/proc/$PID` still exists. – Hauke Laging Jun 04 '13 at 14:46
  • Sorry I might not have made myself totally clear. The problem is that `%pre` never ends when this job is running; I'm surmising that Anaconda is detecting the child process was spawned (despite my later doing a `disown` on it) and is hanging around waiting for it to finish. If I flip to an console session and kill the script `%pre` completes and the build continues. – bxm Jun 04 '13 at 15:00
  • Not familiar with *kickstart* but perhaps you could pipe through `tee` to send output to a file. Or run some or all of the it using `screen -L` (creates ~/screenlog.0 log file) and monitor the progress that way. – justbrowsing Aug 14 '13 at 10:00

4 Answers4

3

You were very close to the solution. Anaconda (the installer) is written in Python, so I went digging into the code.

Ultimately, it executes the script like this:

    rc = iutil.execWithRedirect(self.interp, ["/tmp/%s" % os.path.basename(path)],
                                stdin = messages, stdout = messages, stderr = messages,
                                root = scriptRoot)

A bit more digging and you can find iutil.execWithRedirect defined in 'iutil.py'. This function ultimately uses subprocess.Popen (a Python built in) to execute the command. It also tries very hard to get the contents of STDOUT and STDERR from the %pre script.

The code looks like this:

    #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

So, with what you have, you get past the proc.wait() and os.close calls by forking into the background.

proc_std and proc_err are threads that repeatedly call readline on STDOUT and STDERR. They keep reading until EOF is encountered. Since your script inherits the STDOUT and STDERR socket from the %pre script, they will never encounter EOF. Ananconda then hangs waiting for the thread reading STDOUT to exit (on the 'proc_std.join()') line, which never happens.

It's a very confusing issue, but ultimately a very simple fix. Instead of:

/bin/sh /tmp/rpm_watcher.sh &

use

/bin/sh /tmp/rpm_watcher.sh > /dev/null 2>&1 < /dev/null &

This ensures your script doesn't inherit STDOUT and STDERR, so Anaconda doesn't hang and the install can continue.

devicenull
  • 176
  • 5
  • Interesting! I've long since moved onto other problems, but if I revisit this kickstart at some point I'll try out your suggestion and report back. Sounds promising though. – bxm Jul 04 '14 at 11:31
1

Rather than background the script what if you background the while loop in the script like this:

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 &

Notice I've added the ampersand at the end of the done line of the while loop.

References

slm
  • 363,520
  • 117
  • 767
  • 871
  • Thanks for your suggestion but I'm still having no joy (also tried some variations with `(` and `)` to create a sub-shell). Reading through the thread you referenced, it doesn't look like the OP found a solution either :( – bxm Jun 19 '13 at 08:37
0

Perhaps the implementation of Anaconda has evolved to break the proposed solution. I was never able to disown the process.

You can now use systemd in the Anaconda environment, and here's what worked for me (adapted to the question):

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
-1

Hmmm sometimes starting with "at now" does the job for me

at now returns immediatly - and the started process runs in the background...

atd has to run of course :)

e.g

/bin/echo "/custom/scripte/test.sh" | /usr/bin/at now
Anthon
  • 78,313
  • 42
  • 165
  • 222
fgordon
  • 11
  • 1