Statistics
| Branch: | Tag: | Revision:

root / logic / dispatcher.py @ 583bfaa0

History | View | Annotate | Download (11.7 kB)

1 d08a5f6f Vangelis Koukis
#!/usr/bin/env python
2 48130e66 Georgios Gousios
# Copyright 2011 GRNET S.A. All rights reserved.
3 87ace70f Vassilios Karakoidas
#
4 48130e66 Georgios Gousios
# Redistribution and use in source and binary forms, with or without
5 48130e66 Georgios Gousios
# modification, are permitted provided that the following conditions
6 48130e66 Georgios Gousios
# are met:
7 7bd50624 Vassilios Karakoidas
#
8 48130e66 Georgios Gousios
#   1. Redistributions of source code must retain the above copyright
9 48130e66 Georgios Gousios
#      notice, this list of conditions and the following disclaimer.
10 48130e66 Georgios Gousios
#
11 48130e66 Georgios Gousios
#  2. Redistributions in binary form must reproduce the above copyright
12 48130e66 Georgios Gousios
#     notice, this list of conditions and the following disclaimer in the
13 48130e66 Georgios Gousios
#     documentation and/or other materials provided with the distribution.
14 48130e66 Georgios Gousios
#
15 48130e66 Georgios Gousios
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16 48130e66 Georgios Gousios
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 48130e66 Georgios Gousios
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 48130e66 Georgios Gousios
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19 48130e66 Georgios Gousios
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 48130e66 Georgios Gousios
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 48130e66 Georgios Gousios
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 48130e66 Georgios Gousios
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 48130e66 Georgios Gousios
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 48130e66 Georgios Gousios
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 48130e66 Georgios Gousios
# SUCH DAMAGE.
26 48130e66 Georgios Gousios
#
27 48130e66 Georgios Gousios
# The views and conclusions contained in the software and documentation are
28 48130e66 Georgios Gousios
# those of the authors and should not be interpreted as representing official
29 48130e66 Georgios Gousios
# policies, either expressed or implied, of GRNET S.A.
30 48130e66 Georgios Gousios
31 48130e66 Georgios Gousios
32 e6209aa2 Georgios Gousios
""" Message queue setup, dispatch and admin
33 c183005e Georgios Gousios

34 2cd99e7a Georgios Gousios
This program sets up connections to the queues configured in settings.py
35 2cd99e7a Georgios Gousios
and implements the message wait and dispatch loops. Actual messages are
36 2cd99e7a Georgios Gousios
handled in the dispatched functions.
37 fcbc5bb3 Vassilios Karakoidas

38 d08a5f6f Vangelis Koukis
"""
39 87ace70f Vassilios Karakoidas
40 d08a5f6f Vangelis Koukis
from django.core.management import setup_environ
41 c99fe4c7 Vassilios Karakoidas
42 d08a5f6f Vangelis Koukis
import sys
43 86221fd5 Panos Louridas
import os
44 86221fd5 Panos Louridas
path = os.path.normpath(os.path.join(os.getcwd(), '..'))
45 86221fd5 Panos Louridas
sys.path.append(path)
46 a5c17ad3 Dimitris Moraitis
import synnefo.settings as settings
47 f13691b3 Georgios Gousios
from synnefo.logic import log
48 d08a5f6f Vangelis Koukis
49 d08a5f6f Vangelis Koukis
setup_environ(settings)
50 d08a5f6f Vangelis Koukis
51 da102335 Georgios Gousios
from amqplib import client_0_8 as amqp
52 8861126f Georgios Gousios
from signal import signal, SIGINT, SIGTERM
53 23c84263 Georgios Gousios
54 8d8ea051 Georgios Gousios
import time
55 8d8ea051 Georgios Gousios
import socket
56 4ed2e471 Georgios Gousios
from daemon import daemon
57 4ed2e471 Georgios Gousios
58 4ed2e471 Georgios Gousios
# Take care of differences between python-daemon versions.
59 4ed2e471 Georgios Gousios
try:
60 4ed2e471 Georgios Gousios
    from daemon import pidfile
61 4ed2e471 Georgios Gousios
except:
62 4ed2e471 Georgios Gousios
    from daemon import pidlockfile
63 8d8ea051 Georgios Gousios
64 9cb903f9 Vangelis Koukis
from synnefo.logic import callbacks
65 c99fe4c7 Vassilios Karakoidas
66 78e2d194 Georgios Gousios
class Dispatcher:
67 8d8ea051 Georgios Gousios
68 78e2d194 Georgios Gousios
    logger = None
