summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2016-03-10 03:24:20 +0100
committerAlexander Sulfrian <alexander@sulfrian.net>2016-03-10 03:24:20 +0100
commit3983638176e3f5b5584a2d1ea7ece59faa128908 (patch)
treeeceb0d72bd32a08fa4e0e0ed78fdc18742020681
parent9a3e83775a288bfc89384f9f394a904f676e000e (diff)
downloadspline-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-xspline-startup56
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