123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851 |
- # General purpose service utilities, both for standard Python scripts,
- # and for for Python programs which run as services...
- #
- # Note that most utility functions here will raise win32api.error's
- # (which is == win32service.error, pywintypes.error, etc)
- # when things go wrong - eg, not enough permissions to hit the
- # registry etc.
- import win32service, win32api, win32con, winerror
- import sys, pywintypes, os, warnings
- error = RuntimeError
- def LocatePythonServiceExe(exeName = None):
- if not exeName and hasattr(sys, "frozen"):
- # If py2exe etc calls this with no exeName, default is current exe.
- return sys.executable
- # Try and find the specified EXE somewhere. If specifically registered,
- # use it. Otherwise look down sys.path, and the global PATH environment.
- if exeName is None:
- if os.path.splitext(win32service.__file__)[0].endswith("_d"):
- exeName = "PythonService_d.exe"
- else:
- exeName = "PythonService.exe"
- # See if it exists as specified
- if os.path.isfile(exeName): return win32api.GetFullPathName(exeName)
- baseName = os.path.splitext(os.path.basename(exeName))[0]
- try:
- exeName = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE,
- "Software\\Python\\%s\\%s" % (baseName, sys.winver))
- if os.path.isfile(exeName):
- return exeName
- raise RuntimeError("The executable '%s' is registered as the Python " \
- "service exe, but it does not exist as specified" \
- % exeName)
- except win32api.error:
- # OK - not there - lets go a-searchin'
- for path in [sys.prefix] + sys.path:
- look = os.path.join(path, exeName)
- if os.path.isfile(look):
- return win32api.GetFullPathName(look)
- # Try the global Path.
- try:
- return win32api.SearchPath(None, exeName)[0]
- except win32api.error:
- 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)
- raise error(msg)
- def _GetServiceShortName(longName):
- # looks up a services name
- # from the display name
- # Thanks to Andy McKay for this code.
- access = win32con.KEY_READ | win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE
- hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services", 0, access)
- num = win32api.RegQueryInfoKey(hkey)[0]
- longName = longName.lower()
- # loop through number of subkeys
- for x in range(0, num):
- # find service name, open subkey
- svc = win32api.RegEnumKey(hkey, x)
- skey = win32api.RegOpenKey(hkey, svc, 0, access)
- try:
- # find display name
- thisName = str(win32api.RegQueryValueEx(skey, "DisplayName")[0])
- if thisName.lower() == longName:
- return svc
- except win32api.error:
- # in case there is no key called DisplayName
- pass
- return None
- # Open a service given either it's long or short name.
- def SmartOpenService(hscm, name, access):
- try:
- return win32service.OpenService(hscm, name, access)
- except win32api.error as details:
- if details.winerror not in [winerror.ERROR_SERVICE_DOES_NOT_EXIST,
- winerror.ERROR_INVALID_NAME]:
- raise
- name = win32service.GetServiceKeyName(hscm, name)
- return win32service.OpenService(hscm, name, access)
- def LocateSpecificServiceExe(serviceName):
- # Given the name of a specific service, return the .EXE name _it_ uses
- # (which may or may not be the Python Service EXE
- hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\%s" % (serviceName), 0, win32con.KEY_ALL_ACCESS)
- try:
- return win32api.RegQueryValueEx(hkey, "ImagePath")[0]
- finally:
- hkey.Close()
- def InstallPerfmonForService(serviceName, iniName, dllName = None):
- # If no DLL name, look it up in the INI file name
- if not dllName: # May be empty string!
- dllName = win32api.GetProfileVal("Python", "dll", "", iniName)
- # Still not found - look for the standard one in the same dir as win32service.pyd
- if not dllName:
- try:
- tryName = os.path.join(os.path.split(win32service.__file__)[0], "perfmondata.dll")
- if os.path.isfile(tryName):
- dllName = tryName
- except AttributeError:
- # Frozen app? - anyway, can't find it!
- pass
- if not dllName:
- raise ValueError("The name of the performance DLL must be available")
- dllName = win32api.GetFullPathName(dllName)
- # Now setup all the required "Performance" entries.
- hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\%s" % (serviceName), 0, win32con.KEY_ALL_ACCESS)
- try:
- subKey = win32api.RegCreateKey(hkey, "Performance")
- try:
- win32api.RegSetValueEx(subKey, "Library", 0, win32con.REG_SZ, dllName)
- win32api.RegSetValueEx(subKey, "Open", 0, win32con.REG_SZ, "OpenPerformanceData")
- win32api.RegSetValueEx(subKey, "Close", 0, win32con.REG_SZ, "ClosePerformanceData")
- win32api.RegSetValueEx(subKey, "Collect", 0, win32con.REG_SZ, "CollectPerformanceData")
- finally:
- win32api.RegCloseKey(subKey)
- finally:
- win32api.RegCloseKey(hkey)
- # Now do the "Lodctr" thang...
- try:
- import perfmon
- path, fname = os.path.split(iniName)
- oldPath = os.getcwd()
- if path:
- os.chdir(path)
- try:
- perfmon.LoadPerfCounterTextStrings("python.exe " + fname)
- finally:
- os.chdir(oldPath)
- except win32api.error as details:
- print("The service was installed OK, but the performance monitor")
- print("data could not be loaded.", details)
- def _GetCommandLine(exeName, exeArgs):
- if exeArgs is not None:
- return exeName + " " + exeArgs
- else:
- return exeName
- 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,
- description = None, delayedstart = None):
- # Handle the default arguments.
- if startType is None:
- startType = win32service.SERVICE_DEMAND_START
- serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
- if bRunInteractive:
- serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
- if errorControl is None:
- errorControl = win32service.SERVICE_ERROR_NORMAL
- exeName = '"%s"' % LocatePythonServiceExe(exeName) # None here means use default PythonService.exe
- commandLine = _GetCommandLine(exeName, exeArgs)
- hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
- try:
- hs = win32service.CreateService(hscm,
- serviceName,
- displayName,
- win32service.SERVICE_ALL_ACCESS, # desired access
- serviceType, # service type
- startType,
- errorControl, # error control type
- commandLine,
- None,
- 0,
- serviceDeps,
- userName,
- password)
- if description is not None:
- try:
- win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DESCRIPTION,description)
- except NotImplementedError:
- pass ## ChangeServiceConfig2 and description do not exist on NT
- if delayedstart is not None:
- try:
- win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayedstart)
- except (win32service.error, NotImplementedError):
- ## delayed start only exists on Vista and later - warn only when trying to set delayed to True
- if delayedstart:
- warnings.warn('Delayed Start not available on this system')
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- InstallPythonClassString(pythonClassString, serviceName)
- # If I have performance monitor info to install, do that.
- if perfMonIni is not None:
- InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
- 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, description = None, delayedstart = None):
- # Before doing anything, remove any perfmon counters.
- try:
- import perfmon
- perfmon.UnloadPerfCounterTextStrings("python.exe "+serviceName)
- except (ImportError, win32api.error):
- pass
- # The EXE location may have changed
- exeName = '"%s"' % LocatePythonServiceExe(exeName)
- # Handle the default arguments.
- if startType is None: startType = win32service.SERVICE_NO_CHANGE
- if errorControl is None: errorControl = win32service.SERVICE_NO_CHANGE
- hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
- serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
- if bRunInteractive:
- serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
- commandLine = _GetCommandLine(exeName, exeArgs)
- try:
- hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
- try:
- win32service.ChangeServiceConfig(hs,
- serviceType, # service type
- startType,
- errorControl, # error control type
- commandLine,
- None,
- 0,
- serviceDeps,
- userName,
- password,
- displayName)
- if description is not None:
- try:
- win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DESCRIPTION,description)
- except NotImplementedError:
- pass ## ChangeServiceConfig2 and description do not exist on NT
- if delayedstart is not None:
- try:
- win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayedstart)
- except (win32service.error, NotImplementedError):
- ## Delayed start only exists on Vista and later. On Nt, will raise NotImplementedError since ChangeServiceConfig2
- ## doensn't exist. On Win2k and XP, will fail with ERROR_INVALID_LEVEL
- ## Warn only if trying to set delayed to True
- if delayedstart:
- warnings.warn('Delayed Start not available on this system')
- finally:
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- InstallPythonClassString(pythonClassString, serviceName)
- # If I have performance monitor info to install, do that.
- if perfMonIni is not None:
- InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
- def InstallPythonClassString(pythonClassString, serviceName):
- # Now setup our Python specific entries.
- if pythonClassString:
- key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\PythonClass" % serviceName)
- try:
- win32api.RegSetValue(key, None, win32con.REG_SZ, pythonClassString);
- finally:
- win32api.RegCloseKey(key)
- # Utility functions for Services, to allow persistant properties.
- def SetServiceCustomOption(serviceName, option, value):
- try:
- serviceName = serviceName._svc_name_
- except AttributeError:
- pass
- key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % serviceName)
- try:
- if type(value)==type(0):
- win32api.RegSetValueEx(key, option, 0, win32con.REG_DWORD, value);
- else:
- win32api.RegSetValueEx(key, option, 0, win32con.REG_SZ, value);
- finally:
- win32api.RegCloseKey(key)
- def GetServiceCustomOption(serviceName, option, defaultValue = None):
- # First param may also be a service class/instance.
- # This allows services to pass "self"
- try:
- serviceName = serviceName._svc_name_
- except AttributeError:
- pass
- key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % serviceName)
- try:
- try:
- return win32api.RegQueryValueEx(key, option)[0]
- except win32api.error: # No value.
- return defaultValue
- finally:
- win32api.RegCloseKey(key)
- def RemoveService(serviceName):
- try:
- import perfmon
- perfmon.UnloadPerfCounterTextStrings("python.exe "+serviceName)
- except (ImportError, win32api.error):
- pass
- hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
- try:
- hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
- win32service.DeleteService(hs)
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- import win32evtlogutil
- try:
- win32evtlogutil.RemoveSourceFromRegistry(serviceName)
- except win32api.error:
- pass
- def ControlService(serviceName, code, machine = None):
- hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
- try:
- hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
- try:
- status = win32service.ControlService(hs, code)
- finally:
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- return status
- def __FindSvcDeps(findName):
- if type(findName) is pywintypes.UnicodeType: findName = str(findName)
- dict = {}
- k = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services")
- num = 0
- while 1:
- try:
- svc = win32api.RegEnumKey(k, num)
- except win32api.error:
- break
- num = num + 1
- sk = win32api.RegOpenKey(k, svc)
- try:
- deps, typ = win32api.RegQueryValueEx(sk, "DependOnService")
- except win32api.error:
- deps = ()
- for dep in deps:
- dep = dep.lower()
- dep_on = dict.get(dep, [])
- dep_on.append(svc)
- dict[dep]=dep_on
- return __ResolveDeps(findName, dict)
- def __ResolveDeps(findName, dict):
- items = dict.get(findName.lower(), [])
- retList = []
- for svc in items:
- retList.insert(0, svc)
- retList = __ResolveDeps(svc, dict) + retList
- return retList
- def WaitForServiceStatus(serviceName, status, waitSecs, machine=None):
- """Waits for the service to return the specified status. You
- should have already requested the service to enter that state"""
- for i in range(waitSecs*4):
- now_status = QueryServiceStatus(serviceName, machine)[1]
- if now_status == status:
- break
- win32api.Sleep(250)
- else:
- raise pywintypes.error(winerror.ERROR_SERVICE_REQUEST_TIMEOUT, "QueryServiceStatus", win32api.FormatMessage(winerror.ERROR_SERVICE_REQUEST_TIMEOUT)[:-2])
-
- def __StopServiceWithTimeout(hs, waitSecs = 30):
- try:
- status = win32service.ControlService(hs, win32service.SERVICE_CONTROL_STOP)
- except pywintypes.error as exc:
- if exc.winerror!=winerror.ERROR_SERVICE_NOT_ACTIVE:
- raise
- for i in range(waitSecs):
- status = win32service.QueryServiceStatus(hs)
- if status[1] == win32service.SERVICE_STOPPED:
- break
- win32api.Sleep(1000)
- else:
- raise pywintypes.error(winerror.ERROR_SERVICE_REQUEST_TIMEOUT, "ControlService", win32api.FormatMessage(winerror.ERROR_SERVICE_REQUEST_TIMEOUT)[:-2])
- def StopServiceWithDeps(serviceName, machine = None, waitSecs = 30):
- # Stop a service recursively looking for dependant services
- hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
- try:
- deps = __FindSvcDeps(serviceName)
- for dep in deps:
- hs = win32service.OpenService(hscm, dep, win32service.SERVICE_ALL_ACCESS)
- try:
- __StopServiceWithTimeout(hs, waitSecs)
- finally:
- win32service.CloseServiceHandle(hs)
- # Now my service!
- hs = win32service.OpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
- try:
- __StopServiceWithTimeout(hs, waitSecs)
- finally:
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- def StopService(serviceName, machine = None):
- return ControlService(serviceName, win32service.SERVICE_CONTROL_STOP, machine)
- def StartService(serviceName, args = None, machine = None):
- hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
- try:
- hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
- try:
- win32service.StartService(hs, args)
- finally:
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- def RestartService(serviceName, args = None, waitSeconds = 30, machine = None):
- "Stop the service, and then start it again (with some tolerance for allowing it to stop.)"
- try:
- StopService(serviceName, machine)
- except pywintypes.error as exc:
- # Allow only "service not running" error
- if exc.winerror!=winerror.ERROR_SERVICE_NOT_ACTIVE:
- raise
- # Give it a few goes, as the service may take time to stop
- for i in range(waitSeconds):
- try:
- StartService(serviceName, args, machine)
- break
- except pywintypes.error as exc:
- if exc.winerror!=winerror.ERROR_SERVICE_ALREADY_RUNNING:
- raise
- win32api.Sleep(1000)
- else:
- print("Gave up waiting for the old service to stop!")
- def _DebugCtrlHandler(evt):
- if evt in (win32con.CTRL_C_EVENT, win32con.CTRL_BREAK_EVENT):
- assert g_debugService
- print("Stopping debug service.")
- g_debugService.SvcStop()
- return True
- return False
- def DebugService(cls, argv = []):
- # Run a service in "debug" mode. Re-implements what pythonservice.exe
- # does when it sees a "-debug" param.
- # Currently only used by "frozen" (ie, py2exe) programs (but later may
- # end up being used for all services should we ever remove
- # pythonservice.exe)
- import servicemanager
- global g_debugService
- print("Debugging service %s - press Ctrl+C to stop." % (cls._svc_name_,))
- servicemanager.Debugging(True)
- servicemanager.PrepareToHostSingle(cls)
- g_debugService = cls(argv)
- # Setup a ctrl+c handler to simulate a "stop"
- win32api.SetConsoleCtrlHandler(_DebugCtrlHandler, True)
- try:
- g_debugService.SvcRun()
- finally:
- win32api.SetConsoleCtrlHandler(_DebugCtrlHandler, False)
- servicemanager.Debugging(False)
- g_debugService = None
- def GetServiceClassString(cls, argv = None):
- if argv is None:
- argv = sys.argv
- import pickle
- modName = pickle.whichmodule(cls, cls.__name__)
- if modName == '__main__':
- try:
- fname = win32api.GetFullPathName(argv[0])
- path = os.path.split(fname)[0]
- # Eaaaahhhh - sometimes this will be a short filename, which causes
- # problems with 1.5.1 and the silly filename case rule.
- filelist = win32api.FindFiles(fname)
- # win32api.FindFiles will not detect files in a zip or exe. If list is empty,
- # skip the test and hope the file really exists.
- if len(filelist) != 0:
- # Get the long name
- fname = os.path.join(path, filelist[0][8])
- except win32api.error:
- raise error("Could not resolve the path name '%s' to a full path" % (argv[0]))
- modName = os.path.splitext(fname)[0]
- return modName + "." + cls.__name__
- def QueryServiceStatus(serviceName, machine=None):
- hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_CONNECT)
- try:
- hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_QUERY_STATUS)
- try:
- status = win32service.QueryServiceStatus(hs)
- finally:
- win32service.CloseServiceHandle(hs)
- finally:
- win32service.CloseServiceHandle(hscm)
- return status
- def usage():
- try:
- fname = os.path.split(sys.argv[0])[1]
- except:
- fname = sys.argv[0]
- print("Usage: '%s [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'" % fname)
- print("Options for 'install' and 'update' commands only:")
- print(" --username domain\\username : The Username the service is to run under")
- print(" --password password : The password for the username")
- print(" --startup [manual|auto|disabled|delayed] : How the service starts, default = manual")
- print(" --interactive : Allow the service to interact with the desktop.")
- print(" --perfmonini file: .ini file to use for registering performance monitor data")
- print(" --perfmondll file: .dll file to use when querying the service for")
- print(" performance data, default = perfmondata.dll")
- print("Options for 'start' and 'stop' commands only:")
- print(" --wait seconds: Wait for the service to actually start or stop.")
- print(" If you specify --wait with the 'stop' option, the service")
- print(" and all dependent services will be stopped, each waiting")
- print(" the specified period.")
- sys.exit(1)
- def HandleCommandLine(cls, serviceClassString = None, argv = None, customInstallOptions = "", customOptionHandler = None):
- """Utility function allowing services to process the command line.
- Allows standard commands such as 'start', 'stop', 'debug', 'install' etc.
- Install supports 'standard' command line options prefixed with '--', such as
- --username, --password, etc. In addition,
- the function allows custom command line options to be handled by the calling function.
- """
- err = 0
- if argv is None: argv = sys.argv
- if len(argv)<=1:
- usage()
- serviceName = cls._svc_name_
- serviceDisplayName = cls._svc_display_name_
- if serviceClassString is None:
- serviceClassString = GetServiceClassString(cls)
- # Pull apart the command line
- import getopt
- try:
- opts, args = getopt.getopt(argv[1:], customInstallOptions,["password=","username=","startup=","perfmonini=", "perfmondll=", "interactive", "wait="])
- except getopt.error as details:
- print(details)
- usage()
- userName = None
- password = None
- perfMonIni = perfMonDll = None
- startup = None
- delayedstart = None
- interactive = None
- waitSecs = 0
- for opt, val in opts:
- if opt=='--username':
- userName = val
- elif opt=='--password':
- password = val
- elif opt=='--perfmonini':
- perfMonIni = val
- elif opt=='--perfmondll':
- perfMonDll = val
- elif opt=='--interactive':
- interactive = 1
- elif opt=='--startup':
- map = {"manual": win32service.SERVICE_DEMAND_START,
- "auto" : win32service.SERVICE_AUTO_START,
- "delayed": win32service.SERVICE_AUTO_START, ## ChangeServiceConfig2 called later
- "disabled": win32service.SERVICE_DISABLED}
- try:
- startup = map[val.lower()]
- except KeyError:
- print("'%s' is not a valid startup option" % val)
- if val.lower() == "delayed":
- delayedstart = True
- elif val.lower() == "auto":
- delayedstart = False
- ## else no change
- elif opt=='--wait':
- try:
- waitSecs = int(val)
- except ValueError:
- print("--wait must specify an integer number of seconds.")
- usage()
- arg=args[0]
- knownArg = 0
- # First we process all arguments which pass additional args on
- if arg=="start":
- knownArg = 1
- print("Starting service %s" % (serviceName))
- try:
- StartService(serviceName, args[1:])
- if waitSecs:
- WaitForServiceStatus(serviceName, win32service.SERVICE_RUNNING, waitSecs)
- except win32service.error as exc:
- print("Error starting service: %s" % exc.strerror)
- err = exc.winerror
- elif arg=="restart":
- knownArg = 1
- print("Restarting service %s" % (serviceName))
- RestartService(serviceName, args[1:])
- if waitSecs:
- WaitForServiceStatus(serviceName, win32service.SERVICE_RUNNING, waitSecs)
- elif arg=="debug":
- knownArg = 1
- if not hasattr(sys, "frozen"):
- # non-frozen services use pythonservice.exe which handles a
- # -debug option
- svcArgs = " ".join(args[1:])
- try:
- exeName = LocateSpecificServiceExe(serviceName)
- except win32api.error as exc:
- if exc.winerror == winerror.ERROR_FILE_NOT_FOUND:
- print("The service does not appear to be installed.")
- print("Please install the service before debugging it.")
- sys.exit(1)
- raise
- try:
- os.system("%s -debug %s %s" % (exeName, serviceName, svcArgs))
- # ^C is used to kill the debug service. Sometimes Python also gets
- # interrupted - ignore it...
- except KeyboardInterrupt:
- pass
- else:
- # py2exe services don't use pythonservice - so we simulate
- # debugging here.
- DebugService(cls, args)
- if not knownArg and len(args)!=1:
- usage() # the rest of the cmds don't take addn args
- if arg=="install":
- knownArg = 1
- try:
- serviceDeps = cls._svc_deps_
- except AttributeError:
- serviceDeps = None
- try:
- exeName = cls._exe_name_
- except AttributeError:
- exeName = None # Default to PythonService.exe
- try:
- exeArgs = cls._exe_args_
- except AttributeError:
- exeArgs = None
- try:
- description = cls._svc_description_
- except AttributeError:
- description = None
- print("Installing service %s" % (serviceName,))
- # Note that we install the service before calling the custom option
- # handler, so if the custom handler fails, we have an installed service (from NT's POV)
- # but is unlikely to work, as the Python code controlling it failed. Therefore
- # we remove the service if the first bit works, but the second doesnt!
- try:
- InstallService(serviceClassString, serviceName, serviceDisplayName, serviceDeps = serviceDeps, startType=startup, bRunInteractive=interactive, userName=userName,password=password, exeName=exeName, perfMonIni=perfMonIni,perfMonDll=perfMonDll,exeArgs=exeArgs,
- description=description, delayedstart=delayedstart)
- if customOptionHandler:
- customOptionHandler(*(opts,))
- print("Service installed")
- except win32service.error as exc:
- if exc.winerror==winerror.ERROR_SERVICE_EXISTS:
- arg = "update" # Fall through to the "update" param!
- else:
- print("Error installing service: %s (%d)" % (exc.strerror, exc.winerror))
- err = exc.winerror
- except ValueError as msg: # Can be raised by custom option handler.
- print("Error installing service: %s" % str(msg))
- err = -1
- # xxx - maybe I should remove after _any_ failed install - however,
- # xxx - it may be useful to help debug to leave the service as it failed.
- # xxx - We really _must_ remove as per the comments above...
- # As we failed here, remove the service, so the next installation
- # attempt works.
- try:
- RemoveService(serviceName)
- except win32api.error:
- print("Warning - could not remove the partially installed service.")
- if arg == "update":
- knownArg = 1
- try:
- serviceDeps = cls._svc_deps_
- except AttributeError:
- serviceDeps = None
- try:
- exeName = cls._exe_name_
- except AttributeError:
- exeName = None # Default to PythonService.exe
- try:
- exeArgs = cls._exe_args_
- except AttributeError:
- exeArgs = None
- try:
- description=cls._svc_description_
- except AttributeError:
- description=None
- print("Changing service configuration")
- try:
- ChangeServiceConfig(serviceClassString, serviceName, serviceDeps = serviceDeps, startType=startup, bRunInteractive=interactive, userName=userName,password=password, exeName=exeName, displayName = serviceDisplayName, perfMonIni=perfMonIni,perfMonDll=perfMonDll,exeArgs=exeArgs,
- description=description, delayedstart=delayedstart)
- if customOptionHandler:
- customOptionHandler(*(opts,))
- print("Service updated")
- except win32service.error as exc:
- print("Error changing service configuration: %s (%d)" % (exc.strerror,exc.winerror))
- err = exc.winerror
- elif arg=="remove":
- knownArg = 1
- print("Removing service %s" % (serviceName))
- try:
- RemoveService(serviceName)
- print("Service removed")
- except win32service.error as exc:
- print("Error removing service: %s (%d)" % (exc.strerror,exc.winerror))
- err = exc.winerror
- elif arg=="stop":
- knownArg = 1
- print("Stopping service %s" % (serviceName))
- try:
- if waitSecs:
- StopServiceWithDeps(serviceName, waitSecs = waitSecs)
- else:
- StopService(serviceName)
- except win32service.error as exc:
- print("Error stopping service: %s (%d)" % (exc.strerror,exc.winerror))
- err = exc.winerror
- if not knownArg:
- err = -1
- print("Unknown command - '%s'" % arg)
- usage()
- return err
- #
- # Useful base class to build services from.
- #
- class ServiceFramework:
- # Required Attributes:
- # _svc_name_ = The service name
- # _svc_display_name_ = The service display name
- # Optional Attributes:
- _svc_deps_ = None # sequence of service names on which this depends
- _exe_name_ = None # Default to PythonService.exe
- _exe_args_ = None # Default to no arguments
- _svc_description_ = None # Only exists on Windows 2000 or later, ignored on windows NT
- def __init__(self, args):
- import servicemanager
- self.ssh = servicemanager.RegisterServiceCtrlHandler(args[0], self.ServiceCtrlHandlerEx, True)
- servicemanager.SetEventSourceName(self._svc_name_)
- self.checkPoint = 0
- def GetAcceptedControls(self):
- # Setup the service controls we accept based on our attributes. Note
- # that if you need to handle controls via SvcOther[Ex](), you must
- # override this.
- accepted = 0
- if hasattr(self, "SvcStop"): accepted = accepted | win32service.SERVICE_ACCEPT_STOP
- if hasattr(self, "SvcPause") and hasattr(self, "SvcContinue"):
- accepted = accepted | win32service.SERVICE_ACCEPT_PAUSE_CONTINUE
- if hasattr(self, "SvcShutdown"): accepted = accepted | win32service.SERVICE_ACCEPT_SHUTDOWN
- return accepted
- def ReportServiceStatus(self, serviceStatus, waitHint = 5000, win32ExitCode = 0, svcExitCode = 0):
- if self.ssh is None: # Debugging!
- return
- if serviceStatus == win32service.SERVICE_START_PENDING:
- accepted = 0
- else:
- accepted = self.GetAcceptedControls()
- if serviceStatus in [win32service.SERVICE_RUNNING, win32service.SERVICE_STOPPED]:
- checkPoint = 0
- else:
- self.checkPoint = self.checkPoint + 1
- checkPoint = self.checkPoint
- # Now report the status to the control manager
- status = (win32service.SERVICE_WIN32_OWN_PROCESS,
- serviceStatus,
- accepted, # dwControlsAccepted,
- win32ExitCode, # dwWin32ExitCode;
- svcExitCode, # dwServiceSpecificExitCode;
- checkPoint, # dwCheckPoint;
- waitHint)
- win32service.SetServiceStatus( self.ssh, status)
- def SvcInterrogate(self):
- # Assume we are running, and everyone is happy.
- self.ReportServiceStatus(win32service.SERVICE_RUNNING)
- def SvcOther(self, control):
- try:
- print("Unknown control status - %d" % control)
- except IOError:
- # services may not have a valid stdout!
- pass
- def ServiceCtrlHandler(self, control):
- return self.ServiceCtrlHandlerEx(control, 0, None)
- # The 'Ex' functions, which take additional params
- def SvcOtherEx(self, control, event_type, data):
- # The default here is to call self.SvcOther as that is the old behaviour.
- # If you want to take advantage of the extra data, override this method
- return self.SvcOther(control)
- def ServiceCtrlHandlerEx(self, control, event_type, data):
- if control==win32service.SERVICE_CONTROL_STOP:
- return self.SvcStop()
- elif control==win32service.SERVICE_CONTROL_PAUSE:
- return self.SvcPause()
- elif control==win32service.SERVICE_CONTROL_CONTINUE:
- return self.SvcContinue()
- elif control==win32service.SERVICE_CONTROL_INTERROGATE:
- return self.SvcInterrogate()
- elif control==win32service.SERVICE_CONTROL_SHUTDOWN:
- return self.SvcShutdown()
- else:
- return self.SvcOtherEx(control, event_type, data)
- def SvcRun(self):
- # This is the entry point the C framework calls when the Service is
- # started. Your Service class should implement SvcDoRun().
- # Or you can override this method for more control over the Service
- # statuses reported to the SCM.
- # If this method raises an exception, the C framework will detect this
- # and report a SERVICE_STOPPED status with a non-zero error code.
- self.ReportServiceStatus(win32service.SERVICE_RUNNING)
- self.SvcDoRun()
- # Once SvcDoRun terminates, the service has stopped.
- # We tell the SCM the service is still stopping - the C framework
- # will automatically tell the SCM it has stopped when this returns.
- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|