/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: Björn Påhlsson
  • Date: 2011-12-21 00:29:33 UTC
  • mfrom: (518.1.15 mandos-persistent)
  • Revision ID: belorn@recompile.se-20111221002933-6sc0a64o1ns2s2s8
Working persistant state

Show diffs side-by-side

added added

removed removed

Lines of Context:
85
85
    except ImportError:
86
86
        SO_BINDTODEVICE = None
87
87
 
88
 
 
89
88
version = "1.4.1"
90
89
stored_state_file = "clients.pickle"
91
90
 
128
127
    logger.setLevel(level)
129
128
 
130
129
 
131
 
class CryptoError(Exception):
 
130
class PGPError(Exception):
 
131
    """Exception if encryption/decryption fails"""
132
132
    pass
133
133
 
134
134
 
135
 
class Crypto(object):
 
135
class PGPEngine(object):
136
136
    """A simple class for OpenPGP symmetric encryption & decryption"""
137
137
    def __init__(self):
138
138
        self.gnupg = GnuPGInterface.GnuPG()
184
184
                    ciphertext = f.read()
185
185
                proc.wait()
186
186
            except IOError as e:
187
 
                raise CryptoError(e)
 
187
                raise PGPError(e)
188
188
        self.gnupg.passphrase = None
189
189
        return ciphertext
190
190
    
201
201
                    decrypted_plaintext = f.read()
202
202
                proc.wait()
203
203
            except IOError as e:
204
 
                raise CryptoError(e)
 
204
                raise PGPError(e)
205
205
        self.gnupg.passphrase = None
206
206
        return decrypted_plaintext
207
207
 
377
377
                                % self.name))
378
378
        return ret
379
379
 
380
 
def _timedelta_to_milliseconds(td):
 
380
def timedelta_to_milliseconds(td):
381
381
    "Convert a datetime.timedelta() to milliseconds"
