/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2008-11-09 06:40:29 UTC
  • mto: (24.1.113 mandos)
  • mto: This revision was merged to the branch mainline in revision 238.
  • Revision ID: teddy@fukt.bsnet.se-20081109064029-df71jpoce308cq3v
First steps of a D-Bus interface to the server.

* mandos: Also import "dbus.service".
  (Client): Inherit from "dbus.service.Object", which is a new-style
            class, so inheriting from "object" is no longer necessary.
  (Client.interface): New temporary variable which only exists during
                     class definition.

  (Client.getName, Client.getFingerprint): New D-Bus getter methods.
  (Client.setSecret): New D-Bus setter method.
  (Client._set_timeout): Emit D-Bus signal "TimeoutChanged".
  (Client.getTimeout): New D-Bus getter method.
  (Client.TimeoutChanged): New D-Bus signal.
  (Client._set_interval): Emit D-Bus signal "IntervalChanged".
  (Client.getInterval): New D-Bus getter method.
  (Client.intervalChanged): New D-Bus signal.
  (Client.__init__): Also call "dbus.service.Object.__init__".
  (Client.started): New boolean attribute.
  (Client.start, Client.stop): Update "self.started", and emit D-Bus
                               signal "StateChanged".
  (Client.StateChanged): New D-Bus signal.
  (Client.stop): Use "self.started" instead of misusing "self.secret".
                 Also simplify code by using "getattr" instead of
                 "hasattr".
  (Client.checker_callback): Emit D-Bus signal "CheckerCompleted".
  (Client.CheckerCompleted): New D-Bus signal.
  (Client.bumpTimeout): D-Bus method name for "bump_timeout".
  (Client.start_checker): Emit D-Bus signal "CheckerStarted".
  (Client.CheckerStarted): New D-Bus signal.
  (Client.checkerIsRunning): New D-Bus method.
  (Client.StopChecker): D-Bus method name for "stop_checker".
  (Client.still_valid): First check "self.started".
  (Client.stillValid): D-Bus method name for "still_valid".

Show diffs side-by-side

added added

removed removed

Lines of Context:
58
58
from contextlib import closing
59
59
 
60
60
import dbus
 
61
import dbus.service
61
62
import gobject
62
63
import avahi
63
64
from dbus.mainloop.glib import DBusGMainLoop
171
172
# End of Avahi example code
172
173
 
173
174
 
174
 
class Client(object):
 
175
class Client(dbus.service.Object):
175
176
    """A representation of a client host served by this server.
176
177
    Attributes:
177
178
    name:      string; from the config file, used in log messages
180
181
    secret:    bytestring; sent verbatim (over TLS) to client
181
182
    host:      string; available for use by the checker command
182
183
    created:   datetime.datetime(); object creation, not client host
 
184
    started:   bool()
183
185
    last_checked_ok: datetime.datetime() or None if not yet checked OK
184
186
    timeout:   datetime.timedelta(); How long from last_checked_ok
185
187
                                     until this client is invalid
201
203
    _timeout_milliseconds: Used when calling gobject.timeout_add()
202
204
    _interval_milliseconds: - '' -
203
205
    """
 
206
    interface = u"org.mandos_system.Mandos.Clients"
 
207
    
 
208
    @dbus.service.method(interface, out_signature="s")
 
209
    def getName(self):
 
210
        "D-Bus getter method"
 
211
        return self.name
 
212
    
 
213
    @dbus.service.method(interface, out_signature="s")
 
214
    def getFingerprint(self):
 
215
        "D-Bus getter method"
 
216
        return self.fingerprint
 
217
    
 
218
    @dbus.service.method(interface, in_signature="ay",
 
219
                         byte_arrays=True)
 
220
    def setSecret(self, secret):
 
221
        "D-Bus setter method"
 
222
        self.secret = secret
 
223
    
204
224
    def _set_timeout(self, timeout):
205
 
        "Setter function for 'timeout' attribute"
 
225
        "Setter function for the 'timeout' attribute"
206
226
        self._timeout = timeout
