Statistics
| Branch: | Tag: | Revision:

root / lib / daemon.py @ 0376655e

History | View | Annotate | Download (23.5 kB)

1 821d9e43 Michael Hanselmann
#
2 821d9e43 Michael Hanselmann
#
3 821d9e43 Michael Hanselmann
4 fe295df3 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2010, 2011 Google Inc.
5 821d9e43 Michael Hanselmann
#
6 821d9e43 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 821d9e43 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 821d9e43 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 821d9e43 Michael Hanselmann
# (at your option) any later version.
10 821d9e43 Michael Hanselmann
#
11 821d9e43 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 821d9e43 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 821d9e43 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 821d9e43 Michael Hanselmann
# General Public License for more details.
15 821d9e43 Michael Hanselmann
#
16 821d9e43 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 821d9e43 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 821d9e43 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 821d9e43 Michael Hanselmann
# 02110-1301, USA.
20 821d9e43 Michael Hanselmann
21 821d9e43 Michael Hanselmann
22 821d9e43 Michael Hanselmann
"""Module with helper classes and functions for daemons"""
23 821d9e43 Michael Hanselmann
24 821d9e43 Michael Hanselmann
25 112d240d Guido Trotter
import asyncore
26 b66ab629 Guido Trotter
import asynchat
27 1e063ccd Guido Trotter
import collections
28 3b1b0cb6 Guido Trotter
import os
29 821d9e43 Michael Hanselmann
import signal
30 04ccf5e9 Guido Trotter
import logging
31 a02b89cf Guido Trotter
import sched
32 a02b89cf Guido Trotter
import time
33 5f3269fc Guido Trotter
import socket
34 6ddf5c8f Guido Trotter
import select
35 c124045f Iustin Pop
import sys
36 821d9e43 Michael Hanselmann
37 821d9e43 Michael Hanselmann
from ganeti import utils
38 04ccf5e9 Guido Trotter
from ganeti import constants
39 a02b89cf Guido Trotter
from ganeti import errors
40 a744b676 Manuel Franceschini
from ganeti import netutils
41 e7323b5e Manuel Franceschini
from ganeti import ssconf
42 f4ec2960 René Nussbaumer
from ganeti import runtime
43 8cabf472 Michael Hanselmann
from ganeti import compat
44 a02b89cf Guido Trotter
45 a02b89cf Guido Trotter
46 a02b89cf Guido Trotter
class SchedulerBreakout(Exception):
47 a02b89cf Guido Trotter
  """Exception used to get out of the scheduler loop
48 a02b89cf Guido Trotter

49 a02b89cf Guido Trotter
  """
50 a02b89cf Guido Trotter
51 a02b89cf Guido Trotter
52 a02b89cf Guido Trotter
def AsyncoreDelayFunction(timeout):
53 a02b89cf Guido Trotter
  """Asyncore-compatible scheduler delay function.
54 a02b89cf Guido Trotter

55 a02b89cf Guido Trotter
  This is a delay function for sched that, rather than actually sleeping,
56 a02b89cf Guido Trotter
  executes asyncore events happening in the meantime.
57 a02b89cf Guido Trotter

58 a02b89cf Guido Trotter
  After an event has occurred, rather than returning, it raises a
59 a02b89cf Guido Trotter
  SchedulerBreakout exception, which will force the current scheduler.run()
60 a02b89cf Guido Trotter
  invocation to terminate, so that we can also check for signals. The main loop
61 a02b89cf Guido Trotter
  will then call the scheduler run again, which will allow it to actually
62 a02b89cf Guido Trotter
  process any due events.
63 a02b89cf Guido Trotter

64 a02b89cf Guido Trotter
  This is needed because scheduler.run() doesn't support a count=..., as
65 a02b89cf Guido Trotter
  asyncore loop, and the scheduler module documents throwing exceptions from
66 a02b89cf Guido Trotter
  inside the delay function as an allowed usage model.
67 a02b89cf Guido Trotter

68 a02b89cf Guido Trotter
  """
69 a02b89cf Guido Trotter
  asyncore.loop(timeout=timeout, count=1, use_poll=True)
70 a02b89cf Guido Trotter
  raise SchedulerBreakout()
71 a02b89cf Guido Trotter
72 a02b89cf Guido Trotter
73 a02b89cf Guido Trotter
class AsyncoreScheduler(sched.scheduler):
74 a02b89cf Guido Trotter
  """Event scheduler integrated with asyncore
75 a02b89cf Guido Trotter

76 a02b89cf Guido Trotter
  """
77 a02b89cf Guido Trotter
  def __init__(self, timefunc):
78 a02b89cf Guido Trotter
    sched.scheduler.__init__(self, timefunc, AsyncoreDelayFunction)
79 821d9e43 Michael Hanselmann
80 821d9e43 Michael Hanselmann
81 b11780bb Guido Trotter
class GanetiBaseAsyncoreDispatcher(asyncore.dispatcher):
82 b11780bb Guido Trotter
  """Base Ganeti Asyncore Dispacher
83 b11780bb Guido Trotter

84 b11780bb Guido Trotter
  """
85 b11780bb Guido Trotter
  # this method is overriding an asyncore.dispatcher method
86 b11780bb Guido Trotter
  def handle_error(self):
87 b11780bb Guido Trotter
    """Log an error in handling any request, and proceed.
88 b11780bb Guido Trotter

89 b11780bb Guido Trotter
    """
90 b11780bb Guido Trotter
    logging.exception("Error while handling asyncore request")
91 b11780bb Guido Trotter
92 b11780bb Guido Trotter
  # this method is overriding an asyncore.dispatcher method
93 b11780bb Guido Trotter
  def writable(self):
94 b11780bb Guido Trotter
    """Most of the time we don't want to check for writability.
95 b11780bb Guido Trotter

96 b11780bb Guido Trotter
    """
97 b11780bb Guido Trotter
    return False
98 b11780bb Guido Trotter
99 b11780bb Guido Trotter
100 a4b605ae Guido Trotter
class AsyncStreamServer(GanetiBaseAsyncoreDispatcher):
101 a4b605ae Guido Trotter
  """A stream server to use with asyncore.
102 a4b605ae Guido Trotter

103 a4b605ae Guido Trotter
  Each request is accepted, and then dispatched to a separate asyncore
104 a4b605ae Guido Trotter
  dispatcher to handle.
105 a4b605ae Guido Trotter

106 a4b605ae Guido Trotter
  """
