Statistics
| Branch: | Tag: | Revision:

root / lib / daemon.py @ 5e12acfe

History | View | Annotate | Download (26.7 kB)

1 821d9e43 Michael Hanselmann
#
2 821d9e43 Michael Hanselmann
#
3 821d9e43 Michael Hanselmann
4 5ae4945a Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012 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 f5acf5d9 Michael Hanselmann
    """Initializes this class.
79 f5acf5d9 Michael Hanselmann

80 f5acf5d9 Michael Hanselmann
    """
81 f5acf5d9 Michael Hanselmann
    sched.scheduler.__init__(self, timefunc, self._LimitedDelay)
82 f5acf5d9 Michael Hanselmann
    self._max_delay = None
83 f5acf5d9 Michael Hanselmann
84 f5acf5d9 Michael Hanselmann
  def run(self, max_delay=None): # pylint: disable=W0221
85 f5acf5d9 Michael Hanselmann
    """Run any pending events.
86 f5acf5d9 Michael Hanselmann

87 f5acf5d9 Michael Hanselmann
    @type max_delay: None or number
88 f5acf5d9 Michael Hanselmann
    @param max_delay: Maximum delay (useful if caller has timeouts running)
89 f5acf5d9 Michael Hanselmann

90 f5acf5d9 Michael Hanselmann
    """
91 f5acf5d9 Michael Hanselmann
    assert self._max_delay is None
92 f5acf5d9 Michael Hanselmann
93 f5acf5d9 Michael Hanselmann
    # The delay function used by the scheduler can't be different on each run,
94 f5acf5d9 Michael Hanselmann
    # hence an instance variable must be used.
95 f5acf5d9 Michael Hanselmann
    if max_delay is None:
96 f5acf5d9 Michael Hanselmann
      self._max_delay = None
97 f5acf5d9 Michael Hanselmann
    else:
98 f5acf5d9 Michael Hanselmann
      self._max_delay = utils.RunningTimeout(max_delay, False)
99 f5acf5d9 Michael Hanselmann
100 f5acf5d9 Michael Hanselmann
    try:
101 f5acf5d9 Michael Hanselmann
      return sched.scheduler.run(self)
102 f5acf5d9 Michael Hanselmann
    finally:
103 f5acf5d9 Michael Hanselmann
      self._max_delay = None
104 f5acf5d9 Michael Hanselmann
105 f5acf5d9 Michael Hanselmann
  def _LimitedDelay(self, duration):
106 f5acf5d9 Michael Hanselmann
    """Custom delay function for C{sched.scheduler}.
107 f5acf5d9 Michael Hanselmann

108 f5acf5d9 Michael Hanselmann
    """
109 f5acf5d9 Michael Hanselmann
    if self._max_delay is None:
110 f5acf5d9 Michael Hanselmann
      timeout = duration
111 f5acf5d9 Michael Hanselmann
    else:
112 f5acf5d9 Michael Hanselmann
      timeout = min(duration, self._max_delay.Remaining())
113 f5acf5d9 Michael Hanselmann
114 f5acf5d9 Michael Hanselmann
    return AsyncoreDelayFunction(timeout)
115 821d9e43 Michael Hanselmann
116 821d9e43 Michael Hanselmann
117 b11780bb Guido Trotter
class GanetiBaseAsyncoreDispatcher(asyncore.dispatcher):
118 b11780bb Guido Trotter
  """Base Ganeti Asyncore Dispacher
119 b11780bb Guido Trotter

120 b11780bb Guido Trotter
  """
121 b11780bb Guido Trotter
  # this method is overriding an asyncore.dispatcher method
122 b11780bb Guido Trotter
  def handle_error(self):
123 b11780bb Guido Trotter
    """Log an error in handling any request, and proceed.
124 b11780bb Guido Trotter

125 b11780bb Guido Trotter
    """
126 b11780bb Guido Trotter
    logging.exception("Error while handling asyncore request")
127 b11780bb Guido Trotter
128 b11780bb Guido Trotter
  # this method is overriding an asyncore.dispatcher method
129 b11780bb Guido Trotter
  def writable(self):
130 b11780bb Guido Trotter
    """Most of the time we don't want to check for writability.
131 b11780bb Guido Trotter

132 b11780bb Guido Trotter
    """
133 b11780bb Guido Trotter
    return False
134 b11780bb Guido Trotter
135 b11780bb Guido Trotter
136 a4b605ae Guido Trotter
class AsyncStreamServer(GanetiBaseAsyncoreDispatcher):
137 a4b605ae Guido Trotter
  """A stream server to use with asyncore.
138 a4b605ae Guido Trotter

139 a4b605ae Guido Trotter
  Each request is accepted, and then dispatched to a separate asyncore
140 a4b605ae Guido Trotter
  dispatcher to handle.
141 a4b605ae Guido Trotter

142 a4b605ae Guido Trotter
  """
143 a4b605ae Guido Trotter
144 a4b605ae Guido Trotter
  _REQUEST_QUEUE_SIZE = 5
145 a4b605ae Guido Trotter
146 a4b605ae Guido Trotter
  def __init__(self, family, address):
147 a4b605ae Guido Trotter
    """Constructor for AsyncUnixStreamSocket
148 a4b605ae Guido Trotter

149 a4b605ae Guido Trotter
    @type family: integer
150 a4b605ae Guido Trotter
    @param family: socket family (one of socket.AF_*)
151 a4b605ae Guido Trotter
    @type address: address family dependent
152 a4b605ae Guido Trotter
    @param address: address to bind the socket to
153 a4b605ae Guido Trotter

154 a4b605ae Guido Trotter
    """
155 a4b605ae Guido Trotter
    GanetiBaseAsyncoreDispatcher.__init__(self)
156 a4b605ae Guido Trotter
    self.family = family
157 a4b605ae Guido Trotter
    self.create_socket(self.family, socket.SOCK_STREAM)
158 a4b605ae Guido Trotter
    self.set_reuse_addr()
159 a4b605ae Guido Trotter
    self.bind(address)
160 a4b605ae Guido Trotter
    self.listen(self._REQUEST_QUEUE_SIZE)
161 a4b605ae Guido Trotter
162 a4b605ae Guido Trotter
  # this method is overriding an asyncore.dispatcher method
163 a4b605ae Guido Trotter
  def handle_accept(self):
164 a4b605ae Guido Trotter
    """Accept a new client connection.
165 a4b605ae Guido Trotter

166 a4b605ae Guido Trotter
    Creates a new instance of the handler class, which will use asyncore to
167 a4b605ae Guido Trotter
    serve the client.
168 a4b605ae Guido Trotter

169 a4b605ae Guido Trotter
    """
