Setting up multiple named instances of a windows service using win32serviceutil.ServiceFramework in Python

23 Views Asked by At

I have a program written in Python that I want to run as a windows service. The program find routes for cables in a database which holds the cables and the nodal network. The program can be run as an endless loop and made to point at a specific schema or project, via a command line argument.

Now what I need to do is to make it so I can install multiple instances of the same program as services, but provide a different name each time, so that each one is installed as a separate service under the specified name. The name will also point the program towards the correct schema in the database, so the name needs to be recovered and passed as an argument when SVCStart and Stop are called. Note also that one instance of the program is used to halt another one, by sending a message via the database.

I'm struggling to figure out how to use the win32serviceutil.ServiceFramework to do this. I tried using global variables as you can see, but as installation and startup are separate they aren't preserved. I'm starting to think I may have to save this name to some kind of log file as I can't find a way of preserving it in the code. Another approach is maybe to dig a bit deeper into the service framework and see if I can't customise things a bit more to suit my needs, but the minimal documentation available online is making this difficult for a beginner like myself.

I've included my latest attempt at the end of this, it doesn't work and I have a good idea why, I'm looking for suggestions on how to move forward more than anything. I am a rookie software developer with no formal training so please be gentle, and any explanations of the underlying mechanics of windows services which might be relevant to my problem would be much appreciated.

Thanks


import win32serviceutil
import win32service
import win32event
import os
import sys
import argparse
import subprocess

AUTOROUTER_SCRIPT = r'\simple_test.py'
PYTHON_EXE = 'python'

service_name = "ARServicePlaceHolderName"
service_display_name = "AutorouterPlaceHolder"
service_description = "Autorouter"

class ARService(win32serviceutil.ServiceFramework):

    _svc_name_ = service_name
    _svc_display_name_ = service_display_name 
    _svc_description_ = service_description

    def __init__(self, args):
        
        win32serviceutil.ServiceFramework.__init__(self, args)

        directory = os.path.dirname(os.path.abspath(__file__))
        self.ar_path = os.path.join(directory, AUTOROUTER_SCRIPT)

        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.autorouter_process = None
        
    def SvcDoRun(self):
        
        command = [PYTHON_EXE, self.ar_path, self._svc_name]
        self.autorouter_process = subprocess.Popen(command)
        win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        command = [PYTHON_EXE, self.ar_path, self._svc_name, '--halt']
        halt_process = subprocess.Popen(command)
        halt_process.wait()
        self.autorouter_process.wait()
        self.autorouter_process = None
        win32event.SetEvent(self.hWaitStop)


if __name__ == '__main__':
    
    parser = argparse.ArgumentParser()
    parser.add_argument('name', help='Name daemon by schema or projid')
    
    args, unknown_args = parser.parse_known_args()

    service_name = 'AutoRouter' + args.name
    service_display_name = 'AutoRouter for ' + args.name
    service_description = 'AutoRouter for ' + args.name
    
    # Remove name argument from sys.argv before calling HandleCommandLine
    sys.argv[1:] = unknown_args
    
    win32serviceutil.HandleCommandLine(ARService)


0

There are 0 best solutions below