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