170 a4b605ae Guido Trotter
    accept_result = utils.IgnoreSignals(self.accept)
171 a4b605ae Guido Trotter
    if accept_result is not None:
172 a4b605ae Guido Trotter
      connected_socket, client_address = accept_result
173 a4b605ae Guido Trotter
      if self.family == socket.AF_UNIX:
174 a4b605ae Guido Trotter
        # override the client address, as for unix sockets nothing meaningful
175 a4b605ae Guido Trotter
        # is passed in from accept anyway
176 a744b676 Manuel Franceschini
        client_address = netutils.GetSocketCredentials(connected_socket)
177 a4b605ae Guido Trotter
      logging.info("Accepted connection from %s",
178 981732fb Manuel Franceschini
                   netutils.FormatAddress(client_address, family=self.family))
179 a4b605ae Guido Trotter
      self.handle_connection(connected_socket, client_address)
180 a4b605ae Guido Trotter
181 a4b605ae Guido Trotter
  def handle_connection(self, connected_socket, client_address):
182 a4b605ae Guido Trotter
    """Handle an already accepted connection.
183 a4b605ae Guido Trotter

184 a4b605ae Guido Trotter
    """
185 a4b605ae Guido Trotter
    raise NotImplementedError
186 a4b605ae Guido Trotter
187 a4b605ae Guido Trotter
188 b66ab629 Guido Trotter
class AsyncTerminatedMessageStream(asynchat.async_chat):
189 b66ab629 Guido Trotter
  """A terminator separated message stream asyncore module.
190 b66ab629 Guido Trotter

191 b66ab629 Guido Trotter
  Handles a stream connection receiving messages terminated by a defined
192 b66ab629 Guido Trotter
  separator. For each complete message handle_message is called.
193 b66ab629 Guido Trotter

194 b66ab629 Guido Trotter
  """
195 37e62cb9 Guido Trotter
  def __init__(self, connected_socket, peer_address, terminator, family,
196 37e62cb9 Guido Trotter
               unhandled_limit):
197 b66ab629 Guido Trotter
    """AsyncTerminatedMessageStream constructor.
198 b66ab629 Guido Trotter

199 b66ab629 Guido Trotter
    @type connected_socket: socket.socket
200 b66ab629 Guido Trotter
    @param connected_socket: connected stream socket to receive messages from
201 b66ab629 Guido Trotter
    @param peer_address: family-specific peer address
202 b66ab629 Guido Trotter
    @type terminator: string
203 b66ab629 Guido Trotter
    @param terminator: terminator separating messages in the stream
204 b66ab629 Guido Trotter
    @type family: integer
205 b66ab629 Guido Trotter
    @param family: socket family
206 37e62cb9 Guido Trotter
    @type unhandled_limit: integer or None
207 37e62cb9 Guido Trotter
    @param unhandled_limit: maximum unanswered messages
208 b66ab629 Guido Trotter

209 b66ab629 Guido Trotter
    """
210 b66ab629 Guido Trotter
    # python 2.4/2.5 uses conn=... while 2.6 has sock=... we have to cheat by
211 b66ab629 Guido Trotter
    # using a positional argument rather than a keyword one.
212 b66ab629 Guido Trotter
    asynchat.async_chat.__init__(self, connected_socket)
213 b66ab629 Guido Trotter
    self.connected_socket = connected_socket
214 b66ab629 Guido Trotter
    # on python 2.4 there is no "family" attribute for the socket class
215 b66ab629 Guido Trotter
    # FIXME: when we move to python 2.5 or above remove the family parameter
216 b66ab629 Guido Trotter
    #self.family = self.connected_socket.family
217 b66ab629 Guido Trotter
    self.family = family
218 b66ab629 Guido Trotter
    self.peer_address = peer_address
219 b66ab629 Guido Trotter
    self.terminator = terminator
220 37e62cb9 Guido Trotter
    self.unhandled_limit = unhandled_limit
221 b66ab629 Guido Trotter
    self.set_terminator(terminator)
222 b66ab629 Guido Trotter
    self.ibuffer = []
223 37e62cb9 Guido Trotter
    self.receive_count = 0
224 37e62cb9 Guido Trotter
    self.send_count = 0
225 1e063ccd Guido Trotter
    self.oqueue = collections.deque()
226 37e62cb9 Guido Trotter
    self.iqueue = collections.deque()
227 b66ab629 Guido Trotter
228 b66ab629 Guido Trotter
  # this method is overriding an asynchat.async_chat method
229 b66ab629 Guido Trotter
  def collect_incoming_data(self, data):
230 b66ab629 Guido Trotter
    self.ibuffer.append(data)
231 b66ab629 Guido Trotter
232 37e62cb9 Guido Trotter
  def _can_handle_message(self):
233 37e62cb9 Guido Trotter
    return (self.unhandled_limit is None or
234 37e62cb9 Guido Trotter
            (self.receive_count < self.send_count + self.unhandled_limit) and
235 37e62cb9 Guido Trotter
             not self.iqueue)
236 37e62cb9 Guido Trotter
237 b66ab629 Guido Trotter
  # this method is overriding an asynchat.async_chat method
238 b66ab629 Guido Trotter
  def found_terminator(self):
239 b66ab629 Guido Trotter
    message = "".join(self.ibuffer)
240 b66ab629 Guido Trotter
    self.ibuffer = []
241 37e62cb9 Guido Trotter
    message_id = self.receive_count
242 37e62cb9 Guido Trotter
    # We need to increase the receive_count after checking if the message can
243 37e62cb9 Guido Trotter
    # be handled, but before calling handle_message
244 37e62cb9 Guido Trotter
    can_handle = self._can_handle_message()
245 37e62cb9 Guido Trotter
    self.receive_count += 1
246 37e62cb9 Guido Trotter
    if can_handle:
247 37e62cb9 Guido Trotter
      self.handle_message(message, message_id)
248 37e62cb9 Guido Trotter
    else:
249 37e62cb9 Guido Trotter
      self.iqueue.append((message, message_id))
250 b66ab629 Guido Trotter
251 b66ab629 Guido Trotter
  def handle_message(self, message, message_id):
252 b66ab629 Guido Trotter
    """Handle a terminated message.
253 b66ab629 Guido Trotter

254 b66ab629 Guido Trotter
    @type message: string
255 b66ab629 Guido Trotter
    @param message: message to handle
256 b66ab629 Guido Trotter
    @type message_id: integer
257 b66ab629 Guido Trotter
    @param message_id: stream's message sequence number
258 b66ab629 Guido Trotter

259 b66ab629 Guido Trotter
    """
260 b66ab629 Guido Trotter
    pass
261 b66ab629 Guido Trotter
    # TODO: move this method to raise NotImplementedError