382
382
    return ((td.days * 24 * 60 * 60 * 1000)
383
383
            + (td.seconds * 1000)
387
387
    """A representation of a client host served by this server.
388
388
    
389
389
    Attributes:
390
 
    _approved:   bool(); 'None' if not yet approved/disapproved
 
390
    approved:   bool(); 'None' if not yet approved/disapproved
391
391
    approval_delay: datetime.timedelta(); Time to wait for approval
392
392
    approval_duration: datetime.timedelta(); Duration of one approval
393
393
    checker:    subprocess.Popen(); a running checker process used
411
411
    interval:   datetime.timedelta(); How often to start a new checker
412
412
    last_approval_request: datetime.datetime(); (UTC) or None
413
413
    last_checked_ok: datetime.datetime(); (UTC) or None
414
 
 
415
414
    last_checker_status: integer between 0 and 255 reflecting exit
416
415
                         status of last checker. -1 reflects crashed
417
416
                         checker, or None.
431
430
                          "created", "enabled", "fingerprint",
432
431
                          "host", "interval", "last_checked_ok",
433
432
                          "last_enabled", "name", "timeout")
 
433
    client_defaults = { "timeout": "5m",
 
434
                        "extended_timeout": "15m",
 
435
                        "interval": "2m",
 
436
                        "checker": "fping -q -- %%(host)s",
 
437
                        "host": "",
 
438
                        "approval_delay": "0s",
 
439
                        "approval_duration": "1s",
 
440
                        "approved_by_default": "True",
 
441
                        "enabled": "True",
 
442
                        }
434
443
    
435
444
    def timeout_milliseconds(self):
436
445
        "Return the 'timeout' attribute in milliseconds"
437
 
        return _timedelta_to_milliseconds(self.timeout)
 
446
        return timedelta_to_milliseconds(self.timeout)
438
447
    
439
448
    def extended_timeout_milliseconds(self):
440
449
        "Return the 'extended_timeout' attribute in milliseconds"
441
 
        return _timedelta_to_milliseconds(self.extended_timeout)
 
450
        return timedelta_to_milliseconds(self.extended_timeout)
442
451
    
443
452
    def interval_milliseconds(self):
444
453
        "Return the 'interval' attribute in milliseconds"
445
 
        return _timedelta_to_milliseconds(self.interval)
 
454
        return timedelta_to_milliseconds(self.interval)
446
455
    
447
456
    def approval_delay_milliseconds(self):
448
 
        return _timedelta_to_milliseconds(self.approval_delay)
449
 
    
450
 
    def __init__(self, name = None, config=None):
 
457
        return timedelta_to_milliseconds(self.approval_delay)
 
458
 
 
459
    @staticmethod
 
460
    def config_parser(config):
 
461
        """ Construct a new dict of client settings of this form:
 
462
        { client_name: {setting_name: value, ...}, ...}
 
463
        with exceptions for any special settings as defined above"""
 
464
        settings = {}
 
465
        for client_name in config.sections():
 
466
            section = dict(config.items(client_name))
 
467
            client = settings[client_name] = {}
 
468
            
 
469
            client["host"] = section["host"]
 
470
            # Reformat values from string types to Python types
 
471
            client["approved_by_default"] = config.getboolean(
 
472
                client_name, "approved_by_default")
 
473
            client["enabled"] = config.getboolean(client_name, "enabled")
 
474
            
 
475
            client["fingerprint"] = (section["fingerprint"].upper()
 
476
                                     .replace(" ", ""))
 
477
            if "secret" in section:
 
478
                client["secret"] = section["secret"].decode("base64")
 
479
            elif "secfile" in section:
 
480
                with open(os.path.expanduser(os.path.expandvars
 
481
                                             (section["secfile"])),
 
482
                          "rb") as secfile:
 
483
                    client["secret"] = secfile.read()
 
484
            else:
 
485
                raise TypeError("No secret or secfile for section %s"
 
486
                                % section)
 
487
            client["timeout"] = string_to_delta(section["timeout"])
 
488
            client["extended_timeout"] = string_to_delta(
 
489
                section["extended_timeout"])
 
490
            client["interval"] = string_to_delta(section["interval"])
 
491
            client["approval_delay"] = string_to_delta(
 
492
                section["approval_delay"])
 
493
            client["approval_duration"] = string_to_delta(
 
494
                section["approval_duration"])
 
495
            client["checker_command"] = section["checker"]
 
496
            client["last_approval_request"] = None
 
497
            client["last_checked_ok"] = None
 
498
            client["last_checker_status"] = None
 
499
            if client["enabled"]:
 
500
                client["last_enabled"] = datetime.datetime.utcnow()
 
501
                client["expires"] = (datetime.datetime.utcnow()
 
502
                                     + client["timeout"])
 
503
            else:
 
504
                client["last_enabled"] = None
 
505
                client["expires"] = None
 
506
 
 
507
        return settings
 
508
        
 
509
        
 
510
    def __init__(self, settings, name = None):
451
511
        """Note: the 'checker' key in 'config' sets the
452
512
        'checker_command' attribute and *not* the 'checker'
453
513
        attribute."""
454
514
        self.name = name
455
 
        if config is None:
456
 
            config = {}
 
515
        # adding all client settings
 
516
        for setting, value in settings.iteritems():
 
517
            setattr(self, setting, value)
 
518
        
457
519
        logger.debug("Creating client %r", self.name)
458
520
        # Uppercase and remove spaces from fingerprint for later
459
521
        # comparison purposes with return value from the fingerprint()
460
522
        # function
461
 
        self.fingerprint = (config["fingerprint"].upper()
462
 
                            .replace(" ", ""))
463
523
        logger.debug("  Fingerprint: %s", self.fingerprint)
464
 
        if "secret" in config:
465
 
            self.secret = config["secret"].decode("base64")
466
 
        elif "secfile" in config:
467
 
            with open(os.path.expanduser(os.path.expandvars
468
 
                                         (config["secfile"])),
469
 
                      "rb") as secfile:
470
 
                self.secret = secfile.read()
471
 
        else:
472
 
            raise TypeError("No secret or secfile for client %s"
473
 
                            % self.name)
474
 
        self.host = config.get("host", "")
475
 
        self.created = datetime.datetime.utcnow()
476
 
        self.enabled = config.get("enabled", True)
477
 
        self.last_approval_request = None
478
 
        if self.enabled:
479
 
            self.last_enabled = datetime.datetime.utcnow()
480
 
        else:
481
 
            self.last_enabled = None
482
 
        self.last_checked_ok = None
483
 
        self.last_checker_status = None
484
 
        self.timeout = string_to_delta(config["timeout"])
485
 
        self.extended_timeout = string_to_delta(config
486
 
                                                ["extended_timeout"])
487
 
        self.interval = string_to_delta(config["interval"])
 
524
        self.created = settings.get("created", datetime.datetime.utcnow())
 
525
 
 
526
        # attributes specific for this server instance
488
527
        self.checker = None
489
528
        self.checker_initiator_tag = None
490
529
        self.disable_initiator_tag = None
491
 
        if self.enabled:
492
 
            self.expires = datetime.datetime.utcnow() + self.timeout
493
 
        else:
494
 
            self.expires = None
495
530
        self.checker_callback_tag = None
496
 
        self.checker_command = config["checker"]
497
531
        self.current_checker_command = None
498
 
        self._approved = None
499
 
        self.approved_by_default = config.get("approved_by_default",
500
 
                                              True)
 
532
        self.approved = None
501
533
        self.approvals_pending = 0
502
 
        self.approval_delay = string_to_delta(
503
 
            config["approval_delay"])
504
 
        self.approval_duration = string_to_delta(
505
 
            config["approval_duration"])
506
534
        self.changedstate = (multiprocessing_manager
507
535
                             .Condition(multiprocessing_manager
508
536
                                        .Lock()))
575
603
        self.checker_callback_tag = None
576
604
        self.checker = None
577
605
        if os.WIFEXITED(condition):
578
 
            self.last_checker_status =  os.WEXITSTATUS(condition)
 
606
            self.last_checker_status = os.WEXITSTATUS(condition)
579
607
            if self.last_checker_status == 0:
580
608
                logger.info("Checker for %(name)s succeeded",
581
609
                            vars(self))
601
629
            gobject.source_remove(self.disable_initiator_tag)
602
630
        if getattr(self, "enabled", False):
603
631
            self.disable_initiator_tag = (gobject.timeout_add
604
 
                                          (_timedelta_to_milliseconds
 
632
                                          (timedelta_to_milliseconds
605
633
                                           (timeout), self.disable))
606
634
            self.expires = datetime.datetime.utcnow() + timeout
607
635
    
1011
1039
    def __init__(self, bus = None, *args, **kwargs):
1012
1040
        self.bus = bus
1013
1041
        Client.__init__(self, *args, **kwargs)
 
1042
        self._approvals_pending = 0
1014
1043
        
1015
1044
        self._approvals_pending = 0
1016
1045
        # Only now, when this client is initialized, can it show up on
1068
1097
        datetime_to_dbus, "LastApprovalRequest")
1069
1098
    approved_by_default = notifychangeproperty(dbus.Boolean,
1070
1099
                                               "ApprovedByDefault")
1071
 
    approval_delay = notifychangeproperty(dbus.UInt16,
 
1100
    approval_delay = notifychangeproperty(dbus.UInt64,
1072
1101
                                          "ApprovalDelay",
1073
1102
                                          type_func =
1074
 
                                          _timedelta_to_milliseconds)
 
1103
                                          timedelta_to_milliseconds)
1075
1104
    approval_duration = notifychangeproperty(
1076
 
        dbus.UInt16, "ApprovalDuration",
1077
 
        type_func = _timedelta_to_milliseconds)
 
1105
        dbus.UInt64, "ApprovalDuration",
 
1106
        type_func = timedelta_to_milliseconds)
1078
1107
    host = notifychangeproperty(dbus.String, "Host")
1079
 
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
 
1108
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1080
1109
                                   type_func =
1081
 
                                   _timedelta_to_milliseconds)
 
1110
                                   timedelta_to_milliseconds)
1082
1111
    extended_timeout = notifychangeproperty(
1083
 
        dbus.UInt16, "ExtendedTimeout",
1084
 
        type_func = _timedelta_to_milliseconds)
1085
 
    interval = notifychangeproperty(dbus.UInt16,
 
1112
        dbus.UInt64, "ExtendedTimeout",
 
1113
        type_func = timedelta_to_milliseconds)
 
1114
    interval = notifychangeproperty(dbus.UInt64,
1086
1115
                                    "Interval",
1087
1116
                                    type_func =
1088
 
                                    _timedelta_to_milliseconds)
 
1117
                                    timedelta_to_milliseconds)
1089
1118
    checker_command = notifychangeproperty(dbus.String, "Checker")
1090
1119
    
1091
1120
    del notifychangeproperty
1133
1162
        return r
1134
1163
    
1135
1164
    def _reset_approved(self):
1136
 
        self._approved = None
 
1165
        self.approved = None
1137
1166
        return False
1138
1167
    
1139
1168
    def approve(self, value=True):
1140
1169
        self.send_changedstate()
1141
 
        self._approved = value
1142
 
        gobject.timeout_add(_timedelta_to_milliseconds
 
1170
        self.approved = value
 
1171
        gobject.timeout_add(timedelta_to_milliseconds
1143
1172
                            (self.approval_duration),
1144
1173
                            self._reset_approved)
1145
1174
    
1259
1288
                           access="readwrite")
1260
1289
    def ApprovalDuration_dbus_property(self, value=None):
1261
1290
        if value is None:       # get
1262
 
            return dbus.UInt64(_timedelta_to_milliseconds(
 
1291
            return dbus.UInt64(timedelta_to_milliseconds(
1263
1292
                    self.approval_duration))
1264
1293
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1265
1294
    
1279
1308
    def Host_dbus_property(self, value=None):
1280
1309
        if value is None:       # get
1281
1310
            return dbus.String(self.host)
1282
 
        self.host = value
 
1311
        self.host = unicode(value)
1283
1312
    
1284
1313
    # Created - property
1285
1314
    @dbus_service_property(_interface, signature="s", access="read")
1334
1363
        gobject.source_remove(self.disable_initiator_tag)
1335
1364
        self.disable_initiator_tag = None
1336
1365
        self.expires = None
1337
 
        time_to_die = _timedelta_to_milliseconds((self
1338
 
                                                  .last_checked_ok
1339
 
                                                  + self.timeout)
1340
 
                                                 - datetime.datetime
1341
 
                                                 .utcnow())
 
1366
        time_to_die = timedelta_to_milliseconds((self
 
1367
                                                 .last_checked_ok
 
1368
                                                 + self.timeout)
 
1369
                                                - datetime.datetime
 
1370
                                                .utcnow())
1342
1371
        if time_to_die <= 0:
1343
1372
            # The timeout has passed
1344
1373
            self.disable()
1379
1408
    def Checker_dbus_property(self, value=None):
1380
1409
        if value is None:       # get
1381
1410
            return dbus.String(self.checker_command)
1382
 
        self.checker_command = value
 
1411
        self.checker_command = unicode(value)
1383
1412
    
1384
1413
    # CheckerRunning - property
1385
1414
    @dbus_service_property(_interface, signature="b",
1414
1443
            raise KeyError()
1415
1444
    
1416
1445
    def __getattribute__(self, name):
1417
 
        if(name == '_pipe'):
 
1446
        if name == '_pipe':
1418
1447
            return super(ProxyClient, self).__getattribute__(name)
1419
1448
        self._pipe.send(('getattr', name))
1420
1449
        data = self._pipe.recv()
1427
1456
            return func
1428
1457
    
1429
1458
    def __setattr__(self, name, value):
1430
 
        if(name == '_pipe'):
 
1459
        if name == '_pipe':
1431
1460
            return super(ProxyClient, self).__setattr__(name, value)
1432
1461
        self._pipe.send(('setattr', name, value))
1433
1462
 
1502
1531
                    logger.warning("Bad certificate: %s", error)
1503
1532
                    return
1504
1533
                logger.debug("Fingerprint: %s", fpr)
1505
 
                if self.server.use_dbus:
1506
 
                    # Emit D-Bus signal
1507
 
                    client.NewRequest(str(self.client_address))
1508
1534
                
1509
1535
                try:
1510
1536
                    client = ProxyClient(child_pipe, fpr,
1512
1538
                except KeyError:
1513
1539
                    return
1514
1540
                
 
1541
                if self.server.use_dbus:
 
1542
                    # Emit D-Bus signal
 
1543
                    client.NewRequest(str(self.client_address))
 
1544
                
1515
1545
                if client.approval_delay:
1516
1546
                    delay = client.approval_delay
1517
1547
                    client.approvals_pending += 1
1526
1556
                            client.Rejected("Disabled")
1527
1557
                        return
1528
1558
                    
1529
 
                    if client._approved or not client.approval_delay:
 
1559
                    if client.approved or not client.approval_delay:
1530
1560
                        #We are approved or approval is disabled
1531
1561
                        break
1532
 
                    elif client._approved is None:
 
1562
                    elif client.approved is None:
1533
1563
                        logger.info("Client %s needs approval",
1534
1564
                                    client.name)
1535
1565
                        if self.server.use_dbus:
1549
1579
                    time = datetime.datetime.now()
1550
1580
                    client.changedstate.acquire()
1551
1581
                    (client.changedstate.wait
1552
 
                     (float(client._timedelta_to_milliseconds(delay)
 
1582
                     (float(client.timedelta_to_milliseconds(delay)
1553
1583
                            / 1000)))
1554
1584
                    client.changedstate.release()
1555
1585
                    time2 = datetime.datetime.now()
1663
1693
    def sub_process_main(self, request, address):
1664
1694
        try:
1665
1695
            self.finish_request(request, address)
1666
 
        except:
 
1696
        except Exception:
1667
1697
            self.handle_error(request, address)
1668
1698
        self.close_request(request)
1669
1699
    
2063
2093
                                % server_settings["servicename"]))
2064
2094
    
2065
2095
    # Parse config file with clients
2066
 
    client_defaults = { "timeout": "5m",
2067
 
                        "extended_timeout": "15m",
2068
 
                        "interval": "2m",
2069
 
                        "checker": "fping -q -- %%(host)s",
2070
 
                        "host": "",
2071
 
                        "approval_delay": "0s",
2072
 
                        "approval_duration": "1s",
2073
 
                        }
2074
 
    client_config = configparser.SafeConfigParser(client_defaults)
 
2096
    client_config = configparser.SafeConfigParser(Client.client_defaults)
2075
2097
    client_config.read(os.path.join(server_settings["configdir"],
2076
2098
                                    "clients.conf"))
2077
2099
    
2178
2200
        client_class = functools.partial(ClientDBusTransitional,
2179
2201
                                         bus = bus)
2180
2202
    
2181
 
    special_settings = {
2182
 
        # Some settings need to be accessd by special methods;
2183
 
        # booleans need .getboolean(), etc.  Here is a list of them:
2184
 
        "approved_by_default":
2185
 
            lambda section:
2186
 
            client_config.getboolean(section, "approved_by_default"),
2187
 
        "enabled":
2188
 
            lambda section:
2189
 
            client_config.getboolean(section, "enabled"),
2190
 
        }
2191
 
    # Construct a new dict of client settings of this form:
2192
 
    # { client_name: {setting_name: value, ...}, ...}
2193
 
    # with exceptions for any special settings as defined above
2194
 
    client_settings = dict((clientname,
2195
 
                           dict((setting,
2196
 
                                 (value
2197
 
                                  if setting not in special_settings
2198
 
                                  else special_settings[setting]
2199
 
                                  (clientname)))
2200
 
                                for setting, value in
2201
 
                                client_config.items(clientname)))
2202
 
                          for clientname in client_config.sections())
2203
 
    
 
2203
    client_settings = Client.config_parser(client_config)
2204
2204
    old_client_settings = {}
2205
 
    clients_data = []
 
2205
    clients_data = {}
2206
2206
    
2207
2207
    # Get client data and settings from last running state.
2208
2208
    if server_settings["restore"]:
2217
2217
            if e.errno != errno.ENOENT:
2218
2218
                raise
2219
2219
    
2220
 
    with Crypto() as crypt:
2221
 
        for client in clients_data:
2222
 
            client_name = client["name"]
2223
 
            
 
2220
    with PGPEngine() as pgp:
 
2221
        for client_name, client in clients_data.iteritems():
2224
2222
            # Decide which value to use after restoring saved state.
2225
2223
            # We have three different values: Old config file,
2226
2224
            # new config file, and saved state.
2234
2232
                    if (name != "secret" and
2235
2233
                        value != old_client_settings[client_name]
2236
2234
                        [name]):
2237
 
                        setattr(client, name, value)
 
2235
                        client[name] = value
2238
2236
                except KeyError:
2239
2237
                    pass
2240
2238
            
2241
2239
            # Clients who has passed its expire date can still be
2242
 
            # enabled if its last checker was sucessful.  Clients
 
2240
            # enabled if its last checker was successful.  Clients
2243
2241
            # whose checker failed before we stored its state is
2244
2242
            # assumed to have failed all checkers during downtime.
2245
 
            if client["enabled"] and client["last_checked_ok"]:
2246
 
                if ((datetime.datetime.utcnow()
2247
 
                     - client["last_checked_ok"])
2248
 
                    > client["interval"]):
2249
 
                    if client["last_checker_status"] != 0:
 
2243
            if client["enabled"]:
 
2244
                if datetime.datetime.utcnow() >= client["expires"]:
 
2245
                    if not client["last_checked_ok"]:
 
2246
                        logger.warning(
 
2247
                            "disabling client {0} - Client never "
 
2248
                            "performed a successfull checker"
 
2249
                            .format(client["name"]))
 
2250
                        client["enabled"] = False
 
2251
                    elif client["last_checker_status"] != 0:
 
2252
                        logger.warning(
 
2253
                            "disabling client {0} - Client "
 
2254
                            "last checker failed with error code {1}"
 
2255
                            .format(client["name"],
 
2256
                                    client["last_checker_status"]))
2250
2257
                        client["enabled"] = False
2251
2258
                    else:
2252
2259
                        client["expires"] = (datetime.datetime
2253
2260
                                             .utcnow()
2254
2261
                                             + client["timeout"])
2255
 
            
2256
 
            client["changedstate"] = (multiprocessing_manager
2257
 
                                      .Condition
2258
 
                                      (multiprocessing_manager
2259
 
                                       .Lock()))
2260
 
            if use_dbus:
2261
 
                new_client = (ClientDBusTransitional.__new__
2262
 
                              (ClientDBusTransitional))
2263
 
                tcp_server.clients[client_name] = new_client
2264
 
                new_client.bus = bus
2265
 
                for name, value in client.iteritems():
2266
 
                    setattr(new_client, name, value)
2267
 
                client_object_name = unicode(client_name).translate(
2268
 
                    {ord("."): ord("_"),
2269
 
                     ord("-"): ord("_")})
2270
 
                new_client.dbus_object_path = (dbus.ObjectPath
2271
 
                                               ("/clients/"
2272
 
                                                + client_object_name))
2273
 
                DBusObjectWithProperties.__init__(new_client,
2274
 
                                                  new_client.bus,
2275
 
                                                  new_client
2276
 
                                                  .dbus_object_path)
2277
 
            else:
2278
 
                tcp_server.clients[client_name] = (Client.__new__
2279
 
                                                   (Client))
2280
 
                for name, value in client.iteritems():
2281
 
                    setattr(tcp_server.clients[client_name],
2282
 
                            name, value)
2283
 
            
 
2262
                    
2284
2263
            try:
2285
 
                tcp_server.clients[client_name].secret = (
2286
 
                    crypt.decrypt(tcp_server.clients[client_name]
2287
 
                                  .encrypted_secret,
2288
 
                                  client_settings[client_name]
2289
 
                                  ["secret"]))
2290
 
            except CryptoError:
 
2264
                client["secret"] = (
 
2265
                    pgp.decrypt(client["encrypted_secret"],
 
2266
                                client_settings[client_name]
 
2267
                                ["secret"]))
 
2268
            except PGPError:
2291
2269
                # If decryption fails, we use secret from new settings
2292
 
                tcp_server.clients[client_name].secret = (
 
2270
                logger.debug("Failed to decrypt {0} old secret"
 
2271
                             .format(client_name))
 
2272
                client["secret"] = (
2293
2273
                    client_settings[client_name]["secret"])
 
2274
 
2294
2275
    
2295
 
    # Create/remove clients based on new changes made to config
2296
 
    for clientname in set(old_client_settings) - set(client_settings):
2297
 
        del tcp_server.clients[clientname]
2298
 
    for clientname in set(client_settings) - set(old_client_settings):
2299
 
        tcp_server.clients[clientname] = (client_class(name
2300
 
                                                       = clientname,
2301
 
                                                       config =
2302
 
                                                       client_settings
2303
 
                                                       [clientname]))
 
2276
    # Add/remove clients based on new changes made to config
 
2277
    for client_name in set(old_client_settings) - set(client_settings):
 
2278
        del clients_data[client_name]
 
2279
    for client_name in set(client_settings) - set(old_client_settings):
 
2280
        clients_data[client_name] = client_settings[client_name]
 
2281
 
 
2282
    # Create clients all clients
 
2283
    for client_name, client in clients_data.iteritems():
 
2284
        tcp_server.clients[client_name] = client_class(
 
2285
            name = client_name, settings = client)
2304
2286
    
2305
2287
    if not tcp_server.clients:
2306
2288
        logger.warning("No clients defined")
2318
2300
            # "pidfile" was never created
2319
2301
            pass
2320
2302
        del pidfilename
2321
 
        
2322
2303
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2323
2304
    
2324
2305
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2393
2374
        # Store client before exiting. Secrets are encrypted with key
2394
2375
        # based on what config file has. If config file is
2395
2376
        # removed/edited, old secret will thus be unrecovable.
2396
 
        clients = []
2397
 
        with Crypto() as crypt:
 
2377
        clients = {}
 
2378
        with PGPEngine() as pgp:
2398
2379
            for client in tcp_server.clients.itervalues():
2399
2380
                key = client_settings[client.name]["secret"]
2400
 
                client.encrypted_secret = crypt.encrypt(client.secret,
2401
 
                                                        key)
 
2381
                client.encrypted_secret = pgp.encrypt(client.secret,
 
2382
                                                      key)
2402
2383
                client_dict = {}
2403
2384
                
2404
 
                # A list of attributes that will not be stored when
2405
 
                # shutting down.
2406
 
                exclude = set(("bus", "changedstate", "secret"))
 
2385
                # A list of attributes that can not be pickled
 
2386
                # + secret.
 
2387
                exclude = set(("bus", "changedstate", "secret",
 
2388
                               "checker"))
2407
2389
                for name, typ in (inspect.getmembers
2408
2390
                                  (dbus.service.Object)):
2409
2391
                    exclude.add(name)
2414
2396
                    if attr not in exclude:
2415
2397
                        client_dict[attr] = getattr(client, attr)
2416
2398
                
2417
 
                clients.append(client_dict)
 
2399
                clients[client.name] = client_dict
2418
2400
                del client_settings[client.name]["secret"]
2419
2401
        
2420
2402
        try:
2496
2478
    # Must run before the D-Bus bus name gets deregistered
2497
2479
    cleanup()
2498
2480
 
2499
 
 
2500
2481
if __name__ == '__main__':
2501
2482
    main()