iotop script does not work via custom script execution
Question: I have CSF installed (configure safe firewall), it has a function to allow you
to have custom scripts executed on load average events.

My script:

    
    
    ##!/usr/bin/env bash
    iotop -bto --iter=1 2>&1 | mail -s "$HOSTNAME iotop output" incidents@
    

It works fine via bash shell but when executed by lfd (the monitoring process
of CSF), I get the following output:

    
    
    Traceback (most recent call last):
      File "/usr/bin/iotop", line 9, in <module>
        from iotop.ui import main
      File "/usr/lib/python2.6/site-packages/iotop/ui.py", line 13, in
    <module>
        from iotop.data import find_uids, TaskStatsNetlink, ProcessList, Stats
      File "/usr/lib/python2.6/site-packages/iotop/data.py", line 36, in
    <module>
        from iotop import ioprio, vmstat
      File "/usr/lib/python2.6/site-packages/iotop/ioprio.py", line 52, in
    <module>
        __NR_ioprio_get = find_ioprio_syscall_number(IOPRIO_GET_ARCH_SYSCALL)
      File "/usr/lib/python2.6/site-packages/iotop/ioprio.py", line 38, in
    find_ioprio_syscall_number
        bits = platform.architecture()[0]
      File "/usr/lib64/python2.6/platform.py", line 1073, in architecture
        output = _syscmd_file(executable, '')
      File "/usr/lib64/python2.6/platform.py", line 1021, in _syscmd_file
        rc = f.close()
    IOError: [Errno 10] No child processes
    

Can anyone shed some light on this?


Answer: Internally it calls an equivalent of:

    
    
    import os
    import sys
    
    f = os.popen('file -b "%s" 2> %s' % (sys.executable, os.devnull))
    f.read()
    f.close()
    

For `popen()` to work it has to be able to get the `SIGCHLD` signal telling it
that a child process exited. It seems that the environment that executes
`iotop` has a custom reaper process that intercepts `SIGCHLD` and prevents
python from getting notified about the process exiting. Thus when the function
calls `.close()`, python tries to kill the process that is already dead and
gets an error from the operating system.

If you cannot reconfigure the environment to let `SIGCHLD` pass, I think
you'll have ro resort to ugly hacking.

Wrapping `iotop` in a script that monkey-patches `platform.architecture()`
with a function that always returns the same tuple (something like `('64bit',
'ELF')`—consult the output of real `architecture()`) should let you progress.

Alternatively, you can just make a local copy of the `platform.py` file and
edit that directly, setting `PYTHONPATH` for the cron job to point to that new
file.