262 b66ab629 Guido Trotter
    # raise NotImplementedError
263 b66ab629 Guido Trotter
264 1e063ccd Guido Trotter
  def send_message(self, message):
265 1e063ccd Guido Trotter
    """Send a message to the remote peer. This function is thread-safe.
266 1e063ccd Guido Trotter

267 1e063ccd Guido Trotter
    @type message: string
268 1e063ccd Guido Trotter
    @param message: message to send, without the terminator
269 1e063ccd Guido Trotter

270 1e063ccd Guido Trotter
    @warning: If calling this function from a thread different than the one
271 1e063ccd Guido Trotter
    performing the main asyncore loop, remember that you have to wake that one
272 1e063ccd Guido Trotter
    up.
273 1e063ccd Guido Trotter

274 1e063ccd Guido Trotter
    """
275 1e063ccd Guido Trotter
    # If we just append the message we received to the output queue, this
276 1e063ccd Guido Trotter
    # function can be safely called by multiple threads at the same time, and
277 37e62cb9 Guido Trotter
    # we don't need locking, since deques are thread safe. handle_write in the
278 37e62cb9 Guido Trotter
    # asyncore thread will handle the next input message if there are any
279 37e62cb9 Guido Trotter
    # enqueued.
280 1e063ccd Guido Trotter
    self.oqueue.append(message)
281 1e063ccd Guido Trotter
282 1e063ccd Guido Trotter
  # this method is overriding an asyncore.dispatcher method
283 37e62cb9 Guido Trotter
  def readable(self):
284 37e62cb9 Guido Trotter
    # read from the socket if we can handle the next requests
285 37e62cb9 Guido Trotter
    return self._can_handle_message() and asynchat.async_chat.readable(self)
286 37e62cb9 Guido Trotter
287 37e62cb9 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
288 1e063ccd Guido Trotter
  def writable(self):
289 1e063ccd Guido Trotter
    # the output queue may become full just after we called writable. This only
290 1e063ccd Guido Trotter
    # works if we know we'll have something else waking us up from the select,
291 1e063ccd Guido Trotter
    # in such case, anyway.
292 1e063ccd Guido Trotter
    return asynchat.async_chat.writable(self) or self.oqueue
293 1e063ccd Guido Trotter
294 1e063ccd Guido Trotter
  # this method is overriding an asyncore.dispatcher method
295 1e063ccd Guido Trotter
  def handle_write(self):
296 1e063ccd Guido Trotter
    if self.oqueue:
297 37e62cb9 Guido Trotter
      # if we have data in the output queue, then send_message was called.
298 37e62cb9 Guido Trotter
      # this means we can process one more message from the input queue, if
299 37e62cb9 Guido Trotter
      # there are any.
300 1e063ccd Guido Trotter
      data = self.oqueue.popleft()
301 1e063ccd Guido Trotter
      self.push(data + self.terminator)
302 37e62cb9 Guido Trotter
      self.send_count += 1
303 37e62cb9 Guido Trotter
      if self.iqueue:
304 37e62cb9 Guido Trotter
        self.handle_message(*self.iqueue.popleft())
305 1e063ccd Guido Trotter
    self.initiate_send()
306 1e063ccd Guido Trotter
307 b66ab629 Guido Trotter
  def close_log(self):
308 b66ab629 Guido Trotter
    logging.info("Closing connection from %s",
309 981732fb Manuel Franceschini
                 netutils.FormatAddress(self.peer_address, family=self.family))
310 b66ab629 Guido Trotter
    self.close()
311 b66ab629 Guido Trotter
312 b66ab629 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
313 b66ab629 Guido Trotter
  def handle_expt(self):
314 b66ab629 Guido Trotter
    self.close_log()
315 b66ab629 Guido Trotter
316 b66ab629 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
317 b66ab629 Guido Trotter
  def handle_error(self):
318 b66ab629 Guido Trotter
    """Log an error in handling any request, and proceed.
319 b66ab629 Guido Trotter

320 b66ab629 Guido Trotter
    """
321 b66ab629 Guido Trotter
    logging.exception("Error while handling asyncore request")
322 b66ab629 Guido Trotter
    self.close_log()
323 b66ab629 Guido Trotter
324 b66ab629 Guido Trotter
325 b11780bb Guido Trotter
class AsyncUDPSocket(GanetiBaseAsyncoreDispatcher):
326 5f3269fc Guido Trotter
  """An improved asyncore udp socket.
327 5f3269fc Guido Trotter

328 5f3269fc Guido Trotter
  """
329 d8bcfe21 Manuel Franceschini
  def __init__(self, family):
330 5f3269fc Guido Trotter
    """Constructor for AsyncUDPSocket
331 5f3269fc Guido Trotter

332 5f3269fc Guido Trotter
    """
333 b11780bb Guido Trotter
    GanetiBaseAsyncoreDispatcher.__init__(self)
334 5f3269fc Guido Trotter
    self._out_queue = []
335 d8bcfe21 Manuel Franceschini
    self._family = family
336 d8bcfe21 Manuel Franceschini
    self.create_socket(family, socket.SOCK_DGRAM)
337 5f3269fc Guido Trotter
338 5f3269fc Guido Trotter
  # this method is overriding an asyncore.dispatcher method
339 5f3269fc Guido Trotter
  def handle_connect(self):
340 5f3269fc Guido Trotter
    # Python thinks that the first udp message from a source qualifies as a
341 5f3269fc Guido Trotter
    # "connect" and further ones are part of the same connection. We beg to
342 5f3269fc Guido Trotter
    # differ and treat all messages equally.
343 5f3269fc Guido Trotter
    pass
344 5f3269fc Guido Trotter
345 5f3269fc Guido Trotter
  # this method is overriding an asyncore.dispatcher method
346 5f3269fc Guido Trotter
  def handle_read(self):
347 6e7e58b4 Guido Trotter
    recv_result = utils.IgnoreSignals(self.recvfrom,
348 6e7e58b4 Guido Trotter
                                      constants.MAX_UDP_DATA_SIZE)
349 6e7e58b4 Guido Trotter
    if recv_result is not None:
350 6e7e58b4 Guido Trotter
      payload, address = recv_result
351 d8bcfe21 Manuel Franceschini
      if self._family == socket.AF_INET6:
352 d8bcfe21 Manuel Franceschini
        # we ignore 'flow info' and 'scope id' as we don't need them
353 d8bcfe21 Manuel Franceschini
        ip, port, _, _ = address
354 d8bcfe21 Manuel Franceschini
      else:
355 d8bcfe21 Manuel Franceschini
        ip, port = address
356 d8bcfe21 Manuel Franceschini
357 6e7e58b4 Guido Trotter
      self.handle_datagram(payload, ip, port)