107 a4b605ae Guido Trotter
108 a4b605ae Guido Trotter
  _REQUEST_QUEUE_SIZE = 5
109 a4b605ae Guido Trotter
110 a4b605ae Guido Trotter
  def __init__(self, family, address):
111 a4b605ae Guido Trotter
    """Constructor for AsyncUnixStreamSocket
112 a4b605ae Guido Trotter

113 a4b605ae Guido Trotter
    @type family: integer
114 a4b605ae Guido Trotter
    @param family: socket family (one of socket.AF_*)
115 a4b605ae Guido Trotter
    @type address: address family dependent
116 a4b605ae Guido Trotter
    @param address: address to bind the socket to
117 a4b605ae Guido Trotter

118 a4b605ae Guido Trotter
    """
119 a4b605ae Guido Trotter
    GanetiBaseAsyncoreDispatcher.__init__(self)
120 a4b605ae Guido Trotter
    self.family = family
121 a4b605ae Guido Trotter
    self.create_socket(self.family, socket.SOCK_STREAM)
122 a4b605ae Guido Trotter
    self.set_reuse_addr()
123 a4b605ae Guido Trotter
    self.bind(address)
124 a4b605ae Guido Trotter
    self.listen(self._REQUEST_QUEUE_SIZE)
125 a4b605ae Guido Trotter
126 a4b605ae Guido Trotter
  # this method is overriding an asyncore.dispatcher method
127 a4b605ae Guido Trotter
  def handle_accept(self):
128 a4b605ae Guido Trotter
    """Accept a new client connection.
129 a4b605ae Guido Trotter

130 a4b605ae Guido Trotter
    Creates a new instance of the handler class, which will use asyncore to
131 a4b605ae Guido Trotter
    serve the client.
132 a4b605ae Guido Trotter

133 a4b605ae Guido Trotter
    """
134 a4b605ae Guido Trotter
    accept_result = utils.IgnoreSignals(self.accept)
135 a4b605ae Guido Trotter
    if accept_result is not None:
136 a4b605ae Guido Trotter
      connected_socket, client_address = accept_result
137 a4b605ae Guido Trotter
      if self.family == socket.AF_UNIX:
138 a4b605ae Guido Trotter
        # override the client address, as for unix sockets nothing meaningful
139 a4b605ae Guido Trotter
        # is passed in from accept anyway
140 a744b676 Manuel Franceschini
        client_address = netutils.GetSocketCredentials(connected_socket)
141 a4b605ae Guido Trotter
      logging.info("Accepted connection from %s",
142 981732fb Manuel Franceschini
                   netutils.FormatAddress(client_address, family=self.family))
143 a4b605ae Guido Trotter
      self.handle_connection(connected_socket, client_address)
144 a4b605ae Guido Trotter
145 a4b605ae Guido Trotter
  def handle_connection(self, connected_socket, client_address):
146 a4b605ae Guido Trotter
    """Handle an already accepted connection.
147 a4b605ae Guido Trotter

148 a4b605ae Guido Trotter
    """
149 a4b605ae Guido Trotter
    raise NotImplementedError
150 a4b605ae Guido Trotter
151 a4b605ae Guido Trotter
152 b66ab629 Guido Trotter
class AsyncTerminatedMessageStream(asynchat.async_chat):
153 b66ab629 Guido Trotter
  """A terminator separated message stream asyncore module.
154 b66ab629 Guido Trotter

155 b66ab629 Guido Trotter
  Handles a stream connection receiving messages terminated by a defined
156 b66ab629 Guido Trotter
  separator. For each complete message handle_message is called.
157 b66ab629 Guido Trotter

158 b66ab629 Guido Trotter
  """
159 37e62cb9 Guido Trotter
  def __init__(self, connected_socket, peer_address, terminator, family,
160 37e62cb9 Guido Trotter
               unhandled_limit):
161 b66ab629 Guido Trotter
    """AsyncTerminatedMessageStream constructor.
162 b66ab629 Guido Trotter

163 b66ab629 Guido Trotter
    @type connected_socket: socket.socket
164 b66ab629 Guido Trotter
    @param connected_socket: connected stream socket to receive messages from
165 b66ab629 Guido Trotter
    @param peer_address: family-specific peer address
166 b66ab629 Guido Trotter
    @type terminator: string
167 b66ab629 Guido Trotter
    @param terminator: terminator separating messages in the stream
168 b66ab629 Guido Trotter
    @type family: integer
169 b66ab629 Guido Trotter
    @param family: socket family
170 37e62cb9 Guido Trotter
    @type unhandled_limit: integer or None
171 37e62cb9 Guido Trotter
    @param unhandled_limit: maximum unanswered messages
172 b66ab629 Guido Trotter

173 b66ab629 Guido Trotter
    """
174 b66ab629 Guido Trotter
    # python 2.4/2.5 uses conn=... while 2.6 has sock=... we have to cheat by
175 b66ab629 Guido Trotter
    # using a positional argument rather than a keyword one.
176 b66ab629 Guido Trotter
    asynchat.async_chat.__init__(self, connected_socket)
177 b66ab629 Guido Trotter
    self.connected_socket = connected_socket
178 b66ab629 Guido Trotter
    # on python 2.4 there is no "family" attribute for the socket class
179 b66ab629 Guido Trotter
    # FIXME: when we move to python 2.5 or above remove the family parameter
180 b66ab629 Guido Trotter
    #self.family = self.connected_socket.family
181 b66ab629 Guido Trotter
    self.family = family
182 b66ab629 Guido Trotter
    self.peer_address = peer_address
183 b66ab629 Guido Trotter
    self.terminator = terminator
184 37e62cb9 Guido Trotter
    self.unhandled_limit = unhandled_limit
185 b66ab629 Guido Trotter
    self.set_terminator(terminator)
186 b66ab629 Guido Trotter
    self.ibuffer = []
187 37e62cb9 Guido Trotter
    self.receive_count = 0
188 37e62cb9 Guido Trotter
    self.send_count = 0
189 1e063ccd Guido Trotter
    self.oqueue = collections.deque()
190 37e62cb9 Guido Trotter
    self.iqueue = collections.deque()
