/mandos/release

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

« 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) (237.2.1 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:
11
11
# and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
14
# Copyright © 2008 Teddy Hogeborn & Björn Påhlsson
15
15
16
16
# This program is free software: you can redistribute it and/or modify
17
17
# it under the terms of the GNU General Public License as published by
30
30
# Contact the authors at <mandos@fukt.bsnet.se>.
31
31
32
32
 
33
 
from __future__ import division
 
33
from __future__ import division, with_statement, absolute_import
34
34
 
35
35
import SocketServer
36
36
import socket
37
 
import select
38
37
from optparse import OptionParser
39
38
import datetime
40
39
import errno
55
54
import stat
56
55
import logging
57
56
import logging.handlers
 
57
import pwd
 
58
from contextlib import closing
58
59
 
59
60
import dbus
 
61
import dbus.service
60
62
import gobject
61
63
import avahi
62
64
from dbus.mainloop.glib import DBusGMainLoop
63
65
import ctypes
 
66
import ctypes.util
64
67
 
65
 
version = "1.0"
 
68
version = "1.0.2"
66
69
 
67
70
logger = logging.Logger('mandos')
68
71
syslogger = logging.handlers.SysLogHandler\
80
83
class AvahiError(Exception):
81
84
    def __init__(self, value):
82
85
        self.value = value
 
86
        super(AvahiError, self).__init__()
83
87
    def __str__(self):
84
88
        return repr(self.value)
85
89
 
107
111
                  a sensible number of times
108
112
    """
109
113
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
110
 
                 type = None, port = None, TXT = None, domain = "",
 
114
                 servicetype = None, port = None, TXT = None, domain = "",
111
115
                 host = "", max_renames = 32768):
112
116
        self.interface = interface
113
117
        self.name = name
114
 
        self.type = type
 
118
        self.type = servicetype
115
119
        self.port = port
116
120
        if TXT is None:
117
121
            self.TXT = []
126
130
        if self.rename_count >= self.max_renames:
127
131
            logger.critical(u"No suitable Zeroconf service name found"
128
132
                            u" after %i retries, exiting.",
129
 
                            rename_count)
 
133
                            self.rename_count)
130
134
            raise AvahiServiceError("Too many renames")
131
135
        self.name = server.GetAlternativeServiceName(self.name)
132
136
        logger.info(u"Changing Zeroconf service name to %r ...",
168
172
# End of Avahi example code
169
173
 
170
174
 
171
 
class Client(object):
 
175
class Client(dbus.service.Object):
172
176
    """A representation of a client host served by this server.
173
177
    Attributes:
174
178
    name:      string; from the config file, used in log messages
177
181
    secret:    bytestring; sent verbatim (over TLS) to client
178
182
    host:      string; available for use by the checker command
179
183
    created:   datetime.datetime(); object creation, not client host
 
184
    started:   bool()
180
185
    last_checked_ok: datetime.datetime() or None if not yet checked OK
181
186
    timeout:   datetime.timedelta(); How long from last_checked_ok
182
187
                                     until this client is invalid
198
203
    _timeout_milliseconds: Used when calling gobject.timeout_add()
199
204
    _interval_milliseconds: - '' -
200
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
    
201
224
    def _set_timeout(self, timeout):
202
 
        "Setter function for 'timeout' attribute"
 
225
        "Setter function for the 'timeout' attribute"
203
226
        self._timeout = timeout
204
227
        self._timeout_milliseconds = ((self.timeout.days
205
228
                                       * 24 * 60 * 60 * 1000)
206
229
                                      + (self.timeout.seconds * 1000)
207
230
                                      + (self.timeout.microseconds
208
231
                                         // 1000))
209
 
    timeout = property(lambda self: self._timeout,
210
 
                       _set_timeout)
 
232
        # Emit D-Bus signal
 
233
        self.TimeoutChanged(self._timeout_milliseconds)
 
234
    timeout = property(lambda self: self._timeout, _set_timeout)
211
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
    
212
247
    def _set_interval(self, interval):
213
 
        "Setter function for 'interval' attribute"
 
248
        "Setter function for the 'interval' attribute"
214
249
        self._interval = interval
215
250
        self._interval_milliseconds = ((self.interval.days
216
251
                                        * 24 * 60 * 60 * 1000)
218
253
                                          * 1000)
219
254
                                       + (self.interval.microseconds
220
255
                                          // 1000))
221
 
    interval = property(lambda self: self._interval,
222
 
                        _set_interval)
 
256
        # Emit D-Bus signal
 
257
        self.IntervalChanged(self._interval_milliseconds)
 
258
    interval = property(lambda self: self._interval, _set_interval)
223
259
    del _set_interval
224
 
    def __init__(self, name = None, stop_hook=None, config={}):
 
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
    
 
271
    def __init__(self, name = None, stop_hook=None, config=None):
225
272
        """Note: the 'checker' key in 'config' sets the
226
273
        'checker_command' attribute and *not* the 'checker'
227
274
        attribute."""
 
275
        dbus.service.Object.__init__(self, bus,
 
276
                                     "/Mandos/Clients/%s"
 
277
                                     % name.replace(".", "_"))
 
278
        if config is None:
 
279
            config = {}
228
280
        self.name = name
229
281
        logger.debug(u"Creating client %r", self.name)
230
282
        # Uppercase and remove spaces from fingerprint for later
236
288
        if "secret" in config:
237
289
            self.secret = config["secret"].decode(u"base64")
238
290
        elif "secfile" in config:
239
 
            sf = open(config["secfile"])
240
 
            self.secret = sf.read()
241
 
            sf.close()
 
291
            with closing(open(os.path.expanduser
 
292
                              (os.path.expandvars
 
293
                               (config["secfile"])))) \
 
294
                               as secfile:
 
295
                self.secret = secfile.read()
242
296
        else:
243
297
            raise TypeError(u"No secret or secfile for client %s"
244
298
                            % self.name)
245
299
        self.host = config.get("host", "")
246
300
        self.created = datetime.datetime.now()
 
301
        self.started = False
247
302
        self.last_checked_ok = None
248
303
        self.timeout = string_to_delta(config["timeout"])
249
304
        self.interval = string_to_delta(config["interval"])
253
308
        self.stop_initiator_tag = None
254
309
        self.checker_callback_tag = None
255
310
        self.check_command = config["checker"]
 
311
    
256
312
    def start(self):
257
313
        """Start this client's checker and timeout hooks"""
 
314
        self.started = True
258
315
        # Schedule a new checker to be started an 'interval' from now,
259
316
        # and every interval from then on.
260
317
        self.checker_initiator_tag = gobject.timeout_add\
266
323
        self.stop_initiator_tag = gobject.timeout_add\
267
324
                                  (self._timeout_milliseconds,
268
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
    
269
334
    def stop(self):
270
 
        """Stop this client.
271
 
        The possibility that a client might be restarted is left open,
272
 
        but not currently used."""
273
 
        # If this client doesn't have a secret, it is already stopped.
274
 
        if hasattr(self, "secret") and self.secret:
 
335
        """Stop this client."""
 
336
        if getattr(self, "started", False):
275
337
            logger.info(u"Stopping client %s", self.name)
276
 
            self.secret = None
277
338
        else:
278
339
            return False
279
340
        if getattr(self, "stop_initiator_tag", False):
285
346
        self.stop_checker()
286
347
        if self.stop_hook:
287
348
            self.stop_hook(self)
 
349
        # Emit D-Bus signal
 
350
        self.StateChanged(False)
288
351
        # Do not run this again if called by a gobject.timeout_add
289
352
        return False
 
353
    # D-Bus variant
 
354
    Stop = dbus.service.method(interface)(stop)
 
355
    
290
356
    def __del__(self):
291
357
        self.stop_hook = None
292
358
        self.stop()
 
359
    
293
360
    def checker_callback(self, pid, condition):
294
361
        """The checker has completed, so take appropriate actions."""
295
 
        now = datetime.datetime.now()
296
362
        self.checker_callback_tag = None
297
363
        self.checker = None
298
364
        if os.WIFEXITED(condition) \
299
365
               and (os.WEXITSTATUS(condition) == 0):
300
366
            logger.info(u"Checker for %(name)s succeeded",
301
367
                        vars(self))
302
 
            self.last_checked_ok = now
303
 
            gobject.source_remove(self.stop_initiator_tag)
304
 
            self.stop_initiator_tag = gobject.timeout_add\
305
 
                                      (self._timeout_milliseconds,
306
 
                                       self.stop)
 
368
            # Emit D-Bus signal
 
369
            self.CheckerCompleted(True)
 
370
            self.bump_timeout()
307
371
        elif not os.WIFEXITED(condition):
308
372
            logger.warning(u"Checker for %(name)s crashed?",
309
373
                           vars(self))
 
374
            # Emit D-Bus signal
 
375
            self.CheckerCompleted(False)
310
376
        else:
311
377
            logger.info(u"Checker for %(name)s failed",
312
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
    
 
387
    def bump_timeout(self):
 
388
        """Bump up the timeout for this client.
 
389
        This should only be called when the client has been seen,
 
390
        alive and well.
 
391
        """
 
392
        self.last_checked_ok = datetime.datetime.now()
 
393
        gobject.source_remove(self.stop_initiator_tag)
 
394
        self.stop_initiator_tag = gobject.timeout_add\
 
395
            (self._timeout_milliseconds, self.stop)
 
396
    # D-Bus variant
 
397
    bumpTimeout = dbus.service.method(interface)(bump_timeout)
 
398
    
313
399
    def start_checker(self):
314
400
        """Start a new checker subprocess if one is not running.
315
401
        If a checker already exists, leave it running and do
350
436
                self.checker_callback_tag = gobject.child_watch_add\
351
437
                                            (self.checker.pid,
352
438
                                             self.checker_callback)
 
439
                # Emit D-Bus signal
 
440
                self.CheckerStarted(command)
353
441
            except OSError, error:
354
442
                logger.error(u"Failed to start subprocess: %s",
355
443
                             error)
356
444
        # Re-run this periodically if run by gobject.timeout_add
357
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
    
358
456
    def stop_checker(self):
359
457
        """Force the checker process, if any, to stop."""
360
458
        if self.checker_callback_tag:
372
470
            if error.errno != errno.ESRCH: # No such process
373
471
                raise
374
472
        self.checker = None
 
473
    # D-Bus variant
 
474
    StopChecker = dbus.service.method(interface)(stop_checker)
 
475
    
375
476
    def still_valid(self):
376
477
        """Has the timeout not yet passed for this client?"""
 
478
        if not self.started:
 
479
            return False
377
480
        now = datetime.datetime.now()
378
481
        if self.last_checked_ok is None:
379
482
            return now < (self.created + self.timeout)
380
483
        else:
381
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
382
490
 
383
491
 
384
492
def peer_certificate(session):
414
522
                    (crt, ctypes.byref(datum),
415
523
                     gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
416
524
    # Verify the self signature in the key
417
 
    crtverify = ctypes.c_uint();
 
525
    crtverify = ctypes.c_uint()
418
526
    gnutls.library.functions.gnutls_openpgp_crt_verify_self\
419
527
        (crt, 0, ctypes.byref(crtverify))
420
528
    if crtverify.value != 0:
421
529
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
422
530
        raise gnutls.errors.CertificateSecurityError("Verify failed")
423
531
    # New buffer for the fingerprint
424
 
    buffer = ctypes.create_string_buffer(20)
425
 
    buffer_length = ctypes.c_size_t()
 
532
    buf = ctypes.create_string_buffer(20)
 
533
    buf_len = ctypes.c_size_t()
426
534
    # Get the fingerprint from the certificate into the buffer
427
535
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
428
 
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
 
536
        (crt, ctypes.byref(buf), ctypes.byref(buf_len))
429
537
    # Deinit the certificate
430
538
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
431
539
    # Convert the buffer to a Python bytestring
432
 
    fpr = ctypes.string_at(buffer, buffer_length.value)
 
540
    fpr = ctypes.string_at(buf, buf_len.value)
433
541
    # Convert the bytestring to hexadecimal notation
434
542
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
435
543
    return hex_fpr
436
544
 
437
545
 
438
 
class tcp_handler(SocketServer.BaseRequestHandler, object):
 
546
class TCP_handler(SocketServer.BaseRequestHandler, object):
439
547
    """A TCP request handler class.
440
548
    Instantiated by IPv6_TCPServer for each request to handle it.
441
549
    Note: This will run in its own forked process."""
442
550
    
443
551
    def handle(self):
444
552
        logger.info(u"TCP connection from: %s",
445
 
                     unicode(self.client_address))
 
553
                    unicode(self.client_address))
446
554
        session = gnutls.connection.ClientSession\
447
555
                  (self.request, gnutls.connection.X509Credentials())
448
556
        
463
571
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
464
572
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
465
573
        #                "+DHE-DSS"))
466
 
        priority = "NORMAL"             # Fallback default, since this
467
 
                                        # MUST be set.
468
 
        if self.server.settings["priority"]:
469
 
            priority = self.server.settings["priority"]
 
574
        # Use a fallback default, since this MUST be set.
 
575
        priority = self.server.settings.get("priority", "NORMAL")
470
576
        gnutls.library.functions.gnutls_priority_set_direct\
471
 
            (session._c_object, priority, None);
 
577
            (session._c_object, priority, None)
472
578
        
473
579
        try:
474
580
            session.handshake()
502
608
                           vars(client))
503
609
            session.bye()
504
610
            return
 
611
        ## This won't work here, since we're in a fork.
 
612
        # client.bump_timeout()
505
613
        sent_size = 0
506
614
        while sent_size < len(client.secret):
507
615
            sent = session.send(client.secret[sent_size:])
512
620
        session.bye()
513
621
 
514
622
 
515
 
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
 
623
class IPv6_TCPServer(SocketServer.ForkingMixIn,
 
624
                     SocketServer.TCPServer, object):
516
625
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
517
626
    Attributes:
518
627
        settings:       Server settings
519
628
        clients:        Set() of Client objects
 
629
        enabled:        Boolean; whether this server is activated yet
520
630
    """
521
631
    address_family = socket.AF_INET6
522
632
    def __init__(self, *args, **kwargs):
526
636
        if "clients" in kwargs:
527
637
            self.clients = kwargs["clients"]
528
638
            del kwargs["clients"]
529
 
        return super(type(self), self).__init__(*args, **kwargs)
 
639
        self.enabled = False
 
640
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
530
641
    def server_bind(self):
531
642
        """This overrides the normal server_bind() function
532
643
        to bind to an interface if one was specified, and also NOT to
561
672
#                                            if_nametoindex
562
673
#                                            (self.settings
563
674
#                                             ["interface"]))
564
 
            return super(type(self), self).server_bind()
 
675
            return super(IPv6_TCPServer, self).server_bind()
 
676
    def server_activate(self):
 
677
        if self.enabled:
 
678
            return super(IPv6_TCPServer, self).server_activate()
 
679
    def enable(self):
 
680
        self.enabled = True
565
681
 
566
682
 
567
683
def string_to_delta(interval):
583
699
    timevalue = datetime.timedelta(0)
584
700
    for s in interval.split():
585
701
        try:
586
 
            suffix=unicode(s[-1])
587
 
            value=int(s[:-1])
 
702
            suffix = unicode(s[-1])
 
703
            value = int(s[:-1])
588
704
            if suffix == u"d":
589
705
                delta = datetime.timedelta(value)
590
706
            elif suffix == u"s":
630
746
    """Call the C function if_nametoindex(), or equivalent"""
631
747
    global if_nametoindex
632
748
    try:
633
 
        if "ctypes.util" not in sys.modules:
634
 
            import ctypes.util
635
749
        if_nametoindex = ctypes.cdll.LoadLibrary\
636
750
            (ctypes.util.find_library("c")).if_nametoindex
637
751
    except (OSError, AttributeError):
642
756
        def if_nametoindex(interface):
643
757
            "Get an interface index the hard way, i.e. using fcntl()"
644
758
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
645
 
            s = socket.socket()
646
 
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
647
 
                                struct.pack("16s16x", interface))
648
 
            s.close()
 
759
            with closing(socket.socket()) as s:
 
760
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
761
                                    struct.pack("16s16x", interface))
649
762
            interface_index = struct.unpack("I", ifreq[16:20])[0]
650
763
            return interface_index
651
764
    return if_nametoindex(interface)
675
788
 
676
789
 
677
790
def main():
678
 
    global main_loop_started
679
 
    main_loop_started = False
680
 
    
681
791
    parser = OptionParser(version = "%%prog %s" % version)
682
792
    parser.add_option("-i", "--interface", type="string",
683
793
                      metavar="IF", help="Bind to interface IF")
698
808
                      default="/etc/mandos", metavar="DIR",
699
809
                      help="Directory to search for configuration"
700
810
                      " files")
701
 
    (options, args) = parser.parse_args()
 
811
    options = parser.parse_args()[0]
702
812
    
703
813
    if options.check:
704
814
        import doctest
758
868
    client_config.read(os.path.join(server_settings["configdir"],
759
869
                                    "clients.conf"))
760
870
    
 
871
    clients = Set()
 
872
    tcp_server = IPv6_TCPServer((server_settings["address"],
 
873
                                 server_settings["port"]),
 
874
                                TCP_handler,
 
875
                                settings=server_settings,
 
876
                                clients=clients)
 
877
    pidfilename = "/var/run/mandos.pid"
 
878
    try:
 
879
        pidfile = open(pidfilename, "w")
 
880
    except IOError, error:
 
881
        logger.error("Could not open file %r", pidfilename)
 
882
    
 
883
    uid = 65534
 
884
    gid = 65534
 
885
    try:
 
886
        uid = pwd.getpwnam("mandos").pw_uid
 
887
    except KeyError:
 
888
        try:
 
889
            uid = pwd.getpwnam("nobody").pw_uid
 
890
        except KeyError:
 
891
            pass
 
892
    try:
 
893
        gid = pwd.getpwnam("mandos").pw_gid
 
894
    except KeyError:
 
895
        try:
 
896
            gid = pwd.getpwnam("nogroup").pw_gid
 
897
        except KeyError:
 
898
            pass
 
899
    try:
 
900
        os.setuid(uid)
 
901
        os.setgid(gid)
 
902
    except OSError, error:
 
903
        if error[0] != errno.EPERM:
 
904
            raise error
 
905
    
761
906
    global service
762
907
    service = AvahiService(name = server_settings["servicename"],
763
 
                           type = "_mandos._tcp", );
 
908
                           servicetype = "_mandos._tcp", )
764
909
    if server_settings["interface"]:
765
910
        service.interface = if_nametoindex\
766
911
                            (server_settings["interface"])
777
922
                            avahi.DBUS_INTERFACE_SERVER)
778
923
    # End of Avahi example code
779
924
    
780
 
    clients = Set()
781
925
    def remove_from_clients(client):
782
926
        clients.remove(client)
783
927
        if not clients:
805
949
        # Close all input and output, do double fork, etc.
806
950
        daemon()
807
951
    
808
 
    pidfilename = "/var/run/mandos/mandos.pid"
809
 
    pid = os.getpid()
810
952
    try:
811
 
        pidfile = open(pidfilename, "w")
 
953
        pid = os.getpid()
812
954
        pidfile.write(str(pid) + "\n")
813
955
        pidfile.close()
814
956
        del pidfile
815
 
    except IOError, err:
816
 
        logger.error(u"Could not write %s file with PID %d",
817
 
                     pidfilename, os.getpid())
 
957
    except IOError:
 
958
        logger.error(u"Could not write to file %r with PID %d",
 
959
                     pidfilename, pid)
 
960
    except NameError:
 
961
        # "pidfile" was never created
 
962
        pass
 
963
    del pidfilename
818
964
    
819
965
    def cleanup():
820
966
        "Cleanup function; run on exit"
840
986
    for client in clients:
841
987
        client.start()
842
988
    
843
 
    tcp_server = IPv6_TCPServer((server_settings["address"],
844
 
                                 server_settings["port"]),
845
 
                                tcp_handler,
846
 
                                settings=server_settings,
847
 
                                clients=clients)
 
989
    tcp_server.enable()
 
990
    tcp_server.server_activate()
 
991
    
848
992
    # Find out what port we got
849
993
    service.port = tcp_server.socket.getsockname()[1]
850
994
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
868
1012
                             (*args[2:], **kwargs) or True)
869
1013
        
870
1014
        logger.debug(u"Starting main loop")
871
 
        main_loop_started = True
872
1015
        main_loop.run()
873
1016
    except AvahiError, error:
874
1017
        logger.critical(u"AvahiError: %s" + unicode(error))