1
"""Windows service. Requires pywin32."""
8
import win32serviceutil
10
from cherrypy.process import wspbus, plugins
13
class ConsoleCtrlHandler(plugins.SimplePlugin):
14
"""A WSPBus plugin for handling Win32 console events (like Ctrl-C)."""
16
def __init__(self, bus):
18
plugins.SimplePlugin.__init__(self, bus)
22
self.bus.log('Handler for console events already set.', level=40)
25
result = win32api.SetConsoleCtrlHandler(self.handle, 1)
27
self.bus.log('Could not SetConsoleCtrlHandler (error %r)' %
28
win32api.GetLastError(), level=40)
30
self.bus.log('Set handler for console events.', level=40)
35
self.bus.log('Handler for console events already off.', level=40)
39
result = win32api.SetConsoleCtrlHandler(self.handle, 0)
41
# "ValueError: The object has not been registered"
45
self.bus.log('Could not remove SetConsoleCtrlHandler (error %r)' %
46
win32api.GetLastError(), level=40)
48
self.bus.log('Removed handler for console events.', level=40)
51
def handle(self, event):
52
"""Handle console control events (like Ctrl-C)."""
53
if event in (win32con.CTRL_C_EVENT, win32con.CTRL_LOGOFF_EVENT,
54
win32con.CTRL_BREAK_EVENT, win32con.CTRL_SHUTDOWN_EVENT,
55
win32con.CTRL_CLOSE_EVENT):
56
self.bus.log('Console event %s: shutting down bus' % event)
58
# Remove self immediately so repeated Ctrl-C doesn't re-call it.
65
# 'First to return True stops the calls'
70
class Win32Bus(wspbus.Bus):
71
"""A Web Site Process Bus implementation for Win32.
73
Instead of time.sleep, this bus blocks using native win32event objects.
78
wspbus.Bus.__init__(self)
80
def _get_state_event(self, state):
81
"""Return a win32event for the given state (creating it if needed)."""
83
return self.events[state]
85
event = win32event.CreateEvent(None, 0, 0,
86
"WSPBus %s Event (pid=%r)" %
87
(state.name, os.getpid()))
88
self.events[state] = event
93
def _set_state(self, value):
95
event = self._get_state_event(value)
96
win32event.PulseEvent(event)
97
state = property(_get_state, _set_state)
99
def wait(self, state, interval=0.1, channel=None):
100
"""Wait for the given state(s), KeyboardInterrupt or SystemExit.
102
Since this class uses native win32event objects, the interval
105
if isinstance(state, (tuple, list)):
106
# Don't wait for an event that beat us to the punch ;)
107
if self.state not in state:
108
events = tuple([self._get_state_event(s) for s in state])
109
win32event.WaitForMultipleObjects(events, 0, win32event.INFINITE)
111
# Don't wait for an event that beat us to the punch ;)
112
if self.state != state:
113
event = self._get_state_event(state)
114
win32event.WaitForSingleObject(event, win32event.INFINITE)
117
class _ControlCodes(dict):
118
"""Control codes used to "signal" a service via ControlService.
120
User-defined control codes are in the range 128-255. We generally use
121
the standard Python value for the Linux signal and add 128. Example:
125
control_codes['graceful'] = 128 + 10
128
def key_for(self, obj):
129
"""For the given value, return its corresponding key."""
130
for key, val in self.items():
133
raise ValueError("The given object could not be found: %r" % obj)
135
control_codes = _ControlCodes({'graceful': 138})
138
def signal_child(service, command):
139
if command == 'stop':
140
win32serviceutil.StopService(service)
141
elif command == 'restart':
142
win32serviceutil.RestartService(service)
144
win32serviceutil.ControlService(service, control_codes[command])
147
class PyWebService(win32serviceutil.ServiceFramework):
148
"""Python Web Service."""
150
_svc_name_ = "Python Web Service"
151
_svc_display_name_ = "Python Web Service"
152
_svc_deps_ = None # sequence of service names on which this depends
153
_exe_name_ = "pywebsvc"
154
_exe_args_ = None # Default to no arguments
156
# Only exists on Windows 2000 or later, ignored on windows NT
157
_svc_description_ = "Python Web Service"
160
from cherrypy import process
165
from cherrypy import process
166
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
169
def SvcOther(self, control):
170
process.bus.publish(control_codes.key_for(control))
173
if __name__ == '__main__':
174
win32serviceutil.HandleCommandLine(PyWebService)