| 1 |
|
|---|
| 2 |
import os |
|---|
| 3 |
import logging |
|---|
| 4 |
|
|---|
| 5 |
from twisted.application import service |
|---|
| 6 |
from twisted.internet import protocol, reactor, defer |
|---|
| 7 |
from twisted.internet import error |
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
from fizzjik.config import ConfigurableMixin, if_config |
|---|
| 12 |
|
|---|
| 13 |
class ProcessProtocol(protocol.ProcessProtocol): |
|---|
| 14 |
posix_exec = '/usr/bin/env' |
|---|
| 15 |
posix_args = ['/usr/bin/env'] |
|---|
| 16 |
|
|---|
| 17 |
running = False |
|---|
| 18 |
outCallback = None |
|---|
| 19 |
errCallback = None |
|---|
| 20 |
outDeferred = None |
|---|
| 21 |
errDeferred = None |
|---|
| 22 |
startedDeferred = None |
|---|
| 23 |
endedDeferred = None |
|---|
| 24 |
out = None |
|---|
| 25 |
err = None |
|---|
| 26 |
|
|---|
| 27 |
def __init__(self, platform="posix", outCallback=None, errCallback=None): |
|---|
| 28 |
self.platform = platform |
|---|
| 29 |
self.outDeferred = defer.Deferred() |
|---|
| 30 |
self.errDeferred = defer.Deferred() |
|---|
| 31 |
self.startedDeferred = defer.Deferred() |
|---|
| 32 |
self.endedDeferred = defer.Deferred() |
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 |
self.endedDeferred.addErrback(self._handleCleanExit) |
|---|
| 36 |
self.endedDeferred.addErrback(self._handleUncleanExit) |
|---|
| 37 |
|
|---|
| 38 |
if not outCallback: |
|---|
| 39 |
outCallback = self.defaultOut |
|---|
| 40 |
self.outCallback = outCallback |
|---|
| 41 |
|
|---|
| 42 |
if not errCallback: |
|---|
| 43 |
errCallback = self.defaultErr |
|---|
| 44 |
self.errCallback = errCallback |
|---|
| 45 |
|
|---|
| 46 |
self.err = [] |
|---|
| 47 |
self.out = [] |
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
def connectionMade(self): |
|---|
| 52 |
self.running = True |
|---|
| 53 |
self.startedDeferred.callback(True) |
|---|
| 54 |
logging.debug("process started...") |
|---|
| 55 |
|
|---|
| 56 |
def spawn(self, *args): |
|---|
| 57 |
if not self.getExec(): |
|---|
| 58 |
return |
|---|
| 59 |
reactor.spawnProcess(self, self.getExec(), args=self.getArgs()+list(args), env=os.environ) |
|---|
| 60 |
return self.startedDeferred |
|---|
| 61 |
|
|---|
| 62 |
def kill(self): |
|---|
| 63 |
if self.running: |
|---|
| 64 |
self.transport.signalProcess('KILL') |
|---|
| 65 |
|
|---|
| 66 |
def processEnded(self, reason): |
|---|
| 67 |
if not self.running: |
|---|
| 68 |
self.startedDeferred.errback(reason) |
|---|
| 69 |
self.endedDeferred.callback(reason) |
|---|
| 70 |
self.outDeferred.callback(self.out) |
|---|
| 71 |
self.errDeferred.callback(self.err) |
|---|
| 72 |
self.running = False |
|---|
| 73 |
|
|---|
| 74 |
def _handleCleanExit(self, reason): |
|---|
| 75 |
if not isinstance(reason.value, error.ProcessDone): |
|---|
| 76 |
return reason |
|---|
| 77 |
if reason.value.exitCode == 0: |
|---|
| 78 |
logging.debug('process ended cleanly') |
|---|
| 79 |
return True |
|---|
| 80 |
return reason |
|---|
| 81 |
|
|---|
| 82 |
def _handleUncleanExit(self, reason): |
|---|
| 83 |
logging.debug('process ended uncleanly: %s' % reason) |
|---|
| 84 |
|
|---|
| 85 |
def outReceived(self, line): |
|---|
| 86 |
for l in line.splitlines(): |
|---|
| 87 |
self.out.append(l) |
|---|
| 88 |
self.outCallback(line) |
|---|
| 89 |
|
|---|
| 90 |
def errReceived(self, l): |
|---|
| 91 |
for l in line.splitlines(): |
|---|
| 92 |
self.err.append(l) |
|---|
| 93 |
self.errCallback(line) |
|---|
| 94 |
|
|---|
| 95 |
@staticmethod |
|---|
| 96 |
def defaultErr(line): |
|---|
| 97 |
logging.debug("\n".join(["err !! %s"%(x) for x in line.splitlines()])) |
|---|
| 98 |
|
|---|
| 99 |
@staticmethod |
|---|
| 100 |
def defaultOut(line): |
|---|
| 101 |
logging.debug("\n".join(["out >> %s"%(x) for x in line.splitlines()])) |
|---|
| 102 |
|
|---|
| 103 |
def getExec(self): |
|---|
| 104 |
return getattr(self, '%s_exec'%(self.platform), None) |
|---|
| 105 |
|
|---|
| 106 |
def getArgs(self): |
|---|
| 107 |
return getattr(self, '%s_args'%(self.platform), None) |
|---|
| 108 |
|
|---|
| 109 |
|
|---|
| 110 |
class ProcessService(service.Service, ConfigurableMixin): |
|---|
| 111 |
enabled = True |
|---|
| 112 |
platform = "posix" |
|---|
| 113 |
protocol = ProcessProtocol |
|---|
| 114 |
|
|---|
| 115 |
|
|---|
| 116 |
@if_config('enabled') |
|---|
| 117 |
def startService(self): |
|---|
| 118 |
self.process = self.protocol(self.platform) |
|---|
| 119 |
pass |
|---|
| 120 |
|
|---|
| 121 |
@if_config('enabled') |
|---|
| 122 |
def stopService(self): |
|---|
| 123 |
self.process.kill() |
|---|
| 124 |
pass |
|---|
| 125 |
|
|---|
| 126 |
def spawn(self, *args): |
|---|
| 127 |
self.process.spawn(*args) |
|---|
| 128 |
|
|---|