user78706
user78706

Reputation:

Python XML-RPC server as a Windows service

I'm trying to create an XML-RPC server as a windows service.
The XML-RPC server is able to get and put files (like FTP) and
also execute commands via os.system() that the remote client
sends to it.

I'm trying the encapsulate the server in the windows service
event loop.

When creating the service there is no problem.

python.exe remoteServer.py --startup=auto install
Installing service XMLRPCServerService
Service installed

When starting the service there is no problem.

python.exe remoteServer.py start
Starting service XMLRPCServerService

When stopping the service there is no problem.

python.exe remoteServer.py stop
Stopping service XMLRPCServerService

When starting the service again there is a problem.

python.exe remoteServer.py start
Starting service XMLRPCServerService
Error starting service: An instance of the service is already running.

Looking at the process monitor, I see a process "pythonservice.exe" that is
still running.

Here are the contents of remoteServer.py:

#!/bin/python

# $Revision: 1.7 $
# $Author: dot $
# $Date: 2011/12/07 01:16:13 $

LISTEN_HOST='0.0.0.0'
LISTEN_PORT=8000

import os
import SocketServer
import BaseHTTPServer
import SimpleHTTPServer
import xmlrpclib
import SimpleXMLRPCServer 
import socket
import httplib
import inspect
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil

class XMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
    def __init__(self, server_address, HandlerClass, logRequests=True):
        """ XML-RPC server. """
        self.logRequests = logRequests

        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,False,None)
        SocketServer.BaseServer.__init__(self, server_address, HandlerClass)

        self.socket = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM)

        self.server_bind()
        self.server_activate()

class XMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """ XML-RPC request handler class. """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)

    def do_POST(self):
        """ Handles the HTTPS request.
            It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
        """

        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))
            # In previous versions of SimpleXMLRPCServer, _dispatch
            # could be overridden in this class, instead of in
            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
            # check to see if a subclass implements _dispatch and dispatch
            # using that method if present.
            response = self.server._marshaled_dispatch(
                    data, getattr(self, '_dispatch', None)
                )
        except: 
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid XML RPC response
            self.send_response(200)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)

            # shut down the connection
            self.wfile.flush()
            self.connection.shutdown() # Modified here!

def XMLRPCServerGet(HandlerClass = XMLRPCRequestHandler,ServerClass = XMLRPCServer):
    """Test xml rpc over http server"""
    class xmlrpc_registers:
        def _listMethods(self):
            return list_public_methods(self)

        def _methodHelp(self, method):
            f = getattr(self, method)
            return inspect.getdoc(f)

        def list(self, dir_name):
            """list(dir_name) => [<filenames>]

            Returns a list containing the contents of the named directory.
            """
            return os.listdir(dir_name)

        def put(self,filename,filedata):
            try:
                with open(filename, "wb") as handle:
                    handle.write(filedata.data) 
                    handle.close()
                return True
            except Exception,ex:
                return 'error'

        def get(self,filepath):
            try:
                handle = open(filepath)
                return xmlrpclib.Binary(handle.read())
                handle.close()
            except:
                return 'error'

        def system(self, command):
            result = os.system(command)
            return result

    server_address = (LISTEN_HOST, LISTEN_PORT) # (address, port)
    server = ServerClass(server_address, HandlerClass)    
    server.register_introspection_functions()
    server.register_instance(xmlrpc_registers())    
    #sa = server.socket.getsockname()
    return server
    #server.serve_forever()
    #print "Serving HTTP on", sa[0], "port", sa[1]
    #try:
    #    #print 'Use Control-C to exit'
    #    server.serve_forever()
    #except KeyboardInterrupt:
    #    #print 'Exiting'

class XMLRPCServerService(win32serviceutil.ServiceFramework):
    _svc_name_         = "XMLRPCServerService"
    _svc_display_name_ = "XMLRPCServerService"
    _svc_description_  = "Tests Python service framework by receiving and echoing messages over a named pipe"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)                    

    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
        self.timeout = 100
        server = XMLRPCServerGet()
        #server.serve_forever()

        while 1:
            # Wait for service stop signal, if I timeout, loop again
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            # Check to see if self.hWaitStop happened
            if rc == win32event.WAIT_OBJECT_0:
                # Stop signal encountered
                server.shutdown()
                servicemanager.LogInfoMsg("XMLRPCServerService - STOPPED")
                break
            else:
                server.handle_request()
                servicemanager.LogInfoMsg("XMLRPCServerService - is alive and well")   

