win32serviceutil.py 35 KB

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