simple twisted server (twistd .tap)with a pexpect instance error
Question
I have been creating an async server socket that sends and recives xml using twisted.
The application works great! but because my main objective was to embed it in an init.d script and make it run in the background i decided to transform it in a "twisted application" in order to run it using twistd
# from twisted.internet import reactor
from twisted.internet.protocol import ServerFactory
from twisted.protocols.basic import LineOnlyReceiver
from twisted.application import internet, service
from xml.etree import ElementTree as ET
from aMuleClass import amulecmd
class DialogueProtocol(LineOnlyReceiver):
def connectionMade(self):
print "Connected: %s" % self.transport.getPeer().host
def lineReceived(self, line):
parsed= ET.XML(line)
if parsed.attrib['type'] == 'request':
if parsed.attrib['prompt'] == 'results':
self.transport.write(self.factory.mule.results())
elif parsed.attrib['prompt'] == 'downloads':
self.transport.write(self.factory.mule.downloads())
else:
print "Invalid request: %s\n" % line
else:
query= parsed.attrib['value']
if parsed.attrib['type'] == 'search':
print "must search for %s" % query
self.factory.mule.search(query)
elif parsed.attrib['type'] == 'cancel':
print "must cancel %s" % query
self.factory.mule.command("cancel %s" % query)
elif parsed.attrib['type'] == 'download':
print "must download %s" % query
self.factory.mule.command("download %s" % query)
class DialogueProtocolFactory(ServerFactory):
def __init__(self):
self.protocol= DialogueProtocol
self.mule= amulecmd()
def main():
factory= DialogueProtocolFactory()
port = 14000
#
daemon= internet.TCPServer(port, factory)
application= service.Application("aMuleSocket")
#
daemon.setServiceParent(application)
if __name__ == '__main__':
main()
Running this with "twistd -noy file" (debug) works PERFECTLY. The problem is when i want to background my script! ("twistd -y file") the socket dosent respond and the log gets filled with errors from pexpect, which is imported in my amulecmd class... pexpect communicates with a terminal-prompt application and returns the answers to the socket..
logfile:
2010/02/17 19:54 +0200 [-] Log opened.
2010/02/17 19:54 +0200 [-] twistd 2.5.0 (/usr/bin/python 2.5.2) starting up
2010/02/17 19:54 +0200 [-] reactor class: <class 'twisted.internet.selectreactor.SelectReactor'>
2010/02/17 19:54 +0200 [-] Loading aMuleSocket.tac...
2010/02/17 19:54 +0200 [-] Starting parent
2010/02/17 19:54 +0200 [-] Loaded.
2010/02/17 19:54 +0200 [-] __builtin__.DialogueProtocolFactory starting on 2000
2010/02/17 19:54 +0200 [-] Starting factory <__builtin__.DialogueProtocolFactory instance at 0x82dbd8c>
2010/02/17 19:54 +0200 [__builtin__.DialogueProtocolFactory] Connected: 192.168.0.2
2010/02/17 19:54 +0200 [DialogueProtocol,0,192.168.0.2] Unhandled Error
Traceback (most recent call last):
File "/usr/lib/python2.5/site-packages/twisted/python/log.py", line 48, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/usr/lib/python2.5/site-packages/twisted/python/log.py", line 33, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/usr/lib/python2.5/site-packages/twisted/python/context.py", line 59, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/usr/lib/python2.5/site-packages/twisted/python/context.py", line 37, in callWithContext
return func(*args,**kw)
--- <exception caught here> ---
File "/usr/lib/python2.5/site-packages/twisted/internet/selectreactor.py", line 139, in _doReadOrWrite
why = getattr(selectable, method)()
File "/usr/lib/python2.5/site-packages/twisted/internet/tcp.py", line 362, in doRead
return self.protocol.dataReceived(data)
File "/usr/lib/python2.5/site-packages/twisted/protocols/basic.py", line 149, in dataReceived
self.lineReceived(line)
File "aMuleSocket.tac", line 19, in lineReceived
self.transport.write(self.factory.mule.downloads())
File "/home/hecyra/amule_scripts/amule-remote-read-only/server/aMuleClass.py", line 60, in downloads
list= self.command('show DL').splitlines()
File "/home/hecyra/amule_scripts/amule-remote-read-only/server/aMuleClass.py", line 42, in command
self.prompt()
File "/home/hecyra/amule_scripts/amule-remote-read-only/server/aMuleClass.py", line 27, in prompt
self.process.expect('aMulecmd')
File "/usr/lib/python2.5/site-packages/pexpect.py", line 1064, in expect
return self.expect_list(compiled_pattern_list, timeout, searchwindowsize)
File "/usr/lib/python2.5/site-packages/pexpect.py", line 1116, in expect_list
c = self.read_nonblocking (self.maxread, timeout)
File "/usr/lib/python2.5/site-packages/pexpect.py", line 656, in read_nonblocking
if not self.isalive():
File "/usr/lib/python2.5/site-packages/pexpect.py", line 914, in isalive
raise ExceptionPexpect ('isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?')
pexpect.ExceptionPexpect: isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?
What could it be?? all i need is to background this script :( looked easy
Solution
You're spawning a child process before daemonizing. After daemonizing that child is now a child of init, and not a child of your daemon.
You need to subclass from twisted.application.service import Service
and spawn the child process in startService
, which will be called after daemonizing.
Á La: Twisted network client with multiprocessing workers?
Edit: implementation
I can't test this entirely as I don't have your amulecmd
, but try something more like this:
#!/usr/bin/env python
# vim:ai:et:ts=2:sw=2:bg=dark
from twisted.internet import protocol
from twisted.protocols.basic import LineOnlyReceiver
from twisted.application import service
from xml.etree import ElementTree as ET
from aMuleClass import amulecmd
class DialogueProtocol(LineOnlyReceiver):
def connectionMade(self):
print "Connected: %s" % self.transport.getPeer().host
def lineReceived(self, line):
parsed= ET.XML(line)
if parsed.attrib['type'] == 'request':
if parsed.attrib['prompt'] == 'results':
self.transport.write(self.factory.mule.results())
elif parsed.attrib['prompt'] == 'downloads':
self.transport.write(self.factory.mule.downloads())
else:
print "Invalid request: %s\n" % line
else:
query= parsed.attrib['value']
if parsed.attrib['type'] == 'search':
print "must search for %s" % query
self.factory.mule.search(query)
elif parsed.attrib['type'] == 'cancel':
print "must cancel %s" % query
self.factory.mule.command("cancel %s" % query)
elif parsed.attrib['type'] == 'download':
print "must download %s" % query
self.factory.mule.command("download %s" % query)
class MyService(service.Service):
def __init__(self,port=14000):
self.port = port
def startService(self):
self.factory = protocol.Factory()
self.factory.protocol = DialogueProtocol
from twisted.internet import reactor
reactor.callWhenRunning(self.startListening)
def startListening(self):
self.factory.mule = amulecmd()
from twisted.internet import reactor
self.listener = reactor.listenTCP(self.port,self.factory)
print "Started listening"
def stopService(self):
self.listener.stopListening()
if __name__ == '__main__':
pass
else:
application = service.Application("aMuleSocket")
services = service.IServiceCollection(application)
MyService().setServiceParent(services)