def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':   
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)   
    win32serviceutil.HandleCommandLine(XMLRPCServerService)

Upvotes: 7

Views: 3146

Answers (2)

David K. Hess
David K. Hess

Reputation: 17246

It looks like you are missing a call to self.ReportServiceStatus(win32service.SERVICE_STOPPED) at the end of your SvcStop method.

Upvotes: 4

user78706
user78706

Reputation:

David K. Hess deserves credit for this one.
He suggested I add the following after line 142 in class XMLRPCServerService, def SvcStop:

        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

Here is the final version for anyone interested:

#!/bin/python

# $Revision: 1.7 $
# $Author: dot $
# $Date: 2011/12/07 01:16:13 $

LISTEN_HOST='0.0.0.0'
LISTEN_PORT=8000

import os
import SocketServer
import BaseHTTPServer
import SimpleHTTPServer
import xmlrpclib
import SimpleXMLRPCServer 
import socket
import httplib
import inspect
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil

class XMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
    def __init__(self, server_address, HandlerClass, logRequests=True):
        """ XML-RPC server. """
        self.logRequests = logRequests

        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,False,None)
        SocketServer.BaseServer.__init__(self, server_address, HandlerClass)

        self.socket = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM)

        self.server_bind()
        self.server_activate()

class XMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """ XML-RPC request handler class. """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)

    def do_POST(self):
        """ Handles the HTTPS request.
            It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
        """

        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))
            # In previous versions of SimpleXMLRPCServer, _dispatch
            # could be overridden in this class, instead of in
            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
            # check to see if a subclass implements _dispatch and dispatch
            # using that method if present.
            response = self.server._marshaled_dispatch(
                    data, getattr(self, '_dispatch', None)
                )
        except: 
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid XML RPC response
            self.send_response(200)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)

            # shut down the connection
            self.wfile.flush()
            self.connection.shutdown() # Modified here!

def XMLRPCServerGet(HandlerClass = XMLRPCRequestHandler,ServerClass = XMLRPCServer):
    """Test xml rpc over http server"""
    class xmlrpc_registers:
        def _listMethods(self):
            return list_public_methods(self)

        def _methodHelp(self, method):
            f = getattr(self, method)
            return inspect.getdoc(f)

        def list(self, dir_name):
            """list(dir_name) => [<filenames>]

            Returns a list containing the contents of the named directory.
            """
            return os.listdir(dir_name)

        def put(self,filename,filedata):
            try:
                with open(filename, "wb") as handle:
                    handle.write(filedata.data) 
                    handle.close()
                return True
            except Exception,ex:
                return 'error'

        def get(self,filepath):
            try:
                handle = open(filepath)
                return xmlrpclib.Binary(handle.read())
                handle.close()
            except:
                return 'error'

        def system(self, command):
            result = os.system(command)
            return result

    server_address = (LISTEN_HOST, LISTEN_PORT) # (address, port)
    server = ServerClass(server_address, HandlerClass)    
    server.register_introspection_functions()
    server.register_instance(xmlrpc_registers())    
    #sa = server.socket.getsockname()
    return server
    #server.serve_forever()
    #print "Serving HTTP on", sa[0], "port", sa[1]
    #try:
    #    #print 'Use Control-C to exit'
    #    server.serve_forever()
    #except KeyboardInterrupt:
    #    #print 'Exiting'

class XMLRPCServerService(win32serviceutil.ServiceFramework):
    _svc_name_         = "XMLRPCServerService"
    _svc_display_name_ = "XMLRPCServerService"
    _svc_description_  = "Tests Python service framework by receiving and echoing messages over a named pipe"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        win32event.SetEvent(self.hWaitStop)                    

    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
        self.timeout = 100
        server = XMLRPCServerGet()
        #server.serve_forever()

        while 1:
            # Wait for service stop signal, if I timeout, loop again
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            # Check to see if self.hWaitStop happened
            if rc == win32event.WAIT_OBJECT_0:
                # Stop signal encountered
                server.shutdown()
                servicemanager.LogInfoMsg("XMLRPCServerService - STOPPED")
                break
            else:
                server.handle_request()
                servicemanager.LogInfoMsg("XMLRPCServerService - is alive and well")   

def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':   
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)   
    win32serviceutil.HandleCommandLine(XMLRPCServerService)

Upvotes: 3

Related Questions