Python becoming orphaned over ssh

Jean-Paul Calderone exarkun at twistedmatrix.com
Thu Sep 30 10:01:09 EDT 2010


On Sep 30, 9:08 am, David <wizza... at gmail.com> wrote:
> On Wed, Sep 29, 2010 at 6:49 PM, John Nagle <na... at animats.com> wrote:
> >   Python's signal handling for multithread and multiprocess programs
> > leaves something to be desired.
>
> Thanks for the confirmation (that I'm not missing something obvious).
>
> I've reported a bug for this behavior in the Python issue tracker.
>
> In the meanwhile, I've made a workaround function called
> "check_call_and_monitor_ppids", that behaves like
> subprocess.check_call, except that it regularly checks if the parent
> pid "chain" (up to init process) changes during execution, and then
> terminates the subprocess and raises an exception.
>
> Actually I tried this before, and it didn't work. But strangely, it
> seems to work fine so long as I don't try to print any warning
> messages to stderr or stdout from the Python script (though, the
> called tool itself may print to stdout or stderr without problems).
> Quite peculiar...
>
> Anyway, I hope that one of the Python developers will fix this sometime.
>
> David.

Python ignores SIGPIPE by default.  The default SIGPIPE behavior is to
exit.  This is sort of what people on POSIX expect.  If you're talking
to another process over a pipe and that process goes away, and then
you write to the pipe, you get a SIGPIPE and you exit (of course, if
it takes you 20 minutes before you do another write, then it's 20
minutes before you exit).

But with SIGPIPE ignored, a Python process won't do exactly this.
Instead, you'll get an exception from the write.  If you don't handle
the exception, then it'll propagate to the top-level and you'll exit.
Just like with a "normal" process.  Except you also get the option to
doing something other than exiting.  Pretty nice.

But signal dispositions are inherited by child processes.  So you run
ping from your short Python program, and it inherits SIGPIPE being
ignored.  And it's written in C, not Python, so when it writes to the
pipe, there's no exception.  So ping never gets any indication that it
should exit.  No Python writes ever happen in this scenario.  The SSH
supplied stdout is shared with the ping process, which writes to it
directly.

You can fix this by resetting the signal disposition of SIGPIPE for
the ping process:

    #!/usr/bin/python
    import signal

    def reset():
        signal.signal(signal.SIGPIPE, signal.SIG_DFL)

    from subprocess import check_call
    check_call(['ping', 'www.google.com'], preexec_fn=reset)

Very likely the subprocess module should be resetting the disposition
of signals that Python itself has fiddled with (and resetting any
other unusual state that the child is going to inherit, but nothing
else comes immediately to mind).



More information about the Python-list mailing list