191 b66ab629 Guido Trotter
192 b66ab629 Guido Trotter
  # this method is overriding an asynchat.async_chat method
193 b66ab629 Guido Trotter
  def collect_incoming_data(self, data):
194 b66ab629 Guido Trotter
    self.ibuffer.append(data)
195 b66ab629 Guido Trotter
196 37e62cb9 Guido Trotter
  def _can_handle_message(self):
197 37e62cb9 Guido Trotter
    return (self.unhandled_limit is None or
198 37e62cb9 Guido Trotter
            (self.receive_count < self.send_count + self.unhandled_limit) and
199 37e62cb9 Guido Trotter
             not self.iqueue)
200 37e62cb9 Guido Trotter
201 b66ab629 Guido Trotter
  # this method is overriding an asynchat.async_chat method
202 b66ab629 Guido Trotter
  def found_terminator(self):
203 b66ab629 Guido Trotter
    message = "".join(self.ibuffer)
204 b66ab629 Guido Trotter
    self.ibuffer = []
205 37e62cb9 Guido Trotter
    message_id = self.receive_count
206 37e62cb9 Guido Trotter
    # We need to increase the receive_count after checking if the message can
207 37e62cb9 Guido Trotter
    # be handled, but before calling handle_message
208 37e62cb9 Guido Trotter
    can_handle = self._can_handle_message()
209 37e62cb9 Guido Trotter
    self.receive_count += 1
210 37e62cb9 Guido Trotter
    if can_handle:
211 37e62cb9 Guido Trotter
      self.handle_message(message, message_id)
212 37e62cb9 Guido Trotter
    else:
213 37e62cb9 Guido Trotter
      self.iqueue.append((message, message_id))
214 b66ab629 Guido Trotter
215 b66ab629 Guido Trotter
  def handle_message(self, message, message_id):
216 b66ab629 Guido Trotter
    """Handle a terminated message.
217 b66ab629 Guido Trotter

218 b66ab629 Guido Trotter
    @type message: string
219 b66ab629 Guido Trotter
    @param message: message to handle
220 b66ab629 Guido Trotter
    @type message_id: integer
221 b66ab629 Guido Trotter
    @param message_id: stream's message sequence number
222 b66ab629 Guido Trotter

223 b66ab629 Guido Trotter
    """
224 b66ab629 Guido Trotter
    pass
225 b66ab629 Guido Trotter
    # TODO: move this method to raise NotImplementedError
226 b66ab629 Guido Trotter
    # raise NotImplementedError
227 b66ab629 Guido Trotter
228 1e063ccd Guido Trotter
  def send_message(self, message):
229 1e063ccd Guido Trotter
    """Send a message to the remote peer. This function is thread-safe.
230 1e063ccd Guido Trotter

231 1e063ccd Guido Trotter
    @type message: string
232 1e063ccd Guido Trotter
    @param message: message to send, without the terminator
233 1e063ccd Guido Trotter

234 1e063ccd Guido Trotter
    @warning: If calling this function from a thread different than the one
235 1e063ccd Guido Trotter
    performing the main asyncore loop, remember that you have to wake that one
236 1e063ccd Guido Trotter
    up.
237 1e063ccd Guido Trotter

238 1e063ccd Guido Trotter
    """
239 1e063ccd Guido Trotter
    # If we just append the message we received to the output queue, this
240 1e063ccd Guido Trotter
    # function can be safely called by multiple threads at the same time, and
241 37e62cb9 Guido Trotter
    # we don't need locking, since deques are thread safe. handle_write in the
242 37e62cb9 Guido Trotter
    # asyncore thread will handle the next input message if there are any
243 37e62cb9 Guido Trotter
    # enqueued.
244 1e063ccd Guido Trotter
    self.oqueue.append(message)
245 1e063ccd Guido Trotter
246 1e063ccd Guido Trotter
  # this method is overriding an asyncore.dispatcher method
247 37e62cb9 Guido Trotter
  def readable(self):
248 37e62cb9 Guido Trotter
    # read from the socket if we can handle the next requests
249 37e62cb9 Guido Trotter
    return self._can_handle_message() and asynchat.async_chat.readable(self)
250 37e62cb9 Guido Trotter
251 37e62cb9 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
252 1e063ccd Guido Trotter
  def writable(self):
253 1e063ccd Guido Trotter
    # the output queue may become full just after we called writable. This only
254 1e063ccd Guido Trotter
    # works if we know we'll have something else waking us up from the select,
255 1e063ccd Guido Trotter
    # in such case, anyway.
256 1e063ccd Guido Trotter
    return asynchat.async_chat.writable(self) or self.oqueue
257 1e063ccd Guido Trotter
258 1e063ccd Guido Trotter
  # this method is overriding an asyncore.dispatcher method
259 1e063ccd Guido Trotter
  def handle_write(self):
260 1e063ccd Guido Trotter
    if self.oqueue:
261 37e62cb9 Guido Trotter
      # if we have data in the output queue, then send_message was called.
262 37e62cb9 Guido Trotter
      # this means we can process one more message from the input queue, if
263 37e62cb9 Guido Trotter
      # there are any.
264 1e063ccd Guido Trotter
      data = self.oqueue.popleft()
265 1e063ccd Guido Trotter
      self.push(data + self.terminator)
266 37e62cb9 Guido Trotter
      self.send_count += 1
267 37e62cb9 Guido Trotter
      if self.iqueue:
268 37e62cb9 Guido Trotter
        self.handle_message(*self.iqueue.popleft())
269 1e063ccd Guido Trotter
    self.initiate_send()
270 1e063ccd Guido Trotter
271 b66ab629 Guido Trotter
  def close_log(self):
272 b66ab629 Guido Trotter
    logging.info("Closing connection from %s",
273 981732fb Manuel Franceschini
                 netutils.FormatAddress(self.peer_address, family=self.family))
274 b66ab629 Guido Trotter
    self.close()
275 b66ab629 Guido Trotter
276 b66ab629 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
277 b66ab629 Guido Trotter
  def handle_expt(self):
278 b66ab629 Guido Trotter
    self.close_log()
279 b66ab629 Guido Trotter
280 b66ab629 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
281 b66ab629 Guido Trotter
  def handle_error(self):
282 b66ab629 Guido Trotter
    """Log an error in handling any request, and proceed.
283 b66ab629 Guido Trotter

284 b66ab629 Guido Trotter
    """
