/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: 2009-03-31 01:32:12 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090331013212-yes2wy6njzlyniaa
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
  (Client.start_checker): Bug fix: update "current_checker_command" in
                          all cases.
  (ClientDBus): New class.
  (main): Use Client or ClientDBus depending on "use_dbus".
  (main/MandosDBusService.RemoveClient): Bug fix: make sure the client
                                         is removed from the D-Bus.

Show diffs side-by-side

added added

removed removed

Lines of Context:
179
179
    return dbus.String(dt.isoformat(), variant_level=variant_level)
180
180
 
181
181
 
182
 
class Client(dbus.service.Object):
 
182
class Client(object):
183
183
    """A representation of a client host served by this server.
184
184
    Attributes:
185
185
    name:       string; from the config file, used in log messages and
207
207
                     runtime with vars(self) as dict, so that for
208
208
                     instance %(name)s can be used in the command.
209
209
    current_checker_command: string; current running checker_command
210
 
    use_dbus: bool(); Whether to provide D-Bus interface and signals
211
 
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
212
210
    """
213
211
    def timeout_milliseconds(self):
214
212
        "Return the 'timeout' attribute in milliseconds"
222
220
                + (self.interval.seconds * 1000)
223
221
                + (self.interval.microseconds // 1000))
224
222
    
225
 
    def __init__(self, name = None, disable_hook=None, config=None,
226
 
                 use_dbus=True):
 
223
    def __init__(self, name = None, disable_hook=None, config=None):
227
224
        """Note: the 'checker' key in 'config' sets the
228
225
        'checker_command' attribute and *not* the 'checker'
229
226
        attribute."""
231
228
        if config is None:
232
229
            config = {}
233
230
        logger.debug(u"Creating client %r", self.name)
234
 
        self.use_dbus = False   # During __init__
235
231
        # Uppercase and remove spaces from fingerprint for later
236
232
        # comparison purposes with return value from the fingerprint()
237
233
        # function
263
259
        self.checker_command = config["checker"]
264
260
        self.current_checker_command = None
265
261
        self.last_connect = None
266
 
        # Only now, when this client is initialized, can it show up on
267
 
        # the D-Bus
268
 
        self.use_dbus = use_dbus
269
 
        if self.use_dbus:
270
 
            self.dbus_object_path = (dbus.ObjectPath
271
 
                                     ("/clients/"
272
 
                                      + self.name.replace(".", "_")))
273
 
            dbus.service.Object.__init__(self, bus,
274
 
                                         self.dbus_object_path)
275
262
    
276
263
    def enable(self):
277
264
        """Start this client's checker and timeout hooks"""
288
275
                                   (self.timeout_milliseconds(),
289
276
                                    self.disable))
290
277
        self.enabled = True
291
 
        if self.use_dbus:
292
 
            # Emit D-Bus signals
293
 
            self.PropertyChanged(dbus.String(u"enabled"),
294
 
                                 dbus.Boolean(True, variant_level=1))
295
 
            self.PropertyChanged(dbus.String(u"last_enabled"),
296
 
                                 (_datetime_to_dbus(self.last_enabled,
297
 
                                                    variant_level=1)))
298
278
    
299
279
    def disable(self):
300
280
        """Disable this client."""
311
291
        if self.disable_hook:
312
292
            self.disable_hook(self)
313
293
        self.enabled = False
314
 
        if self.use_dbus:
315
 
            # Emit D-Bus signal
316
 
            self.PropertyChanged(dbus.String(u"enabled"),
317
 
                                 dbus.Boolean(False, variant_level=1))
318
294
        # Do not run this again if called by a gobject.timeout_add
319
295
        return False
320
296
    
326
302
        """The checker has completed, so take appropriate actions."""
327
303
        self.checker_callback_tag = None
328
304
        self.checker = None
329
 
        if self.use_dbus:
330
 
            # Emit D-Bus signal
331
 
            self.PropertyChanged(dbus.String(u"checker_running"),
332
 
                                 dbus.Boolean(False, variant_level=1))
333
305
        if os.WIFEXITED(condition):
334
306
            exitstatus = os.WEXITSTATUS(condition)
335
307
            if exitstatus == 0:
339
311
            else:
340
312
                logger.info(u"Checker for %(name)s failed",
341
313
                            vars(self))
342
 
            if self.use_dbus:
343
 
                # Emit D-Bus signal
344
 
                self.CheckerCompleted(dbus.Int16(exitstatus),
345
 
                                      dbus.Int64(condition),
346
 
                                      dbus.String(command))
347
314
        else:
348
315
            logger.warning(u"Checker for %(name)s crashed?",
349
316
                           vars(self))
350
 
            if self.use_dbus:
351
 
                # Emit D-Bus signal
352
 
                self.CheckerCompleted(dbus.Int16(-1),
353
 
                                      dbus.Int64(condition),
354
 
                                      dbus.String(command))
355
317
    
356
318
    def checked_ok(self):
357
319
        """Bump up the timeout for this client.
363
325
        self.disable_initiator_tag = (gobject.timeout_add
364
326
                                      (self.timeout_milliseconds(),
365
327
                                       self.disable))
366
 
        if self.use_dbus:
367
 
            # Emit D-Bus signal
368
 
            self.PropertyChanged(
369
 
                dbus.String(u"last_checked_ok"),
370
 
                (_datetime_to_dbus(self.last_checked_ok,
371
 
                                   variant_level=1)))
372
328
    
373
329
    def start_checker(self):
374
330
        """Start a new checker subprocess if one is not running.
407
363
                    logger.error(u'Could not format string "%s":'
408
364
                                 u' %s', self.checker_command, error)
409
365
                    return True # Try again later
410
 
                self.current_checker_command = command
 
366
            self.current_checker_command = command
411
367
            try:
412
368
                logger.info(u"Starting checker %r for %s",
413
369
                            command, self.name)
418
374
                self.checker = subprocess.Popen(command,
419
375
                                                close_fds=True,
420
376
                                                shell=True, cwd="/")
421
 
                if self.use_dbus:
422
 
                    # Emit D-Bus signal
423
 
                    self.CheckerStarted(command)
424
 
                    self.PropertyChanged(
425
 
                        dbus.String("checker_running"),
426
 
                        dbus.Boolean(True, variant_level=1))
