/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

* mandos: Use all new builtins.
* mandos-ctl: - '' -
* mandos-monitor: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
68
68
import binascii
69
69
import tempfile
70
70
import itertools
71
 
import collections
72
71
 
73
72
import dbus
74
73
import dbus.service
89
88
    except ImportError:
90
89
        SO_BINDTODEVICE = None
91
90
 
92
 
version = "1.6.0"
 
91
version = "1.5.3"
93
92
stored_state_file = "clients.pickle"
94
93
 
95
94
logger = logging.getLogger()
152
151
    def __enter__(self):
153
152
        return self
154
153
    
155
 
    def __exit__(self, exc_type, exc_value, traceback):
 
154
    def __exit__ (self, exc_type, exc_value, traceback):
156
155
        self._cleanup()
157
156
        return False
158
157
    
380
379
                                 self.server_state_changed)
381
380
        self.server_state_changed(self.server.GetState())
382
381
 
383
 
 
384
382
class AvahiServiceToSyslog(AvahiService):
385
383
    def rename(self):
386
384
        """Add the new name to the syslog messages"""
391
389
                                .format(self.name)))
392
390
        return ret
393
391
 
394
 
 
395
392
def timedelta_to_milliseconds(td):
396
393
    "Convert a datetime.timedelta() to milliseconds"