285 b66ab629 Guido Trotter
    logging.exception("Error while handling asyncore request")
286 b66ab629 Guido Trotter
    self.close_log()
287 b66ab629 Guido Trotter
288 b66ab629 Guido Trotter
289 b11780bb Guido Trotter
class AsyncUDPSocket(GanetiBaseAsyncoreDispatcher):
290 5f3269fc Guido Trotter
  """An improved asyncore udp socket.
291 5f3269fc Guido Trotter

292 5f3269fc Guido Trotter
  """
293 d8bcfe21 Manuel Franceschini
  def __init__(self, family):
294 5f3269fc Guido Trotter
    """Constructor for AsyncUDPSocket
295 5f3269fc Guido Trotter

296 5f3269fc Guido Trotter
    """
297 b11780bb Guido Trotter
    GanetiBaseAsyncoreDispatcher.__init__(self)
298 5f3269fc Guido Trotter
    self._out_queue = []
299 d8bcfe21 Manuel Franceschini
    self._family = family
300 d8bcfe21 Manuel Franceschini
    self.create_socket(family, socket.SOCK_DGRAM)
301 5f3269fc Guido Trotter
302 5f3269fc Guido Trotter
  # this method is overriding an asyncore.dispatcher method
303 5f3269fc Guido Trotter
  def handle_connect(self):
304 5f3269fc Guido Trotter
    # Python thinks that the first udp message from a source qualifies as a
305 5f3269fc Guido Trotter
    # "connect" and further ones are part of the same connection. We beg to
306 5f3269fc Guido Trotter
    # differ and treat all messages equally.
307 5f3269fc Guido Trotter
    pass
308 5f3269fc Guido Trotter
309 5f3269fc Guido Trotter
  # this method is overriding an asyncore.dispatcher method
310 5f3269fc Guido Trotter
  def handle_read(self):
311 6e7e58b4 Guido Trotter
    recv_result = utils.IgnoreSignals(self.recvfrom,
312 6e7e58b4 Guido Trotter
                                      constants.MAX_UDP_DATA_SIZE)
313 6e7e58b4 Guido Trotter
    if recv_result is not None:
314 6e7e58b4 Guido Trotter
      payload, address = recv_result
315 d8bcfe21 Manuel Franceschini
      if self._family == socket.AF_INET6:
316 d8bcfe21 Manuel Franceschini
        # we ignore 'flow info' and 'scope id' as we don't need them
317 d8bcfe21 Manuel Franceschini
        ip, port, _, _ = address
318 d8bcfe21 Manuel Franceschini
      else:
319 d8bcfe21 Manuel Franceschini
        ip, port = address
320 d8bcfe21 Manuel Franceschini
321 6e7e58b4 Guido Trotter
      self.handle_datagram(payload, ip, port)
322 5f3269fc Guido Trotter
323 5f3269fc Guido Trotter
  def handle_datagram(self, payload, ip, port):
324 5f3269fc Guido Trotter
    """Handle an already read udp datagram
325 5f3269fc Guido Trotter

326 5f3269fc Guido Trotter
    """
327 5f3269fc Guido Trotter
    raise NotImplementedError
328 5f3269fc Guido Trotter
329 5f3269fc Guido Trotter
  # this method is overriding an asyncore.dispatcher method
330 5f3269fc Guido Trotter
  def writable(self):
331 5f3269fc Guido Trotter
    # We should check whether we can write to the socket only if we have
332 5f3269fc Guido Trotter
    # something scheduled to be written
333 5f3269fc Guido Trotter
    return bool(self._out_queue)
334 5f3269fc Guido Trotter
335 48bf6352 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
336 5f3269fc Guido Trotter
  def handle_write(self):
337 3660fcf5 Guido Trotter
    if not self._out_queue:
338 3660fcf5 Guido Trotter
      logging.error("handle_write called with empty output queue")
339 3660fcf5 Guido Trotter
      return
340 3660fcf5 Guido Trotter
    (ip, port, payload) = self._out_queue[0]
341 232144d0 Guido Trotter
    utils.IgnoreSignals(self.sendto, payload, 0, (ip, port))
342 3660fcf5 Guido Trotter
    self._out_queue.pop(0)
343 3660fcf5 Guido Trotter
344 5f3269fc Guido Trotter
  def enqueue_send(self, ip, port, payload):
345 5f3269fc Guido Trotter
    """Enqueue a datagram to be sent when possible
346 5f3269fc Guido Trotter

347 5f3269fc Guido Trotter
    """
348 c8eded0b Guido Trotter
    if len(payload) > constants.MAX_UDP_DATA_SIZE:
349 c8eded0b Guido Trotter
      raise errors.UdpDataSizeError('Packet too big: %s > %s' % (len(payload),
350 c8eded0b Guido Trotter
                                    constants.MAX_UDP_DATA_SIZE))
351 5f3269fc Guido Trotter
    self._out_queue.append((ip, port, payload))
352 5f3269fc Guido Trotter
353 6ddf5c8f Guido Trotter
  def process_next_packet(self, timeout=0):
354 6ddf5c8f Guido Trotter
    """Process the next datagram, waiting for it if necessary.
355 6ddf5c8f Guido Trotter

356 6ddf5c8f Guido Trotter
    @type timeout: float
357 6ddf5c8f Guido Trotter
    @param timeout: how long to wait for data
358 6ddf5c8f Guido Trotter
    @rtype: boolean
359 6ddf5c8f Guido Trotter
    @return: True if some data has been handled, False otherwise
360 6ddf5c8f Guido Trotter

361 6ddf5c8f Guido Trotter
    """
362 1b429e2a Iustin Pop
    result = utils.WaitForFdCondition(self, select.POLLIN, timeout)
363 1b429e2a Iustin Pop
    if result is not None and result & select.POLLIN:
364 3660fcf5 Guido Trotter
      self.handle_read()
365 6ddf5c8f Guido Trotter
      return True
366 6ddf5c8f Guido Trotter
    else:
367 6ddf5c8f Guido Trotter
      return False