427
377
                self.checker_callback_tag = (gobject.child_watch_add
428
378
                                             (self.checker.pid,
429
379
                                              self.checker_callback,
457
407
            if error.errno != errno.ESRCH: # No such process
458
408
                raise
459
409
        self.checker = None
460
 
        if self.use_dbus:
461
 
            self.PropertyChanged(dbus.String(u"checker_running"),
462
 
                                 dbus.Boolean(False, variant_level=1))
463
410
    
464
411
    def still_valid(self):
465
412
        """Has the timeout not yet passed for this client?"""
470
417
            return now < (self.created + self.timeout)
471
418
        else:
472
419
            return now < (self.last_checked_ok + self.timeout)
 
420
 
 
421
 
 
422
class ClientDBus(Client, dbus.service.Object):
 
423
    """A Client class using D-Bus
 
424
    Attributes:
 
425
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
 
426
    """
 
427
    # dbus.service.Object doesn't use super(), so we can't either.
 
428
    
 
429
    def __init__(self, *args, **kwargs):
 
430
        Client.__init__(self, *args, **kwargs)
 
431
        # Only now, when this client is initialized, can it show up on
 
432
        # the D-Bus
 
433
        self.dbus_object_path = (dbus.ObjectPath
 
434
                                 ("/clients/"
 
435
                                  + self.name.replace(".", "_")))
 
436
        dbus.service.Object.__init__(self, bus,
 
437
                                     self.dbus_object_path)
 
438
    def enable(self):
 
439
        oldstate = getattr(self, "enabled", False)
 
440
        r = Client.enable(self)
 
441
        if oldstate != self.enabled:
 
442
            # Emit D-Bus signals
 
443
            self.PropertyChanged(dbus.String(u"enabled"),
 
444
                                 dbus.Boolean(True, variant_level=1))
 
445
            self.PropertyChanged(dbus.String(u"last_enabled"),
 
446
                                 (_datetime_to_dbus(self.last_enabled,
 
447
                                                    variant_level=1)))
 
448
        return r
 
449
    
 
450
    def disable(self, signal = True):
 
451
        oldstate = getattr(self, "enabled", False)
 
452
        r = Client.disable(self)
 
453
        if signal and oldstate != self.enabled:
 
454
            # Emit D-Bus signal
 
455
            self.PropertyChanged(dbus.String(u"enabled"),
 
456
                                 dbus.Boolean(False, variant_level=1))
 
457
        return r
 
458
    
 
459
    def __del__(self, *args, **kwargs):
 
460
        try:
 
461
            self.remove_from_connection()
 
462
        except org.freedesktop.DBus.Python.LookupError:
 
463
            pass
 
464
        dbus.service.Object.__del__(self, *args, **kwargs)
 
465
        Client.__del__(self, *args, **kwargs)
 
466
    
 
467
    def checker_callback(self, pid, condition, command,
 
468
                         *args, **kwargs):
 
469
        self.checker_callback_tag = None
 
470
        self.checker = None
 
471
        # Emit D-Bus signal
 
472
        self.PropertyChanged(dbus.String(u"checker_running"),
 
473
                             dbus.Boolean(False, variant_level=1))
 
474
        if os.WIFEXITED(condition):
 
475
            exitstatus = os.WEXITSTATUS(condition)
 
476
            # Emit D-Bus signal
 
477
            self.CheckerCompleted(dbus.Int16(exitstatus),
 
478
                                  dbus.Int64(condition),
 
479
                                  dbus.String(command))
 
480
        else:
 
481
            # Emit D-Bus signal
 
482
            self.CheckerCompleted(dbus.Int16(-1),
 
483
                                  dbus.Int64(condition),
 
484
                                  dbus.String(command))
 
485
        
 
486
        return Client.checker_callback(self, pid, condition, command,
 
487
                                       *args, **kwargs)
 
488
    
 
489
    def checked_ok(self, *args, **kwargs):
 
490
        r = Client.checked_ok(self, *args, **kwargs)
 
491
        # Emit D-Bus signal
 
492
        self.PropertyChanged(
 
493
            dbus.String(u"last_checked_ok"),
 
494
            (_datetime_to_dbus(self.last_checked_ok,
 
495
                               variant_level=1)))
 
496
        return r
 
497
    
 
498
    def start_checker(self, *args, **kwargs):
 
499
        old_checker = self.checker
 
500
        if self.checker is not None:
 
501
            old_checker_pid = self.checker.pid
 
502
        else:
 
503
            old_checker_pid = None
 
504
        r = Client.start_checker(self, *args, **kwargs)
 
505
        # Only emit D-Bus signal if new checker process was started
 
506
        if ((self.checker is not None)
 
507
            and not (old_checker is not None
 
508
                     and old_checker_pid == self.checker.pid)):
 
509
            self.CheckerStarted(self.current_checker_command)
 
510
            self.PropertyChanged(
 
511
                dbus.String("checker_running"),
 
512
                dbus.Boolean(True, variant_level=1))
 
513
        return r
 
514
    
 
515
    def stop_checker(self, *args, **kwargs):
 
516
        old_checker = getattr(self, "checker", None)
 
517
        r = Client.stop_checker(self, *args, **kwargs)
 
518
        if (old_checker is not None
 
519
            and getattr(self, "checker", None) is None):
 
520
            self.PropertyChanged(dbus.String(u"checker_running"),
 
521
                                 dbus.Boolean(False, variant_level=1))
 
522
        return r
473
523
    
474
524
    ## D-Bus methods & signals
475
525
    _interface = u"se.bsnet.fukt.Mandos.Client"
533
583
                }, signature="sv")
534
584
    
535
585
    # IsStillValid - method
536
 
    IsStillValid = (dbus.service.method(_interface, out_signature="b")
537
 
                    (still_valid))
538
 
    IsStillValid.__name__ = "IsStillValid"
 
586
    @dbus.service.method(_interface, out_signature="b")
 
587
    def IsStillValid(self):
 
588
        return self.still_valid()
539
589
    
540
590
    # PropertyChanged - signal
541
591
    @dbus.service.signal(_interface, signature="sv")
1204
1254
    if use_dbus:
1205
1255
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1206
1256
    
1207
 
    clients.update(Set(Client(name = section,
1208
 
                              config
1209
 
                              = dict(client_config.items(section)),
1210
 
                              use_dbus = use_dbus)
1211
 
                       for section in client_config.sections()))
 
1257
    client_class = Client
 
1258
    if use_dbus:
 
1259
        client_class = ClientDBus
 
1260
    clients.update(Set(
 
1261
            client_class(name = section,
 
1262
                         config= dict(client_config.items(section)))
 
1263
            for section in client_config.sections()))
1212
1264
    if not clients:
1213
1265
        logger.warning(u"No clients defined")
1214
1266
    
1299
1351
                for c in clients:
1300
1352
                    if c.dbus_object_path == object_path:
1301
1353
                        clients.remove(c)
 
1354
                        c.remove_from_connection()
1302
1355
                        # Don't signal anything except ClientRemoved
1303
 
                        c.use_dbus = False
1304
 
                        c.disable()
 
1356
                        c.disable(signal=False)
1305
1357
                        # Emit D-Bus signal
1306
1358
                        self.ClientRemoved(object_path, c.name)
1307
1359
                        return