Statistics
| Branch: | Tag: | Revision:

root / lib / daemon.py @ 93b19295

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 3329f4de Michael Hanselmann
from ganeti import pathutils
45 a02b89cf Guido Trotter
46 a02b89cf Guido Trotter
47 a02b89cf Guido Trotter
class SchedulerBreakout(Exception):
48 a02b89cf Guido Trotter
  """Exception used to get out of the scheduler loop
49 a02b89cf Guido Trotter

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

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

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

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

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

77 a02b89cf Guido Trotter
  """
78 a02b89cf Guido Trotter
  def __init__(self, timefunc):
79 f5acf5d9 Michael Hanselmann
    """Initializes this class.
80 f5acf5d9 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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