home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 July / CMCD0704.ISO / Software / Shareware / Comunicatii / jyte / jyte.exe / win32serviceutil.py < prev    next >
Text File  |  2004-01-26  |  29KB  |  675 lines

  1. # General purpose service utilities, both for standard Python scripts,
  2. # and for for Python programs which run as services...
  3. #
  4. # Note that most utility functions here will raise win32api.error's
  5. # (which is == win32service.error, pywintypes.error, etc)
  6. # when things go wrong - eg, not enough permissions to hit the
  7. # registry etc.
  8.  
  9. import win32service, win32api, win32con, winerror
  10. import sys, string, pywintypes, os
  11.  
  12. error = "Python Service Utility Error"
  13.  
  14. def LocatePythonServiceExe(exeName = None):
  15.     # Try and find the specified EXE somewhere.  If specifically registered,
  16.     # use it.  Otherwise look down sys.path, and the global PATH environment.
  17.     if exeName is None:
  18.         if win32service.__file__.find("_d")>=0:
  19.             exeName = "PythonService_d.exe"
  20.         else:
  21.             exeName = "PythonService.exe"
  22.     # See if it exists as specified
  23.     if os.path.isfile(exeName): return win32api.GetFullPathName(exeName)
  24.     baseName = os.path.splitext(os.path.basename(exeName))[0]
  25.     try:
  26.         exeName = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE,
  27.                                          "Software\\Python\\%s\\%s" % (baseName, sys.winver))
  28.         if os.path.isfile(exeName):
  29.             return exeName
  30.         raise RuntimeError, "The executable '%s' is registered as the Python " \
  31.                             "service exe, but it does not exist as specified" \
  32.                             % exeName
  33.     except win32api.error:
  34.         # OK - not there - lets go a-searchin'
  35.         for path in sys.path:
  36.             look = os.path.join(path, exeName)
  37.             if os.path.isfile(look):
  38.                 return win32api.GetFullPathName(look)
  39.         # Try the global Path.
  40.         try:
  41.             return win32api.SearchPath(None, exeName)[0]
  42.         except win32api.error:
  43.             msg = "%s is not correctly registered\nPlease locate and run %s, and it will self-register\nThen run this service registration process again." % (exeName, exeName)
  44.             raise error, msg
  45.  
  46. def _GetServiceShortName(longName):
  47.     # looks up a services name
  48.     # from the display name
  49.     # Thanks to Andy McKay for this code.
  50.     access = win32con.KEY_READ | win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE
  51.     hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services", 0, access)
  52.     num = win32api.RegQueryInfoKey(hkey)[0]
  53.     # loop through number of subkeys
  54.     for x in range(0, num):
  55.     # find service name, open subkey
  56.         svc = win32api.RegEnumKey(hkey, x)
  57.         skey = win32api.RegOpenKey(hkey, svc, 0, access)
  58.         try:
  59.             # find short name
  60.             shortName = str(win32api.RegQueryValueEx(skey, "DisplayName")[0])
  61.             if shortName == longName:
  62.                 return svc
  63.         except win32api.error:
  64.             # in case there is no key called DisplayName
  65.             pass
  66.     return None
  67.  
  68. # Open a service given either it's long or short name.
  69. def SmartOpenService(hscm, name, access):
  70.     try:
  71.         return win32service.OpenService(hscm, name, access)
  72.     except win32api.error, details:
  73.         if details[0]!=winerror.ERROR_SERVICE_DOES_NOT_EXIST:
  74.             raise
  75.         name = _GetServiceShortName(name)
  76.         if name is None:
  77.             raise
  78.         return win32service.OpenService(hscm, name, access)
  79.  
  80. def LocateSpecificServiceExe(serviceName):
  81.     # Given the name of a specific service, return the .EXE name _it_ uses
  82.     # (which may or may not be the Python Service EXE
  83.     hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\%s" % (serviceName), 0, win32con.KEY_ALL_ACCESS)
  84.     try:
  85.         return win32api.RegQueryValueEx(hkey, "ImagePath")[0]
  86.     finally:
  87.         hkey.Close()
  88.  
  89. def InstallPerfmonForService(serviceName, iniName, dllName = None):
  90.     # If no DLL name, look it up in the INI file name
  91.     if not dllName: # May be empty string!
  92.         dllName = win32api.GetProfileVal("Python", "dll", "", iniName)
  93.     # Still not found - look for the standard one in the same dir as win32service.pyd
  94.     if not dllName:
  95.         try:
  96.             tryName = os.path.join(os.path.split(win32service.__file__)[0], "perfmondata.dll")
  97.             if os.path.isfile(tryName):
  98.                 dllName = tryName
  99.         except AttributeError:
  100.             # Frozen app? - anyway, can't find it!
  101.             pass
  102.     if not dllName:
  103.         raise ValueError, "The name of the performance DLL must be available"
  104.     dllName = win32api.GetFullPathName(dllName)
  105.     # Now setup all the required "Performance" entries.
  106.     hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\%s" % (serviceName), 0, win32con.KEY_ALL_ACCESS)
  107.     try:
  108.         subKey = win32api.RegCreateKey(hkey, "Performance")
  109.         try:
  110.             win32api.RegSetValueEx(subKey, "Library", 0, win32con.REG_SZ, dllName)
  111.             win32api.RegSetValueEx(subKey, "Open", 0, win32con.REG_SZ, "OpenPerformanceData")
  112.             win32api.RegSetValueEx(subKey, "Close", 0, win32con.REG_SZ, "ClosePerformanceData")
  113.             win32api.RegSetValueEx(subKey, "Collect", 0, win32con.REG_SZ, "CollectPerformanceData")
  114.         finally:
  115.             win32api.RegCloseKey(subKey)
  116.     finally:
  117.         win32api.RegCloseKey(hkey)
  118.     # Now do the "Lodctr" thang...
  119.  
  120.     try:
  121.         import perfmon
  122.         path, fname = os.path.split(iniName)
  123.         oldPath = os.getcwd()
  124.         if path:
  125.             os.chdir(path)
  126.         try:
  127.             perfmon.LoadPerfCounterTextStrings("python.exe " + fname)
  128.         finally:
  129.             os.chdir(oldPath)
  130.     except win32api.error, details:
  131.         print "The service was installed OK, but the performance monitor"
  132.         print "data could not be loaded.", details
  133.  
  134. def _GetCommandLine(exeName, exeArgs):
  135.     if exeArgs is not None:
  136.         return exeName + " " + exeArgs
  137.     else:
  138.         return exeName
  139.  
  140. def InstallService(pythonClassString, serviceName, displayName, startType = None, errorControl = None, bRunInteractive = 0, serviceDeps = None, userName = None, password = None, exeName = None, perfMonIni = None, perfMonDll = None, exeArgs = None):
  141.     # Handle the default arguments.
  142.     if startType is None:
  143.         startType = win32service.SERVICE_DEMAND_START
  144.     serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
  145.     if bRunInteractive:
  146.         serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
  147.     if errorControl is None:
  148.         errorControl = win32service.SERVICE_ERROR_NORMAL
  149.  
  150.     exeName = '"%s"' % LocatePythonServiceExe(exeName) # None here means use default PythonService.exe
  151.     commandLine = _GetCommandLine(exeName, exeArgs)
  152.     hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
  153.     try:
  154.         hs = win32service.CreateService(hscm,
  155.                                 serviceName,
  156.                                 displayName,
  157.                                 win32service.SERVICE_ALL_ACCESS,         # desired access
  158.                     serviceType,        # service type
  159.                     startType,
  160.                     errorControl,       # error control type
  161.                     commandLine,
  162.                     None,
  163.                     0,
  164.                     serviceDeps,
  165.                     userName,
  166.                     password)
  167.         win32service.CloseServiceHandle(hs)
  168.     finally:
  169.         win32service.CloseServiceHandle(hscm)
  170.     InstallPythonClassString(pythonClassString, serviceName)
  171.     # If I have performance monitor info to install, do that.
  172.     if perfMonIni is not None:
  173.         InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
  174.  
  175. def ChangeServiceConfig(pythonClassString, serviceName, startType = None, errorControl = None, bRunInteractive = 0, serviceDeps = None, userName = None, password = None, exeName = None, displayName = None, perfMonIni = None, perfMonDll = None, exeArgs = None):
  176.     # Before doing anything, remove any perfmon counters.
  177.     try:
  178.         import perfmon
  179.         perfmon.UnloadPerfCounterTextStrings("python.exe "+serviceName)
  180.     except (ImportError, win32api.error):
  181.         pass
  182.  
  183.     # The EXE location may have changed
  184.     exeName = '"%s"' % LocatePythonServiceExe(exeName)
  185.  
  186.     # Handle the default arguments.
  187.     if startType is None: startType = win32service.SERVICE_NO_CHANGE
  188.     if errorControl is None: errorControl = win32service.SERVICE_NO_CHANGE
  189.  
  190.     hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
  191.     serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
  192.     if bRunInteractive:
  193.         serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
  194.     commandLine = _GetCommandLine(exeName, exeArgs)
  195.     try:
  196.         hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  197.         try:
  198.  
  199.             win32service.ChangeServiceConfig(hs,
  200.                 serviceType,  # service type
  201.                 startType,
  202.                 errorControl,       # error control type
  203.                 commandLine,
  204.                 None,
  205.                 0,
  206.                 serviceDeps,
  207.                 userName,
  208.                 password,
  209.                     displayName)
  210.         finally:
  211.             win32service.CloseServiceHandle(hs)
  212.     finally:
  213.         win32service.CloseServiceHandle(hscm)
  214.     InstallPythonClassString(pythonClassString, serviceName)
  215.     # If I have performance monitor info to install, do that.
  216.     if perfMonIni is not None:
  217.         InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
  218.  
  219. def InstallPythonClassString(pythonClassString, serviceName):
  220.     # Now setup our Python specific entries.
  221.     if pythonClassString:
  222.         key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\PythonClass" % serviceName)
  223.         try:
  224.             win32api.RegSetValue(key, None, win32con.REG_SZ, pythonClassString);
  225.         finally:
  226.             win32api.RegCloseKey(key)
  227.  
  228. # Utility functions for Services, to allow persistant properties.
  229. def SetServiceCustomOption(serviceName, option, value):
  230.     try:
  231.         serviceName = serviceName._svc_name_
  232.     except AttributeError:
  233.         pass
  234.     key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % serviceName)
  235.     try:
  236.         if type(value)==type(0):
  237.             win32api.RegSetValueEx(key, option, 0, win32con.REG_DWORD, value);
  238.         else:
  239.             win32api.RegSetValueEx(key, option, 0, win32con.REG_SZ, value);
  240.     finally:
  241.         win32api.RegCloseKey(key)
  242.  
  243. def GetServiceCustomOption(serviceName, option, defaultValue = None):
  244.     # First param may also be a service class/instance.
  245.     # This allows services to pass "self"
  246.     try:
  247.         serviceName = serviceName._svc_name_
  248.     except AttributeError:
  249.         pass
  250.     key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % serviceName)
  251.     try:
  252.         try:
  253.             return win32api.RegQueryValueEx(key, option)[0]
  254.         except win32api.error:  # No value.
  255.             return defaultValue
  256.     finally:
  257.         win32api.RegCloseKey(key)
  258.  
  259.  
  260. def RemoveService(serviceName):
  261.     try:
  262.         import perfmon
  263.         perfmon.UnloadPerfCounterTextStrings("python.exe "+serviceName)
  264.     except (ImportError, win32api.error):
  265.         pass
  266.  
  267.     hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
  268.     try:
  269.         hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  270.         win32service.DeleteService(hs)
  271.         win32service.CloseServiceHandle(hs)
  272.     finally:
  273.         win32service.CloseServiceHandle(hscm)
  274.  
  275. def ControlService(serviceName, code, machine = None):
  276.     hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
  277.     try:
  278.  
  279.         hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  280.         try:
  281.             status = win32service.ControlService(hs, code)
  282.         finally:
  283.             win32service.CloseServiceHandle(hs)
  284.     finally:
  285.         win32service.CloseServiceHandle(hscm)
  286.     return status
  287.  
  288. def __FindSvcDeps(findName):
  289.     if type(findName) is pywintypes.UnicodeType: findName = str(findName)
  290.     dict = {}
  291.     k = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services")
  292.     num = 0
  293.     while 1:
  294.         try:
  295.             svc = win32api.RegEnumKey(k, num)
  296.         except win32api.error:
  297.             break
  298.         num = num + 1
  299.         sk = win32api.RegOpenKey(k, svc)
  300.         try:
  301.             deps, typ = win32api.RegQueryValueEx(sk, "DependOnService")
  302.         except win32api.error:
  303.             deps = ()
  304.         for dep in deps:
  305.             dep = string.lower(dep)
  306.             dep_on = dict.get(dep, [])
  307.             dep_on.append(svc)
  308.             dict[dep]=dep_on
  309.  
  310.     return __ResolveDeps(findName, dict)
  311.  
  312.  
  313. def __ResolveDeps(findName, dict):
  314.     items = dict.get(string.lower(findName), [])
  315.     retList = []
  316.     for svc in items:
  317.         retList.insert(0, svc)
  318.         retList = __ResolveDeps(svc, dict) + retList
  319.     return retList
  320.  
  321. def __StopServiceWithTimeout(hs, waitSecs = 30):
  322.     try:
  323.         status = win32service.ControlService(hs, win32service.SERVICE_CONTROL_STOP)
  324.     except pywintypes.error, (hr, name, msg):
  325.         if hr!=winerror.ERROR_SERVICE_NOT_ACTIVE:
  326.             raise win32service.error, (hr, name, msg)
  327.     for i in range(waitSecs):
  328.         status = win32service.QueryServiceStatus(hs)
  329.         if status[1] == win32service.SERVICE_STOPPED:
  330.             break
  331.         win32api.Sleep(1000)
  332.     else:
  333.         raise pywintypes.error, (winerror.ERROR_SERVICE_REQUEST_TIMEOUT, "ControlService", win32api.FormatMessage(winerror.ERROR_SERVICE_REQUEST_TIMEOUT)[:-2])
  334.  
  335.  
  336. def StopServiceWithDeps(serviceName, machine = None, waitSecs = 30):
  337.     # Stop a service recursively looking for dependant services
  338.     hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
  339.     try:
  340.         deps = __FindSvcDeps(serviceName)
  341.         for dep in deps:
  342.             hs = win32service.OpenService(hscm, dep, win32service.SERVICE_ALL_ACCESS)
  343.             try:
  344.                 __StopServiceWithTimeout(hs, waitSecs)
  345.             finally:
  346.                 win32service.CloseServiceHandle(hs)
  347.         # Now my service!
  348.         hs = win32service.OpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  349.         try:
  350.             __StopServiceWithTimeout(hs, waitSecs)
  351.         finally:
  352.             win32service.CloseServiceHandle(hs)
  353.  
  354.     finally:
  355.         win32service.CloseServiceHandle(hscm)
  356.  
  357.  
  358. def StopService(serviceName, machine = None):
  359.     return ControlService(serviceName, win32service.SERVICE_CONTROL_STOP, machine)
  360.  
  361. def StartService(serviceName, args = None, machine = None):
  362.     hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
  363.     try:
  364.  
  365.         hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  366.         try:
  367.             win32service.StartService(hs, args)
  368.         finally:
  369.             win32service.CloseServiceHandle(hs)
  370.     finally:
  371.         win32service.CloseServiceHandle(hscm)
  372.  
  373. def RestartService(serviceName, args = None, waitSeconds = 30, machine = None):
  374.     "Stop the service, and then start it again (with some tolerance for allowing it to stop.)"
  375.     try:
  376.         StopService(serviceName, machine)
  377.     except pywintypes.error, (hr, name, msg):
  378.         # Allow only "service not running" error
  379.         if hr!=winerror.ERROR_SERVICE_NOT_ACTIVE:
  380.             raise win32service.error, (hr, name, msg)
  381.     # Give it a few goes, as the service may take time to stop
  382.     for i in range(waitSeconds):
  383.         try:
  384.             StartService(serviceName, args, machine)
  385.             break
  386.         except pywintypes.error, (hr, name, msg):
  387.             if hr!=winerror.ERROR_SERVICE_ALREADY_RUNNING:
  388.                 raise
  389.             win32api.Sleep(1000)
  390.     else:
  391.         print "Gave up waiting for the old service to stop!"
  392.  
  393.  
  394. def GetServiceClassString(cls, argv = None):
  395.     if argv is None:
  396.         argv = sys.argv
  397.     import pickle, os
  398.     modName = pickle.whichmodule(cls, cls.__name__)
  399.     if modName == '__main__':
  400.         try:
  401.             fname = win32api.GetFullPathName(argv[0])
  402.             path = os.path.split(fname)[0]
  403.             # Eaaaahhhh - sometimes this will be a short filename, which causes
  404.             # problems with 1.5.1 and the silly filename case rule.
  405.             # Get the long name
  406.             fname = os.path.join(path, win32api.FindFiles(fname)[0][8])
  407.         except win32api.error:
  408.             raise error, "Could not resolve the path name '%s' to a full path" % (argv[0])
  409.         modName = os.path.splitext(fname)[0]
  410.     return modName + "." + cls.__name__
  411.  
  412. def QueryServiceStatus(serviceName, machine=None):
  413.     hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_CONNECT)
  414.     try:
  415.  
  416.         hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_QUERY_STATUS)
  417.         try:
  418.             status = win32service.QueryServiceStatus(hs)
  419.         finally:
  420.             win32service.CloseServiceHandle(hs)
  421.     finally:
  422.         win32service.CloseServiceHandle(hscm)
  423.     return status
  424.  
  425. def usage():
  426.     try:
  427.         fname = os.path.split(sys.argv[0])[1]
  428.     except:
  429.         fname = sys.argv[0]
  430.     print "Usage: '%s [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'" % fname
  431.     print "Options for 'install' and 'update' commands only:"
  432.     print " --username domain\username : The Username the service is to run under"
  433.     print " --password password : The password for the username"
  434.     print " --startup [manual|auto|disabled] : How the service starts, default = manual"
  435.     print " --interactive : Allow the service to interact with the desktop."
  436.     sys.exit(1)
  437.  
  438. def HandleCommandLine(cls, serviceClassString = None, argv = None, customInstallOptions = "", customOptionHandler = None):
  439.     """Utility function allowing services to process the command line.
  440.  
  441.     Allows standard commands such as 'start', 'stop', 'debug', 'install' etc.
  442.  
  443.     Install supports 'standard' command line options prefixed with '--', such as
  444.     --username, --password, etc.  In addition,
  445.     the function allows custom command line options to be handled by the calling function.
  446.     """
  447.     err = 0
  448.  
  449.     if argv is None: argv = sys.argv
  450.  
  451.     if len(argv)<=1:
  452.         usage()
  453.  
  454.     serviceName = cls._svc_name_
  455.     serviceDisplayName = cls._svc_display_name_
  456.     if serviceClassString is None:
  457.         serviceClassString = GetServiceClassString(cls)
  458.  
  459.     # First we process all arguments which require access to the
  460.     # arg list directly
  461.     if argv[1]=="start":
  462.         print "Starting service %s" % (serviceName)
  463.         try:
  464.             StartService(serviceName, argv[2:])
  465.         except win32service.error, (hr, fn, msg):
  466.             print "Error starting service: %s" % msg
  467.  
  468.     elif argv[1]=="restart":
  469.         print "Restarting service %s" % (serviceName)
  470.         RestartService(serviceName, argv[2:])
  471.  
  472.     elif argv[1]=="debug":
  473.         svcArgs = string.join(sys.argv[2:])
  474.         exeName = LocateSpecificServiceExe(serviceName)
  475.         try:
  476.             os.system("%s -debug %s %s" % (exeName, serviceName, svcArgs))
  477.         # ^C is used to kill the debug service.  Sometimes Python also gets
  478.         # interrupted - ignore it...
  479.         except KeyboardInterrupt:
  480.             pass
  481.     else:
  482.         # Pull apart the command line
  483.         import getopt
  484.         try:
  485.             opts, args = getopt.getopt(argv[1:], customInstallOptions,["password=","username=","startup=","perfmonini=", "perfmondll=", "interactive"])
  486.         except getopt.error, details:
  487.             print details
  488.             usage()
  489.         userName = None
  490.         password = None
  491.         perfMonIni = perfMonDll = None
  492.         startup = None
  493.         interactive = None
  494.         for opt, val in opts:
  495.             if opt=='--username':
  496.                 userName = val
  497.             elif opt=='--password':
  498.                 password = val
  499.             elif opt=='--perfmonini':
  500.                 perfMonIni = val
  501.             elif opt=='--perfmondll':
  502.                 perfMonDll = val
  503.             elif opt=='--interactive':
  504.                 interactive = 1
  505.             elif opt=='--startup':
  506.                 map = {"manual": win32service.SERVICE_DEMAND_START, "auto" : win32service.SERVICE_AUTO_START, "disabled": win32service.SERVICE_DISABLED}
  507.                 try:
  508.                     startup = map[string.lower(val)]
  509.                 except KeyError:
  510.                     print "'%s' is not a valid startup option" % val
  511.         if len(args)<>1:
  512.             usage()
  513.         arg=args[0]
  514.         knownArg = 0
  515.         if arg=="install":
  516.             knownArg = 1
  517.             try:
  518.                 serviceDeps = cls._svc_deps_
  519.             except AttributeError:
  520.                 serviceDeps = None
  521.             try:
  522.                 exeName = cls._exe_name_
  523.             except AttributeError:
  524.                 exeName = None # Default to PythonService.exe
  525.             try:
  526.                 exeArgs = cls._exe_args_
  527.             except AttributeError:
  528.                 exeArgs = None
  529.             print "Installing service %s to Python class %s" % (serviceName,serviceClassString)
  530.             # Note that we install the service before calling the custom option
  531.             # handler, so if the custom handler fails, we have an installed service (from NT's POV)
  532.             # but is unlikely to work, as the Python code controlling it failed.  Therefore
  533.             # we remove the service if the first bit works, but the second doesnt!
  534.             try:
  535.                 InstallService(serviceClassString, serviceName, serviceDisplayName, serviceDeps = serviceDeps, startType=startup, bRunInteractive=interactive, userName=userName,password=password, exeName=exeName, perfMonIni=perfMonIni,perfMonDll=perfMonDll,exeArgs=exeArgs)
  536.                 if customOptionHandler:
  537.                     apply( customOptionHandler, (opts,) )
  538.                 print "Service installed"
  539.             except win32service.error, (hr, fn, msg):
  540.                 if hr==winerror.ERROR_SERVICE_EXISTS:
  541.                     arg = "update" # Fall through to the "update" param!
  542.                 else:
  543.                     print "Error installing service: %s (%d)" % (msg, hr)
  544.                     err = hr
  545.             except ValueError, msg: # Can be raised by custom option handler.
  546.                 print "Error installing service: %s" % str(msg)
  547.                 err = -1
  548.                 # xxx - maybe I should remove after _any_ failed install - however,
  549.                 # xxx - it may be useful to help debug to leave the service as it failed.
  550.                 # xxx - We really _must_ remove as per the comments above...
  551.                 # As we failed here, remove the service, so the next installation
  552.                 # attempt works.
  553.                 try:
  554.                     RemoveService(serviceName)
  555.                 except win32api.error:
  556.                     print "Warning - could not remove the partially installed service."
  557.  
  558.         if arg == "update":
  559.             knownArg = 1
  560.             try:
  561.                 serviceDeps = cls._svc_deps_
  562.             except AttributeError:
  563.                 serviceDeps = None
  564.             try:
  565.                 exeName = cls._exe_name_
  566.             except AttributeError:
  567.                 exeName = None # Default to PythonService.exe
  568.             try:
  569.                 exeArgs = cls._exe_args_
  570.             except AttributeError:
  571.                 exeArgs = None
  572.             print "Changing service configuration"
  573.             try:
  574.                 ChangeServiceConfig(serviceClassString, serviceName, serviceDeps = serviceDeps, startType=startup, bRunInteractive=interactive, userName=userName,password=password, exeName=exeName, displayName = serviceDisplayName, perfMonIni=perfMonIni,perfMonDll=perfMonDll,exeArgs=exeArgs)
  575.                 print "Service updated"
  576.             except win32service.error, (hr, fn, msg):
  577.                 print "Error changing service configuration: %s (%d)" % (msg,hr)
  578.                 err = hr
  579.  
  580.         elif arg=="remove":
  581.             knownArg = 1
  582.             print "Removing service %s" % (serviceName)
  583.             try:
  584.                 RemoveService(serviceName)
  585.                 print "Service removed"
  586.             except win32service.error, (hr, fn, msg):
  587.                 print "Error removing service: %s (%d)" % (msg,hr)
  588.                 err = hr
  589.         elif arg=="stop":
  590.             knownArg = 1
  591.             print "Stopping service %s" % (serviceName)
  592.             try:
  593.                 StopService(serviceName)
  594.             except win32service.error, (hr, fn, msg):
  595.                 print "Error stopping service: %s (%d)" % (msg,hr)
  596.                 err = hr
  597.         if not knownArg:
  598.             err = -1
  599.             print "Unknown command - '%s'" % arg
  600.             usage()
  601.     return err
  602.  
  603. #
  604. # Useful base class to build services from.
  605. #
  606. class ServiceFramework:
  607.     # _svc_name = The service name
  608.     # _svc_display_name = The service display name
  609.     def __init__(self, args):
  610.         import servicemanager
  611.         self.ssh = servicemanager.RegisterServiceCtrlHandler(args[0], self.ServiceCtrlHandler)
  612.         self.checkPoint = 0
  613.  
  614.     def GetAcceptedControls(self):
  615.         # Setup the service controls we accept based on our attributes
  616.         accepted = 0
  617.         if hasattr(self, "SvcStop"): accepted = accepted | win32service.SERVICE_ACCEPT_STOP
  618.         if hasattr(self, "SvcPause") and hasattr(self, "SvcContinue"):
  619.             accepted = accepted | win32service.SERVICE_ACCEPT_PAUSE_CONTINUE
  620.         if hasattr(self, "SvcShutdown"): accepted = accepted | win32service.SERVICE_ACCEPT_SHUTDOWN
  621.         return accepted
  622.  
  623.     def ReportServiceStatus(self, serviceStatus, waitHint = 5000, win32ExitCode = 0, svcExitCode = 0):
  624.         if self.ssh is None: # Debugging!
  625.             return
  626.         if serviceStatus == win32service.SERVICE_START_PENDING:
  627.             accepted = 0
  628.         else:
  629.             accepted = self.GetAcceptedControls()
  630.  
  631.         if serviceStatus in [win32service.SERVICE_RUNNING,  win32service.SERVICE_STOPPED]:
  632.             checkPoint = 0
  633.         else:
  634.             self.checkPoint = self.checkPoint + 1
  635.             checkPoint = self.checkPoint
  636.  
  637.         # Now report the status to the control manager
  638.         status = (win32service.SERVICE_WIN32_OWN_PROCESS,
  639.                  serviceStatus,
  640.                  accepted, # dwControlsAccepted,
  641.                  win32ExitCode, # dwWin32ExitCode;
  642.                  svcExitCode, # dwServiceSpecificExitCode;
  643.                  checkPoint, # dwCheckPoint;
  644.                  waitHint)
  645.         win32service.SetServiceStatus( self.ssh, status)
  646.  
  647.     def SvcInterrogate(self):
  648.         # Assume we are running, and everyone is happy.
  649.         self.ReportServiceStatus(win32service.SERVICE_RUNNING)
  650.  
  651.     def SvcOther(self, control):
  652.         print "Unknown control status - %d" % control
  653.  
  654.     def ServiceCtrlHandler(self, control):
  655.         if control==win32service.SERVICE_CONTROL_STOP:
  656.             self.SvcStop()
  657.         elif control==win32service.SERVICE_CONTROL_PAUSE:
  658.             self.SvcPause()
  659.         elif control==win32service.SERVICE_CONTROL_CONTINUE:
  660.             self.SvcContinue()
  661.         elif control==win32service.SERVICE_CONTROL_INTERROGATE:
  662.             self.SvcInterrogate()
  663.         elif control==win32service.SERVICE_CONTROL_SHUTDOWN:
  664.             self.SvcShutdown()
  665.         else:
  666.             self.SvcOther(control)
  667.  
  668.     def SvcRun(self):
  669.         self.ReportServiceStatus(win32service.SERVICE_RUNNING)
  670.         self.SvcDoRun()
  671.         # Once SvcDoRun terminates, the service has stopped.
  672.         # We tell the SCM the service is still stopping - the C framework
  673.         # will automatically tell the SCM it has stopped when this returns.
  674.         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
  675.