358 5f3269fc Guido Trotter
359 5f3269fc Guido Trotter
  def handle_datagram(self, payload, ip, port):
360 5f3269fc Guido Trotter
    """Handle an already read udp datagram
361 5f3269fc Guido Trotter

362 5f3269fc Guido Trotter
    """
363 5f3269fc Guido Trotter
    raise NotImplementedError
364 5f3269fc Guido Trotter
365 5f3269fc Guido Trotter
  # this method is overriding an asyncore.dispatcher method
366 5f3269fc Guido Trotter
  def writable(self):
367 5f3269fc Guido Trotter
    # We should check whether we can write to the socket only if we have
368 5f3269fc Guido Trotter
    # something scheduled to be written
369 5f3269fc Guido Trotter
    return bool(self._out_queue)
370 5f3269fc Guido Trotter
371 48bf6352 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
372 5f3269fc Guido Trotter
  def handle_write(self):
373 3660fcf5 Guido Trotter
    if not self._out_queue:
374 3660fcf5 Guido Trotter
      logging.error("handle_write called with empty output queue")
375 3660fcf5 Guido Trotter
      return
376 3660fcf5 Guido Trotter
    (ip, port, payload) = self._out_queue[0]
377 232144d0 Guido Trotter
    utils.IgnoreSignals(self.sendto, payload, 0, (ip, port))
378 3660fcf5 Guido Trotter
    self._out_queue.pop(0)
379 3660fcf5 Guido Trotter
380 5f3269fc Guido Trotter
  def enqueue_send(self, ip, port, payload):
381 5f3269fc Guido Trotter
    """Enqueue a datagram to be sent when possible
382 5f3269fc Guido Trotter

383 5f3269fc Guido Trotter
    """
384 c8eded0b Guido Trotter
    if len(payload) > constants.MAX_UDP_DATA_SIZE:
385 3ccb3a64 Michael Hanselmann
      raise errors.UdpDataSizeError("Packet too big: %s > %s" % (len(payload),
386 c8eded0b Guido Trotter
                                    constants.MAX_UDP_DATA_SIZE))
387 5f3269fc Guido Trotter
    self._out_queue.append((ip, port, payload))
388 5f3269fc Guido Trotter
389 6ddf5c8f Guido Trotter
  def process_next_packet(self, timeout=0):
390 6ddf5c8f Guido Trotter
    """Process the next datagram, waiting for it if necessary.
391 6ddf5c8f Guido Trotter

392 6ddf5c8f Guido Trotter
    @type timeout: float
393 6ddf5c8f Guido Trotter
    @param timeout: how long to wait for data
394 6ddf5c8f Guido Trotter
    @rtype: boolean
395 6ddf5c8f Guido Trotter
    @return: True if some data has been handled, False otherwise
396 6ddf5c8f Guido Trotter

397 6ddf5c8f Guido Trotter
    """
398 1b429e2a Iustin Pop
    result = utils.WaitForFdCondition(self, select.POLLIN, timeout)
399 1b429e2a Iustin Pop
    if result is not None and result & select.POLLIN:
400 3660fcf5 Guido Trotter
      self.handle_read()
401 6ddf5c8f Guido Trotter
      return True
402 6ddf5c8f Guido Trotter
    else:
403 6ddf5c8f Guido Trotter
      return False
404 6ddf5c8f Guido Trotter
405 5f3269fc Guido Trotter
406 495ba852 Guido Trotter
class AsyncAwaker(GanetiBaseAsyncoreDispatcher):
407 495ba852 Guido Trotter
  """A way to notify the asyncore loop that something is going on.
408 495ba852 Guido Trotter

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

414 495ba852 Guido Trotter
  """
415 495ba852 Guido Trotter
  def __init__(self, signal_fn=None):
416 495ba852 Guido Trotter
    """Constructor for AsyncAwaker
417 495ba852 Guido Trotter

418 495ba852 Guido Trotter
    @type signal_fn: function
419 495ba852 Guido Trotter
    @param signal_fn: function to call when awaken
420 495ba852 Guido Trotter

421 495ba852 Guido Trotter
    """
422 495ba852 Guido Trotter
    GanetiBaseAsyncoreDispatcher.__init__(self)
423 5ae4945a Iustin Pop
    assert signal_fn is None or callable(signal_fn)
424 495ba852 Guido Trotter
    (self.in_socket, self.out_socket) = socket.socketpair(socket.AF_UNIX,
425 495ba852 Guido Trotter
                                                          socket.SOCK_STREAM)
426 495ba852 Guido Trotter
    self.in_socket.setblocking(0)
427 b628191f Guido Trotter
    self.in_socket.shutdown(socket.SHUT_WR)
428 b628191f Guido Trotter
    self.out_socket.shutdown(socket.SHUT_RD)
429 495ba852 Guido Trotter
    self.set_socket(self.in_socket)
430 495ba852 Guido Trotter
    self.need_signal = True
431 495ba852 Guido Trotter
    self.signal_fn = signal_fn
432 495ba852 Guido Trotter
    self.connected = True
433 495ba852 Guido Trotter
434 495ba852 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
435 495ba852 Guido Trotter
  def handle_read(self):
436 495ba852 Guido Trotter
    utils.IgnoreSignals(self.recv, 4096)
437 495ba852 Guido Trotter
    if self.signal_fn:
438 495ba852 Guido Trotter
      self.signal_fn()
439 495ba852 Guido Trotter
    self.need_signal = True
440 495ba852 Guido Trotter
441 495ba852 Guido Trotter
  # this method is overriding an asyncore.dispatcher method
442 495ba852 Guido Trotter
  def close(self):
443 495ba852 Guido Trotter
    asyncore.dispatcher.close(self)
444 495ba852 Guido Trotter
    self.out_socket.close()
445 495ba852 Guido Trotter
446 495ba852 Guido Trotter
  def signal(self):
447 495ba852 Guido Trotter
    """Signal the asyncore main loop.
448 495ba852 Guido Trotter

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

452 495ba852 Guido Trotter
    """
453 495ba852 Guido Trotter
    # Yes, there is a race condition here. No, we don't care, at worst we're
454 495ba852 Guido Trotter
    # sending more than one wakeup token, which doesn't harm at all.
455 495ba852 Guido Trotter
    if self.need_signal:
456 495ba852 Guido Trotter
      self.need_signal = False
457 495ba852 Guido Trotter
      self.out_socket.send("\0")
458 495ba852 Guido Trotter
459 495ba852 Guido Trotter
460 2d6b5414 Michael Hanselmann
class _ShutdownCheck:
461 2d6b5414 Michael Hanselmann
  """Logic for L{Mainloop} shutdown.
462 2d6b5414 Michael Hanselmann

463 2d6b5414 Michael Hanselmann
  """
464 2d6b5414 Michael Hanselmann
  def __init__(self, fn):
465 2d6b5414 Michael Hanselmann
    """Initializes this class.
466 2d6b5414 Michael Hanselmann

467 2d6b5414 Michael Hanselmann
    @type fn: callable
468 2d6b5414 Michael Hanselmann
    @param fn: Function returning C{None} if mainloop can be stopped or a
469 2d6b5414 Michael Hanselmann
      duration in seconds after which the function should be called again
470 2d6b5414 Michael Hanselmann
    @see: L{Mainloop.Run}
471 2d6b5414 Michael Hanselmann

472 2d6b5414 Michael Hanselmann
    """
473 2d6b5414 Michael Hanselmann
    assert callable(fn)
474 2d6b5414 Michael Hanselmann
475 2d6b5414 Michael Hanselmann
    self._fn = fn
476 2d6b5414 Michael Hanselmann
    self._defer = None
477 2d6b5414 Michael Hanselmann
478 2d6b5414 Michael Hanselmann
  def CanShutdown(self):
479 2d6b5414 Michael Hanselmann
    """Checks whether mainloop can be stopped.
480 2d6b5414 Michael Hanselmann

481 2d6b5414 Michael Hanselmann
    @rtype: bool
482 2d6b5414 Michael Hanselmann

483 2d6b5414 Michael Hanselmann
    """
484 2d6b5414 Michael Hanselmann
    if self._defer and self._defer.Remaining() > 0:
485 2d6b5414 Michael Hanselmann
      # A deferred check has already been scheduled
486 2d6b5414 Michael Hanselmann
      return False
487 2d6b5414 Michael Hanselmann
488 2d6b5414 Michael Hanselmann
    # Ask mainloop driver whether we can stop or should check again
489 2d6b5414 Michael Hanselmann
    timeout = self._fn()
490 2d6b5414 Michael Hanselmann
491 2d6b5414 Michael Hanselmann
    if timeout is None:
492 2d6b5414 Michael Hanselmann
      # Yes, can stop mainloop
493 2d6b5414 Michael Hanselmann
      return True
494 2d6b5414 Michael Hanselmann
495 2d6b5414 Michael Hanselmann
    # Schedule another check in the future
496 2d6b5414 Michael Hanselmann
    self._defer = utils.RunningTimeout(timeout, True)
497 2d6b5414 Michael Hanselmann
498 2d6b5414 Michael Hanselmann
    return False
499 2d6b5414 Michael Hanselmann
500 2d6b5414 Michael Hanselmann
501 821d9e43 Michael Hanselmann
class Mainloop(object):
502 821d9e43 Michael Hanselmann
  """Generic mainloop for daemons
503 821d9e43 Michael Hanselmann

504 69b99987 Michael Hanselmann
  @ivar scheduler: A sched.scheduler object, which can be used to register
505 69b99987 Michael Hanselmann
    timed events
506 69b99987 Michael Hanselmann

507 821d9e43 Michael Hanselmann
  """
508 2d6b5414 Michael Hanselmann
  _SHUTDOWN_TIMEOUT_PRIORITY = -(sys.maxint - 1)
509 2d6b5414 Michael Hanselmann
510 821d9e43 Michael Hanselmann
  def __init__(self):
511 b14b975f Michael Hanselmann
    """Constructs a new Mainloop instance.
512 b14b975f Michael Hanselmann

513 b14b975f Michael Hanselmann
    """
514 821d9e43 Michael Hanselmann
    self._signal_wait = []
515 a02b89cf Guido Trotter
    self.scheduler = AsyncoreScheduler(time.time)
516 821d9e43 Michael Hanselmann
517 b604d0c8 René Nussbaumer
    # Resolve uid/gids used
518 b604d0c8 René Nussbaumer
    runtime.GetEnts()
519 b604d0c8 René Nussbaumer
520 9b739173 Guido Trotter
  @utils.SignalHandled([signal.SIGCHLD])
521 9b739173 Guido Trotter
  @utils.SignalHandled([signal.SIGTERM])
522 f59dce3e Guido Trotter
  @utils.SignalHandled([signal.SIGINT])
523 2d6b5414 Michael Hanselmann
  def Run(self, shutdown_wait_fn=None, signal_handlers=None):
524 b14b975f Michael Hanselmann
    """Runs the mainloop.
525 b14b975f Michael Hanselmann

526 2d6b5414 Michael Hanselmann
    @type shutdown_wait_fn: callable
527 2d6b5414 Michael Hanselmann
    @param shutdown_wait_fn: Function to check whether loop can be terminated;
528 2d6b5414 Michael Hanselmann
      B{important}: function must be idempotent and must return either None
529 2d6b5414 Michael Hanselmann
      for shutting down or a timeout for another call
530 9b739173 Guido Trotter
    @type signal_handlers: dict
531 9b739173 Guido Trotter
    @param signal_handlers: signal->L{utils.SignalHandler} passed by decorator
532 b14b975f Michael Hanselmann

533 b14b975f Michael Hanselmann
    """
534 9b739173 Guido Trotter
    assert isinstance(signal_handlers, dict) and \
535 9b739173 Guido Trotter
           len(signal_handlers) > 0, \
536 9b739173 Guido Trotter
           "Broken SignalHandled decorator"
537 e0545ee9 Michael Hanselmann
538 e0545ee9 Michael Hanselmann
    # Counter for received signals
539 e0545ee9 Michael Hanselmann
    shutdown_signals = 0
540 b604d0c8 René Nussbaumer
541 2d6b5414 Michael Hanselmann
    # Logic to wait for shutdown
542 2d6b5414 Michael Hanselmann
    shutdown_waiter = None
543 2d6b5414 Michael Hanselmann
544 9b739173 Guido Trotter
    # Start actual main loop
545 2d6b5414 Michael Hanselmann
    while True:
546 2d6b5414 Michael Hanselmann
      if shutdown_signals == 1 and shutdown_wait_fn is not None:
547 2d6b5414 Michael Hanselmann
        if shutdown_waiter is None:
548 2d6b5414 Michael Hanselmann
          shutdown_waiter = _ShutdownCheck(shutdown_wait_fn)
549 2d6b5414 Michael Hanselmann
550 2d6b5414 Michael Hanselmann
        # Let mainloop driver decide if we can already abort
551 2d6b5414 Michael Hanselmann
        if shutdown_waiter.CanShutdown():
552 2d6b5414 Michael Hanselmann
          break
553 2d6b5414 Michael Hanselmann
554 2d6b5414 Michael Hanselmann
        # Re-evaluate in a second
555 2d6b5414 Michael Hanselmann
        timeout = 1.0
556 2d6b5414 Michael Hanselmann
557 2d6b5414 Michael Hanselmann
      elif shutdown_signals >= 1:
558 2d6b5414 Michael Hanselmann
        # Abort loop if more than one signal has been sent or no callback has
559 2d6b5414 Michael Hanselmann
        # been given
560 2d6b5414 Michael Hanselmann
        break
561 2d6b5414 Michael Hanselmann
562 2d6b5414 Michael Hanselmann
      else:
563 2d6b5414 Michael Hanselmann
        # Wait forever on I/O events
564 2d6b5414 Michael Hanselmann
        timeout = None
565 2d6b5414 Michael Hanselmann
566 2d6b5414 Michael Hanselmann
      if self.scheduler.empty():
567 2d6b5414 Michael Hanselmann
        asyncore.loop(count=1, timeout=timeout, use_poll=True)
568 2d6b5414 Michael Hanselmann
      else:
569 a02b89cf Guido Trotter
        try:
570 2d6b5414 Michael Hanselmann
          self.scheduler.run(max_delay=timeout)
571 a02b89cf Guido Trotter
        except SchedulerBreakout:
572 a02b89cf Guido Trotter
          pass
573 9b739173 Guido Trotter
574 9b739173 Guido Trotter
      # Check whether a signal was raised
575 e0545ee9 Michael Hanselmann
      for (sig, handler) in signal_handlers.items():
576 9b739173 Guido Trotter
        if handler.called:
577 9b739173 Guido Trotter
          self._CallSignalWaiters(sig)
578 e0545ee9 Michael Hanselmann
          if sig in (signal.SIGTERM, signal.SIGINT):
579 e0545ee9 Michael Hanselmann
            logging.info("Received signal %s asking for shutdown", sig)
580 e0545ee9 Michael Hanselmann
            shutdown_signals += 1
581 9b739173 Guido Trotter
          handler.Clear()
582 a570e2a8 Guido Trotter
583 b14b975f Michael Hanselmann
  def _CallSignalWaiters(self, signum):
584 b14b975f Michael Hanselmann
    """Calls all signal waiters for a certain signal.
585 b14b975f Michael Hanselmann

586 b14b975f Michael Hanselmann
    @type signum: int
587 b14b975f Michael Hanselmann
    @param signum: Signal number
588 b14b975f Michael Hanselmann

589 b14b975f Michael Hanselmann
    """
590 b14b975f Michael Hanselmann
    for owner in self._signal_wait:
591 a9fe7232 Guido Trotter
      owner.OnSignal(signum)
592 821d9e43 Michael Hanselmann
593 821d9e43 Michael Hanselmann
  def RegisterSignal(self, owner):
594 821d9e43 Michael Hanselmann
    """Registers a receiver for signal notifications
595 821d9e43 Michael Hanselmann

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

598 821d9e43 Michael Hanselmann
    @type owner: instance
599 821d9e43 Michael Hanselmann
    @param owner: Receiver
600 821d9e43 Michael Hanselmann

601 821d9e43 Michael Hanselmann
    """
602 821d9e43 Michael Hanselmann
    self._signal_wait.append(owner)
603 b11c9e5c Michael Hanselmann
604 04ccf5e9 Guido Trotter
605 f4ec2960 René Nussbaumer
def _VerifyDaemonUser(daemon_name):
606 f4ec2960 René Nussbaumer
  """Verifies the process uid matches the configured uid.
607 f4ec2960 René Nussbaumer

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

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

615 f4ec2960 René Nussbaumer
  """
616 f4ec2960 René Nussbaumer
  getents = runtime.GetEnts()
617 f4ec2960 René Nussbaumer
  running_uid = os.getuid()
618 f4ec2960 René Nussbaumer
  daemon_uids = {
619 f4ec2960 René Nussbaumer
    constants.MASTERD: getents.masterd_uid,
620 f4ec2960 René Nussbaumer
    constants.RAPI: getents.rapi_uid,
621 f4ec2960 René Nussbaumer
    constants.NODED: getents.noded_uid,
622 f4ec2960 René Nussbaumer
    constants.CONFD: getents.confd_uid,
623 f4ec2960 René Nussbaumer
    }
624 1fdfa87a Iustin Pop
  assert daemon_name in daemon_uids, "Invalid daemon %s" % daemon_name
625 f4ec2960 René Nussbaumer
626 f4ec2960 René Nussbaumer
  return (daemon_uids[daemon_name] == running_uid, running_uid,
627 f4ec2960 René Nussbaumer
          daemon_uids[daemon_name])
628 f4ec2960 René Nussbaumer
629 f4ec2960 René Nussbaumer
630 3e87c1bf Iustin Pop
def _BeautifyError(err):
631 3e87c1bf Iustin Pop
  """Try to format an error better.
632 3e87c1bf Iustin Pop

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

636 3e87c1bf Iustin Pop
  @param err: an exception object
637 3e87c1bf Iustin Pop
  @rtype: string
638 3e87c1bf Iustin Pop
  @return: the formatted error description
639 3e87c1bf Iustin Pop

640 3e87c1bf Iustin Pop
  """
641 3e87c1bf Iustin Pop
  try:
642 3e87c1bf Iustin Pop
    if isinstance(err, socket.error):
643 3e87c1bf Iustin Pop
      return "Socket-related error: %s (errno=%s)" % (err.args[1], err.args[0])
644 3e87c1bf Iustin Pop
    elif isinstance(err, EnvironmentError):
645 3e87c1bf Iustin Pop
      if err.filename is None:
646 3e87c1bf Iustin Pop
        return "%s (errno=%s)" % (err.strerror, err.errno)
647 3e87c1bf Iustin Pop
      else:
648 3e87c1bf Iustin Pop
        return "%s (file %s) (errno=%s)" % (err.strerror, err.filename,
649 3e87c1bf Iustin Pop
                                            err.errno)
650 3e87c1bf Iustin Pop
    else:
651 3e87c1bf Iustin Pop
      return str(err)
652 b459a848 Andrea Spadaccini
  except Exception: # pylint: disable=W0703
653 3e87c1bf Iustin Pop
    logging.exception("Error while handling existing error %s", err)
654 3e87c1bf Iustin Pop
    return "%s" % str(err)
655 3e87c1bf Iustin Pop
656 3e87c1bf Iustin Pop
657 b459a848 Andrea Spadaccini
def _HandleSigHup(reopen_fn, signum, frame): # pylint: disable=W0613
658 8cabf472 Michael Hanselmann
  """Handler for SIGHUP.
659 8cabf472 Michael Hanselmann

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

662 8cabf472 Michael Hanselmann
  """
663 8cabf472 Michael Hanselmann
  logging.info("Reopening log files after receiving SIGHUP")
664 110f49ef Michael Hanselmann
665 110f49ef Michael Hanselmann
  for fn in reopen_fn:
666 110f49ef Michael Hanselmann
    if fn:
667 110f49ef Michael Hanselmann
      fn()
668 8cabf472 Michael Hanselmann
669 8cabf472 Michael Hanselmann
670 b42ea9ed Iustin Pop
def GenericMain(daemon_name, optionparser,
671 b42ea9ed Iustin Pop
                check_fn, prepare_fn, exec_fn,
672 1c54156d Luca Bigliardi
                multithreaded=False, console_logging=False,
673 0070a462 René Nussbaumer
                default_ssl_cert=None, default_ssl_key=None):
674 04ccf5e9 Guido Trotter
  """Shared main function for daemons.
675 04ccf5e9 Guido Trotter

676 04ccf5e9 Guido Trotter
  @type daemon_name: string
677 04ccf5e9 Guido Trotter
  @param daemon_name: daemon name
678 69b99987 Michael Hanselmann
  @type optionparser: optparse.OptionParser
679 04ccf5e9 Guido Trotter
  @param optionparser: initialized optionparser with daemon-specific options
680 04ccf5e9 Guido Trotter
                       (common -f -d options will be handled by this module)
681 04ccf5e9 Guido Trotter
  @type check_fn: function which accepts (options, args)
682 04ccf5e9 Guido Trotter
  @param check_fn: function that checks start conditions and exits if they're
683 04ccf5e9 Guido Trotter
                   not met
684 b42ea9ed Iustin Pop
  @type prepare_fn: function which accepts (options, args)
685 b42ea9ed Iustin Pop
  @param prepare_fn: function that is run before forking, or None;
686 b42ea9ed Iustin Pop
      it's result will be passed as the third parameter to exec_fn, or
687 b42ea9ed Iustin Pop
      if None was passed in, we will just pass None to exec_fn
688 b42ea9ed Iustin Pop
  @type exec_fn: function which accepts (options, args, prepare_results)
689 04ccf5e9 Guido Trotter
  @param exec_fn: function that's executed with the daemon's pid file held, and
690 04ccf5e9 Guido Trotter
                  runs the daemon itself.
691 30dabd03 Michael Hanselmann
  @type multithreaded: bool
692 30dabd03 Michael Hanselmann
  @param multithreaded: Whether the daemon uses threads
693 ff917534 Luca Bigliardi
  @type console_logging: boolean
694 ff917534 Luca Bigliardi
  @param console_logging: if True, the daemon will fall back to the system
695 ff917534 Luca Bigliardi
                          console if logging fails
696 0648750e Michael Hanselmann
  @type default_ssl_cert: string
697 0648750e Michael Hanselmann
  @param default_ssl_cert: Default SSL certificate path
698 0648750e Michael Hanselmann
  @type default_ssl_key: string
699 0648750e Michael Hanselmann
  @param default_ssl_key: Default SSL key path
700 04ccf5e9 Guido Trotter

701 04ccf5e9 Guido Trotter
  """
702 04ccf5e9 Guido Trotter
  optionparser.add_option("-f", "--foreground", dest="fork",
703 04ccf5e9 Guido Trotter
                          help="Don't detach from the current terminal",
704 04ccf5e9 Guido Trotter
                          default=True, action="store_false")
705 04ccf5e9 Guido Trotter
  optionparser.add_option("-d", "--debug", dest="debug",
706 04ccf5e9 Guido Trotter
                          help="Enable some debug messages",
707 04ccf5e9 Guido Trotter
                          default=False, action="store_true")
708 551b6283 Iustin Pop
  optionparser.add_option("--syslog", dest="syslog",
709 551b6283 Iustin Pop
                          help="Enable logging to syslog (except debug"
710 551b6283 Iustin Pop
                          " messages); one of 'no', 'yes' or 'only' [%s]" %
711 551b6283 Iustin Pop
                          constants.SYSLOG_USAGE,
712 551b6283 Iustin Pop
                          default=constants.SYSLOG_USAGE,
713 551b6283 Iustin Pop
                          choices=["no", "yes", "only"])
714 0a71aa17 Michael Hanselmann
715 04ccf5e9 Guido Trotter
  if daemon_name in constants.DAEMONS_PORTS:
716 14f5f1b6 Manuel Franceschini
    default_bind_address = constants.IP4_ADDRESS_ANY
717 7dd999fc Manuel Franceschini
    family = ssconf.SimpleStore().GetPrimaryIPFamily()
718 7dd999fc Manuel Franceschini
    # family will default to AF_INET if there is no ssconf file (e.g. when
719 7dd999fc Manuel Franceschini
    # upgrading a cluster from 2.2 -> 2.3. This is intended, as Ganeti clusters
720 7dd999fc Manuel Franceschini
    # <= 2.2 can not be AF_INET6
721 7dd999fc Manuel Franceschini
    if family == netutils.IP6Address.family:
722 7dd999fc Manuel Franceschini
      default_bind_address = constants.IP6_ADDRESS_ANY
723 e7323b5e Manuel Franceschini
724 a744b676 Manuel Franceschini
    default_port = netutils.GetDaemonPort(daemon_name)
725 0a71aa17 Michael Hanselmann
726 0a71aa17 Michael Hanselmann
    # For networked daemons we allow choosing the port and bind address
727 04ccf5e9 Guido Trotter
    optionparser.add_option("-p", "--port", dest="port",
728 0a71aa17 Michael Hanselmann
                            help="Network port (default: %s)" % default_port,
729 0a71aa17 Michael Hanselmann
                            default=default_port, type="int")
730 04ccf5e9 Guido Trotter
    optionparser.add_option("-b", "--bind", dest="bind_address",
731 e7323b5e Manuel Franceschini
                            help=("Bind address (default: '%s')" %
732 0a71aa17 Michael Hanselmann
                                  default_bind_address),
733 0a71aa17 Michael Hanselmann
                            default=default_bind_address, metavar="ADDRESS")
734 04ccf5e9 Guido Trotter
735 0648750e Michael Hanselmann
  if default_ssl_key is not None and default_ssl_cert is not None:
736 3b1b0cb6 Guido Trotter
    optionparser.add_option("--no-ssl", dest="ssl",
737 3b1b0cb6 Guido Trotter
                            help="Do not secure HTTP protocol with SSL",
738 3b1b0cb6 Guido Trotter
                            default=True, action="store_false")
739 3b1b0cb6 Guido Trotter
    optionparser.add_option("-K", "--ssl-key", dest="ssl_key",
740 0648750e Michael Hanselmann
                            help=("SSL key path (default: %s)" %
741 0648750e Michael Hanselmann
                                  default_ssl_key),
742 0648750e Michael Hanselmann
                            default=default_ssl_key, type="string",
743 0648750e Michael Hanselmann
                            metavar="SSL_KEY_PATH")
744 3b1b0cb6 Guido Trotter
    optionparser.add_option("-C", "--ssl-cert", dest="ssl_cert",
745 0648750e Michael Hanselmann
                            help=("SSL certificate path (default: %s)" %
746 0648750e Michael Hanselmann
                                  default_ssl_cert),
747 0648750e Michael Hanselmann
                            default=default_ssl_cert, type="string",
748 0648750e Michael Hanselmann
                            metavar="SSL_CERT_PATH")
749 3b1b0cb6 Guido Trotter
750 30dabd03 Michael Hanselmann
  # Disable the use of fork(2) if the daemon uses threads
751 7b4baeb1 Michael Hanselmann
  if multithreaded:
752 7b4baeb1 Michael Hanselmann
    utils.DisableFork()
753 04ccf5e9 Guido Trotter
754 04ccf5e9 Guido Trotter
  options, args = optionparser.parse_args()
755 04ccf5e9 Guido Trotter
756 0648750e Michael Hanselmann
  if getattr(options, "ssl", False):
757 0648750e Michael Hanselmann
    ssl_paths = {
758 0648750e Michael Hanselmann
      "certificate": options.ssl_cert,
759 0648750e Michael Hanselmann
      "key": options.ssl_key,
760 0648750e Michael Hanselmann
      }
761 0648750e Michael Hanselmann
762 0648750e Michael Hanselmann
    for name, path in ssl_paths.iteritems():
763 0648750e Michael Hanselmann
      if not os.path.isfile(path):
764 0648750e Michael Hanselmann
        print >> sys.stderr, "SSL %s file '%s' was not found" % (name, path)
765 3b1b0cb6 Guido Trotter
        sys.exit(constants.EXIT_FAILURE)
766 3b1b0cb6 Guido Trotter
767 0648750e Michael Hanselmann
    # TODO: By initiating http.HttpSslParams here we would only read the files
768 0648750e Michael Hanselmann
    # once and have a proper validation (isfile returns False on directories)
769 0648750e Michael Hanselmann
    # at the same time.
770 0648750e Michael Hanselmann
771 f4ec2960 René Nussbaumer
  result, running_uid, expected_uid = _VerifyDaemonUser(daemon_name)
772 f4ec2960 René Nussbaumer
  if not result:
773 f4ec2960 René Nussbaumer
    msg = ("%s started using wrong user ID (%d), expected %d" %
774 f4ec2960 René Nussbaumer
           (daemon_name, running_uid, expected_uid))
775 f4ec2960 René Nussbaumer
    print >> sys.stderr, msg
776 f4ec2960 René Nussbaumer
    sys.exit(constants.EXIT_FAILURE)
777 f4ec2960 René Nussbaumer
778 3b1b0cb6 Guido Trotter
  if check_fn is not None:
779 3b1b0cb6 Guido Trotter
    check_fn(options, args)
780 3b1b0cb6 Guido Trotter
781 3a6a89d7 Michael Hanselmann
  log_filename = constants.DAEMONS_LOGFILES[daemon_name]
782 3329f4de Michael Hanselmann
783 04ccf5e9 Guido Trotter
  if options.fork:
784 04ccf5e9 Guido Trotter
    utils.CloseFDs()
785 3329f4de Michael Hanselmann
    (wpipe, stdio_reopen_fn) = utils.Daemonize(logfile=log_filename)
786 b78aa8c2 Iustin Pop
  else:
787 110f49ef Michael Hanselmann
    (wpipe, stdio_reopen_fn) = (None, None)
788 04ccf5e9 Guido Trotter
789 8cabf472 Michael Hanselmann
  log_reopen_fn = \
790 3329f4de Michael Hanselmann
    utils.SetupLogging(log_filename, daemon_name,
791 8cabf472 Michael Hanselmann
                       debug=options.debug,
792 8cabf472 Michael Hanselmann
                       stderr_logging=not options.fork,
793 8cabf472 Michael Hanselmann
                       multithreaded=multithreaded,
794 8cabf472 Michael Hanselmann
                       syslog=options.syslog,
795 8cabf472 Michael Hanselmann
                       console_logging=console_logging)
796 8cabf472 Michael Hanselmann
797 8cabf472 Michael Hanselmann
  # Reopen log file(s) on SIGHUP
798 110f49ef Michael Hanselmann
  signal.signal(signal.SIGHUP,
799 110f49ef Michael Hanselmann
                compat.partial(_HandleSigHup, [log_reopen_fn, stdio_reopen_fn]))
800 8cabf472 Michael Hanselmann
801 4958b41e Michael Hanselmann
  try:
802 4958b41e Michael Hanselmann
    utils.WritePidFile(utils.DaemonPidFileName(daemon_name))
803 4958b41e Michael Hanselmann
  except errors.PidFileLockError, err:
804 4958b41e Michael Hanselmann
    print >> sys.stderr, "Error while locking PID file:\n%s" % err
805 4958b41e Michael Hanselmann
    sys.exit(constants.EXIT_FAILURE)
806 4958b41e Michael Hanselmann
807 04ccf5e9 Guido Trotter
  try:
808 b78aa8c2 Iustin Pop
    try:
809 fe295df3 Iustin Pop
      logging.info("%s daemon startup", daemon_name)
810 b78aa8c2 Iustin Pop
      if callable(prepare_fn):
811 b78aa8c2 Iustin Pop
        prep_results = prepare_fn(options, args)
812 b78aa8c2 Iustin Pop
      else:
813 b78aa8c2 Iustin Pop
        prep_results = None
814 b78aa8c2 Iustin Pop
    except Exception, err:
815 ed3920e3 Iustin Pop
      utils.WriteErrorToFD(wpipe, _BeautifyError(err))
816 b78aa8c2 Iustin Pop
      raise
817 b78aa8c2 Iustin Pop
818 b78aa8c2 Iustin Pop
    if wpipe is not None:
819 b78aa8c2 Iustin Pop
      # we're done with the preparation phase, we close the pipe to
820 b78aa8c2 Iustin Pop
      # let the parent know it's safe to exit
821 b78aa8c2 Iustin Pop
      os.close(wpipe)
822 b42ea9ed Iustin Pop
823 b42ea9ed Iustin Pop
    exec_fn(options, args, prep_results)
824 04ccf5e9 Guido Trotter
  finally:
825 79b60c1c Michael Hanselmann
    utils.RemoveFile(utils.DaemonPidFileName(daemon_name))