207
227
        self._timeout_milliseconds = ((self.timeout.days
208
228
                                       * 24 * 60 * 60 * 1000)
209
229
                                      + (self.timeout.seconds * 1000)
210
230
                                      + (self.timeout.microseconds
211
231
                                         // 1000))
212
 
    timeout = property(lambda self: self._timeout,
213
 
                       _set_timeout)
 
232
        # Emit D-Bus signal
 
233
        self.TimeoutChanged(self._timeout_milliseconds)
 
234
    timeout = property(lambda self: self._timeout, _set_timeout)
214
235
    del _set_timeout
 
236
    
 
237
    @dbus.service.method(interface, out_signature="t")
 
238
    def getTimeout(self):
 
239
        "D-Bus getter method"
 
240
        return self._timeout_milliseconds
 
241
    
 
242
    @dbus.service.signal(interface, signature="t")
 
243
    def TimeoutChanged(self, t):
 
244
        "D-Bus signal"
 
245
        pass
 
246
    
215
247
    def _set_interval(self, interval):
216
 
        "Setter function for 'interval' attribute"
 
248
        "Setter function for the 'interval' attribute"
217
249
        self._interval = interval
218
250
        self._interval_milliseconds = ((self.interval.days
219
251
                                        * 24 * 60 * 60 * 1000)
221
253
                                          * 1000)
222
254
                                       + (self.interval.microseconds
223
255
                                          // 1000))
224
 
    interval = property(lambda self: self._interval,
225
 
                        _set_interval)
 
256
        # Emit D-Bus signal
 
257
        self.IntervalChanged(self._interval_milliseconds)
 
258
    interval = property(lambda self: self._interval, _set_interval)
226
259
    del _set_interval
 
260
    
 
261
    @dbus.service.method(interface, out_signature="t")
 
262
    def getInterval(self):
 
263
        "D-Bus getter method"
 
264
        return self._interval_milliseconds
 
265
    
 
266
    @dbus.service.signal(interface, signature="t")
 
267
    def IntervalChanged(self, t):
 
268
        "D-Bus signal"
 
269
        pass
 
270
    
227
271
    def __init__(self, name = None, stop_hook=None, config=None):
228
272
        """Note: the 'checker' key in 'config' sets the
229
273
        'checker_command' attribute and *not* the 'checker'
230
274
        attribute."""
 
275
        dbus.service.Object.__init__(self, bus,
 
276
                                     "/Mandos/Clients/%s"
 
277
                                     % name.replace(".", "_"))
231
278
        if config is None:
232
279
            config = {}
233
280
        self.name = name
251
298
                            % self.name)
252
299
        self.host = config.get("host", "")
253
300
        self.created = datetime.datetime.now()
 
301
        self.started = False
254
302
        self.last_checked_ok = None
255
303
        self.timeout = string_to_delta(config["timeout"])
256
304
        self.interval = string_to_delta(config["interval"])
260
308
        self.stop_initiator_tag = None
261
309
        self.checker_callback_tag = None
262
310
        self.check_command = config["checker"]
 
311
    
263
312
    def start(self):
264
313
        """Start this client's checker and timeout hooks"""
 
314
        self.started = True
265
315
        # Schedule a new checker to be started an 'interval' from now,
266
316
        # and every interval from then on.
267
317
        self.checker_initiator_tag = gobject.timeout_add\
273
323
        self.stop_initiator_tag = gobject.timeout_add\
274
324
                                  (self._timeout_milliseconds,
275
325
                                   self.stop)
 
326
        # Emit D-Bus signal
 
327
        self.StateChanged(True)
 
328
    
 
329
    @dbus.service.signal(interface, signature="b")
 
330
    def StateChanged(self, started):
 
331
        "D-Bus signal"
 
332
        pass
 
333
    
276
334
    def stop(self):
277
 
        """Stop this client.
278
 
        The possibility that a client might be restarted is left open,
279
 
        but not currently used."""
280
 
        # If this client doesn't have a secret, it is already stopped.
281
 
        if hasattr(self, "secret") and self.secret:
 
335
        """Stop this client."""
 
336
        if getattr(self, "started", False):
282
337
            logger.info(u"Stopping client %s", self.name)
283
 
            self.secret = None
284
338
        else:
285
339
            return False
286
340
        if getattr(self, "stop_initiator_tag", False):
292
346
        self.stop_checker()
293
347
        if self.stop_hook:
294
348
            self.stop_hook(self)
 
349
        # Emit D-Bus signal
 
350
        self.StateChanged(False)
295
351
        # Do not run this again if called by a gobject.timeout_add
296
352
        return False
 
353
    # D-Bus variant
 
354
    Stop = dbus.service.method(interface)(stop)
 
355
    
297
356
    def __del__(self):
298
357
        self.stop_hook = None
299
358
        self.stop()
 
359
    
300
360
    def checker_callback(self, pid, condition):
301
361
        """The checker has completed, so take appropriate actions."""
302
362
        self.checker_callback_tag = None
305
365
               and (os.WEXITSTATUS(condition) == 0):
306
366
            logger.info(u"Checker for %(name)s succeeded",
307
367
                        vars(self))
 
368
            # Emit D-Bus signal
 
369
            self.CheckerCompleted(True)
308
370
            self.bump_timeout()
309
371
        elif not os.WIFEXITED(condition):
310
372
            logger.warning(u"Checker for %(name)s crashed?",
311
373
                           vars(self))
 
374
            # Emit D-Bus signal
 
375
            self.CheckerCompleted(False)
312
376
        else:
313
377
            logger.info(u"Checker for %(name)s failed",
314
378
                        vars(self))
 
379
            # Emit D-Bus signal
 
380
            self.CheckerCompleted(False)
 
381
    
 
382
    @dbus.service.signal(interface, signature="b")
 
383
    def CheckerCompleted(self, success):
 
384
        "D-Bus signal"
 
385
        pass
 
386
    
315
387
    def bump_timeout(self):
316
388
        """Bump up the timeout for this client.
317
389
        This should only be called when the client has been seen,
321
393
        gobject.source_remove(self.stop_initiator_tag)
322
394
        self.stop_initiator_tag = gobject.timeout_add\
323
395
            (self._timeout_milliseconds, self.stop)
 
396
    # D-Bus variant
 
397
    bumpTimeout = dbus.service.method(interface)(bump_timeout)
 
398
    
324
399
    def start_checker(self):
325
400
        """Start a new checker subprocess if one is not running.
326
401
        If a checker already exists, leave it running and do
361
436
                self.checker_callback_tag = gobject.child_watch_add\
362
437
                                            (self.checker.pid,
363
438
                                             self.checker_callback)
 
439
                # Emit D-Bus signal
 
440
                self.CheckerStarted(command)
364
441
            except OSError, error:
365
442
                logger.error(u"Failed to start subprocess: %s",
366
443
                             error)
367
444
        # Re-run this periodically if run by gobject.timeout_add
368
445
        return True
 
446
    
 
447
    @dbus.service.signal(interface, signature="s")
 
448
    def CheckerStarted(self, command):
 
449
        pass
 
450
    
 
451
    @dbus.service.method(interface, out_signature="b")
 
452
    def checkerIsRunning(self):
 
453
        "D-Bus getter method"
 
454
        return self.checker is not None
 
455
    
369
456
    def stop_checker(self):
370
457
        """Force the checker process, if any, to stop."""
371
458
        if self.checker_callback_tag:
383
470
            if error.errno != errno.ESRCH: # No such process
384
471
                raise
385
472
        self.checker = None
 
473
    # D-Bus variant
 
474
    StopChecker = dbus.service.method(interface)(stop_checker)
 
475
    
386
476
    def still_valid(self):
387
477
        """Has the timeout not yet passed for this client?"""
 
478
        if not self.started:
 
479
            return False
388
480
        now = datetime.datetime.now()
389
481
        if self.last_checked_ok is None:
390
482
            return now < (self.created + self.timeout)
391
483
        else:
392
484
            return now < (self.last_checked_ok + self.timeout)
 
485
    # D-Bus variant
 
486
    stillValid = dbus.service.method(interface, out_signature="b")\
 
487
        (still_valid)
 
488
    
 
489
    del interface
393
490
 
394
491
 
395
492
def peer_certificate(session):