397
394
    return ((td.days * 24 * 60 * 60 * 1000)
398
395
            + (td.seconds * 1000)
399
396
            + (td.microseconds // 1000))
400
397
 
401
 
 
402
398
class Client(object):
403
399
    """A representation of a client host served by this server.
404
400
    
443
439
    """
444
440
    
445
441
    runtime_expansions = ("approval_delay", "approval_duration",
446
 
                          "created", "enabled", "expires",
447
 
                          "fingerprint", "host", "interval",
448
 
                          "last_approval_request", "last_checked_ok",
 
442
                          "created", "enabled", "fingerprint",
 
443
                          "host", "interval", "last_checked_ok",
449
444
                          "last_enabled", "name", "timeout")
450
 
    client_defaults = { "timeout": "PT5M",
451
 
                        "extended_timeout": "PT15M",
452
 
                        "interval": "PT2M",
 
445
    client_defaults = { "timeout": "5m",
 
446
                        "extended_timeout": "15m",
 
447
                        "interval": "2m",
453
448
                        "checker": "fping -q -- %%(host)s",
454
449
                        "host": "",
455
 
                        "approval_delay": "PT0S",
456
 
                        "approval_duration": "PT1S",
 
450
                        "approval_delay": "0s",
 
451
                        "approval_duration": "1s",
457
452
                        "approved_by_default": "True",
458
453
                        "enabled": "True",
459
454
                        }
577
572
        if getattr(self, "enabled", False):
578
573
            # Already enabled
579
574
            return
 
575
        self.send_changedstate()
580
576
        self.expires = datetime.datetime.utcnow() + self.timeout
581
577
        self.enabled = True
582
578
        self.last_enabled = datetime.datetime.utcnow()
583
579
        self.init_checker()
584
 
        self.send_changedstate()
585
580
    
586
581
    def disable(self, quiet=True):
587
582
        """Disable this client."""
588
583
        if not getattr(self, "enabled", False):
589
584
            return False
590
585
        if not quiet:
 
586
            self.send_changedstate()
 
587
        if not quiet:
591
588
            logger.info("Disabling client %s", self.name)
592
 
        if getattr(self, "disable_initiator_tag", None) is not None:
 
589
        if getattr(self, "disable_initiator_tag", False):
593
590
            gobject.source_remove(self.disable_initiator_tag)
594
591
            self.disable_initiator_tag = None
595
592
        self.expires = None
596
 
        if getattr(self, "checker_initiator_tag", None) is not None:
 
593
        if getattr(self, "checker_initiator_tag", False):
597
594
            gobject.source_remove(self.checker_initiator_tag)
598
595
            self.checker_initiator_tag = None
599
596
        self.stop_checker()
600
597
        self.enabled = False
601
 
        if not quiet:
602
 
            self.send_changedstate()
603
598
        # Do not run this again if called by a gobject.timeout_add
604
599
        return False
605
600
    
609
604
    def init_checker(self):
610
605
        # Schedule a new checker to be started an 'interval' from now,
611
606
        # and every interval from then on.
612
 
        if self.checker_initiator_tag is not None:
613
 
            gobject.source_remove(self.checker_initiator_tag)
614
607
        self.checker_initiator_tag = (gobject.timeout_add
615
608
                                      (self.interval_milliseconds(),
616
609
                                       self.start_checker))
617
610
        # Schedule a disable() when 'timeout' has passed
618
 
        if self.disable_initiator_tag is not None:
619
 
            gobject.source_remove(self.disable_initiator_tag)
620
611
        self.disable_initiator_tag = (gobject.timeout_add
621
612
                                   (self.timeout_milliseconds(),
622
613
                                    self.disable))
653
644
            timeout = self.timeout
654
645
        if self.disable_initiator_tag is not None:
655
646
            gobject.source_remove(self.disable_initiator_tag)
656
 
            self.disable_initiator_tag = None
657
647
        if getattr(self, "enabled", False):
658
648
            self.disable_initiator_tag = (gobject.timeout_add
659
649
                                          (timedelta_to_milliseconds
692
682
                                      self.current_checker_command)
693
683
        # Start a new checker if needed
694
684
        if self.checker is None:
695
 
            # Escape attributes for the shell
696
 
            escaped_attrs = dict(
697
 
                (attr, re.escape(unicode(getattr(self, attr))))
698
 
                for attr in
699
 
                self.runtime_expansions)
700
685
            try:
701
 
                command = self.checker_command % escaped_attrs
702
 
            except TypeError as error:
703
 
                logger.error('Could not format string "%s"',
704
 
                             self.checker_command, exc_info=error)
705
 
                return True # Try again later
 
686
                # In case checker_command has exactly one % operator
 
687
                command = self.checker_command % self.host
 
688
            except TypeError:
 
689
                # Escape attributes for the shell
 
690
                escaped_attrs = dict(
 
691
                    (attr,
 
692
                     re.escape(unicode(str(getattr(self, attr, "")),
 
693
                                       errors=
 
694
                                       'replace')))
 
695
                    for attr in
 
696
                    self.runtime_expansions)
 
697
                
 
698
                try:
 
699
                    command = self.checker_command % escaped_attrs
 
700
                except TypeError as error:
 
701
                    logger.error('Could not format string "%s"',
 
702
                                 self.checker_command, exc_info=error)
 
703
                    return True # Try again later
706
704
            self.current_checker_command = command
707
705
            try:
708
706
                logger.info("Starting checker %r for %s",
714
712
                self.checker = subprocess.Popen(command,
715
713
                                                close_fds=True,
716
714
                                                shell=True, cwd="/")
 
715
                self.checker_callback_tag = (gobject.child_watch_add
 
716
                                             (self.checker.pid,
 
717
                                              self.checker_callback,
 
718
                                              data=command))
 
719
                # The checker may have completed before the gobject
 
720
                # watch was added.  Check for this.
 
721
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
722
                if pid:
 
723
                    gobject.source_remove(self.checker_callback_tag)
 
724
                    self.checker_callback(pid, status, command)
717
725
            except OSError as error:
718
726
                logger.error("Failed to start subprocess",
719
727
                             exc_info=error)
720
 
            self.checker_callback_tag = (gobject.child_watch_add
721
 
                                         (self.checker.pid,
722
 
                                          self.checker_callback,
723
 
                                          data=command))
724
 
            # The checker may have completed before the gobject
725
 
            # watch was added.  Check for this.
726
 
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
727
 
            if pid:
728
 
                gobject.source_remove(self.checker_callback_tag)
729
 
                self.checker_callback(pid, status, command)
730
728
        # Re-run this periodically if run by gobject.timeout_add
731
729
        return True
732
730
    
1017
1015
        return xmlstring
1018
1016
 
1019
1017
 
1020
 
def datetime_to_dbus(dt, variant_level=0):
 
1018
def datetime_to_dbus (dt, variant_level=0):
1021
1019
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1022
1020
    if dt is None:
1023
1021
        return dbus.String("", variant_level = variant_level)
1031
1029
    interface names according to the "alt_interface_names" mapping.
1032
1030
    Usage:
1033
1031
    
1034
 
    @alternate_dbus_interfaces({"org.example.Interface":
1035
 
                                    "net.example.AlternateInterface"})
 
1032
    @alternate_dbus_names({"org.example.Interface":
 
1033
                               "net.example.AlternateInterface"})
1036
1034
    class SampleDBusObject(dbus.service.Object):
1037
1035
        @dbus.service.method("org.example.Interface")
1038
1036
        def SampleDBusMethod():
1337
1335
        return False
1338
1336
    
1339
1337
    def approve(self, value=True):
 
1338
        self.send_changedstate()
1340
1339
        self.approved = value
1341
1340
        gobject.timeout_add(timedelta_to_milliseconds
1342
1341
                            (self.approval_duration),
1343
1342
                            self._reset_approved)
1344
 
        self.send_changedstate()
1345
1343
    
1346
1344
    ## D-Bus methods, signals & properties
1347
1345
    _interface = "se.recompile.Mandos.Client"
1531
1529
    def Timeout_dbus_property(self, value=None):
1532
1530
        if value is None:       # get
1533
1531
            return dbus.UInt64(self.timeout_milliseconds())
1534
 
        old_timeout = self.timeout
1535
1532
        self.timeout = datetime.timedelta(0, 0, 0, value)
1536
 
        # Reschedule disabling
 
1533
        # Reschedule timeout
1537
1534
        if self.enabled:
1538
1535
            now = datetime.datetime.utcnow()
1539
 
            self.expires += self.timeout - old_timeout
1540
 
            if self.expires <= now:
 
1536
            time_to_die = timedelta_to_milliseconds(
 
1537
                (self.last_checked_ok + self.timeout) - now)
 
1538
            if time_to_die <= 0:
1541
1539
                # The timeout has passed
1542
1540
                self.disable()
1543
1541
            else:
 
1542
                self.expires = (now +
 
1543
                                datetime.timedelta(milliseconds =
 
1544
                                                   time_to_die))
1544
1545
                if (getattr(self, "disable_initiator_tag", None)
1545
1546
                    is None):
1546
1547
                    return
1547
1548
                gobject.source_remove(self.disable_initiator_tag)
1548
 
                self.disable_initiator_tag = (
1549
 
                    gobject.timeout_add(
1550
 
                        timedelta_to_milliseconds(self.expires - now),
1551
 
                        self.disable))
 
1549
                self.disable_initiator_tag = (gobject.timeout_add
 
1550
                                              (time_to_die,
 
1551
                                               self.disable))
1552
1552
    
1553
1553
    # ExtendedTimeout - property
1554
1554
    @dbus_service_property(_interface, signature="t",
1742
1742
                    #wait until timeout or approved
1743
1743
                    time = datetime.datetime.now()
1744
1744
                    client.changedstate.acquire()
1745
 
                    client.changedstate.wait(
1746
 
                        float(timedelta_to_milliseconds(delay)
1747
 
                              / 1000))
 
1745
                    (client.changedstate.wait
 
1746
                     (float(client.timedelta_to_milliseconds(delay)
 
1747
                            / 1000)))
1748
1748
                    client.changedstate.release()
1749
1749
                    time2 = datetime.datetime.now()
1750
1750
                    if (time2 - time) >= delay:
1900
1900
        use_ipv6:       Boolean; to use IPv6 or not
1901
1901
    """
1902
1902
    def __init__(self, server_address, RequestHandlerClass,
1903
 
                 interface=None, use_ipv6=True, socketfd=None):
1904
 
        """If socketfd is set, use that file descriptor instead of
1905
 
        creating a new one with socket.socket().
1906
 
        """
 
1903
                 interface=None, use_ipv6=True):
1907
1904
        self.interface = interface
1908
1905
        if use_ipv6:
1909
1906
            self.address_family = socket.AF_INET6
1910
 
        if socketfd is not None:
1911
 
            # Save the file descriptor
1912
 
            self.socketfd = socketfd
1913
 
            # Save the original socket.socket() function
1914
 
            self.socket_socket = socket.socket
1915
 
            # To implement --socket, we monkey patch socket.socket.
1916
 
            # 
1917
 
            # (When socketserver.TCPServer is a new-style class, we
1918
 
            # could make self.socket into a property instead of monkey
1919
 
            # patching socket.socket.)
1920
 
            # 
1921
 
            # Create a one-time-only replacement for socket.socket()
1922
 
            @functools.wraps(socket.socket)
1923
 
            def socket_wrapper(*args, **kwargs):
1924
 
                # Restore original function so subsequent calls are
1925
 
                # not affected.
1926
 
                socket.socket = self.socket_socket
1927
 
                del self.socket_socket
1928
 
                # This time only, return a new socket object from the
1929
 
                # saved file descriptor.
1930
 
                return socket.fromfd(self.socketfd, *args, **kwargs)
1931
 
            # Replace socket.socket() function with wrapper
1932
 
            socket.socket = socket_wrapper
1933
 
        # The socketserver.TCPServer.__init__ will call
1934
 
        # socket.socket(), which might be our replacement,
1935
 
        # socket_wrapper(), if socketfd was set.
1936
1907
        socketserver.TCPServer.__init__(self, server_address,
1937
1908
                                        RequestHandlerClass)
1938
 
    
1939
1909
    def server_bind(self):
1940
1910
        """This overrides the normal server_bind() function
1941
1911
        to bind to an interface if one was specified, and also NOT to
1949
1919
                try:
1950
1920
                    self.socket.setsockopt(socket.SOL_SOCKET,
1951
1921
                                           SO_BINDTODEVICE,
1952
 
                                           str(self.interface + '\0'))
 
1922
                                           str(self.interface
 
1923
                                               + '\0'))
1953
1924
                except socket.error as error:
1954
 
                    if error.errno == errno.EPERM:
1955
 
                        logger.error("No permission to bind to"
1956
 
                                     " interface %s", self.interface)
1957
 
                    elif error.errno == errno.ENOPROTOOPT:
 
1925
                    if error[0] == errno.EPERM:
 
1926
                        logger.error("No permission to"
 
1927
                                     " bind to interface %s",
 
1928
                                     self.interface)
 
1929
                    elif error[0] == errno.ENOPROTOOPT:
1958
1930
                        logger.error("SO_BINDTODEVICE not available;"
1959
1931
                                     " cannot bind to interface %s",
1960
1932
                                     self.interface)
1961
 
                    elif error.errno == errno.ENODEV:
1962
 
                        logger.error("Interface %s does not exist,"
1963
 
                                     " cannot bind", self.interface)
1964
1933
                    else:
1965
1934
                        raise
1966
1935
        # Only bind(2) the socket if we really need to.
1996
1965
    """
1997
1966
    def __init__(self, server_address, RequestHandlerClass,
1998
1967
                 interface=None, use_ipv6=True, clients=None,
1999
 
                 gnutls_priority=None, use_dbus=True, socketfd=None):
 
1968
                 gnutls_priority=None, use_dbus=True):
2000
1969
        self.enabled = False
2001
1970
        self.clients = clients
2002
1971
        if self.clients is None:
2006
1975
        IPv6_TCPServer.__init__(self, server_address,
2007
1976
                                RequestHandlerClass,
2008
1977
                                interface = interface,
2009
 
                                use_ipv6 = use_ipv6,
2010
 
                                socketfd = socketfd)
 
1978
                                use_ipv6 = use_ipv6)
2011
1979
    def server_activate(self):
2012
1980
        if self.enabled:
2013
1981
            return socketserver.TCPServer.server_activate(self)
2091
2059
        return True
2092
2060
 
2093
2061
 
2094
 
def rfc3339_duration_to_delta(duration):
2095
 
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2096
 
    
2097
 
    >>> rfc3339_duration_to_delta("P7D")
2098
 
    datetime.timedelta(7)
2099
 
    >>> rfc3339_duration_to_delta("PT60S")
2100
 
    datetime.timedelta(0, 60)
2101
 
    >>> rfc3339_duration_to_delta("PT60M")
2102
 
    datetime.timedelta(0, 3600)
2103
 
    >>> rfc3339_duration_to_delta("PT24H")
2104
 
    datetime.timedelta(1)
2105
 
    >>> rfc3339_duration_to_delta("P1W")
2106
 
    datetime.timedelta(7)
2107
 
    >>> rfc3339_duration_to_delta("PT5M30S")
2108
 
    datetime.timedelta(0, 330)
2109
 
    >>> rfc3339_duration_to_delta("P1DT3M20S")
2110
 
    datetime.timedelta(1, 200)
2111
 
    """
2112
 
    
2113
 
    # Parsing an RFC 3339 duration with regular expressions is not
2114
 
    # possible - there would have to be multiple places for the same
2115
 
    # values, like seconds.  The current code, while more esoteric, is
2116
 
    # cleaner without depending on a parsing library.  If Python had a
2117
 
    # built-in library for parsing we would use it, but we'd like to
2118
 
    # avoid excessive use of external libraries.
2119
 
    
2120
 
    # New type for defining tokens, syntax, and semantics all-in-one
2121
 
    Token = collections.namedtuple("Token",
2122
 
                                   ("regexp", # To match token; if
2123
 
                                              # "value" is not None,
2124
 
                                              # must have a "group"
2125
 
                                              # containing digits
2126
 
                                    "value",  # datetime.timedelta or
2127
 
                                              # None
2128
 
                                    "followers")) # Tokens valid after
2129
 
                                                  # this token
2130
 
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
2131
 
    # the "duration" ABNF definition in RFC 3339, Appendix A.
2132
 
    token_end = Token(re.compile(r"$"), None, frozenset())
2133
 
    token_second = Token(re.compile(r"(\d+)S"),
2134
 
                         datetime.timedelta(seconds=1),
2135
 
                         frozenset((token_end,)))
2136
 
    token_minute = Token(re.compile(r"(\d+)M"),
2137
 
                         datetime.timedelta(minutes=1),
2138
 
                         frozenset((token_second, token_end)))
2139
 
    token_hour = Token(re.compile(r"(\d+)H"),
2140
 
                       datetime.timedelta(hours=1),
2141
 
                       frozenset((token_minute, token_end)))
2142
 
    token_time = Token(re.compile(r"T"),
2143
 
                       None,
2144
 
                       frozenset((token_hour, token_minute,
2145
 
                                  token_second)))
2146
 
    token_day = Token(re.compile(r"(\d+)D"),
2147
 
                      datetime.timedelta(days=1),
2148
 
                      frozenset((token_time, token_end)))
2149
 
    token_month = Token(re.compile(r"(\d+)M"),
2150
 
                        datetime.timedelta(weeks=4),
2151
 
                        frozenset((token_day, token_end)))
2152
 
    token_year = Token(re.compile(r"(\d+)Y"),
2153
 
                       datetime.timedelta(weeks=52),
2154
 
                       frozenset((token_month, token_end)))
2155
 
    token_week = Token(re.compile(r"(\d+)W"),
2156
 
                       datetime.timedelta(weeks=1),
2157
 
                       frozenset((token_end,)))
2158
 
    token_duration = Token(re.compile(r"P"), None,
2159
 
                           frozenset((token_year, token_month,
2160
 
                                      token_day, token_time,
2161
 
                                      token_week))),
2162
 
    # Define starting values
2163
 
    value = datetime.timedelta() # Value so far
2164
 
    found_token = None
2165
 
    followers = frozenset(token_duration,) # Following valid tokens
2166
 
    s = duration                # String left to parse
2167
 
    # Loop until end token is found
2168
 
    while found_token is not token_end:
2169
 
        # Search for any currently valid tokens
2170
 
        for token in followers:
2171
 
            match = token.regexp.match(s)
2172
 
            if match is not None:
2173
 
                # Token found
2174
 
                if token.value is not None:
2175
 
                    # Value found, parse digits
2176
 
                    factor = int(match.group(1), 10)
2177
 
                    # Add to value so far
2178
 
                    value += factor * token.value
2179
 
                # Strip token from string
2180
 
                s = token.regexp.sub("", s, 1)
2181
 
                # Go to found token
2182
 
                found_token = token
2183
 
                # Set valid next tokens
2184
 
                followers = found_token.followers
2185
 
                break
2186
 
        else:
2187
 
            # No currently valid tokens were found
2188
 
            raise ValueError("Invalid RFC 3339 duration")
2189
 
    # End token found
2190
 
    return value
2191
 
 
2192
 
 
2193
2062
def string_to_delta(interval):
2194
2063
    """Parse a string and return a datetime.timedelta
2195
2064
    
2206
2075
    >>> string_to_delta('5m 30s')
2207
2076
    datetime.timedelta(0, 330)
2208
2077
    """
2209
 
    
2210
 
    try:
2211
 
        return rfc3339_duration_to_delta(interval)
2212
 
    except ValueError:
2213
 
        pass
2214
 
    
2215
2078
    timevalue = datetime.timedelta(0)
2216
2079
    for s in interval.split():
2217
2080
        try:
2299
2162
    parser.add_argument("--no-restore", action="store_false",
2300
2163
                        dest="restore", help="Do not restore stored"
2301
2164
                        " state")
2302
 
    parser.add_argument("--socket", type=int,
2303
 
                        help="Specify a file descriptor to a network"
2304
 
                        " socket to use instead of creating one")
2305
2165
    parser.add_argument("--statedir", metavar="DIR",
2306
2166
                        help="Directory to save/restore state in")
2307
 
    parser.add_argument("--foreground", action="store_true",
2308
 
                        help="Run in foreground")
2309
2167
    
2310
2168
    options = parser.parse_args()
2311
2169
    
2326
2184
                        "use_ipv6": "True",
2327
2185
                        "debuglevel": "",
2328
2186
                        "restore": "True",
2329
 
                        "socket": "",
2330
 
                        "statedir": "/var/lib/mandos",
2331
 
                        "foreground": "False",
 
2187
                        "statedir": "/var/lib/mandos"
2332
2188
                        }
2333
2189
    
2334
2190
    # Parse config file for server-global settings
2339
2195
    # Convert the SafeConfigParser object to a dict
2340
2196
    server_settings = server_config.defaults()
2341
2197
    # Use the appropriate methods on the non-string config options
2342
 
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
 
2198
    for option in ("debug", "use_dbus", "use_ipv6"):
2343
2199
        server_settings[option] = server_config.getboolean("DEFAULT",
2344
2200
                                                           option)
2345
2201
    if server_settings["port"]:
2346
2202
        server_settings["port"] = server_config.getint("DEFAULT",
2347
2203
                                                       "port")
2348
 
    if server_settings["socket"]:
2349
 
        server_settings["socket"] = server_config.getint("DEFAULT",
2350
 
                                                         "socket")
2351
 
        # Later, stdin will, and stdout and stderr might, be dup'ed
2352
 
        # over with an opened os.devnull.  But we don't want this to
2353
 
        # happen with a supplied network socket.
2354
 
        if 0 <= server_settings["socket"] <= 2:
2355
 
            server_settings["socket"] = os.dup(server_settings
2356
 
                                               ["socket"])
2357
2204
    del server_config
2358
2205
    
2359
2206
    # Override the settings from the config file with command line
2361
2208
    for option in ("interface", "address", "port", "debug",
2362
2209
                   "priority", "servicename", "configdir",
2363
2210
                   "use_dbus", "use_ipv6", "debuglevel", "restore",
2364
 
                   "statedir", "socket", "foreground"):
 
2211
                   "statedir"):
2365
2212
        value = getattr(options, option)
2366
2213
        if value is not None:
2367
2214
            server_settings[option] = value
2370
2217
    for option in server_settings.keys():
2371
2218
        if type(server_settings[option]) is str:
2372
2219
            server_settings[option] = unicode(server_settings[option])
2373
 
    # Debug implies foreground
2374
 
    if server_settings["debug"]:
2375
 
        server_settings["foreground"] = True
2376
2220
    # Now we have our good server settings in "server_settings"
2377
2221
    
2378
2222
    ##################################################################
2384
2228
    use_ipv6 = server_settings["use_ipv6"]
2385
2229
    stored_state_path = os.path.join(server_settings["statedir"],
2386
2230
                                     stored_state_file)
2387
 
    foreground = server_settings["foreground"]
2388
2231
    
2389
2232
    if debug:
2390
2233
        initlogger(debug, logging.DEBUG)
2419
2262
                              use_ipv6=use_ipv6,
2420
2263
                              gnutls_priority=
2421
2264
                              server_settings["priority"],
2422
 
                              use_dbus=use_dbus,
2423
 
                              socketfd=(server_settings["socket"]
2424
 
                                        or None))
2425
 
    if not foreground:
 
2265
                              use_dbus=use_dbus)
 
2266
    if not debug:
2426
2267
        pidfilename = "/var/run/mandos.pid"
2427
 
        pidfile = None
2428
2268
        try:
2429
2269
            pidfile = open(pidfilename, "w")
2430
2270
        except IOError as e:
2445
2285
        os.setgid(gid)
2446
2286
        os.setuid(uid)
2447
2287
    except OSError as error:
2448
 
        if error.errno != errno.EPERM:
 
2288
        if error[0] != errno.EPERM:
2449
2289
            raise error
2450
2290
    
2451
2291
    if debug:
2469
2309
            os.close(null)
2470
2310
    
2471
2311
    # Need to fork before connecting to D-Bus
2472
 
    if not foreground:
 
2312
    if not debug:
2473
2313
        # Close all input and output, do double fork, etc.
2474
2314
        daemon()
2475
2315
    
2476
 
    # multiprocessing will use threads, so before we use gobject we
2477
 
    # need to inform gobject that threads will be used.
2478
2316
    gobject.threads_init()
2479
2317
    
2480
2318
    global main_loop
2608
2446
    if not tcp_server.clients:
2609
2447
        logger.warning("No clients defined")
2610
2448
    
2611
 
    if not foreground:
2612
 
        if pidfile is not None:
2613
 
            try:
2614
 
                with pidfile:
2615
 
                    pid = os.getpid()
2616
 
                    pidfile.write(str(pid) + "\n".encode("utf-8"))
2617
 
            except IOError:
2618
 
                logger.error("Could not write to file %r with PID %d",
2619
 
                             pidfilename, pid)
2620
 
        del pidfile
 
2449
    if not debug:
 
2450
        try:
 
2451
            with pidfile:
 
2452
                pid = os.getpid()
 
2453
                pidfile.write(str(pid) + "\n".encode("utf-8"))
 
2454
            del pidfile
 
2455
        except IOError:
 
2456
            logger.error("Could not write to file %r with PID %d",
 
2457
                         pidfilename, pid)
 
2458
        except NameError:
 
2459
            # "pidfile" was never created
 
2460
            pass
2621
2461
        del pidfilename
 
2462
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2622
2463
    
2623
2464
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2624
2465
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())