69 78e2d194 Georgios Gousios
    chan = None
70 5d081749 Georgios Gousios
    debug = False
71 5d081749 Georgios Gousios
    clienttags = []
72 78e2d194 Georgios Gousios
73 57d0082a Georgios Gousios
    def __init__(self, debug = False):
74 f13691b3 Georgios Gousios
        
75 57d0082a Georgios Gousios
        # Initialize logger
76 f13691b3 Georgios Gousios
        self.logger = log.get_logger('synnefo.dispatcher')
77 57d0082a Georgios Gousios
78 5d081749 Georgios Gousios
        self.debug = debug
79 5d081749 Georgios Gousios
        self._init()
80 da102335 Georgios Gousios
81 78e2d194 Georgios Gousios
    def wait(self):
82 78e2d194 Georgios Gousios
        while True:
83 78e2d194 Georgios Gousios
            try:
84 78e2d194 Georgios Gousios
                self.chan.wait()
85 78e2d194 Georgios Gousios
            except SystemExit:
86 78e2d194 Georgios Gousios
                break
87 cadaffb1 Georgios Gousios
            except amqp.exceptions.AMQPConnectionException:
88 cadaffb1 Georgios Gousios
                self.logger.error("Server went away, reconnecting...")
89 cadaffb1 Georgios Gousios
                self._init()
90 5d081749 Georgios Gousios
            except socket.error:
91 5d081749 Georgios Gousios
                self.logger.error("Server went away, reconnecting...")
92 5d081749 Georgios Gousios
                self._init()
93 78e2d194 Georgios Gousios
94 5d081749 Georgios Gousios
        [self.chan.basic_cancel(clienttag) for clienttag in self.clienttags]
95 78e2d194 Georgios Gousios
        self.chan.connection.close()
96 838239fa Georgios Gousios
        self.chan.close()
97 78e2d194 Georgios Gousios
98 5d081749 Georgios Gousios
    def _init(self):
99 57d0082a Georgios Gousios
        self.logger.info("Initializing")
100 57d0082a Georgios Gousios
        
101 c183005e Georgios Gousios
        # Connect to RabbitMQ
102 23c84263 Georgios Gousios
        conn = None
103 23c84263 Georgios Gousios
        while conn == None:
104 c183005e Georgios Gousios
            self.logger.info("Attempting to connect to %s",
105 c183005e Georgios Gousios
                             settings.RABBIT_HOST)
106 23c84263 Georgios Gousios
            try:
107 41f2249e Vangelis Koukis
                conn = amqp.Connection(host=settings.RABBIT_HOST,
108 41f2249e Vangelis Koukis
                                       userid=settings.RABBIT_USERNAME,
109 41f2249e Vangelis Koukis
                                       password=settings.RABBIT_PASSWORD,
110 41f2249e Vangelis Koukis
                                       virtual_host=settings.RABBIT_VHOST)
111 23c84263 Georgios Gousios
            except socket.error:
112 23c84263 Georgios Gousios
                time.sleep(1)
113 23c84263 Georgios Gousios
114 23c84263 Georgios Gousios
        self.logger.info("Connection succesful, opening channel")
115 23c84263 Georgios Gousios
        self.chan = conn.channel()
116 78e2d194 Georgios Gousios
117 c183005e Georgios Gousios
        # Declare queues and exchanges
118 f30730c0 Georgios Gousios
        for exchange in settings.EXCHANGES:
119 c183005e Georgios Gousios
            self.chan.exchange_declare(exchange=exchange, type="topic",
120 c183005e Georgios Gousios
                                       durable=True, auto_delete=False)
121 f30730c0 Georgios Gousios
122 f30730c0 Georgios Gousios
        for queue in settings.QUEUES:
123 c183005e Georgios Gousios
            self.chan.queue_declare(queue=queue, durable=True,
124 c183005e Georgios Gousios
                                    exclusive=False, auto_delete=False)
125 78e2d194 Georgios Gousios
126 23c84263 Georgios Gousios
        bindings = settings.BINDINGS
127 78e2d194 Georgios Gousios
128 c183005e Georgios Gousios
        # Special queue for debugging, should not appear in production
129 5d081749 Georgios Gousios
        if self.debug:
130 c183005e Georgios Gousios
            self.chan.queue_declare(queue=settings.QUEUE_DEBUG, durable=True,
131 c183005e Georgios Gousios
                                    exclusive=False, auto_delete=False)
132 23c84263 Georgios Gousios
            bindings += settings.BINDINGS_DEBUG
133 78e2d194 Georgios Gousios
134 c183005e Georgios Gousios
        # Bind queues to handler methods
135 5d081749 Georgios Gousios
        for binding in bindings:
136 78e2d194 Georgios Gousios
            try:
137 9cb903f9 Vangelis Koukis
                callback = getattr(callbacks, binding[3])
138 23c84263 Georgios Gousios
            except AttributeError:
139 23c84263 Georgios Gousios
                self.logger.error("Cannot find callback %s" % binding[3])
140 c183005e Georgios Gousios
                continue
141 8d8ea051 Georgios Gousios
142 c183005e Georgios Gousios
            self.chan.queue_bind(queue=binding[0], exchange=binding[1],
143 c183005e Georgios Gousios
                                 routing_key=binding[2])
144 2cd99e7a Georgios Gousios
            tag = self.chan.basic_consume(queue=binding[0], callback=callback)
145 23c84263 Georgios Gousios
            self.logger.debug("Binding %s(%s) to queue %s with handler %s" %
146 23c84263 Georgios Gousios
                              (binding[1], binding[2], binding[0], binding[3]))
147 23c84263 Georgios Gousios
            self.clienttags.append(tag)
148 8d8ea051 Georgios Gousios
149 c183005e Georgios Gousios
150 c183005e Georgios Gousios
def _exit_handler(signum, frame):
151 c183005e Georgios Gousios
    """"Catch exit signal in children processes."""
152 2cd99e7a Georgios Gousios
    print "%d: Caught signal %d, will raise SystemExit" % (os.getpid(), signum)
153 8d8ea051 Georgios Gousios
    raise SystemExit
154 8d8ea051 Georgios Gousios
155 c183005e Georgios Gousios
156 c183005e Georgios Gousios
def _parent_handler(signum, frame):
157 c183005e Georgios Gousios
    """"Catch exit signal in parent process and forward it to children."""
158 8861126f Georgios Gousios
    global children
159 8861126f Georgios Gousios
    print "Caught signal %d, sending kill signal to children" % signum
160 8861126f Georgios Gousios
    [os.kill(pid, SIGTERM) for pid in children]
161 8861126f Georgios Gousios
162 c183005e Georgios Gousios
163 57d0082a Georgios Gousios
def child(cmdline):
164 c183005e Georgios Gousios
    """The context of the child process"""
165 c183005e Georgios Gousios
166 c183005e Georgios Gousios
    # Cmd line argument parsing
167 78e2d194 Georgios Gousios
    (opts, args) = parse_arguments(cmdline)
168 57d0082a Georgios Gousios
    disp = Dispatcher(debug = opts.debug)
169 78e2d194 Georgios Gousios
170 c183005e Georgios Gousios
    # Start the event loop
171 2cd99e7a Georgios Gousios
    disp.wait()
172 78e2d194 Georgios Gousios
173 c183005e Georgios Gousios
174 78e2d194 Georgios Gousios
def parse_arguments(args):
175 78e2d194 Georgios Gousios
    from optparse import OptionParser
176 78e2d194 Georgios Gousios
177 78e2d194 Georgios Gousios
    parser = OptionParser()
178 c183005e Georgios Gousios
    parser.add_option("-d", "--debug", action="store_true", default=False,
179 2cd99e7a Georgios Gousios
                      dest="debug", help="Enable debug mode")
180 e6209aa2 Georgios Gousios
    parser.add_option("-w", "--workers", default=2, dest="workers",
181 e6209aa2 Georgios Gousios
                      help="Number of workers to spawn", type="int")
182 e6209aa2 Georgios Gousios
    parser.add_option("-p", '--pid-file', dest="pid_file",
183 e6209aa2 Georgios Gousios
                      default=os.path.join(os.getcwd(), "dispatcher.pid"),
184 e6209aa2 Georgios Gousios
                      help="Save PID to file (default:%s)" %
185 e6209aa2 Georgios Gousios
                           os.path.join(os.getcwd(), "dispatcher.pid"))
