openmedialibrary_platform_w.../Lib/site-packages/win32/lib/win32serviceutil.py

840 lines
35 KiB
Python
Raw Normal View History

2016-04-14 19:54:42 +00:00
# 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.
# Get the long name
fname = os.path.join(path, win32api.FindFiles(fname)[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):
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)