368 6ddf5c8f Guido Trotter
369 5f3269fc Guido Trotter
370 495ba852 Guido Trotter
class AsyncAwaker(GanetiBaseAsyncoreDispatcher):
371 495ba852 Guido Trotter
  """A way to notify the asyncore loop that something is going on.
372 495ba852 Guido Trotter

373 495ba852 Guido Trotter
  If an asyncore daemon is multithreaded when a thread tries to push some data
374 495ba852 Guido Trotter
  to a socket, the main loop handling asynchronous requests might be sleeping
375 495ba852 Guido Trotter
  waiting on a select(). To avoid this it can create an instance of the
376 495ba852 Guido Trotter
  AsyncAwaker, which other threads can use to wake it up.
377 495ba852 Guido Trotter

378 495ba852 Guido Trotter
  """
379 495ba852 Guido Trotter
  def __init__(self, signal_fn=None):
380 495ba852 Guido Trotter
    """Constructor for AsyncAwaker
381 495ba852 Guido Trotter

382 495ba852 Guido Trotter
    @type signal_fn: function
383 495ba852 Guido Trotter
    @param signal_fn: function to call when awaken
384 495ba852 Guido Trotter

385 495ba852 Guido Trotter
    """
386 495ba852 Guido Trotter
    GanetiBaseAsyncoreDispatcher.__init__(self)
387 495ba852 Guido Trotter
    assert signal_fn == None or callable(signal_fn)
388 495ba852 Guido Trotter
    (self.in_socket, self.out_socket) = socket.socketpair(socket.AF_UNIX,
389 495ba852 Guido Trotter
                                                          socket.SOCK_STREAM)
390 495ba852 Guido Trotter
    self.in_socket.setblocking(0)
391 b628191f Guido Trotter
    self.in_socket.shutdown(socket.SHUT_WR)
392 b628191f Guido Trotter
    self.out_socket.shutdown(socket.SHUT_RD)
393 495ba852 Guido Trotter
    self.set_socket(self.in_socket)
394 495ba852 Guido Trotter
    self.need_signal = True
395 495ba852 Guido Trotter
    self.signal_fn = signal_fn
396 495ba852 Guido Trotter
    self.connected = True
397 495ba852 Guido Trotter
398 495ba852 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
399 495ba852 Guido Trotter
  def handle_read(self):
400 495ba852 Guido Trotter
    utils.IgnoreSignals(self.recv, 4096)
401 495ba852 Guido Trotter
    if self.signal_fn:
402 495ba852 Guido Trotter
      self.signal_fn()
403 495ba852 Guido Trotter
    self.need_signal = True
404 495ba852 Guido Trotter
405 495ba852 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
406 495ba852 Guido Trotter
  def close(self):
407 495ba852 Guido Trotter
    asyncore.dispatcher.close(self)
408 495ba852 Guido Trotter
    self.out_socket.close()
409 495ba852 Guido Trotter
410 495ba852 Guido Trotter
  def signal(self):
411 495ba852 Guido Trotter
    """Signal the asyncore main loop.
412 495ba852 Guido Trotter

413 495ba852 Guido Trotter
    Any data we send here will be ignored, but it will cause the select() call
414 495ba852 Guido Trotter
    to return.
415 495ba852 Guido Trotter

416 495ba852 Guido Trotter
    """
417 495ba852 Guido Trotter
    # Yes, there is a race condition here. No, we don't care, at worst we're
418 495ba852 Guido Trotter
    # sending more than one wakeup token, which doesn't harm at all.
419 495ba852 Guido Trotter
    if self.need_signal:
420 495ba852 Guido Trotter
      self.need_signal = False
421 495ba852 Guido Trotter
      self.out_socket.send("\0")
422 495ba852 Guido Trotter
423 495ba852 Guido Trotter
424 821d9e43 Michael Hanselmann
class Mainloop(object):
425 821d9e43 Michael Hanselmann
  """Generic mainloop for daemons
426 821d9e43 Michael Hanselmann

427 69b99987 Michael Hanselmann
  @ivar scheduler: A sched.scheduler object, which can be used to register
428 69b99987 Michael Hanselmann
    timed events
429 69b99987 Michael Hanselmann

430 821d9e43 Michael Hanselmann
  """
431 821d9e43 Michael Hanselmann
  def __init__(self):
432 b14b975f Michael Hanselmann
    """Constructs a new Mainloop instance.
433 b14b975f Michael Hanselmann

434 b14b975f Michael Hanselmann
    """
435 821d9e43 Michael Hanselmann
    self._signal_wait = []
436 a02b89cf Guido Trotter
    self.scheduler = AsyncoreScheduler(time.time)
437 821d9e43 Michael Hanselmann
438 b604d0c8 René Nussbaumer
    # Resolve uid/gids used
439 b604d0c8 René Nussbaumer
    runtime.GetEnts()
440 b604d0c8 René Nussbaumer
441 9b739173 Guido Trotter
  @utils.SignalHandled([signal.SIGCHLD])
442 9b739173 Guido Trotter
  @utils.SignalHandled([signal.SIGTERM])
443 f59dce3e Guido Trotter
  @utils.SignalHandled([signal.SIGINT])
444 69b99987 Michael Hanselmann
  def Run(self, signal_handlers=None):
445 b14b975f Michael Hanselmann
    """Runs the mainloop.
446 b14b975f Michael Hanselmann

447 9b739173 Guido Trotter
    @type signal_handlers: dict
448 9b739173 Guido Trotter
    @param signal_handlers: signal->L{utils.SignalHandler} passed by decorator
449 b14b975f Michael Hanselmann

450 b14b975f Michael Hanselmann
    """
451 9b739173 Guido Trotter
    assert isinstance(signal_handlers, dict) and \
452 9b739173 Guido Trotter
           len(signal_handlers) > 0, \
453 9b739173 Guido Trotter
           "Broken SignalHandled decorator"
454 9b739173 Guido Trotter
    running = True
455 b604d0c8 René Nussbaumer
456 9b739173 Guido Trotter
    # Start actual main loop
457 9b739173 Guido Trotter
    while running:
458 a02b89cf Guido Trotter
      if not self.scheduler.empty():
459 a02b89cf Guido Trotter
        try:
460 a02b89cf Guido Trotter
          self.scheduler.run()
461 a02b89cf Guido Trotter
        except SchedulerBreakout:
462 a02b89cf Guido Trotter
          pass
463 a02b89cf Guido Trotter
      else:
464 a02b89cf Guido Trotter
        asyncore.loop(count=1, use_poll=True)
465 9b739173 Guido Trotter
466 9b739173 Guido Trotter
      # Check whether a signal was raised
467 9b739173 Guido Trotter
      for sig in signal_handlers:
468 9b739173 Guido Trotter
        handler = signal_handlers[sig]
469 9b739173 Guido Trotter
        if handler.called:
470 9b739173 Guido Trotter
          self._CallSignalWaiters(sig)
471 f59dce3e Guido Trotter
          running = sig not in (signal.SIGTERM, signal.SIGINT)
472 9b739173 Guido Trotter
          handler.Clear()
473 a570e2a8 Guido Trotter
474 b14b975f Michael Hanselmann
  def _CallSignalWaiters(self, signum):
475 b14b975f Michael Hanselmann
    """Calls all signal waiters for a certain signal.
476 b14b975f Michael Hanselmann

477 b14b975f Michael Hanselmann
    @type signum: int
478 b14b975f Michael Hanselmann
    @param signum: Signal number
479 b14b975f Michael Hanselmann

480 b14b975f Michael Hanselmann
    """
481 b14b975f Michael Hanselmann
    for owner in self._signal_wait:
482 a9fe7232 Guido Trotter
      owner.OnSignal(signum)
483 821d9e43 Michael Hanselmann
484 821d9e43 Michael Hanselmann
  def RegisterSignal(self, owner):
485 821d9e43 Michael Hanselmann
    """Registers a receiver for signal notifications
486 821d9e43 Michael Hanselmann

487 821d9e43 Michael Hanselmann
    The receiver must support a "OnSignal(self, signum)" function.
488 821d9e43 Michael Hanselmann

489 821d9e43 Michael Hanselmann
    @type owner: instance
490 821d9e43 Michael Hanselmann
    @param owner: Receiver
491 821d9e43 Michael Hanselmann

492 821d9e43 Michael Hanselmann
    """
493 821d9e43 Michael Hanselmann
    self._signal_wait.append(owner)
494 b11c9e5c Michael Hanselmann
495 04ccf5e9 Guido Trotter
496 f4ec2960 René Nussbaumer
def _VerifyDaemonUser(daemon_name):
497 f4ec2960 René Nussbaumer
  """Verifies the process uid matches the configured uid.
498 f4ec2960 René Nussbaumer

499 3e87c1bf Iustin Pop
  This method verifies that a daemon is started as the user it is
500 3e87c1bf Iustin Pop
  intended to be run
501 f4ec2960 René Nussbaumer

502 f4ec2960 René Nussbaumer
  @param daemon_name: The name of daemon to be started
503 f4ec2960 René Nussbaumer
  @return: A tuple with the first item indicating success or not,
504 f4ec2960 René Nussbaumer
           the second item current uid and third with expected uid
505 f4ec2960 René Nussbaumer

506 f4ec2960 René Nussbaumer
  """
507 f4ec2960 René Nussbaumer
  getents = runtime.GetEnts()
508 f4ec2960 René Nussbaumer
  running_uid = os.getuid()
509 f4ec2960 René Nussbaumer
  daemon_uids = {
510 f4ec2960 René Nussbaumer
    constants.MASTERD: getents.masterd_uid,
511 f4ec2960 René Nussbaumer
    constants.RAPI: getents.rapi_uid,
512 f4ec2960 René Nussbaumer
    constants.NODED: getents.noded_uid,
513 f4ec2960 René Nussbaumer
    constants.CONFD: getents.confd_uid,
514 f4ec2960 René Nussbaumer
    }
515 f4ec2960 René Nussbaumer
516 f4ec2960 René Nussbaumer
  return (daemon_uids[daemon_name] == running_uid, running_uid,
517 f4ec2960 René Nussbaumer
          daemon_uids[daemon_name])
518 f4ec2960 René Nussbaumer
519 f4ec2960 René Nussbaumer
520 3e87c1bf Iustin Pop
def _BeautifyError(err):
521 3e87c1bf Iustin Pop
  """Try to format an error better.
522 3e87c1bf Iustin Pop

523 3e87c1bf Iustin Pop
  Since we're dealing with daemon startup errors, in many cases this
524 3e87c1bf Iustin Pop
  will be due to socket error and such, so we try to format these cases better.
525 3e87c1bf Iustin Pop

526 3e87c1bf Iustin Pop
  @param err: an exception object
527 3e87c1bf Iustin Pop
  @rtype: string
528 3e87c1bf Iustin Pop
  @return: the formatted error description
529 3e87c1bf Iustin Pop

530 3e87c1bf Iustin Pop
  """
531 3e87c1bf Iustin Pop
  try:
532 3e87c1bf Iustin Pop
    if isinstance(err, socket.error):
533 3e87c1bf Iustin Pop
      return "Socket-related error: %s (errno=%s)" % (err.args[1], err.args[0])
534 3e87c1bf Iustin Pop
    elif isinstance(err, EnvironmentError):
535 3e87c1bf Iustin Pop
      if err.filename is None:
536 3e87c1bf Iustin Pop
        return "%s (errno=%s)" % (err.strerror, err.errno)
537 3e87c1bf Iustin Pop
      else:
538 3e87c1bf Iustin Pop
        return "%s (file %s) (errno=%s)" % (err.strerror, err.filename,
539 3e87c1bf Iustin Pop
                                            err.errno)
540 3e87c1bf Iustin Pop
    else:
541 3e87c1bf Iustin Pop
      return str(err)
542 3e87c1bf Iustin Pop
  except Exception: # pylint: disable-msg=W0703
543 3e87c1bf Iustin Pop
    logging.exception("Error while handling existing error %s", err)
544 3e87c1bf Iustin Pop
    return "%s" % str(err)
545 3e87c1bf Iustin Pop
546 3e87c1bf Iustin Pop
547 8cabf472 Michael Hanselmann
def _HandleSigHup(reopen_cb, signum, frame): # pylint: disable-msg=W0613
548 8cabf472 Michael Hanselmann
  """Handler for SIGHUP.
549 8cabf472 Michael Hanselmann

550 8cabf472 Michael Hanselmann
  @param reopen_cb: Callback function for reopening log files
551 8cabf472 Michael Hanselmann

552 8cabf472 Michael Hanselmann
  """
