[REF] openerp-server: cleaned imports.
[odoo/odoo.git] / openerp-server
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 ##############################################################################
4 #
5 #    OpenERP, Open Source Management Solution
6 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 """
24 OpenERP - Server
25 OpenERP is an ERP+CRM program for small and medium businesses.
26
27 The whole source code is distributed under the terms of the
28 GNU Public Licence.
29
30 (c) 2003-TODAY, Fabien Pinckaers - OpenERP s.a.
31 """
32
33 #----------------------------------------------------------
34 # python imports
35 #----------------------------------------------------------
36 import logging
37 import os
38 import signal
39 import sys
40 import threading
41 import traceback
42 import time
43
44 import openerp.release as release
45 __author__ = release.author
46 __version__ = release.version
47
48 if os.name == 'posix':
49     import pwd
50     # We DON't log this using the standard logger, because we might mess
51     # with the logfile's permissions. Just do a quick exit here.
52     if pwd.getpwuid(os.getuid())[0] == 'root' :
53         sys.stderr.write("Attempted to run OpenERP server as root. This is not good, aborting.\n")
54         sys.exit(1)
55
56 #-----------------------------------------------------------------------
57 # import the tools module so that the commandline parameters are parsed
58 #-----------------------------------------------------------------------
59 import openerp.tools as tools
60 tools.config.parse_config(sys.argv[1:])
61
62 #----------------------------------------------------------
63 # get logger
64 #----------------------------------------------------------
65 import openerp.netsvc as netsvc
66 netsvc.init_logger()
67 logger = logging.getLogger('server')
68
69 logger.info("OpenERP version - %s", release.version)
70 for name, value in [('addons_path', tools.config['addons_path']),
71                     ('database hostname', tools.config['db_host'] or 'localhost'),
72                     ('database port', tools.config['db_port'] or '5432'),
73                     ('database user', tools.config['db_user'])]:
74     logger.info("%s - %s", name, value)
75
76 # Don't allow if the connection to PostgreSQL done by postgres user
77 if tools.config['db_user'] == 'postgres':
78     logger.error("Connecting to the database as 'postgres' user is forbidden, as it present major security issues. Shutting down.")
79     sys.exit(1)
80
81 #----------------------------------------------------------
82 # init net service
83 #----------------------------------------------------------
84 logger.info('initialising distributed objects services')
85
86 #---------------------------------------------------------------
87 # connect to the database and initialize it with base if needed
88 #---------------------------------------------------------------
89 import openerp.pooler as pooler
90
91 #----------------------------------------------------------
92 # Load and update databases if requested
93 #----------------------------------------------------------
94
95 import openerp.service.http_server as http_server
96 import openerp.service.netrpc_server as netrpc_server
97
98 if not ( tools.config["stop_after_init"] or \
99     tools.config["translate_in"] or \
100     tools.config["translate_out"] ):
101     http_server.init_servers()
102     http_server.init_xmlrpc()
103     http_server.init_static_http()
104     netrpc_server.init_servers()
105
106 if tools.config['db_name']:
107     for dbname in tools.config['db_name'].split(','):
108         db,pool = pooler.get_db_and_pool(dbname, update_module=tools.config['init'] or tools.config['update'], pooljobs=False)
109         cr = db.cursor()
110
111         if tools.config["test_file"]:
112             logger.info('loading test file %s', tools.config["test_file"])
113             tools.convert_yaml_import(cr, 'base', file(tools.config["test_file"]), {}, 'test', True)
114             cr.rollback()
115
116         pool.get('ir.cron')._poolJobs(db.dbname)
117
118         cr.close()
119
120 #----------------------------------------------------------
121 # translation stuff
122 #----------------------------------------------------------
123 if tools.config["translate_out"]:
124     if tools.config["language"]:
125         msg = "language %s" % (tools.config["language"],)
126     else:
127         msg = "new language"
128     logger.info('writing translation file for %s to %s', msg, tools.config["translate_out"])
129
130     fileformat = os.path.splitext(tools.config["translate_out"])[-1][1:].lower()
131     buf = file(tools.config["translate_out"], "w")
132     dbname = tools.config['db_name']
133     cr = pooler.get_db(dbname).cursor()
134     tools.trans_export(tools.config["language"], tools.config["translate_modules"] or ["all"], buf, fileformat, cr)
135     cr.close()
136     buf.close()
137
138     logger.info('translation file written successfully')
139     sys.exit(0)
140
141 if tools.config["translate_in"]:
142     context = {'overwrite': tools.config["overwrite_existing_translations"]}
143     dbname = tools.config['db_name']
144     cr = pooler.get_db(dbname).cursor()
145     tools.trans_load(cr,
146                      tools.config["translate_in"], 
147                      tools.config["language"],
148                      context=context)
149     tools.trans_update_res_ids(cr)
150     cr.commit()
151     cr.close()
152     sys.exit(0)
153
154 #----------------------------------------------------------------------------------
155 # if we don't want the server to continue to run after initialization, we quit here
156 #----------------------------------------------------------------------------------
157 if tools.config["stop_after_init"]:
158     sys.exit(0)
159
160 #----------------------------------------------------------
161 # Launch Servers
162 #----------------------------------------------------------
163
164 LST_SIGNALS = ['SIGINT', 'SIGTERM']
165
166 SIGNALS = dict(
167     [(getattr(signal, sign), sign) for sign in LST_SIGNALS]
168 )
169
170 quit_signals_received = 0
171
172 def handler(signum, frame):
173     """
174     :param signum: the signal number
175     :param frame: the interrupted stack frame or None
176     """
177     global quit_signals_received
178     quit_signals_received += 1
179     if quit_signals_received > 1:
180         sys.stderr.write("Forced shutdown.\n")
181         os._exit(0)
182
183 def dumpstacks(signum, frame):
184     # code from http://stackoverflow.com/questions/132058/getting-stack-trace-from-a-running-python-application#answer-2569696
185     # modified for python 2.5 compatibility
186     thread_map = dict(threading._active, **threading._limbo)
187     id2name = dict([(threadId, thread.getName()) for threadId, thread in thread_map.items()])
188     code = []
189     for threadId, stack in sys._current_frames().items():
190         code.append("\n# Thread: %s(%d)" % (id2name[threadId], threadId))
191         for filename, lineno, name, line in traceback.extract_stack(stack):
192             code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
193             if line:
194                 code.append("  %s" % (line.strip()))
195     logging.getLogger('dumpstacks').info("\n".join(code))
196
197 for signum in SIGNALS:
198     signal.signal(signum, handler)
199
200 if os.name == 'posix':
201     signal.signal(signal.SIGQUIT, dumpstacks)
202
203 def quit():
204     netsvc.Agent.quit()
205     netsvc.Server.quitAll()
206     if tools.config['pidfile']:
207         os.unlink(tools.config['pidfile'])
208     logger = logging.getLogger('shutdown')
209     logger.info("Initiating OpenERP Server shutdown")
210     logger.info("Hit CTRL-C again or send a second signal to immediately terminate the server...")
211     logging.shutdown()
212
213     # manually join() all threads before calling sys.exit() to allow a second signal
214     # to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
215     # threading.Thread.join() should not mask signals (at least in python 2.5)
216     for thread in threading.enumerate():
217         if thread != threading.currentThread() and not thread.isDaemon():
218             while thread.isAlive():
219                 # need a busyloop here as thread.join() masks signals
220                 # and would present the forced shutdown
221                 thread.join(0.05)
222                 time.sleep(0.05)
223     sys.exit(0)
224
225 if tools.config['pidfile']:
226     fd = open(tools.config['pidfile'], 'w')
227     pidtext = "%d" % (os.getpid())
228     fd.write(pidtext)
229     fd.close()
230
231 netsvc.Server.startAll()
232
233 logger.info('OpenERP server is running, waiting for connections...')
234
235 while quit_signals_received == 0:
236     time.sleep(60)
237
238 quit()
239
240 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: