-
Notifications
You must be signed in to change notification settings - Fork 0
/
boot_service.py
200 lines (175 loc) · 7.18 KB
/
boot_service.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
# boot_service.py
import sys
import os
import servicemanager
import win32service
import win32serviceutil
import winerror
# We assume that py2exe has magically set service_module_names
# to the module names that expose the services we host.
service_klasses = []
try:
service_module_names
except NameError:
print("This script is designed to be run from inside py2exe")
sys.exit(1)
for name in service_module_names:
# Use the documented fact that when a fromlist is present,
# __import__ returns the innermost module in 'name'.
# This makes it possible to have a dotted name work the
# way you'd expect.
mod = __import__(name, globals(), locals(), ['DUMMY'])
for ob in mod.__dict__.values():
if hasattr(ob, "_svc_name_"):
service_klasses.append(ob)
if not service_klasses:
raise RuntimeError("No service classes found")
# Event source records come from servicemanager
evtsrc_dll = os.path.abspath(servicemanager.__file__)
# Tell the Python servicemanager what classes we host.
if len(service_klasses)==1:
k = service_klasses[0]
# One service - make the event name the same as the service.
servicemanager.Initialize(k._svc_name_, evtsrc_dll)
# And the class that hosts it.
servicemanager.PrepareToHostSingle(k)
else:
# Multiple services (NOTE - this hasn't been tested!)
# Use the base name of the exe as the event source
servicemanager.Initialize(os.path.basename(sys.executable), evtsrc_dll)
for k in service_klasses:
servicemanager.PrepareToHostMultiple(k._svc_name_, k)
################################################################
if cmdline_style == "py2exe":
# Simulate the old py2exe service command line handling (to some extent)
# This could do with some re-thought
class GetoptError(Exception):
pass
def w_getopt(args, options):
"""A getopt for Windows style command lines.
Options may start with either '-' or '/', the option names may
have more than one letter (examples are /tlb or -RegServer), and
option names are case insensitive.
Returns two elements, just as getopt.getopt. The first is a list
of (option, value) pairs in the same way getopt.getopt does, but
there is no '-' or '/' prefix to the option name, and the option
name is always lower case. The second is the list of arguments
which do not belong to any option.
Different from getopt.getopt, a single argument not belonging to an option
does not terminate parsing.
"""
opts = []
arguments = []
while args:
if args[0][:1] in "/-":
arg = args[0][1:] # strip the '-' or '/'
arg = arg.lower()
if arg + ':' in options:
try:
opts.append((arg, args[1]))
except IndexError:
raise GetoptError("option '%s' requires an argument" % args[0])
args = args[1:]
elif arg in options:
opts.append((arg, ''))
else:
raise GetoptError("invalid option '%s'" % args[0])
args = args[1:]
else:
arguments.append(args[0])
args = args[1:]
return opts, arguments
options = "help install remove auto disabled interactive user: password:".split()
def usage():
print("Services are supposed to be run by the system after they have been installed.")
print("These command line options are available for (de)installation:")
for opt in options:
if opt.endswith(":"):
print("\t-%s <arg>" % opt)
else:
print("\t-%s" % opt)
print()
try:
opts, args = w_getopt(sys.argv[1:], options)
except GetoptError as detail:
print(detail)
usage()
sys.exit(1)
if opts:
startType = None
bRunInteractive = 0
serviceDeps = None
userName = None
password = None
do_install = False
do_remove = False
done = False
for o, a in opts:
if o == "help":
usage()
done = True
elif o == "install":
do_install = True
elif o == "remove":
do_remove = True
elif o == "auto":
startType = win32service.SERVICE_AUTO_START
elif o == "disabled":
startType = win32service.SERVICE_DISABLED
elif o == "user":
userName = a
elif o == "password":
password = a
elif o == "interactive":
bRunInteractive = True
if do_install:
for k in service_klasses:
svc_display_name = getattr(k, "_svc_display_name_", k._svc_name_)
svc_deps = getattr(k, "_svc_deps_", None)
win32serviceutil.InstallService(None,
k._svc_name_,
svc_display_name,
exeName = sys.executable,
userName = userName,
password = password,
startType = startType,
bRunInteractive = bRunInteractive,
serviceDeps = svc_deps,
description = getattr(k, "_svc_description_", None),
)
done = True
if do_remove:
for k in service_klasses:
win32serviceutil.RemoveService(k._svc_name_)
done = True
if done:
sys.exit(0)
else:
usage()
print("Connecting to the Service Control Manager")
servicemanager.StartServiceCtrlDispatcher()
elif cmdline_style == "pywin32":
assert len(service_klasses) == 1, "Can only handle 1 service!"
k = service_klasses[0]
if len(sys.argv) == 1:
try:
servicemanager.StartServiceCtrlDispatcher()
except win32service.error as details:
if details.winerror == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
win32serviceutil.usage()
else:
win32serviceutil.HandleCommandLine(k)
elif cmdline_style == "custom":
assert len(service_module_names) == 1, "Can only handle 1 service!"
# Unlike services implemented in .py files, when a py2exe service exe is
# executed without args, it may mean the service is being started.
if len(sys.argv) == 1:
try:
servicemanager.StartServiceCtrlDispatcher()
except win32service.error as details:
if details.winerror == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
win32serviceutil.usage()
else:
# assume/insist that the module provides a HandleCommandLine function.
mod = sys.modules[service_module_names[0]]
mod.HandleCommandLine()