553 8cabf472 Michael Hanselmann
  assert callable(reopen_cb)
554 8cabf472 Michael Hanselmann
  logging.info("Reopening log files after receiving SIGHUP")
555 8cabf472 Michael Hanselmann
  reopen_cb()
556 8cabf472 Michael Hanselmann
557 8cabf472 Michael Hanselmann
558 b42ea9ed Iustin Pop
def GenericMain(daemon_name, optionparser,
559 b42ea9ed Iustin Pop
                check_fn, prepare_fn, exec_fn,
560 1c54156d Luca Bigliardi
                multithreaded=False, console_logging=False,
561 0070a462 René Nussbaumer
                default_ssl_cert=None, default_ssl_key=None):
562 04ccf5e9 Guido Trotter
  """Shared main function for daemons.
563 04ccf5e9 Guido Trotter

564 04ccf5e9 Guido Trotter
  @type daemon_name: string
565 04ccf5e9 Guido Trotter
  @param daemon_name: daemon name
566 69b99987 Michael Hanselmann
  @type optionparser: optparse.OptionParser
567 04ccf5e9 Guido Trotter
  @param optionparser: initialized optionparser with daemon-specific options
568 04ccf5e9 Guido Trotter
                       (common -f -d options will be handled by this module)
569 04ccf5e9 Guido Trotter
  @type check_fn: function which accepts (options, args)
570 04ccf5e9 Guido Trotter
  @param check_fn: function that checks start conditions and exits if they're
571 04ccf5e9 Guido Trotter
                   not met
572 b42ea9ed Iustin Pop
  @type prepare_fn: function which accepts (options, args)
573 b42ea9ed Iustin Pop
  @param prepare_fn: function that is run before forking, or None;
574 b42ea9ed Iustin Pop
      it's result will be passed as the third parameter to exec_fn, or
575 b42ea9ed Iustin Pop
      if None was passed in, we will just pass None to exec_fn
576 b42ea9ed Iustin Pop
  @type exec_fn: function which accepts (options, args, prepare_results)
577 04ccf5e9 Guido Trotter
  @param exec_fn: function that's executed with the daemon's pid file held, and
578 04ccf5e9 Guido Trotter
                  runs the daemon itself.
579 30dabd03 Michael Hanselmann
  @type multithreaded: bool
580 30dabd03 Michael Hanselmann
  @param multithreaded: Whether the daemon uses threads
581 ff917534 Luca Bigliardi
  @type console_logging: boolean
582 ff917534 Luca Bigliardi
  @param console_logging: if True, the daemon will fall back to the system
583 ff917534 Luca Bigliardi
                          console if logging fails
584 0648750e Michael Hanselmann
  @type default_ssl_cert: string
585 0648750e Michael Hanselmann
  @param default_ssl_cert: Default SSL certificate path
586 0648750e Michael Hanselmann
  @type default_ssl_key: string
587 0648750e Michael Hanselmann
  @param default_ssl_key: Default SSL key path
588 04ccf5e9 Guido Trotter

589 04ccf5e9 Guido Trotter
  """
590 04ccf5e9 Guido Trotter
  optionparser.add_option("-f", "--foreground", dest="fork",
591 04ccf5e9 Guido Trotter
                          help="Don't detach from the current terminal",
592 04ccf5e9 Guido Trotter
                          default=True, action="store_false")
593 04ccf5e9 Guido Trotter
  optionparser.add_option("-d", "--debug", dest="debug",
594 04ccf5e9 Guido Trotter
                          help="Enable some debug messages",
595 04ccf5e9 Guido Trotter
                          default=False, action="store_true")
596 551b6283 Iustin Pop
  optionparser.add_option("--syslog", dest="syslog",
597 551b6283 Iustin Pop
                          help="Enable logging to syslog (except debug"
598 551b6283 Iustin Pop
                          " messages); one of 'no', 'yes' or 'only' [%s]" %
599 551b6283 Iustin Pop
                          constants.SYSLOG_USAGE,
600 551b6283 Iustin Pop
                          default=constants.SYSLOG_USAGE,
601 551b6283 Iustin Pop
                          choices=["no", "yes", "only"])
602 0a71aa17 Michael Hanselmann
603 04ccf5e9 Guido Trotter
  if daemon_name in constants.DAEMONS_PORTS:
604 14f5f1b6 Manuel Franceschini
    default_bind_address = constants.IP4_ADDRESS_ANY
605 7dd999fc Manuel Franceschini
    family = ssconf.SimpleStore().GetPrimaryIPFamily()
606 7dd999fc Manuel Franceschini
    # family will default to AF_INET if there is no ssconf file (e.g. when
607 7dd999fc Manuel Franceschini
    # upgrading a cluster from 2.2 -> 2.3. This is intended, as Ganeti clusters
608 7dd999fc Manuel Franceschini
    # <= 2.2 can not be AF_INET6
609 7dd999fc Manuel Franceschini
    if family == netutils.IP6Address.family:
610 7dd999fc Manuel Franceschini
      default_bind_address = constants.IP6_ADDRESS_ANY
611 e7323b5e Manuel Franceschini
612 a744b676 Manuel Franceschini
    default_port = netutils.GetDaemonPort(daemon_name)
613 0a71aa17 Michael Hanselmann
614 0a71aa17 Michael Hanselmann
    # For networked daemons we allow choosing the port and bind address
615 04ccf5e9 Guido Trotter
    optionparser.add_option("-p", "--port", dest="port",
616 0a71aa17 Michael Hanselmann
                            help="Network port (default: %s)" % default_port,
617 0a71aa17 Michael Hanselmann
                            default=default_port, type="int")
618 04ccf5e9 Guido Trotter
    optionparser.add_option("-b", "--bind", dest="bind_address",
619 e7323b5e Manuel Franceschini
                            help=("Bind address (default: '%s')" %
620 0a71aa17 Michael Hanselmann
                                  default_bind_address),
621 0a71aa17 Michael Hanselmann
                            default=default_bind_address, metavar="ADDRESS")
622 04ccf5e9 Guido Trotter
623 0648750e Michael Hanselmann
  if default_ssl_key is not None and default_ssl_cert is not None:
624 3b1b0cb6 Guido Trotter
    optionparser.add_option("--no-ssl", dest="ssl",
625 3b1b0cb6 Guido Trotter
                            help="Do not secure HTTP protocol with SSL",
626 3b1b0cb6 Guido Trotter
                            default=True, action="store_false")
