Advertisement
Guest User

Untitled

a guest
Nov 15th, 2016
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 34.28 KB | None | 0 0
  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, pywintypes, os, warnings
  11. error = RuntimeError
  12.  
  13. def LocatePythonServiceExe(exeName = None):
  14. if not exeName and hasattr(sys, "frozen"):
  15. # If py2exe etc calls this with no exeName, default is current exe.
  16. return sys.executable
  17.  
  18. # Try and find the specified EXE somewhere. If specifically registered,
  19. # use it. Otherwise look down sys.path, and the global PATH environment.
  20. if exeName is None:
  21. if os.path.splitext(win32service.__file__)[0].endswith("_d"):
  22. exeName = "PythonService_d.exe"
  23. else:
  24. exeName = "PythonService.exe"
  25. # See if it exists as specified
  26. if os.path.isfile(exeName): return win32api.GetFullPathName(exeName)
  27. baseName = os.path.splitext(os.path.basename(exeName))[0]
  28. try:
  29. exeName = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE,
  30. "Software\\Python\\%s\\%s" % (baseName, sys.winver))
  31. if os.path.isfile(exeName):
  32. return exeName
  33. raise RuntimeError("The executable '%s' is registered as the Python " \
  34. "service exe, but it does not exist as specified" \
  35. % exeName)
  36. except win32api.error:
  37. # OK - not there - lets go a-searchin'
  38. for path in [sys.prefix] + sys.path:
  39. look = os.path.join(path, exeName)
  40. if os.path.isfile(look):
  41. return win32api.GetFullPathName(look)
  42. # Try the global Path.
  43. try:
  44. return win32api.SearchPath(None, exeName)[0]
  45. except win32api.error:
  46. 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)
  47. raise error(msg)
  48.  
  49. def _GetServiceShortName(longName):
  50. # looks up a services name
  51. # from the display name
  52. # Thanks to Andy McKay for this code.
  53. access = win32con.KEY_READ | win32con.KEY_ENUMERATE_SUB_KEYS | win32con.KEY_QUERY_VALUE
  54. hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services", 0, access)
  55. num = win32api.RegQueryInfoKey(hkey)[0]
  56. longName = longName.lower()
  57. # loop through number of subkeys
  58. for x in range(0, num):
  59. # find service name, open subkey
  60. svc = win32api.RegEnumKey(hkey, x)
  61. skey = win32api.RegOpenKey(hkey, svc, 0, access)
  62. try:
  63. # find display name
  64. thisName = str(win32api.RegQueryValueEx(skey, "DisplayName")[0])
  65. if thisName.lower() == longName:
  66. return svc
  67. except win32api.error:
  68. # in case there is no key called DisplayName
  69. pass
  70. return None
  71.  
  72. # Open a service given either it's long or short name.
  73. def SmartOpenService(hscm, name, access):
  74. try:
  75. return win32service.OpenService(hscm, name, access)
  76. except win32api.error, details:
  77. if details.winerror not in [winerror.ERROR_SERVICE_DOES_NOT_EXIST,
  78. winerror.ERROR_INVALID_NAME]:
  79. raise
  80. name = win32service.GetServiceKeyName(hscm, name)
  81. return win32service.OpenService(hscm, name, access)
  82.  
  83. def LocateSpecificServiceExe(serviceName):
  84. # Given the name of a specific service, return the .EXE name _it_ uses
  85. # (which may or may not be the Python Service EXE
  86. hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\%s" % (serviceName), 0, win32con.KEY_ALL_ACCESS)
  87. try:
  88. return win32api.RegQueryValueEx(hkey, "ImagePath")[0]
  89. finally:
  90. hkey.Close()
  91.  
  92. def InstallPerfmonForService(serviceName, iniName, dllName = None):
  93. # If no DLL name, look it up in the INI file name
  94. if not dllName: # May be empty string!
  95. dllName = win32api.GetProfileVal("Python", "dll", "", iniName)
  96. # Still not found - look for the standard one in the same dir as win32service.pyd
  97. if not dllName:
  98. try:
  99. tryName = os.path.join(os.path.split(win32service.__file__)[0], "perfmondata.dll")
  100. if os.path.isfile(tryName):
  101. dllName = tryName
  102. except AttributeError:
  103. # Frozen app? - anyway, can't find it!
  104. pass
  105. if not dllName:
  106. raise ValueError("The name of the performance DLL must be available")
  107. dllName = win32api.GetFullPathName(dllName)
  108. # Now setup all the required "Performance" entries.
  109. hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\%s" % (serviceName), 0, win32con.KEY_ALL_ACCESS)
  110. try:
  111. subKey = win32api.RegCreateKey(hkey, "Performance")
  112. try:
  113. win32api.RegSetValueEx(subKey, "Library", 0, win32con.REG_SZ, dllName)
  114. win32api.RegSetValueEx(subKey, "Open", 0, win32con.REG_SZ, "OpenPerformanceData")
  115. win32api.RegSetValueEx(subKey, "Close", 0, win32con.REG_SZ, "ClosePerformanceData")
  116. win32api.RegSetValueEx(subKey, "Collect", 0, win32con.REG_SZ, "CollectPerformanceData")
  117. finally:
  118. win32api.RegCloseKey(subKey)
  119. finally:
  120. win32api.RegCloseKey(hkey)
  121. # Now do the "Lodctr" thang...
  122.  
  123. try:
  124. import perfmon
  125. path, fname = os.path.split(iniName)
  126. oldPath = os.getcwd()
  127. if path:
  128. os.chdir(path)
  129. try:
  130. perfmon.LoadPerfCounterTextStrings("python.exe " + fname)
  131. finally:
  132. os.chdir(oldPath)
  133. except win32api.error, details:
  134. print "The service was installed OK, but the performance monitor"
  135. print "data could not be loaded.", details
  136.  
  137. def _GetCommandLine(exeName, exeArgs):
  138. if exeArgs is not None:
  139. return exeName + " " + exeArgs
  140. else:
  141. return exeName
  142.  
  143. 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,
  144. description = None, delayedstart = None):
  145. # Handle the default arguments.
  146. if startType is None:
  147. startType = win32service.SERVICE_DEMAND_START
  148. serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
  149. if bRunInteractive:
  150. serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
  151. if errorControl is None:
  152. errorControl = win32service.SERVICE_ERROR_NORMAL
  153.  
  154. exeName = '"%s"' % LocatePythonServiceExe(exeName) # None here means use default PythonService.exe
  155. commandLine = _GetCommandLine(exeName, exeArgs)
  156. hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
  157. try:
  158. hs = win32service.CreateService(hscm,
  159. serviceName,
  160. displayName,
  161. win32service.SERVICE_ALL_ACCESS, # desired access
  162. serviceType, # service type
  163. startType,
  164. errorControl, # error control type
  165. commandLine,
  166. None,
  167. 0,
  168. serviceDeps,
  169. userName,
  170. password)
  171. if description is not None:
  172. try:
  173. win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DESCRIPTION,description)
  174. except NotImplementedError:
  175. pass ## ChangeServiceConfig2 and description do not exist on NT
  176. if delayedstart is not None:
  177. try:
  178. win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayedstart)
  179. except (win32service.error, NotImplementedError):
  180. ## delayed start only exists on Vista and later - warn only when trying to set delayed to True
  181. if delayedstart:
  182. warnings.warn('Delayed Start not available on this system')
  183. win32service.CloseServiceHandle(hs)
  184. finally:
  185. win32service.CloseServiceHandle(hscm)
  186. InstallPythonClassString(pythonClassString, serviceName)
  187. # If I have performance monitor info to install, do that.
  188. if perfMonIni is not None:
  189. InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
  190.  
  191. def ChangeServiceConfig(pythonClassString, serviceName, startType = None, errorControl = None, bRunInteractive = 0,
  192. serviceDeps = None, userName = None, password = None,
  193. exeName = None, displayName = None, perfMonIni = None, perfMonDll = None,
  194. exeArgs = None, description = None, delayedstart = None):
  195. # Before doing anything, remove any perfmon counters.
  196. try:
  197. import perfmon
  198. perfmon.UnloadPerfCounterTextStrings("python.exe "+serviceName)
  199. except (ImportError, win32api.error):
  200. pass
  201.  
  202. # The EXE location may have changed
  203. exeName = '"%s"' % LocatePythonServiceExe(exeName)
  204.  
  205. # Handle the default arguments.
  206. if startType is None: startType = win32service.SERVICE_NO_CHANGE
  207. if errorControl is None: errorControl = win32service.SERVICE_NO_CHANGE
  208.  
  209. hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
  210. serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
  211. if bRunInteractive:
  212. serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
  213. commandLine = _GetCommandLine(exeName, exeArgs)
  214. try:
  215. hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  216. try:
  217.  
  218. win32service.ChangeServiceConfig(hs,
  219. serviceType, # service type
  220. startType,
  221. errorControl, # error control type
  222. commandLine,
  223. None,
  224. 0,
  225. serviceDeps,
  226. userName,
  227. password,
  228. displayName)
  229. if description is not None:
  230. try:
  231. win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DESCRIPTION,description)
  232. except NotImplementedError:
  233. pass ## ChangeServiceConfig2 and description do not exist on NT
  234. if delayedstart is not None:
  235. try:
  236. win32service.ChangeServiceConfig2(hs,win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayedstart)
  237. except (win32service.error, NotImplementedError):
  238. ## Delayed start only exists on Vista and later. On Nt, will raise NotImplementedError since ChangeServiceConfig2
  239. ## doensn't exist. On Win2k and XP, will fail with ERROR_INVALID_LEVEL
  240. ## Warn only if trying to set delayed to True
  241. if delayedstart:
  242. warnings.warn('Delayed Start not available on this system')
  243. finally:
  244. win32service.CloseServiceHandle(hs)
  245. finally:
  246. win32service.CloseServiceHandle(hscm)
  247. InstallPythonClassString(pythonClassString, serviceName)
  248. # If I have performance monitor info to install, do that.
  249. if perfMonIni is not None:
  250. InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
  251.  
  252. def InstallPythonClassString(pythonClassString, serviceName):
  253. # Now setup our Python specific entries.
  254. if pythonClassString:
  255. key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\PythonClass" % serviceName)
  256. try:
  257. win32api.RegSetValue(key, None, win32con.REG_SZ, pythonClassString);
  258. finally:
  259. win32api.RegCloseKey(key)
  260.  
  261. # Utility functions for Services, to allow persistant properties.
  262. def SetServiceCustomOption(serviceName, option, value):
  263. try:
  264. serviceName = serviceName._svc_name_
  265. except AttributeError:
  266. pass
  267. key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % serviceName)
  268. try:
  269. if type(value)==type(0):
  270. win32api.RegSetValueEx(key, option, 0, win32con.REG_DWORD, value);
  271. else:
  272. win32api.RegSetValueEx(key, option, 0, win32con.REG_SZ, value);
  273. finally:
  274. win32api.RegCloseKey(key)
  275.  
  276. def GetServiceCustomOption(serviceName, option, defaultValue = None):
  277. # First param may also be a service class/instance.
  278. # This allows services to pass "self"
  279. try:
  280. serviceName = serviceName._svc_name_
  281. except AttributeError:
  282. pass
  283. key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % serviceName)
  284. try:
  285. try:
  286. return win32api.RegQueryValueEx(key, option)[0]
  287. except win32api.error: # No value.
  288. return defaultValue
  289. finally:
  290. win32api.RegCloseKey(key)
  291.  
  292.  
  293. def RemoveService(serviceName):
  294. try:
  295. import perfmon
  296. perfmon.UnloadPerfCounterTextStrings("python.exe "+serviceName)
  297. except (ImportError, win32api.error):
  298. pass
  299.  
  300. hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS)
  301. try:
  302. hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  303. win32service.DeleteService(hs)
  304. win32service.CloseServiceHandle(hs)
  305. finally:
  306. win32service.CloseServiceHandle(hscm)
  307.  
  308. import win32evtlogutil
  309. try:
  310. win32evtlogutil.RemoveSourceFromRegistry(serviceName)
  311. except win32api.error:
  312. pass
  313.  
  314. def ControlService(serviceName, code, machine = None):
  315. hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
  316. try:
  317.  
  318. hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  319. try:
  320. status = win32service.ControlService(hs, code)
  321. finally:
  322. win32service.CloseServiceHandle(hs)
  323. finally:
  324. win32service.CloseServiceHandle(hscm)
  325. return status
  326.  
  327. def __FindSvcDeps(findName):
  328. if type(findName) is pywintypes.UnicodeType: findName = str(findName)
  329. dict = {}
  330. k = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services")
  331. num = 0
  332. while 1:
  333. try:
  334. svc = win32api.RegEnumKey(k, num)
  335. except win32api.error:
  336. break
  337. num = num + 1
  338. sk = win32api.RegOpenKey(k, svc)
  339. try:
  340. deps, typ = win32api.RegQueryValueEx(sk, "DependOnService")
  341. except win32api.error:
  342. deps = ()
  343. for dep in deps:
  344. dep = dep.lower()
  345. dep_on = dict.get(dep, [])
  346. dep_on.append(svc)
  347. dict[dep]=dep_on
  348.  
  349. return __ResolveDeps(findName, dict)
  350.  
  351.  
  352. def __ResolveDeps(findName, dict):
  353. items = dict.get(findName.lower(), [])
  354. retList = []
  355. for svc in items:
  356. retList.insert(0, svc)
  357. retList = __ResolveDeps(svc, dict) + retList
  358. return retList
  359.  
  360. def WaitForServiceStatus(serviceName, status, waitSecs, machine=None):
  361. """Waits for the service to return the specified status. You
  362. should have already requested the service to enter that state"""
  363. for i in range(waitSecs*4):
  364. now_status = QueryServiceStatus(serviceName, machine)[1]
  365. if now_status == status:
  366. break
  367. win32api.Sleep(250)
  368. else:
  369. raise pywintypes.error(winerror.ERROR_SERVICE_REQUEST_TIMEOUT, "QueryServiceStatus", win32api.FormatMessage(winerror.ERROR_SERVICE_REQUEST_TIMEOUT)[:-2])
  370.  
  371. def __StopServiceWithTimeout(hs, waitSecs = 30):
  372. try:
  373. status = win32service.ControlService(hs, win32service.SERVICE_CONTROL_STOP)
  374. except pywintypes.error, exc:
  375. if exc.winerror!=winerror.ERROR_SERVICE_NOT_ACTIVE:
  376. raise
  377. for i in range(waitSecs):
  378. status = win32service.QueryServiceStatus(hs)
  379. if status[1] == win32service.SERVICE_STOPPED:
  380. break
  381. win32api.Sleep(1000)
  382. else:
  383. raise pywintypes.error(winerror.ERROR_SERVICE_REQUEST_TIMEOUT, "ControlService", win32api.FormatMessage(winerror.ERROR_SERVICE_REQUEST_TIMEOUT)[:-2])
  384.  
  385.  
  386. def StopServiceWithDeps(serviceName, machine = None, waitSecs = 30):
  387. # Stop a service recursively looking for dependant services
  388. hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
  389. try:
  390. deps = __FindSvcDeps(serviceName)
  391. for dep in deps:
  392. hs = win32service.OpenService(hscm, dep, win32service.SERVICE_ALL_ACCESS)
  393. try:
  394. __StopServiceWithTimeout(hs, waitSecs)
  395. finally:
  396. win32service.CloseServiceHandle(hs)
  397. # Now my service!
  398. hs = win32service.OpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  399. try:
  400. __StopServiceWithTimeout(hs, waitSecs)
  401. finally:
  402. win32service.CloseServiceHandle(hs)
  403.  
  404. finally:
  405. win32service.CloseServiceHandle(hscm)
  406.  
  407.  
  408. def StopService(serviceName, machine = None):
  409. return ControlService(serviceName, win32service.SERVICE_CONTROL_STOP, machine)
  410.  
  411. def StartService(serviceName, args = None, machine = None):
  412. hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_ALL_ACCESS)
  413. try:
  414.  
  415. hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_ALL_ACCESS)
  416. try:
  417. win32service.StartService(hs, args)
  418. finally:
  419. win32service.CloseServiceHandle(hs)
  420. finally:
  421. win32service.CloseServiceHandle(hscm)
  422.  
  423. def RestartService(serviceName, args = None, waitSeconds = 30, machine = None):
  424. "Stop the service, and then start it again (with some tolerance for allowing it to stop.)"
  425. try:
  426. StopService(serviceName, machine)
  427. except pywintypes.error, exc:
  428. # Allow only "service not running" error
  429. if exc.winerror!=winerror.ERROR_SERVICE_NOT_ACTIVE:
  430. raise
  431. # Give it a few goes, as the service may take time to stop
  432. for i in range(waitSeconds):
  433. try:
  434. StartService(serviceName, args, machine)
  435. break
  436. except pywintypes.error, exc:
  437. if exc.winerror!=winerror.ERROR_SERVICE_ALREADY_RUNNING:
  438. raise
  439. win32api.Sleep(1000)
  440. else:
  441. print "Gave up waiting for the old service to stop!"
  442.  
  443. def _DebugCtrlHandler(evt):
  444. if evt in (win32con.CTRL_C_EVENT, win32con.CTRL_BREAK_EVENT):
  445. assert g_debugService
  446. print "Stopping debug service."
  447. g_debugService.SvcStop()
  448. return True
  449. return False
  450.  
  451. def DebugService(cls, argv = []):
  452. # Run a service in "debug" mode. Re-implements what pythonservice.exe
  453. # does when it sees a "-debug" param.
  454. # Currently only used by "frozen" (ie, py2exe) programs (but later may
  455. # end up being used for all services should we ever remove
  456. # pythonservice.exe)
  457. import servicemanager
  458. global g_debugService
  459.  
  460. print "Debugging service %s - press Ctrl+C to stop." % (cls._svc_name_,)
  461. servicemanager.Debugging(True)
  462. servicemanager.PrepareToHostSingle(cls)
  463. g_debugService = cls(argv)
  464. # Setup a ctrl+c handler to simulate a "stop"
  465. win32api.SetConsoleCtrlHandler(_DebugCtrlHandler, True)
  466. try:
  467. g_debugService.SvcRun()
  468. finally:
  469. win32api.SetConsoleCtrlHandler(_DebugCtrlHandler, False)
  470. servicemanager.Debugging(False)
  471. g_debugService = None
  472.  
  473. def GetServiceClassString(cls, argv = None):
  474. if argv is None:
  475. argv = sys.argv
  476. import pickle
  477. modName = pickle.whichmodule(cls, cls.__name__)
  478. if modName == '__main__':
  479. try:
  480. fname = win32api.GetFullPathName(argv[0])
  481. path = os.path.split(fname)[0]
  482. # Eaaaahhhh - sometimes this will be a short filename, which causes
  483. # problems with 1.5.1 and the silly filename case rule.
  484. # Get the long name
  485. fname = os.path.join(path, win32api.FindFiles(fname)[0][8])
  486. except win32api.error:
  487. raise error("Could not resolve the path name '%s' to a full path" % (argv[0]))
  488. modName = os.path.splitext(fname)[0]
  489. return modName + "." + cls.__name__
  490.  
  491. def QueryServiceStatus(serviceName, machine=None):
  492. hscm = win32service.OpenSCManager(machine,None,win32service.SC_MANAGER_CONNECT)
  493. try:
  494.  
  495. hs = SmartOpenService(hscm, serviceName, win32service.SERVICE_QUERY_STATUS)
  496. try:
  497. status = win32service.QueryServiceStatus(hs)
  498. finally:
  499. win32service.CloseServiceHandle(hs)
  500. finally:
  501. win32service.CloseServiceHandle(hscm)
  502. return status
  503.  
  504. def usage():
  505. try:
  506. fname = os.path.split(sys.argv[0])[1]
  507. except:
  508. fname = sys.argv[0]
  509. print "Usage: '%s [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'" % fname
  510. print "Options for 'install' and 'update' commands only:"
  511. print " --username domain\\username : The Username the service is to run under"
  512. print " --password password : The password for the username"
  513. print " --startup [manual|auto|disabled|delayed] : How the service starts, default = manual"
  514. print " --interactive : Allow the service to interact with the desktop."
  515. print " --perfmonini file: .ini file to use for registering performance monitor data"
  516. print " --perfmondll file: .dll file to use when querying the service for"
  517. print " performance data, default = perfmondata.dll"
  518. print "Options for 'start' and 'stop' commands only:"
  519. print " --wait seconds: Wait for the service to actually start or stop."
  520. print " If you specify --wait with the 'stop' option, the service"
  521. print " and all dependent services will be stopped, each waiting"
  522. print " the specified period."
  523. sys.exit(1)
  524.  
  525. def HandleCommandLine(cls, serviceClassString = None, argv = None, customInstallOptions = "", customOptionHandler = None):
  526. """Utility function allowing services to process the command line.
  527.  
  528. Allows standard commands such as 'start', 'stop', 'debug', 'install' etc.
  529.  
  530. Install supports 'standard' command line options prefixed with '--', such as
  531. --username, --password, etc. In addition,
  532. the function allows custom command line options to be handled by the calling function.
  533. """
  534. err = 0
  535.  
  536. if argv is None: argv = sys.argv
  537.  
  538. if len(argv)<=1:
  539. usage()
  540.  
  541. serviceName = cls._svc_name_
  542. serviceDisplayName = cls._svc_display_name_
  543. if serviceClassString is None:
  544. serviceClassString = GetServiceClassString(cls)
  545.  
  546. # Pull apart the command line
  547. import getopt
  548. try:
  549. opts, args = getopt.getopt(argv[1:], customInstallOptions,["password=","username=","startup=","perfmonini=", "perfmondll=", "interactive", "wait="])
  550. except getopt.error, details:
  551. print details
  552. usage()
  553. userName = None
  554. password = None
  555. perfMonIni = perfMonDll = None
  556. startup = None
  557. delayedstart = None
  558. interactive = None
  559. waitSecs = 0
  560. for opt, val in opts:
  561. if opt=='--username':
  562. userName = val
  563. elif opt=='--password':
  564. password = val
  565. elif opt=='--perfmonini':
  566. perfMonIni = val
  567. elif opt=='--perfmondll':
  568. perfMonDll = val
  569. elif opt=='--interactive':
  570. interactive = 1
  571. elif opt=='--startup':
  572. map = {"manual": win32service.SERVICE_DEMAND_START,
  573. "auto" : win32service.SERVICE_AUTO_START,
  574. "delayed": win32service.SERVICE_AUTO_START, ## ChangeServiceConfig2 called later
  575. "disabled": win32service.SERVICE_DISABLED}
  576. try:
  577. startup = map[val.lower()]
  578. except KeyError:
  579. print "'%s' is not a valid startup option" % val
  580. if val.lower() == "delayed":
  581. delayedstart = True
  582. elif val.lower() == "auto":
  583. delayedstart = False
  584. ## else no change
  585. elif opt=='--wait':
  586. try:
  587. waitSecs = int(val)
  588. except ValueError:
  589. print "--wait must specify an integer number of seconds."
  590. usage()
  591.  
  592. arg=args[0]
  593. knownArg = 0
  594. # First we process all arguments which pass additional args on
  595. if arg=="start":
  596. knownArg = 1
  597. print "Starting service %s" % (serviceName)
  598. try:
  599. StartService(serviceName, args[1:])
  600. if waitSecs:
  601. WaitForServiceStatus(serviceName, win32service.SERVICE_RUNNING, waitSecs)
  602. except win32service.error, exc:
  603. print "Error starting service: %s" % exc.strerror
  604. err = exc.winerror
  605.  
  606. elif arg=="restart":
  607. knownArg = 1
  608. print "Restarting service %s" % (serviceName)
  609. RestartService(serviceName, args[1:])
  610. if waitSecs:
  611. WaitForServiceStatus(serviceName, win32service.SERVICE_RUNNING, waitSecs)
  612.  
  613. elif arg=="debug":
  614. knownArg = 1
  615. if not hasattr(sys, "frozen"):
  616. # non-frozen services use pythonservice.exe which handles a
  617. # -debug option
  618. svcArgs = " ".join(args[1:])
  619. try:
  620. exeName = LocateSpecificServiceExe(serviceName)
  621. except win32api.error, exc:
  622. if exc.winerror == winerror.ERROR_FILE_NOT_FOUND:
  623. print "The service does not appear to be installed."
  624. print "Please install the service before debugging it."
  625. sys.exit(1)
  626. raise
  627. try:
  628. os.system("%s -debug %s %s" % (exeName, serviceName, svcArgs))
  629. # ^C is used to kill the debug service. Sometimes Python also gets
  630. # interrupted - ignore it...
  631. except KeyboardInterrupt:
  632. pass
  633. else:
  634. # py2exe services don't use pythonservice - so we simulate
  635. # debugging here.
  636. DebugService(cls, args)
  637.  
  638. if not knownArg and len(args)!=1:
  639. usage() # the rest of the cmds don't take addn args
  640.  
  641. if arg=="install":
  642. knownArg = 1
  643. try:
  644. serviceDeps = cls._svc_deps_
  645. except AttributeError:
  646. serviceDeps = None
  647. try:
  648. exeName = cls._exe_name_
  649. except AttributeError:
  650. exeName = None # Default to PythonService.exe
  651. try:
  652. exeArgs = cls._exe_args_
  653. except AttributeError:
  654. exeArgs = None
  655. try:
  656. description = cls._svc_description_
  657. except AttributeError:
  658. description = None
  659. print "Installing service %s" % (serviceName,)
  660. # Note that we install the service before calling the custom option
  661. # handler, so if the custom handler fails, we have an installed service (from NT's POV)
  662. # but is unlikely to work, as the Python code controlling it failed. Therefore
  663. # we remove the service if the first bit works, but the second doesnt!
  664. try:
  665. InstallService(serviceClassString, serviceName, serviceDisplayName, serviceDeps = serviceDeps, startType=startup, bRunInteractive=interactive, userName=userName,password=password, exeName=exeName, perfMonIni=perfMonIni,perfMonDll=perfMonDll,exeArgs=exeArgs,
  666. description=description, delayedstart=delayedstart)
  667. if customOptionHandler:
  668. customOptionHandler(*(opts,))
  669. print "Service installed"
  670. except win32service.error, exc:
  671. if exc.winerror==winerror.ERROR_SERVICE_EXISTS:
  672. arg = "update" # Fall through to the "update" param!
  673. else:
  674. print "Error installing service: %s (%d)" % (exc.strerror, exc.winerror)
  675. err = exc.winerror
  676. except ValueError, msg: # Can be raised by custom option handler.
  677. print "Error installing service: %s" % str(msg)
  678. err = -1
  679. # xxx - maybe I should remove after _any_ failed install - however,
  680. # xxx - it may be useful to help debug to leave the service as it failed.
  681. # xxx - We really _must_ remove as per the comments above...
  682. # As we failed here, remove the service, so the next installation
  683. # attempt works.
  684. try:
  685. RemoveService(serviceName)
  686. except win32api.error:
  687. print "Warning - could not remove the partially installed service."
  688.  
  689. if arg == "update":
  690. knownArg = 1
  691. try:
  692. serviceDeps = cls._svc_deps_
  693. except AttributeError:
  694. serviceDeps = None
  695. try:
  696. exeName = cls._exe_name_
  697. except AttributeError:
  698. exeName = None # Default to PythonService.exe
  699. try:
  700. exeArgs = cls._exe_args_
  701. except AttributeError:
  702. exeArgs = None
  703. try:
  704. description=cls._svc_description_
  705. except AttributeError:
  706. description=None
  707. print "Changing service configuration"
  708. try:
  709. ChangeServiceConfig(serviceClassString, serviceName, serviceDeps = serviceDeps, startType=startup, bRunInteractive=interactive, userName=userName,password=password, exeName=exeName, displayName = serviceDisplayName, perfMonIni=perfMonIni,perfMonDll=perfMonDll,exeArgs=exeArgs,
  710. description=description, delayedstart=delayedstart)
  711. if customOptionHandler:
  712. customOptionHandler(*(opts,))
  713. print "Service updated"
  714. except win32service.error, exc:
  715. print "Error changing service configuration: %s (%d)" % (exc.strerror,exc.winerror)
  716. err = exc.winerror
  717.  
  718. elif arg=="remove":
  719. knownArg = 1
  720. print "Removing service %s" % (serviceName)
  721. try:
  722. RemoveService(serviceName)
  723. print "Service removed"
  724. except win32service.error, exc:
  725. print "Error removing service: %s (%d)" % (exc.strerror,exc.winerror)
  726. err = exc.winerror
  727. elif arg=="stop":
  728. knownArg = 1
  729. print "Stopping service %s" % (serviceName)
  730. try:
  731. if waitSecs:
  732. StopServiceWithDeps(serviceName, waitSecs = waitSecs)
  733. else:
  734. StopService(serviceName)
  735. except win32service.error, exc:
  736. print "Error stopping service: %s (%d)" % (exc.strerror,exc.winerror)
  737. err = exc.winerror
  738. if not knownArg:
  739. err = -1
  740. print "Unknown command - '%s'" % arg
  741. usage()
  742. return err
  743.  
  744. #
  745. # Useful base class to build services from.
  746. #
  747. class ServiceFramework:
  748. # Required Attributes:
  749. # _svc_name_ = The service name
  750. # _svc_display_name_ = The service display name
  751.  
  752. # Optional Attributes:
  753. _svc_deps_ = None # sequence of service names on which this depends
  754. _exe_name_ = None # Default to PythonService.exe
  755. _exe_args_ = None # Default to no arguments
  756. _svc_description_ = None # Only exists on Windows 2000 or later, ignored on windows NT
  757.  
  758. def __init__(self, args):
  759. import servicemanager
  760. self.ssh = servicemanager.RegisterServiceCtrlHandler(args[0], self.ServiceCtrlHandlerEx, True)
  761. servicemanager.SetEventSourceName(self._svc_name_)
  762. self.checkPoint = 0
  763.  
  764. def GetAcceptedControls(self):
  765. # Setup the service controls we accept based on our attributes. Note
  766. # that if you need to handle controls via SvcOther[Ex](), you must
  767. # override this.
  768. accepted = 0
  769. if hasattr(self, "SvcStop"): accepted = accepted | win32service.SERVICE_ACCEPT_STOP
  770. if hasattr(self, "SvcPause") and hasattr(self, "SvcContinue"):
  771. accepted = accepted | win32service.SERVICE_ACCEPT_PAUSE_CONTINUE
  772. if hasattr(self, "SvcShutdown"): accepted = accepted | win32service.SERVICE_ACCEPT_SHUTDOWN
  773. return accepted
  774.  
  775. def ReportServiceStatus(self, serviceStatus, waitHint = 5000, win32ExitCode = 0, svcExitCode = 0):
  776. if self.ssh is None: # Debugging!
  777. return
  778. if serviceStatus == win32service.SERVICE_START_PENDING:
  779. accepted = 0
  780. else:
  781. accepted = self.GetAcceptedControls()
  782.  
  783. if serviceStatus in [win32service.SERVICE_RUNNING, win32service.SERVICE_STOPPED]:
  784. checkPoint = 0
  785. else:
  786. self.checkPoint = self.checkPoint + 1
  787. checkPoint = self.checkPoint
  788.  
  789. # Now report the status to the control manager
  790. status = (win32service.SERVICE_WIN32_OWN_PROCESS,
  791. serviceStatus,
  792. accepted, # dwControlsAccepted,
  793. win32ExitCode, # dwWin32ExitCode;
  794. svcExitCode, # dwServiceSpecificExitCode;
  795. checkPoint, # dwCheckPoint;
  796. waitHint)
  797. win32service.SetServiceStatus( self.ssh, status)
  798.  
  799. def SvcInterrogate(self):
  800. # Assume we are running, and everyone is happy.
  801. self.ReportServiceStatus(win32service.SERVICE_RUNNING)
  802.  
  803. def SvcOther(self, control):
  804. try:
  805. print "Unknown control status - %d" % control
  806. except IOError:
  807. # services may not have a valid stdout!
  808. pass
  809.  
  810. def ServiceCtrlHandler(self, control):
  811. return self.ServiceCtrlHandlerEx(control, 0, None)
  812.  
  813. # The 'Ex' functions, which take additional params
  814. def SvcOtherEx(self, control, event_type, data):
  815. # The default here is to call self.SvcOther as that is the old behaviour.
  816. # If you want to take advantage of the extra data, override this method
  817. return self.SvcOther(control)
  818.  
  819. def ServiceCtrlHandlerEx(self, control, event_type, data):
  820. if control==win32service.SERVICE_CONTROL_STOP:
  821. return self.SvcStop()
  822. elif control==win32service.SERVICE_CONTROL_PAUSE:
  823. return self.SvcPause()
  824. elif control==win32service.SERVICE_CONTROL_CONTINUE:
  825. return self.SvcContinue()
  826. elif control==win32service.SERVICE_CONTROL_INTERROGATE:
  827. return self.SvcInterrogate()
  828. elif control==win32service.SERVICE_CONTROL_SHUTDOWN:
  829. return self.SvcShutdown()
  830. else:
  831. return self.SvcOtherEx(control, event_type, data)
  832.  
  833. def SvcRun(self):
  834. self.ReportServiceStatus(win32service.SERVICE_RUNNING)
  835. self.SvcDoRun()
  836. # Once SvcDoRun terminates, the service has stopped.
  837. # We tell the SCM the service is still stopping - the C framework
  838. # will automatically tell the SCM it has stopped when this returns.
  839. self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement