Package gluon :: Module winservice
[hide private]
[frames] | no frames]

Source Code for Module gluon.winservice

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  """ 
  4  This file is part of the web2py Web Framework 
  5  Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu> and 
  6  Limodou <limodou@gmail.com>. 
  7  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
  8   
  9  This makes uses of the pywin32 package 
 10  (http://sourceforge.net/projects/pywin32/). 
 11  You do not need to install this package to use web2py. 
 12   
 13   
 14  """ 
 15   
 16  import time 
 17  import os 
 18  import sys 
 19  import traceback 
 20  try: 
 21      import win32serviceutil 
 22      import win32service 
 23      import win32event 
 24  except: 
 25      if os.name == 'nt': 
 26          print "Warning, winservice is unable to install the Mark Hammond Win32 extensions" 
 27  import servicemanager 
 28  import _winreg 
 29  from gluon.fileutils import up 
 30   
 31   
 32  __all__ = ['web2py_windows_service_handler'] 
 33   
 34   
35 -class Service(win32serviceutil.ServiceFramework):
36 37 _svc_name_ = '_unNamed' 38 _svc_display_name_ = '_Service Template' 39 _svc_description_ = '_Service Template description' 40
41 - def __init__(self, *args):
42 win32serviceutil.ServiceFramework.__init__(self, *args) 43 self.stop_event = win32event.CreateEvent(None, 0, 0, None)
44
45 - def log(self, msg):
46 servicemanager.LogInfoMsg(str(msg))
47
48 - def log_error(self, msg):
49 """ 50 Log an error message to windows application event log. 51 """ 52 servicemanager.LogErrorMsg(str(msg))
53
54 - def SvcDoRun(self):
55 self.ReportServiceStatus(win32service.SERVICE_START_PENDING) 56 try: 57 self.ReportServiceStatus(win32service.SERVICE_RUNNING) 58 self.start() 59 win32event.WaitForSingleObject(self.stop_event, 60 win32event.INFINITE) 61 except: 62 self.log(traceback.format_exc(sys.exc_info)) 63 self.SvcStop() 64 self.ReportServiceStatus(win32service.SERVICE_STOPPED)
65
66 - def SvcStop(self):
67 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 68 try: 69 self.stop() 70 except: 71 self.log(traceback.format_exc(sys.exc_info)) 72 win32event.SetEvent(self.stop_event) 73 self.ReportServiceStatus(win32service.SERVICE_STOPPED)
74 75 # to be overridden 76
77 - def start(self):
78 pass
79 80 # to be overridden 81
82 - def stop(self):
83 pass
84 85
86 -class Web2pyService(Service):
87 88 _svc_name_ = 'web2py' 89 _svc_display_name_ = 'web2py Service' 90 _svc_description_ = 'Web2py application framework service' 91 _exe_args_ = 'options' 92 server = None 93
94 - def chdir(self):
95 try: 96 h = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 97 r'SYSTEM\CurrentControlSet\Services\%s' 98 % self._svc_name_) 99 try: 100 cls = _winreg.QueryValue(h, 'PythonClass') 101 finally: 102 _winreg.CloseKey(h) 103 dir = os.path.dirname(cls) 104 os.chdir(dir) 105 from gluon.settings import global_settings 106 global_settings.gluon_parent = dir 107 return True 108 except: 109 self.log("Can't change to web2py working path; server is stopped") 110 return False
111
112 - def start(self):
113 self.log('web2py server starting') 114 if not self.chdir(): 115 return 116 if len(sys.argv) == 2: 117 opt_mod = sys.argv[1] 118 else: 119 opt_mod = self._exe_args_ 120 options = __import__(opt_mod, [], [], '') 121 if True: # legacy support for old options files, which have only (deprecated) numthreads 122 if hasattr(options, 'numthreads') and not hasattr(options, 'minthreads'): 123 options.minthreads = options.numthreads 124 if not hasattr(options, 'minthreads'): 125 options.minthreads = None 126 if not hasattr(options, 'maxthreads'): 127 options.maxthreads = None 128 from gluon import main 129 self.server = main.HttpServer( 130 ip=options.ip, 131 port=options.port, 132 password=options.password, 133 pid_filename=options.pid_filename, 134 log_filename=options.log_filename, 135 profiler_filename=options.profiler_filename, 136 ssl_certificate=options.ssl_certificate, 137 ssl_private_key=options.ssl_private_key, 138 min_threads=options.minthreads, 139 max_threads=options.maxthreads, 140 server_name=options.server_name, 141 request_queue_size=options.request_queue_size, 142 timeout=options.timeout, 143 socket_timeout=options.socket_timeout, 144 shutdown_timeout=options.shutdown_timeout, 145 path=options.folder, 146 interfaces=options.interfaces 147 ) 148 try: 149 from gluon.rewrite import load 150 load() 151 self.server.start() 152 except: 153 self.server = None 154 raise
155
156 - def stop(self):
157 self.log('web2py server stopping') 158 if not self.chdir(): 159 return 160 if self.server: 161 self.server.stop() 162 time.sleep(1)
163 164
165 -class Web2pyCronService(Web2pyService):
166 167 _svc_name_ = 'web2py_cron' 168 _svc_display_name_ = 'web2py Cron Service' 169 _svc_description_ = 'web2py Windows cron service for scheduled tasks' 170 _exe_args_ = 'options' 171
172 - def start(self):
173 from gluon import newcron 174 import logging 175 import logging.config 176 from gluon.settings import global_settings 177 from gluon.fileutils import abspath 178 from os.path import exists, join 179 180 self.log('web2py Cron service starting') 181 if not self.chdir(): 182 return 183 if len(sys.argv) == 2: 184 opt_mod = sys.argv[1] 185 else: 186 opt_mod = self._exe_args_ 187 options = __import__(opt_mod, [], [], '') 188 logpath = abspath(join(options.folder, "logging.conf")) 189 190 if exists(logpath): 191 logging.config.fileConfig(logpath) 192 else: 193 logging.basicConfig() 194 logger = logging.getLogger("web2py.cron") 195 global_settings.web2py_crontype = 'external' 196 if options.scheduler: # -K 197 apps = [app.strip() for app in options.scheduler.split( 198 ',') if check_existent_app(options, app.strip())] 199 else: 200 apps = None 201 202 misfire_gracetime = float(options.misfire_gracetime) if 'misfire_gracetime' in dir(options) else 0.0 203 logger.info('Starting Window cron service with %0.2f secs gracetime.' % misfire_gracetime) 204 self._started = True 205 wait_full_min = lambda: 60 - time.time() % 60 206 while True: 207 try: 208 if wait_full_min() >= misfire_gracetime: # an offset of max. 5 secs before full minute (e.g. time.sleep(60) == 58.99 secs) 209 self.extcron = newcron.extcron(options.folder, apps=apps) 210 self.extcron.start() 211 time.sleep(wait_full_min()) 212 else: 213 logger.debug('time.sleep() offset detected: %0.3f s' % wait_full_min()) 214 while wait_full_min() <= misfire_gracetime: 215 pass 216 if apps != None: 217 break 218 if not self._started: 219 break 220 except Exception, ex: 221 self.extcron = None 222 self.log_error('%s, restarting service.' % ex) 223 logger.exception('Exception! Restarting Windows cron service.' % ex) 224 self.start()
225
226 - def stop(self):
227 self.log('web2py Cron service stopping') 228 if not self.chdir(): 229 return 230 if self.extcron: 231 self._started = False 232 self.extcron.join()
233
234 -def register_service_handler(argv=None, opt_file='options', cls=Web2pyService):
235 path = os.path.dirname(__file__) 236 web2py_path = up(path) 237 if web2py_path.endswith('.zip'): # in case bianry distro 'library.zip' 238 web2py_path = os.path.dirname(web2py_path) 239 os.chdir(web2py_path) 240 classstring = os.path.normpath( 241 os.path.join(web2py_path, 'gluon.winservice.'+cls.__name__)) 242 if opt_file: 243 cls._exe_args_ = opt_file 244 win32serviceutil.HandleCommandLine( 245 cls, serviceClassString=classstring, argv=['', 'install']) 246 win32serviceutil.HandleCommandLine( 247 cls, serviceClassString=classstring, argv=argv)
248 249 if __name__ == '__main__': 250 register_service_handler(cls=Web2pyService) 251 register_service_handler(cls=Web2pyCronService) 252