diff options
author | Alexander Sulfrian <alexander@sulfrian.net> | 2016-03-10 03:24:20 +0100 |
---|---|---|
committer | Alexander Sulfrian <alexander@sulfrian.net> | 2016-03-10 03:24:20 +0100 |
commit | 3983638176e3f5b5584a2d1ea7ece59faa128908 (patch) | |
tree | eceb0d72bd32a08fa4e0e0ed78fdc18742020681 | |
parent | 9a3e83775a288bfc89384f9f394a904f676e000e (diff) | |
download | spline-startup-3983638176e3f5b5584a2d1ea7ece59faa128908.tar.gz spline-startup-3983638176e3f5b5584a2d1ea7ece59faa128908.tar.bz2 spline-startup-3983638176e3f5b5584a2d1ea7ece59faa128908.zip |
Non-Blocking read of subprocess output
Some deamons does not close stdout (and the other fds) during forking.
So our direct child will die, but Popen.communicate will block until
the daemon dies.
We now read the output non-blocking and stop reading it, if we get SIGCHLD
and our direct child is dead. Because we now handle SIGCHLD by ourselfs, we
need to ensure to call Popen.wait so that the kernel can cleanup our child
and we do not get defunct processes.
-rwxr-xr-x | spline-startup | 56 |
1 files changed, 50 insertions, 6 deletions
diff --git a/spline-startup b/spline-startup index e85dfc4..6012b38 100755 --- a/spline-startup +++ b/spline-startup @@ -2,8 +2,10 @@ from __future__ import print_function import argparse +import fcntl import os import pwd +import select import signal import sys import syslog @@ -41,6 +43,51 @@ def _get_users(config): return users +class Executor(Popen): + def __init__(self, *args, **kwargs): + self.running = True + + self.orig_signal = signal.signal(signal.SIGCHLD, self._signal) + return super(Executor, self).__init__(*args, **kwargs) + + def readlines(self): + fl = fcntl.fcntl(self.stdout, fcntl.F_GETFL) + fcntl.fcntl(self.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK) + + buffer = '' + while self.running: + try: + ready = select.select([self.stdout], [], [], 0) + except select.error: + pass + + if ready[0] != []: + buffer += self.stdout.read(8192) + lines = buffer.splitlines() + for line in lines[:-1]: + yield line + if len(lines) > 0: + buffer = lines[-1] + + self.wait() + + try: + buffer += self.stdout.read(8192) + except: + pass + + for line in buffer.splitlines(): + yield line + + def wait(self): + result = super(Executor, self).wait() + signal.signal(signal.SIGCHLD, self.orig_signal) + return result + + def _signal(self, *args): + self.running = False + + class SplineStartup(object): def __init__(self): @@ -104,12 +151,9 @@ class SplineStartup(object): def _call(self, cmd): self._pinfo('Calling: %s' % ' '.join(cmd)) if not self.options.dry_run: - proc = Popen(cmd, stdout=PIPE, stderr=STDOUT) - output, _ = proc.communicate() - output = output.strip() - if output != '': - for line in output.split('\n'): - self._pinfo(line) + proc = Executor(cmd, stdout=PIPE, stderr=STDOUT) + for line in proc.readlines(): + self._pinfo(line) return proc.returncode else: return 0 |