186 979482ce Georgios Gousios
    parser.add_option("--purge-queues", action="store_true",
187 979482ce Georgios Gousios
                      default=False, dest="purge_queues",
188 57d0082a Georgios Gousios
                      help="Remove all declared queues (DANGEROUS!)")
189 979482ce Georgios Gousios
    parser.add_option("--purge-exchanges", action="store_true",
190 979482ce Georgios Gousios
                      default=False, dest="purge_exchanges",
191 979482ce Georgios Gousios
                      help="Remove all exchanges. Implies deleting all queues \
192 979482ce Georgios Gousios
                           first (DANGEROUS!)")
193 d5470cdd Georgios Gousios
    parser.add_option("--drain-queue", dest="drain_queue",
194 e6209aa2 Georgios Gousios
                      help="Strips a queue from all outstanding messages")
195 de081774 Georgios Gousios
196 78e2d194 Georgios Gousios
    return parser.parse_args(args)
197 78e2d194 Georgios Gousios
198 f30730c0 Georgios Gousios
199 979482ce Georgios Gousios
def purge_queues() :
200 979482ce Georgios Gousios
    """
201 979482ce Georgios Gousios
        Delete declared queues from RabbitMQ. Use with care!
202 979482ce Georgios Gousios
    """
203 979482ce Georgios Gousios
    conn = get_connection()
204 f30730c0 Georgios Gousios
    chan = conn.channel()
205 f30730c0 Georgios Gousios
206 f30730c0 Georgios Gousios
    print "Queues to be deleted: ",  settings.QUEUES
207 f30730c0 Georgios Gousios
208 979482ce Georgios Gousios
    if not get_user_confirmation():
209 f30730c0 Georgios Gousios
        return
210 f30730c0 Georgios Gousios
211 f30730c0 Georgios Gousios
    for queue in settings.QUEUES:
212 f30730c0 Georgios Gousios
        try:
213 f30730c0 Georgios Gousios
            chan.queue_delete(queue=queue)
214 979482ce Georgios Gousios
            print "Deleting queue %s" % queue
215 979482ce Georgios Gousios
        except amqp.exceptions.AMQPChannelException as e:
216 979482ce Georgios Gousios
            print e.amqp_reply_code, " ", e.amqp_reply_text
217 979482ce Georgios Gousios
            chan = conn.channel()
218 979482ce Georgios Gousios
219 979482ce Georgios Gousios
    chan.connection.close()
220 979482ce Georgios Gousios
221 979482ce Georgios Gousios
222 979482ce Georgios Gousios
def purge_exchanges():
223 979482ce Georgios Gousios
    """
224 979482ce Georgios Gousios
        Delete declared exchanges from RabbitMQ, after removing all queues first
225 979482ce Georgios Gousios
    """
226 979482ce Georgios Gousios
    purge_queues()
227 979482ce Georgios Gousios
228 979482ce Georgios Gousios
    conn = get_connection()
229 979482ce Georgios Gousios
    chan = conn.channel()
230 979482ce Georgios Gousios
231 979482ce Georgios Gousios
    print "Exchnages to be deleted: ", settings.EXCHANGES
232 979482ce Georgios Gousios
233 979482ce Georgios Gousios
    if not get_user_confirmation():
234 979482ce Georgios Gousios
        return
235 979482ce Georgios Gousios
236 979482ce Georgios Gousios
    for exchange in settings.EXCHANGES:
237 979482ce Georgios Gousios
        try:
238 979482ce Georgios Gousios
            chan.exchange_delete(exchange=exchange)
239 f30730c0 Georgios Gousios
        except amqp.exceptions.AMQPChannelException as e:
240 f30730c0 Georgios Gousios
            print e.amqp_reply_code, " ", e.amqp_reply_text
241 979482ce Georgios Gousios
242 8861126f Georgios Gousios
    chan.connection.close()
243 f30730c0 Georgios Gousios
244 c183005e Georgios Gousios
245 979482ce Georgios Gousios
def drain_queue(queue):
246 979482ce Georgios Gousios
    """
247 979482ce Georgios Gousios
        Strip a (declared) queue from all outstanding messages
248 979482ce Georgios Gousios
    """
249 979482ce Georgios Gousios
    if not queue:
250 979482ce Georgios Gousios
        return
251 979482ce Georgios Gousios
252 979482ce Georgios Gousios
    if not queue in settings.QUEUES:
253 979482ce Georgios Gousios
        print "Queue %s not configured" % queue
254 979482ce Georgios Gousios
        return
255 979482ce Georgios Gousios
256 979482ce Georgios Gousios
    print "Queue to be drained: %s" % queue
257 979482ce Georgios Gousios
258 979482ce Georgios Gousios
    if not get_user_confirmation():
259 979482ce Georgios Gousios
        return
260 979482ce Georgios Gousios
    conn = get_connection()
261 979482ce Georgios Gousios
    chan = conn.channel()
262 979482ce Georgios Gousios
263 e6209aa2 Georgios Gousios
    # Register a temporary queue binding
264 e6209aa2 Georgios Gousios
    for binding in settings.BINDINGS:
265 e6209aa2 Georgios Gousios
        if binding[0] == queue:
266 e6209aa2 Georgios Gousios
            exch = binding[1]
267 e6209aa2 Georgios Gousios
268 e6209aa2 Georgios Gousios
    if not exch:
269 e6209aa2 Georgios Gousios
        print "Queue not bound to any exchange: %s" % queue
270 e6209aa2 Georgios Gousios
        return
271 e6209aa2 Georgios Gousios
272 e6209aa2 Georgios Gousios
    chan.queue_bind(queue=queue, exchange=exch,routing_key='#')
273 e6209aa2 Georgios Gousios
    tag = chan.basic_consume(queue=queue, callback=callbacks.dummy_proc)
274 e6209aa2 Georgios Gousios
275 e6209aa2 Georgios Gousios
    print "Queue draining about to start, hit Ctrl+c when done"
276 e6209aa2 Georgios Gousios
    time.sleep(2)
277 e6209aa2 Georgios Gousios
    print "Queue draining starting"
278 e6209aa2 Georgios Gousios
279 e6209aa2 Georgios Gousios
    signal(SIGTERM, _exit_handler)
280 e6209aa2 Georgios Gousios
    signal(SIGINT, _exit_handler)
281 e6209aa2 Georgios Gousios
282 e6209aa2 Georgios Gousios
    while True:
283 e6209aa2 Georgios Gousios
        chan.wait()
284 e6209aa2 Georgios Gousios
    chan.basic_cancel(tag)
285 979482ce Georgios Gousios
    chan.connection.close()
286 979482ce Georgios Gousios
287 979482ce Georgios Gousios
def get_connection():
288 979482ce Georgios Gousios
    conn = amqp.Connection( host=settings.RABBIT_HOST,
289 979482ce Georgios Gousios
                        userid=settings.RABBIT_USERNAME,
290 979482ce Georgios Gousios
                        password=settings.RABBIT_PASSWORD,
291 979482ce Georgios Gousios
                        virtual_host=settings.RABBIT_VHOST)
292 979482ce Georgios Gousios
    return conn
293 979482ce Georgios Gousios
294 979482ce Georgios Gousios
def get_user_confirmation():
295 979482ce Georgios Gousios
    ans = raw_input("Are you sure (N/y):")
296 979482ce Georgios Gousios
297 979482ce Georgios Gousios
    if not ans:
298 979482ce Georgios Gousios
        return False
299 979482ce Georgios Gousios
    if ans not in ['Y', 'y']:
300 979482ce Georgios Gousios
        return False
301 979482ce Georgios Gousios
    return True
302 979482ce Georgios Gousios
303 979482ce Georgios Gousios
304 57d0082a Georgios Gousios
def debug_mode():
305 57d0082a Georgios Gousios
    disp = Dispatcher(debug = True)
306 838239fa Georgios Gousios
    signal(SIGINT, _exit_handler)
307 838239fa Georgios Gousios
    signal(SIGTERM, _exit_handler)
308 838239fa Georgios Gousios
309 838239fa Georgios Gousios
    disp.wait()
310 838239fa Georgios Gousios
311 838239fa Georgios Gousios
312 78e2d194 Georgios Gousios
def main():
313 8861126f Georgios Gousios
    global children, logger
314 78e2d194 Georgios Gousios
    (opts, args) = parse_arguments(sys.argv[1:])
315 78e2d194 Georgios Gousios
316 f13691b3 Georgios Gousios
    logger = log.get_logger("synnefo.dispatcher")
317 8861126f Georgios Gousios
318 c183005e Georgios Gousios
    # Special case for the clean up queues action
319 979482ce Georgios Gousios
    if opts.purge_queues:
320 979482ce Georgios Gousios
        purge_queues()
321 979482ce Georgios Gousios
        return
322 979482ce Georgios Gousios
323 979482ce Georgios Gousios
    # Special case for the clean up exch action
324 979482ce Georgios Gousios
    if opts.purge_exchanges:
325 979482ce Georgios Gousios
        purge_exchanges()
326 979482ce Georgios Gousios
        return
327 979482ce Georgios Gousios
328 d5470cdd Georgios Gousios
    if opts.drain_queue:
329 d5470cdd Georgios Gousios
        drain_queue(opts.drain_queue)
330 f30730c0 Georgios Gousios
        return
331 f30730c0 Georgios Gousios
332 838239fa Georgios Gousios
    # Debug mode, process messages without spawning workers
333 838239fa Georgios Gousios
    if opts.debug:
334 57d0082a Georgios Gousios
        debug_mode()
335 838239fa Georgios Gousios
        return
336 838239fa Georgios Gousios
337 57d0082a Georgios Gousios
    # Become a daemon
338 57d0082a Georgios Gousios
    daemon_context = daemon.DaemonContext(
339 4dc0b46a Georgios Gousios
        stdout=sys.stdout,
340 4dc0b46a Georgios Gousios
        stderr=sys.stderr,
341 4dc0b46a Georgios Gousios
        umask=022)
342 4dc0b46a Georgios Gousios
343 57d0082a Georgios Gousios
    daemon_context.open()
344 de081774 Georgios Gousios
345 4ed2e471 Georgios Gousios
    # Create pidfile. Take care of differences between python-daemon versions.
346 4ed2e471 Georgios Gousios
    try:
347 4ed2e471 Georgios Gousios
        pidf = pidfile.TimeoutPIDLockFile(opts.pid_file, 10)
348 4ed2e471 Georgios Gousios
    except:
349 4ed2e471 Georgios Gousios
        pidf = pidlockfile.TimeoutPIDLockFile(opts.pid_file, 10)
350 4ed2e471 Georgios Gousios
351 de081774 Georgios Gousios
    pidf.acquire()
352 de081774 Georgios Gousios
353 57d0082a Georgios Gousios
    logger.info("Became a daemon")
354 57d0082a Georgios Gousios
355 c183005e Georgios Gousios
    # Fork workers
356 8861126f Georgios Gousios
    children = []
357 8861126f Georgios Gousios
358 8861126f Georgios Gousios
    i = 0
359 8861126f Georgios Gousios
    while i < opts.workers:
360 8861126f Georgios Gousios
        newpid = os.fork()
361 8861126f Georgios Gousios
362 8861126f Georgios Gousios
        if newpid == 0:
363 4dc0b46a Georgios Gousios
            signal(SIGINT,  _exit_handler)
364 c183005e Georgios Gousios
            signal(SIGTERM, _exit_handler)
365 57d0082a Georgios Gousios
            child(sys.argv[1:])
366 4dc0b46a Georgios Gousios
            sys.exit(1)
367 8861126f Georgios Gousios
        else:
368 8861126f Georgios Gousios
            pids = (os.getpid(), newpid)
369 8861126f Georgios Gousios
            logger.debug("%d, forked child: %d" % pids)
370 8861126f Georgios Gousios
            children.append(pids[1])
371 8861126f Georgios Gousios
        i += 1
372 8861126f Georgios Gousios
373 8d8ea051 Georgios Gousios
    # Catch signals to ensure graceful shutdown
374 c183005e Georgios Gousios
    signal(SIGINT,  _parent_handler)
375 c183005e Georgios Gousios
    signal(SIGTERM, _parent_handler)
376 8861126f Georgios Gousios
377 57d0082a Georgios Gousios
    # Wait for all children processes to die, one by one
378 de081774 Georgios Gousios
    try :
379 de081774 Georgios Gousios
        for pid in children:
380 de081774 Georgios Gousios
            try:
381 de081774 Georgios Gousios
                os.waitpid(pid, 0)
382 de081774 Georgios Gousios
            except Exception:
383 de081774 Georgios Gousios
                pass
384 de081774 Georgios Gousios
    finally:
385 de081774 Georgios Gousios
        pidf.release()
386 c183005e Georgios Gousios
387 d08a5f6f Vangelis Koukis
if __name__ == "__main__":
388 d08a5f6f Vangelis Koukis
    sys.exit(main())
389 d08a5f6f Vangelis Koukis
390 8d8ea051 Georgios Gousios
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :