1

I'm using twisted to spawn a local process, which may be terminated in some condition.

I have a custom twisted.internet.protocol.ProcessProtocol class for the reactor. If the local process abruptly terminated, I can't get the return value in processEnded. The exitCode is set to None.

A mcv example is like this:

from twisted.internet import error,protocol,reactor

class MyPP(protocol.ProcessProtocol):
    def processEnded(self, reason):
        if reason.check(error.ProcessTerminated):
            err_info = "wrong termination: %s; exitCode: %s; signal: %s" % \
                        (reason, reason.value.exitCode, reason.value.signal)
            print(err_info)
        else:
            print("processEnded, status %d" % (reason.value.exitCode,))
            print("quitting")
        reactor.stop()

pp = MyPP()
reactor.spawnProcess(pp, "throw_exception", ["throw_exception"], {})
reactor.run()

And the throw_exception executable could be compiled from:

#include <iostream>
int main() {
    throw std::runtime_error("some exception");
    return 0;
}

Execute the python example will print

wrong termination: [Failure instance: Traceback (failure with no frames): : A process has ended with a probable error condition: process ended by signal 6. ]; exitCode: None; signal: 6

The C++ example will have a return value of 134 if run in a shell, which means SIGABRT(6) sent. (I've also tested sending SIGINT to terminate and still getting no exit code.)

How can I get it in the ProcessProtocal instance? Or it is impossible?

halfelf
  • 9,737
  • 13
  • 54
  • 63

1 Answers1

1

In your example, 134 is the "wait status" (or, in the man pages, the "wstatus"). It encodes a few pieces of information about a running-state-transition the process being wait() on underwent.

The possible transitions are:

  • exited with a code (ie, called exit(2))
  • was killed by a signal
  • whether a core dump was produced
  • was stopped (eg by SIGSTOP)
  • was un-stopped (eg with SIGCONT)

POSIX provides macros for extracting the details from the wait status: WIFEXITED, WIFSIGNALED, WTERMSIG, WEXITSTATUS, etc.

Python exposes these through the os module: os.WIFEXITED, etc.

os.WIFEXITED(134) evaluates to False. os.WIFSIGNALED(134) evaluates to true and os.WTERMSIG(134) evaluates to 6.

ProcessDone and ProcessTerminated use these macros to extract the information and present it in a slightly more useful form - the exitCode and signal attributes.

POSIX doesn't provide a way to go the other way, though. There's no standard API for constructing a "wait status" representing, say, "the process was killed by signal 6".

Fortunately, partially because there's no way to go backwards, Twisted preserves the original wait status on the exception as the status attribute. If you check that attribute on the exception wrapped in the Failure passed to your ProcessProtocol, you should find the wait status you're looking for.

Jean-Paul Calderone
  • 47,755
  • 6
  • 94
  • 122
  • Thank you! I found it in as an attribute of `ProcessTerminated` class at last. It's a platform specific value and I always get a `6` on my mac, which cause lots of confusion until testing it on my linux server and getting 134 eventually. – halfelf Feb 23 '17 at 04:13
  • Yea, that platform-specificness is why it's usually a better idea to use `exitCode` and `signal`. :) – Jean-Paul Calderone Feb 23 '17 at 12:42