[FIX] correct reexecute. works for windows service
[odoo/odoo.git] / openerp / service / __init__.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010-2012 OpenERP SA (<http://www.openerp.com>)
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 import logging
24 import os
25 import signal
26 import subprocess
27 import sys
28 import threading
29 import time
30
31 import netrpc_server
32 import web_services
33
34 import openerp.cron
35 import openerp.modules
36 import openerp.netsvc
37 import openerp.osv
38 from openerp.release import nt_service_name
39 import openerp.tools
40 import openerp.service.wsgi_server
41
42 #.apidoc title: RPC Services
43
44 """ Classes of this module implement the network protocols that the
45     OpenERP server uses to communicate with remote clients.
46
47     Some classes are mostly utilities, whose API need not be visible to
48     the average user/developer. Study them only if you are about to
49     implement an extension to the network protocols, or need to debug some
50     low-level behavior of the wire.
51 """
52
53 _logger = logging.getLogger(__name__)
54
55 def load_server_wide_modules():
56     for m in openerp.conf.server_wide_modules:
57         try:
58             openerp.modules.module.load_openerp_module(m)
59         except Exception:
60             msg = ''
61             if m == 'web':
62                 msg = """
63 The `web` module is provided by the addons found in the `openerp-web` project.
64 Maybe you forgot to add those addons in your addons_path configuration."""
65             _logger.exception('Failed to load server-wide module `%s`.%s', m, msg)
66
67 start_internal_done = False
68 main_thread_id = threading.currentThread().ident
69
70 def start_internal():
71     global start_internal_done
72     if start_internal_done:
73         return
74     openerp.netsvc.init_logger()
75     openerp.modules.loading.open_openerp_namespace()
76     # Instantiate local services (this is a legacy design).
77     openerp.osv.osv.start_object_proxy()
78     # Export (for RPC) services.
79     web_services.start_web_services()
80     load_server_wide_modules()
81     start_internal_done = True
82
83 def start_services():
84     """ Start all services including http, netrpc and cron """
85     start_internal()
86
87     # Initialize the HTTP stack.
88     netrpc_server.init_servers()
89
90     # Start the main cron thread.
91     if openerp.conf.max_cron_threads:
92         openerp.cron.start_master_thread()
93
94     # Start the top-level servers threads (normally HTTP, HTTPS, and NETRPC).
95     openerp.netsvc.Server.startAll()
96
97     # Start the WSGI server.
98     openerp.service.wsgi_server.start_server()
99
100 def stop_services():
101     """ Stop all services. """
102     # stop scheduling new jobs; we will have to wait for the jobs to complete below
103     openerp.cron.cancel_all()
104
105     openerp.netsvc.Server.quitAll()
106     openerp.service.wsgi_server.stop_server()
107     _logger.info("Initiating shutdown")
108     _logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
109
110     # Manually join() all threads before calling sys.exit() to allow a second signal
111     # to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
112     # threading.Thread.join() should not mask signals (at least in python 2.5).
113     me = threading.currentThread()
114     _logger.debug('current thread: %r', me)
115     for thread in threading.enumerate():
116         _logger.debug('process %r (%r)', thread, thread.isDaemon())
117         if thread != me and not thread.isDaemon() and thread.ident != main_thread_id:
118             while thread.isAlive():
119                 _logger.debug('join and sleep')
120                 # Need a busyloop here as thread.join() masks signals
121                 # and would prevent the forced shutdown.
122                 thread.join(0.05)
123                 time.sleep(0.05)
124
125     _logger.debug('--')
126     openerp.modules.registry.RegistryManager.delete_all()
127     logging.shutdown()
128
129 def start_services_workers():
130     import openerp.service.workers
131     openerp.multi_process = True
132     openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
133
134 def _reexec():
135     """reexecute openerp-server process with (nearly) the same arguments"""
136     if openerp.tools.osutil.is_running_as_nt_service():
137         subprocess.call('sc stop {0} && sc start {0}'.format(nt_service_name), shell=True)
138     exe = os.path.basename(sys.executable)
139     strip_args = ['-d', '-u']
140     a = sys.argv[:]
141     args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args]
142     if not args or args[0] != exe:
143         args.insert(0, exe)
144     os.execv(sys.executable, args)
145
146 def restart_server():
147     if openerp.multi_process:
148         raise NotImplementedError("Multicorn is not supported (but gunicorn was)")
149         pid = openerp.wsgi.core.arbiter_pid
150         os.kill(pid, signal.SIGHUP)
151     else:
152         if os.name == 'nt':
153             def reborn():
154                 stop_services()
155                 _reexec()
156
157             # run in a thread to let the current thread return response to the caller.
158             threading.Thread(target=reborn).start()
159         else:
160             openerp.phoenix = True
161             os.kill(os.getpid(), signal.SIGINT)
162
163 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: