Files
web2py/gluon/winservice.py
T

252 lines
8.2 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu> and
Limodou <limodou@gmail.com>.
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
This makes uses of the pywin32 package
(http://sourceforge.net/projects/pywin32/).
You do not need to install this package to use web2py.
"""
import time
import os
import sys
import traceback
try:
import win32serviceutil
import win32service
import win32event
except:
if os.name == 'nt':
print "Warning, winservice is unable to install the Mark Hammond Win32 extensions"
import servicemanager
import _winreg
from fileutils import up
__all__ = ['web2py_windows_service_handler']
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = '_unNamed'
_svc_display_name_ = '_Service Template'
_svc_description_ = '_Service Template description'
def __init__(self, *args):
win32serviceutil.ServiceFramework.__init__(self, *args)
self.stop_event = win32event.CreateEvent(None, 0, 0, None)
def log(self, msg):
servicemanager.LogInfoMsg(str(msg))
def log_error(self, msg):
"""
Log an error message to windows application event log.
"""
servicemanager.LogErrorMsg(str(msg))
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
try:
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.start()
win32event.WaitForSingleObject(self.stop_event,
win32event.INFINITE)
except:
self.log(traceback.format_exc(sys.exc_info))
self.SvcStop()
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
try:
self.stop()
except:
self.log(traceback.format_exc(sys.exc_info))
win32event.SetEvent(self.stop_event)
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
# to be overridden
def start(self):
pass
# to be overridden
def stop(self):
pass
class Web2pyService(Service):
_svc_name_ = 'web2py'
_svc_display_name_ = 'web2py Service'
_svc_description_ = 'Web2py application framework service'
_exe_args_ = 'options'
server = None
def chdir(self):
try:
h = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
r'SYSTEM\CurrentControlSet\Services\%s'
% self._svc_name_)
try:
cls = _winreg.QueryValue(h, 'PythonClass')
finally:
_winreg.CloseKey(h)
dir = os.path.dirname(cls)
os.chdir(dir)
from gluon.settings import global_settings
global_settings.gluon_parent = dir
return True
except:
self.log("Can't change to web2py working path; server is stopped")
return False
def start(self):
self.log('web2py server starting')
if not self.chdir():
return
if len(sys.argv) == 2:
opt_mod = sys.argv[1]
else:
opt_mod = self._exe_args_
options = __import__(opt_mod, [], [], '')
if True: # legacy support for old options files, which have only (deprecated) numthreads
if hasattr(options, 'numthreads') and not hasattr(options, 'minthreads'):
options.minthreads = options.numthreads
if not hasattr(options, 'minthreads'):
options.minthreads = None
if not hasattr(options, 'maxthreads'):
options.maxthreads = None
import main
self.server = main.HttpServer(
ip=options.ip,
port=options.port,
password=options.password,
pid_filename=options.pid_filename,
log_filename=options.log_filename,
profiler_filename=options.profiler_filename,
ssl_certificate=options.ssl_certificate,
ssl_private_key=options.ssl_private_key,
min_threads=options.minthreads,
max_threads=options.maxthreads,
server_name=options.server_name,
request_queue_size=options.request_queue_size,
timeout=options.timeout,
socket_timeout=options.socket_timeout,
shutdown_timeout=options.shutdown_timeout,
path=options.folder,
interfaces=options.interfaces
)
try:
from rewrite import load
load()
self.server.start()
except:
self.server = None
raise
def stop(self):
self.log('web2py server stopping')
if not self.chdir():
return
if self.server:
self.server.stop()
time.sleep(1)
class Web2pyCronService(Web2pyService):
_svc_name_ = 'web2py_cron'
_svc_display_name_ = 'web2py Cron Service'
_svc_description_ = 'web2py Windows cron service for scheduled tasks'
_exe_args_ = 'options'
def start(self):
import newcron
import logging
import logging.config
from settings import global_settings
from fileutils import abspath
from os.path import exists, join
self.log('web2py Cron service starting')
if not self.chdir():
return
if len(sys.argv) == 2:
opt_mod = sys.argv[1]
else:
opt_mod = self._exe_args_
options = __import__(opt_mod, [], [], '')
logpath = abspath(join(options.folder, "logging.conf"))
if exists(logpath):
logging.config.fileConfig(logpath)
else:
logging.basicConfig()
logger = logging.getLogger("web2py.cron")
global_settings.web2py_crontype = 'external'
if options.scheduler: # -K
apps = [app.strip() for app in options.scheduler.split(
',') if check_existent_app(options, app.strip())]
else:
apps = None
misfire_gracetime = float(options.misfire_gracetime) if 'misfire_gracetime' in dir(options) else 0.0
logger.info('Starting Window cron service with %0.2f secs gracetime.' % misfire_gracetime)
self._started = True
wait_full_min = lambda: 60 - time.time() % 60
while True:
try:
if wait_full_min() >= misfire_gracetime: # an offset of max. 5 secs before full minute (e.g. time.sleep(60) == 58.99 secs)
self.extcron = newcron.extcron(options.folder, apps=apps)
self.extcron.start()
time.sleep(wait_full_min())
else:
logger.debug('time.sleep() offset detected: %0.3f s' % wait_full_min())
while wait_full_min() <= misfire_gracetime:
pass
if apps != None:
break
if not self._started:
break
except Exception, ex:
self.extcron = None
self.log_error('%s, restarting service.' % ex)
logger.exception('Exception! Restarting Windows cron service.' % ex)
self.start()
def stop(self):
self.log('web2py Cron service stopping')
if not self.chdir():
return
if self.extcron:
self._started = False
self.extcron.join()
def register_service_handler(argv=None, opt_file='options', cls=Web2pyService):
path = os.path.dirname(__file__)
web2py_path = up(path)
if web2py_path.endswith('.zip'): # in case bianry distro 'library.zip'
web2py_path = os.path.dirname(web2py_path)
os.chdir(web2py_path)
classstring = os.path.normpath(
os.path.join(web2py_path, 'gluon.winservice.'+cls.__name__))
if opt_file:
cls._exe_args_ = opt_file
win32serviceutil.HandleCommandLine(
cls, serviceClassString=classstring, argv=['', 'install'])
win32serviceutil.HandleCommandLine(
cls, serviceClassString=classstring, argv=argv)
if __name__ == '__main__':
register_service_handler(cls=Web2pyService)
register_service_handler(cls=Web2pyCronService)