Bundled cherrypy.
[smonitor.git] / monitor / cherrypy / process / win32.py
blob83f99a5d4677660efda8fe8e625beb9d3b002afa
1 """Windows service. Requires pywin32."""
3 import os
4 import win32api
5 import win32con
6 import win32event
7 import win32service
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):
17 self.is_set = False
18 plugins.SimplePlugin.__init__(self, bus)
20 def start(self):
21 if self.is_set:
22 self.bus.log('Handler for console events already set.', level=40)
23 return
25 result = win32api.SetConsoleCtrlHandler(self.handle, 1)
26 if result == 0:
27 self.bus.log('Could not SetConsoleCtrlHandler (error %r)' %
28 win32api.GetLastError(), level=40)
29 else:
30 self.bus.log('Set handler for console events.', level=40)
31 self.is_set = True
33 def stop(self):
34 if not self.is_set:
35 self.bus.log('Handler for console events already off.', level=40)
36 return
38 try:
39 result = win32api.SetConsoleCtrlHandler(self.handle, 0)
40 except ValueError:
41 # "ValueError: The object has not been registered"
42 result = 1
44 if result == 0:
45 self.bus.log('Could not remove SetConsoleCtrlHandler (error %r)' %
46 win32api.GetLastError(), level=40)
47 else:
48 self.bus.log('Removed handler for console events.', level=40)
49 self.is_set = False
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.
59 try:
60 self.stop()
61 except ValueError:
62 pass
64 self.bus.exit()
65 # 'First to return True stops the calls'
66 return 1
67 return 0
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.
74 """
76 def __init__(self):
77 self.events = {}
78 wspbus.Bus.__init__(self)
80 def _get_state_event(self, state):
81 """Return a win32event for the given state (creating it if needed)."""
82 try:
83 return self.events[state]
84 except KeyError:
85 event = win32event.CreateEvent(None, 0, 0,
86 "WSPBus %s Event (pid=%r)" %
87 (state.name, os.getpid()))
88 self.events[state] = event
89 return event
91 def _get_state(self):
92 return self._state
93 def _set_state(self, value):
94 self._state = 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
103 argument is ignored.
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)
110 else:
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:
123 >>> signal.SIGUSR1
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():
131 if val is obj:
132 return key
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)
143 else:
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"
159 def SvcDoRun(self):
160 from cherrypy import process
161 process.bus.start()
162 process.bus.block()
164 def SvcStop(self):
165 from cherrypy import process
166 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
167 process.bus.exit()
169 def SvcOther(self, control):
170 process.bus.publish(control_codes.key_for(control))
173 if __name__ == '__main__':
174 win32serviceutil.HandleCommandLine(PyWebService)