151
151
    def __enter__(self):
 
154
 
    def __exit__ (self, exc_type, exc_value, traceback):
 
 
154
    def __exit__(self, exc_type, exc_value, traceback):
 
 
389
390
                                .format(self.name)))
 
392
394
def timedelta_to_milliseconds(td):
 
393
395
    "Convert a datetime.timedelta() to milliseconds"
 
394
396
    return ((td.days * 24 * 60 * 60 * 1000)
 
395
397
            + (td.seconds * 1000)
 
396
398
            + (td.microseconds // 1000))
 
398
401
class Client(object):
 
399
402
    """A representation of a client host served by this server.
 
 
441
444
    runtime_expansions = ("approval_delay", "approval_duration",
 
442
 
                          "created", "enabled", "fingerprint",
 
443
 
                          "host", "interval", "last_checked_ok",
 
 
445
                          "created", "enabled", "expires",
 
 
446
                          "fingerprint", "host", "interval",
 
 
447
                          "last_approval_request", "last_checked_ok",
 
444
448
                          "last_enabled", "name", "timeout")
 
445
449
    client_defaults = { "timeout": "5m",
 
446
450
                        "extended_timeout": "15m",
 
 
687
691
                                      self.current_checker_command)
 
688
692
        # Start a new checker if needed
 
689
693
        if self.checker is None:
 
 
694
            # Escape attributes for the shell
 
 
695
            escaped_attrs = dict(
 
 
696
                (attr, re.escape(unicode(getattr(self, attr))))
 
 
698
                self.runtime_expansions)
 
691
 
                # In case checker_command has exactly one % operator
 
692
 
                command = self.checker_command % self.host
 
694
 
                # Escape attributes for the shell
 
695
 
                escaped_attrs = dict(
 
697
 
                     re.escape(unicode(str(getattr(self, attr, "")),
 
701
 
                    self.runtime_expansions)
 
704
 
                    command = self.checker_command % escaped_attrs
 
705
 
                except TypeError as error:
 
706
 
                    logger.error('Could not format string "%s"',
 
707
 
                                 self.checker_command, exc_info=error)
 
708
 
                    return True # Try again later
 
 
700
                command = self.checker_command % escaped_attrs
 
 
701
            except TypeError as error:
 
 
702
                logger.error('Could not format string "%s"',
 
 
703
                             self.checker_command, exc_info=error)
 
 
704
                return True # Try again later
 
709
705
            self.current_checker_command = command
 
711
707
                logger.info("Starting checker %r for %s",
 
 
717
713
                self.checker = subprocess.Popen(command,
 
719
715
                                                shell=True, cwd="/")
 
720
 
                self.checker_callback_tag = (gobject.child_watch_add
 
722
 
                                              self.checker_callback,
 
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)
 
728
 
                    gobject.source_remove(self.checker_callback_tag)
 
729
 
                    self.checker_callback(pid, status, command)
 
730
716
            except OSError as error:
 
731
717
                logger.error("Failed to start subprocess",
 
 
719
            self.checker_callback_tag = (gobject.child_watch_add
 
 
721
                                          self.checker_callback,
 
 
723
            # The checker may have completed before the gobject
 
 
724
            # watch was added.  Check for this.
 
 
725
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
 
727
                gobject.source_remove(self.checker_callback_tag)
 
 
728
                self.checker_callback(pid, status, command)
 
733
729
        # Re-run this periodically if run by gobject.timeout_add
 
 
1020
1016
        return xmlstring
 
1023
 
def datetime_to_dbus (dt, variant_level=0):
 
 
1019
def datetime_to_dbus(dt, variant_level=0):
 
1024
1020
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
1026
1022
        return dbus.String("", variant_level = variant_level)
 
 
1034
1030
    interface names according to the "alt_interface_names" mapping.
 
1037
 
    @alternate_dbus_names({"org.example.Interface":
 
1038
 
                               "net.example.AlternateInterface"})
 
 
1033
    @alternate_dbus_interfaces({"org.example.Interface":
 
 
1034
                                    "net.example.AlternateInterface"})
 
1039
1035
    class SampleDBusObject(dbus.service.Object):
 
1040
1036
        @dbus.service.method("org.example.Interface")
 
1041
1037
        def SampleDBusMethod():
 
 
1903
1899
        use_ipv6:       Boolean; to use IPv6 or not
 
1905
1901
    def __init__(self, server_address, RequestHandlerClass,
 
1906
 
                 interface=None, use_ipv6=True):
 
 
1902
                 interface=None, use_ipv6=True, socketfd=None):
 
 
1903
        """If socketfd is set, use that file descriptor instead of
 
 
1904
        creating a new one with socket.socket().
 
1907
1906
        self.interface = interface
 
1909
1908
            self.address_family = socket.AF_INET6
 
 
1909
        if socketfd is not None:
 
 
1910
            # Save the file descriptor
 
 
1911
            self.socketfd = socketfd
 
 
1912
            # Save the original socket.socket() function
 
 
1913
            self.socket_socket = socket.socket
 
 
1914
            # To implement --socket, we monkey patch socket.socket.
 
 
1916
            # (When socketserver.TCPServer is a new-style class, we
 
 
1917
            # could make self.socket into a property instead of monkey
 
 
1918
            # patching socket.socket.)
 
 
1920
            # Create a one-time-only replacement for socket.socket()
 
 
1921
            @functools.wraps(socket.socket)
 
 
1922
            def socket_wrapper(*args, **kwargs):
 
 
1923
                # Restore original function so subsequent calls are
 
 
1925
                socket.socket = self.socket_socket
 
 
1926
                del self.socket_socket
 
 
1927
                # This time only, return a new socket object from the
 
 
1928
                # saved file descriptor.
 
 
1929
                return socket.fromfd(self.socketfd, *args, **kwargs)
 
 
1930
            # Replace socket.socket() function with wrapper
 
 
1931
            socket.socket = socket_wrapper
 
 
1932
        # The socketserver.TCPServer.__init__ will call
 
 
1933
        # socket.socket(), which might be our replacement,
 
 
1934
        # socket_wrapper(), if socketfd was set.
 
1910
1935
        socketserver.TCPServer.__init__(self, server_address,
 
1911
1936
                                        RequestHandlerClass)
 
1912
1938
    def server_bind(self):
 
1913
1939
        """This overrides the normal server_bind() function
 
1914
1940
        to bind to an interface if one was specified, and also NOT to
 
 
1925
1951
                                           str(self.interface
 
1927
1953
                except socket.error as error:
 
1928
 
                    if error[0] == errno.EPERM:
 
 
1954
                    if error.errno == errno.EPERM:
 
1929
1955
                        logger.error("No permission to"
 
1930
1956
                                     " bind to interface %s",
 
1931
1957
                                     self.interface)
 
1932
 
                    elif error[0] == errno.ENOPROTOOPT:
 
 
1958
                    elif error.errno == errno.ENOPROTOOPT:
 
1933
1959
                        logger.error("SO_BINDTODEVICE not available;"
 
1934
1960
                                     " cannot bind to interface %s",
 
1935
1961
                                     self.interface)
 
 
1962
                    elif error.errno == errno.ENODEV:
 
 
1963
                        logger.error("Interface %s does not"
 
 
1964
                                     " exist, cannot bind",
 
1938
1968
        # Only bind(2) the socket if we really need to.
 
 
1969
1999
    def __init__(self, server_address, RequestHandlerClass,
 
1970
2000
                 interface=None, use_ipv6=True, clients=None,
 
1971
 
                 gnutls_priority=None, use_dbus=True):
 
 
2001
                 gnutls_priority=None, use_dbus=True, socketfd=None):
 
1972
2002
        self.enabled = False
 
1973
2003
        self.clients = clients
 
1974
2004
        if self.clients is None:
 
 
2165
2196
    parser.add_argument("--no-restore", action="store_false",
 
2166
2197
                        dest="restore", help="Do not restore stored"
 
 
2199
    parser.add_argument("--socket", type=int,
 
 
2200
                        help="Specify a file descriptor to a network"
 
 
2201
                        " socket to use instead of creating one")
 
2168
2202
    parser.add_argument("--statedir", metavar="DIR",
 
2169
2203
                        help="Directory to save/restore state in")
 
 
2204
2239
    if server_settings["port"]:
 
2205
2240
        server_settings["port"] = server_config.getint("DEFAULT",
 
 
2242
    if server_settings["socket"]:
 
 
2243
        server_settings["socket"] = server_config.getint("DEFAULT",
 
 
2245
        # Later, stdin will, and stdout and stderr might, be dup'ed
 
 
2246
        # over with an opened os.devnull.  But we don't want this to
 
 
2247
        # happen with a supplied network socket.
 
 
2248
        if 0 <= server_settings["socket"] <= 2:
 
 
2249
            server_settings["socket"] = os.dup(server_settings
 
2207
2251
    del server_config
 
2209
2253
    # Override the settings from the config file with command line
 
 
2211
2255
    for option in ("interface", "address", "port", "debug",
 
2212
2256
                   "priority", "servicename", "configdir",
 
2213
2257
                   "use_dbus", "use_ipv6", "debuglevel", "restore",
 
 
2258
                   "statedir", "socket"):
 
2215
2259
        value = getattr(options, option)
 
2216
2260
        if value is not None:
 
2217
2261
            server_settings[option] = value
 
 
2316
2362
        # Close all input and output, do double fork, etc.
 
 
2365
    # multiprocessing will use threads, so before we use gobject we
 
 
2366
    # need to inform gobject that threads will be used.
 
2319
2367
    gobject.threads_init()
 
2321
2369
    global main_loop
 
 
2462
2510
            # "pidfile" was never created
 
2464
2512
        del pidfilename
 
2465
 
        signal.signal(signal.SIGINT, signal.SIG_IGN)
 
2467
2514
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
 
2468
2515
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())