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())