OLD | NEW |
(Empty) | |
| 1 """Windows service. Requires pywin32.""" |
| 2 |
| 3 import os |
| 4 import win32api |
| 5 import win32con |
| 6 import win32event |
| 7 import win32service |
| 8 import win32serviceutil |
| 9 |
| 10 from cherrypy.process import wspbus, plugins |
| 11 |
| 12 |
| 13 class ConsoleCtrlHandler(plugins.SimplePlugin): |
| 14 """A WSPBus plugin for handling Win32 console events (like Ctrl-C).""" |
| 15 |
| 16 def __init__(self, bus): |
| 17 self.is_set = False |
| 18 plugins.SimplePlugin.__init__(self, bus) |
| 19 |
| 20 def start(self): |
| 21 if self.is_set: |
| 22 self.bus.log('Handler for console events already set.', level=40) |
| 23 return |
| 24 |
| 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 |
| 32 |
| 33 def stop(self): |
| 34 if not self.is_set: |
| 35 self.bus.log('Handler for console events already off.', level=40) |
| 36 return |
| 37 |
| 38 try: |
| 39 result = win32api.SetConsoleCtrlHandler(self.handle, 0) |
| 40 except ValueError: |
| 41 # "ValueError: The object has not been registered" |
| 42 result = 1 |
| 43 |
| 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 |
| 50 |
| 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) |
| 57 |
| 58 # Remove self immediately so repeated Ctrl-C doesn't re-call it. |
| 59 try: |
| 60 self.stop() |
| 61 except ValueError: |
| 62 pass |
| 63 |
| 64 self.bus.exit() |
| 65 # 'First to return True stops the calls' |
| 66 return 1 |
| 67 return 0 |
| 68 |
| 69 |
| 70 class Win32Bus(wspbus.Bus): |
| 71 """A Web Site Process Bus implementation for Win32. |
| 72 |
| 73 Instead of time.sleep, this bus blocks using native win32event objects. |
| 74 """ |
| 75 |
| 76 def __init__(self): |
| 77 self.events = {} |
| 78 wspbus.Bus.__init__(self) |
| 79 |
| 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 |
| 90 |
| 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) |
| 98 |
| 99 def wait(self, state, interval=0.1, channel=None): |
| 100 """Wait for the given state(s), KeyboardInterrupt or SystemExit. |
| 101 |
| 102 Since this class uses native win32event objects, the interval |
| 103 argument is ignored. |
| 104 """ |
| 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) |
| 115 |
| 116 |
| 117 class _ControlCodes(dict): |
| 118 """Control codes used to "signal" a service via ControlService. |
| 119 |
| 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: |
| 122 |
| 123 >>> signal.SIGUSR1 |
| 124 10 |
| 125 control_codes['graceful'] = 128 + 10 |
| 126 """ |
| 127 |
| 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) |
| 134 |
| 135 control_codes = _ControlCodes({'graceful': 138}) |
| 136 |
| 137 |
| 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]) |
| 145 |
| 146 |
| 147 class PyWebService(win32serviceutil.ServiceFramework): |
| 148 """Python Web Service.""" |
| 149 |
| 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 |
| 155 |
| 156 # Only exists on Windows 2000 or later, ignored on windows NT |
| 157 _svc_description_ = "Python Web Service" |
| 158 |
| 159 def SvcDoRun(self): |
| 160 from cherrypy import process |
| 161 process.bus.start() |
| 162 process.bus.block() |
| 163 |
| 164 def SvcStop(self): |
| 165 from cherrypy import process |
| 166 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) |
| 167 process.bus.exit() |
| 168 |
| 169 def SvcOther(self, control): |
| 170 process.bus.publish(control_codes.key_for(control)) |
| 171 |
| 172 |
| 173 if __name__ == '__main__': |
| 174 win32serviceutil.HandleCommandLine(PyWebService) |
OLD | NEW |