Suppress Errno 57 when attempting to close an already shut socket, fixes issue1624 Revert from join() on enumerated threads to join() on individual service threadsto preserve debugging environments
251 lines
9.0 KiB
Python
251 lines
9.0 KiB
Python
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
#this repository contains the full copyright notices and license terms.
|
|
"""
|
|
%prog [options]
|
|
"""
|
|
import logging
|
|
import logging.handlers
|
|
import sys, os, signal
|
|
import time
|
|
from trytond.config import CONFIG
|
|
from getpass import getpass
|
|
try:
|
|
import hashlib
|
|
except ImportError:
|
|
hashlib = None
|
|
import sha
|
|
import threading
|
|
import string
|
|
import random
|
|
|
|
|
|
class TrytonServer(object):
|
|
|
|
def __init__(self):
|
|
format = '[%(asctime)s] %(levelname)s:%(name)s:%(message)s'
|
|
datefmt = '%a %b %d %H:%M:%S %Y'
|
|
logging.basicConfig(level=logging.DEBUG, format=format,
|
|
datefmt=datefmt)
|
|
|
|
CONFIG.parse()
|
|
|
|
if CONFIG['logfile']:
|
|
logf = CONFIG['logfile']
|
|
# test if the directories exist, else create them
|
|
try:
|
|
diff = 0
|
|
if os.path.isfile(logf):
|
|
diff = int(time.time()) - int(os.stat(logf)[-1])
|
|
handler = logging.handlers.TimedRotatingFileHandler(
|
|
logf, 'D', 1, 30)
|
|
handler.rolloverAt -= diff
|
|
except Exception, exception:
|
|
sys.stderr.write(\
|
|
"ERROR: couldn't create the logfile directory:" \
|
|
+ str(exception))
|
|
else:
|
|
formatter = logging.Formatter(format, datefmt)
|
|
# tell the handler to use this format
|
|
handler.setFormatter(formatter)
|
|
|
|
# add the handler to the root logger
|
|
logging.getLogger().addHandler(handler)
|
|
logging.getLogger().setLevel(logging.INFO)
|
|
elif os.name != 'nt':
|
|
reverse = '\x1b[7m'
|
|
reset = '\x1b[0m'
|
|
# reverse color for error and critical messages
|
|
for level in logging.ERROR, logging.CRITICAL:
|
|
msg = reverse + logging.getLevelName(level) + reset
|
|
logging.addLevelName(level, msg)
|
|
|
|
self.logger = logging.getLogger("server")
|
|
|
|
if CONFIG.configfile:
|
|
self.logger.info('using %s as configuration file' % \
|
|
CONFIG.configfile)
|
|
else:
|
|
self.logger.info('using default configuration')
|
|
self.logger.info('initialising distributed objects services')
|
|
|
|
def run(self):
|
|
"Run the server and never return"
|
|
from trytond.backend import Database
|
|
from trytond.pool import Pool
|
|
from trytond.monitor import monitor
|
|
|
|
update = False
|
|
init = {}
|
|
|
|
if not CONFIG["db_name"] \
|
|
and bool(CONFIG['init'] or CONFIG['update']):
|
|
raise Exception('Missing database option!')
|
|
|
|
for db_name in CONFIG["db_name"]:
|
|
init[db_name] = False
|
|
database = Database(db_name).connect()
|
|
cursor = database.cursor()
|
|
|
|
if CONFIG['init']:
|
|
if not cursor.test():
|
|
self.logger.info("init db")
|
|
Database.init(cursor)
|
|
init[db_name] = True
|
|
cursor.commit()
|
|
cursor.close()
|
|
elif not cursor.test():
|
|
raise Exception("'%s' is not a Tryton database!" % db_name)
|
|
|
|
Pool.start()
|
|
|
|
for db_name in CONFIG["db_name"]:
|
|
cursor = Database(db_name).connect().cursor()
|
|
if not cursor.test():
|
|
raise Exception("'%s' is not a Tryton database!" % db_name)
|
|
cursor.execute('SELECT code FROM ir_lang ' \
|
|
'WHERE translatable')
|
|
lang = [x[0] for x in cursor.fetchall()]
|
|
cursor.close()
|
|
update = bool(CONFIG['init'] or CONFIG['update'])
|
|
Pool(db_name).init(update=update, lang=lang)
|
|
|
|
for kind in ('init', 'update'):
|
|
CONFIG[kind] = {}
|
|
|
|
for db_name in CONFIG['db_name']:
|
|
if init[db_name]:
|
|
while True:
|
|
password = getpass('Admin Password for %s: ' % db_name)
|
|
password2 = getpass('Admin Password Confirmation: ')
|
|
if password != password2:
|
|
sys.stderr.write('Admin Password Confirmation ' \
|
|
'doesn\'t match Admin Password!\n')
|
|
continue
|
|
if not password:
|
|
sys.stderr.write('Admin Password is required!\n')
|
|
continue
|
|
break
|
|
|
|
database = Database(db_name).connect()
|
|
cursor = database.cursor()
|
|
salt = ''.join(random.sample(string.letters + string.digits, 8))
|
|
password += salt
|
|
if hashlib:
|
|
password = hashlib.sha1(password).hexdigest()
|
|
else:
|
|
password = sha.new(password).hexdigest()
|
|
cursor.execute('UPDATE res_user ' \
|
|
'SET password = %s, salt = %s ' \
|
|
'WHERE login = \'admin\'', (password, salt))
|
|
cursor.commit()
|
|
cursor.close()
|
|
|
|
if update:
|
|
self.logger.info('Update/Init succeed!')
|
|
logging.shutdown()
|
|
sys.exit(0)
|
|
|
|
# Launch Server
|
|
if CONFIG['xmlrpc']:
|
|
from trytond.protocols.xmlrpc import XMLRPCDaemon
|
|
xmlrpcd = XMLRPCDaemon(CONFIG['interface'], CONFIG['xmlport'],
|
|
CONFIG['secure_xmlrpc'])
|
|
self.logger.info("starting XML-RPC%s protocol, port %d" % \
|
|
(CONFIG['secure_xmlrpc'] and ' Secure' or '',
|
|
CONFIG['xmlport']))
|
|
|
|
if CONFIG['jsonrpc']:
|
|
from trytond.protocols.jsonrpc import JSONRPCDaemon
|
|
jsonrpcd = JSONRPCDaemon(CONFIG['interface'], CONFIG['jsonport'],
|
|
CONFIG['secure_jsonrpc'])
|
|
self.logger.info("starting JSON-RPC%s protocol, port %d" % \
|
|
(CONFIG['secure_jsonrpc'] and ' Secure' or '',
|
|
CONFIG['jsonport']))
|
|
|
|
if CONFIG['netrpc']:
|
|
from trytond.protocols.netrpc import NetRPCServerThread
|
|
netrpcd = NetRPCServerThread(CONFIG['interface'], CONFIG['netport'],
|
|
CONFIG['secure_netrpc'])
|
|
self.logger.info("starting NetRPC%s protocol, port %d" % \
|
|
(CONFIG['secure_netrpc'] and ' Secure' or '',
|
|
CONFIG['netport']))
|
|
|
|
if CONFIG['webdav']:
|
|
from trytond.protocols.webdav import WebDAVServerThread
|
|
webdavd = WebDAVServerThread(CONFIG['interface'],
|
|
CONFIG['webdavport'], CONFIG['secure_webdav'])
|
|
self.logger.info("starting WebDAV%s protocol, port %d" % \
|
|
(CONFIG['secure_webdav'] and ' Secure' or '',
|
|
CONFIG['webdavport']))
|
|
|
|
def handler(signum, frame):
|
|
if hasattr(signal, 'SIGUSR1'):
|
|
if signum == signal.SIGUSR1:
|
|
Pool.start()
|
|
return
|
|
if CONFIG['netrpc']:
|
|
netrpcd.stop()
|
|
netrpcd.join()
|
|
if CONFIG['xmlrpc']:
|
|
xmlrpcd.stop()
|
|
xmlrpcd.join()
|
|
if CONFIG['jsonrpc']:
|
|
jsonrpcd.stop()
|
|
jsonrpcd.join()
|
|
if CONFIG['webdav']:
|
|
webdavd.stop()
|
|
webdavd.join()
|
|
if CONFIG['pidfile']:
|
|
os.unlink(CONFIG['pidfile'])
|
|
logging.getLogger('server').info('stopped')
|
|
logging.shutdown()
|
|
sys.exit(0)
|
|
|
|
if CONFIG['pidfile']:
|
|
fd_pid = open(CONFIG['pidfile'], 'w')
|
|
pidtext = "%d" % (os.getpid())
|
|
fd_pid.write(pidtext)
|
|
fd_pid.close()
|
|
|
|
signal.signal(signal.SIGINT, handler)
|
|
signal.signal(signal.SIGTERM, handler)
|
|
if hasattr(signal, 'SIGQUIT'):
|
|
signal.signal(signal.SIGQUIT, handler)
|
|
if hasattr(signal, 'SIGUSR1'):
|
|
signal.signal(signal.SIGUSR1, handler)
|
|
|
|
self.logger.info('waiting for connections...')
|
|
if CONFIG['netrpc']:
|
|
netrpcd.start()
|
|
if CONFIG['xmlrpc']:
|
|
xmlrpcd.start()
|
|
if CONFIG['jsonrpc']:
|
|
jsonrpcd.start()
|
|
if CONFIG['webdav']:
|
|
webdavd.start()
|
|
|
|
if CONFIG['psyco']:
|
|
import psyco
|
|
psyco.full()
|
|
|
|
while True:
|
|
if CONFIG['auto_reload'] and monitor():
|
|
try:
|
|
Pool.start()
|
|
except Exception:
|
|
pass
|
|
for dbname in Pool.database_list():
|
|
pool = Pool(dbname)
|
|
if 'ir.cron' not in pool.object_name_list():
|
|
continue
|
|
cron_obj = pool.get('ir.cron')
|
|
thread = threading.Thread(
|
|
target=cron_obj.pool_jobs,
|
|
args=(dbname,), kwargs={})
|
|
thread.start()
|
|
time.sleep(60)
|
|
|
|
if __name__ == "__main__":
|
|
SERVER = TrytonServer()
|
|
SERVER.run()
|