627 3b1b0cb6 Guido Trotter
    optionparser.add_option("-K", "--ssl-key", dest="ssl_key",
628 0648750e Michael Hanselmann
                            help=("SSL key path (default: %s)" %
629 0648750e Michael Hanselmann
                                  default_ssl_key),
630 0648750e Michael Hanselmann
                            default=default_ssl_key, type="string",
631 0648750e Michael Hanselmann
                            metavar="SSL_KEY_PATH")
632 3b1b0cb6 Guido Trotter
    optionparser.add_option("-C", "--ssl-cert", dest="ssl_cert",
633 0648750e Michael Hanselmann
                            help=("SSL certificate path (default: %s)" %
634 0648750e Michael Hanselmann
                                  default_ssl_cert),
635 0648750e Michael Hanselmann
                            default=default_ssl_cert, type="string",
636 0648750e Michael Hanselmann
                            metavar="SSL_CERT_PATH")
637 3b1b0cb6 Guido Trotter
638 30dabd03 Michael Hanselmann
  # Disable the use of fork(2) if the daemon uses threads
639 7b4baeb1 Michael Hanselmann
  if multithreaded:
640 7b4baeb1 Michael Hanselmann
    utils.DisableFork()
641 04ccf5e9 Guido Trotter
642 04ccf5e9 Guido Trotter
  options, args = optionparser.parse_args()
643 04ccf5e9 Guido Trotter
644 0648750e Michael Hanselmann
  if getattr(options, "ssl", False):
645 0648750e Michael Hanselmann
    ssl_paths = {
646 0648750e Michael Hanselmann
      "certificate": options.ssl_cert,
647 0648750e Michael Hanselmann
      "key": options.ssl_key,
648 0648750e Michael Hanselmann
      }
649 0648750e Michael Hanselmann
650 0648750e Michael Hanselmann
    for name, path in ssl_paths.iteritems():
651 0648750e Michael Hanselmann
      if not os.path.isfile(path):
652 0648750e Michael Hanselmann
        print >> sys.stderr, "SSL %s file '%s' was not found" % (name, path)
653 3b1b0cb6 Guido Trotter
        sys.exit(constants.EXIT_FAILURE)
654 3b1b0cb6 Guido Trotter
655 0648750e Michael Hanselmann
    # TODO: By initiating http.HttpSslParams here we would only read the files
656 0648750e Michael Hanselmann
    # once and have a proper validation (isfile returns False on directories)
657 0648750e Michael Hanselmann
    # at the same time.
658 0648750e Michael Hanselmann
659 f4ec2960 René Nussbaumer
  result, running_uid, expected_uid = _VerifyDaemonUser(daemon_name)
660 f4ec2960 René Nussbaumer
  if not result:
661 f4ec2960 René Nussbaumer
    msg = ("%s started using wrong user ID (%d), expected %d" %
662 f4ec2960 René Nussbaumer
           (daemon_name, running_uid, expected_uid))
663 f4ec2960 René Nussbaumer
    print >> sys.stderr, msg
664 f4ec2960 René Nussbaumer
    sys.exit(constants.EXIT_FAILURE)
665 f4ec2960 René Nussbaumer
666 3b1b0cb6 Guido Trotter
  if check_fn is not None:
667 3b1b0cb6 Guido Trotter
    check_fn(options, args)
668 3b1b0cb6 Guido Trotter
669 04ccf5e9 Guido Trotter
  if options.fork:
670 04ccf5e9 Guido Trotter
    utils.CloseFDs()
671 b78aa8c2 Iustin Pop
    wpipe = utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
672 b78aa8c2 Iustin Pop
  else:
673 b78aa8c2 Iustin Pop
    wpipe = None
674 04ccf5e9 Guido Trotter
675 8cabf472 Michael Hanselmann
  log_reopen_fn = \
676 8cabf472 Michael Hanselmann
    utils.SetupLogging(constants.DAEMONS_LOGFILES[daemon_name], daemon_name,
677 8cabf472 Michael Hanselmann
                       debug=options.debug,
678 8cabf472 Michael Hanselmann
                       stderr_logging=not options.fork,
679 8cabf472 Michael Hanselmann
                       multithreaded=multithreaded,
680 8cabf472 Michael Hanselmann
                       syslog=options.syslog,
681 8cabf472 Michael Hanselmann
                       console_logging=console_logging)
682 8cabf472 Michael Hanselmann
683 8cabf472 Michael Hanselmann
  # Reopen log file(s) on SIGHUP
684 8cabf472 Michael Hanselmann
  signal.signal(signal.SIGHUP, compat.partial(_HandleSigHup, log_reopen_fn))
685 8cabf472 Michael Hanselmann
686 5c4d37f9 Iustin Pop
  utils.WritePidFile(utils.DaemonPidFileName(daemon_name))
687 04ccf5e9 Guido Trotter
  try:
688 b78aa8c2 Iustin Pop
    try:
689 fe295df3 Iustin Pop
      logging.info("%s daemon startup", daemon_name)
690 b78aa8c2 Iustin Pop
      if callable(prepare_fn):
691 b78aa8c2 Iustin Pop
        prep_results = prepare_fn(options, args)
692 b78aa8c2 Iustin Pop
      else:
693 b78aa8c2 Iustin Pop
        prep_results = None
694 b78aa8c2 Iustin Pop
    except Exception, err:
695 ed3920e3 Iustin Pop
      utils.WriteErrorToFD(wpipe, _BeautifyError(err))
696 b78aa8c2 Iustin Pop
      raise
697 b78aa8c2 Iustin Pop
698 b78aa8c2 Iustin Pop
    if wpipe is not None:
699 b78aa8c2 Iustin Pop
      # we're done with the preparation phase, we close the pipe to
700 b78aa8c2 Iustin Pop
      # let the parent know it's safe to exit
701 b78aa8c2 Iustin Pop
      os.close(wpipe)
702 b42ea9ed Iustin Pop
703 b42ea9ed Iustin Pop
    exec_fn(options, args, prep_results)
704 04ccf5e9 Guido Trotter
  finally:
705 79b60c1c Michael Hanselmann
    utils.RemoveFile(utils.DaemonPidFileName(daemon_name))