Statistics
| Branch: | Tag: | Revision:

root / lib / daemon.py @ 37c4d509

History | View | Annotate | Download (23.6 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 b459a848 Andrea Spadaccini
  except Exception: # pylint: disable=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 b459a848 Andrea Spadaccini
def _HandleSigHup(reopen_fn, signum, frame): # pylint: disable=W0613
548 8cabf472 Michael Hanselmann
  """Handler for SIGHUP.
549 8cabf472 Michael Hanselmann

550 110f49ef Michael Hanselmann
  @param reopen_fn: List of callback functions for reopening log files
551 8cabf472 Michael Hanselmann

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

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

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