1   
  2   
  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   
 42          win32serviceutil.ServiceFramework.__init__(self, *args) 
 43          self.stop_event = win32event.CreateEvent(None, 0, 0, None) 
  44   
 46          servicemanager.LogInfoMsg(str(msg)) 
  47   
 49          """ 
 50          Log an error message to windows application event log. 
 51          """ 
 52          servicemanager.LogErrorMsg(str(msg)) 
  53   
 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   
 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       
 76   
 79   
 80       
 81   
  84   
 85   
 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   
 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   
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:   
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   
 163   
164   
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   
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:    
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:  
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   
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   
248   
249  if __name__ == '__main__': 
250      register_service_handler(cls=Web2pyService) 
251      register_service_handler(cls=Web2pyCronService) 
252