=== modified file 'TODO' --- TODO 2008-12-21 19:19:25 +0000 +++ TODO 2008-12-29 02:44:54 +0000 @@ -17,8 +17,6 @@ ** TODO [#B] Run-time communication with server :bugs: Probably using D-Bus See also [[*Mandos-tools]] -** Handle non-existing D-Bus server. - Also, possibly a "--no-dbus" option? *** Client class *** Main server + SetLogLevel @@ -35,8 +33,13 @@ This will not be strictly necessary when the D-Bus interface is implemented. +* mandos.xml +** [[file:mandos.xml::XXX][Document D-Bus interface]] + +* Provide and install /etc/dbus-1/system.d/mandos.conf + * mandos-list -*** Handle no D-Bus server and/or no Mandos server found better +*** Handle "no D-Bus server" and/or "no Mandos server found" better *** [#B] --dump option ** TODO Disable client ** TODO Enable client === modified file 'mandos' --- mandos 2008-12-28 10:35:32 +0000 +++ mandos 2008-12-29 02:44:54 +0000 @@ -201,57 +201,37 @@ client lives. %() expansions are done at runtime with vars(self) as dict, so that for instance %(name)s can be used in the command. - dbus_object_path: dbus.ObjectPath - Private attibutes: - _timeout: Real variable for 'timeout' - _interval: Real variable for 'interval' - _timeout_milliseconds: Used when calling gobject.timeout_add() - _interval_milliseconds: - '' - + use_dbus: bool(); Whether to provide D-Bus interface and signals + dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus """ - def _set_timeout(self, timeout): - "Setter function for the 'timeout' attribute" - self._timeout = timeout - self._timeout_milliseconds = ((self.timeout.days - * 24 * 60 * 60 * 1000) - + (self.timeout.seconds * 1000) - + (self.timeout.microseconds - // 1000)) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"timeout"), - (dbus.UInt64(self._timeout_milliseconds, - variant_level=1))) - timeout = property(lambda self: self._timeout, _set_timeout) - del _set_timeout - - def _set_interval(self, interval): - "Setter function for the 'interval' attribute" - self._interval = interval - self._interval_milliseconds = ((self.interval.days - * 24 * 60 * 60 * 1000) - + (self.interval.seconds - * 1000) - + (self.interval.microseconds - // 1000)) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"interval"), - (dbus.UInt64(self._interval_milliseconds, - variant_level=1))) - interval = property(lambda self: self._interval, _set_interval) - del _set_interval - - def __init__(self, name = None, disable_hook=None, config=None): + def timeout_milliseconds(self): + "Return the 'timeout' attribute in milliseconds" + return ((self.timeout.days * 24 * 60 * 60 * 1000) + + (self.timeout.seconds * 1000) + + (self.timeout.microseconds // 1000)) + + def interval_milliseconds(self): + "Return the 'interval' attribute in milliseconds" + return ((self.interval.days * 24 * 60 * 60 * 1000) + + (self.interval.seconds * 1000) + + (self.interval.microseconds // 1000)) + + def __init__(self, name = None, disable_hook=None, config=None, + use_dbus=True): """Note: the 'checker' key in 'config' sets the 'checker_command' attribute and *not* the 'checker' attribute.""" - self.dbus_object_path = (dbus.ObjectPath - ("/Mandos/clients/" - + name.replace(".", "_"))) - dbus.service.Object.__init__(self, bus, - self.dbus_object_path) + self.name = name if config is None: config = {} - self.name = name logger.debug(u"Creating client %r", self.name) + self.use_dbus = use_dbus + if self.use_dbus: + self.dbus_object_path = (dbus.ObjectPath + ("/Mandos/clients/" + + self.name.replace(".", "_"))) + dbus.service.Object.__init__(self, bus, + self.dbus_object_path) # Uppercase and remove spaces from fingerprint for later # comparison purposes with return value from the fingerprint() # function @@ -288,21 +268,22 @@ # Schedule a new checker to be started an 'interval' from now, # and every interval from then on. self.checker_initiator_tag = (gobject.timeout_add - (self._interval_milliseconds, + (self.interval_milliseconds(), self.start_checker)) # Also start a new checker *right now*. self.start_checker() # Schedule a disable() when 'timeout' has passed self.disable_initiator_tag = (gobject.timeout_add - (self._timeout_milliseconds, + (self.timeout_milliseconds(), self.disable)) self.enabled = True - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"enabled"), - dbus.Boolean(True, variant_level=1)) - self.PropertyChanged(dbus.String(u"last_enabled"), - (_datetime_to_dbus(self.last_enabled, - variant_level=1))) + if self.use_dbus: + # Emit D-Bus signals + self.PropertyChanged(dbus.String(u"enabled"), + dbus.Boolean(True, variant_level=1)) + self.PropertyChanged(dbus.String(u"last_enabled"), + (_datetime_to_dbus(self.last_enabled, + variant_level=1))) def disable(self): """Disable this client.""" @@ -319,9 +300,10 @@ if self.disable_hook: self.disable_hook(self) self.enabled = False - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"enabled"), - dbus.Boolean(False, variant_level=1)) + if self.use_dbus: + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"enabled"), + dbus.Boolean(False, variant_level=1)) # Do not run this again if called by a gobject.timeout_add return False @@ -333,32 +315,36 @@ """The checker has completed, so take appropriate actions.""" self.checker_callback_tag = None self.checker = None - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"checker_running"), - dbus.Boolean(False, variant_level=1)) + if self.use_dbus: + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"checker_running"), + dbus.Boolean(False, variant_level=1)) if (os.WIFEXITED(condition) and (os.WEXITSTATUS(condition) == 0)): logger.info(u"Checker for %(name)s succeeded", vars(self)) - # Emit D-Bus signal - self.CheckerCompleted(dbus.Boolean(True), - dbus.UInt16(condition), - dbus.String(command)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(True), + dbus.UInt16(condition), + dbus.String(command)) self.bump_timeout() elif not os.WIFEXITED(condition): logger.warning(u"Checker for %(name)s crashed?", vars(self)) - # Emit D-Bus signal - self.CheckerCompleted(dbus.Boolean(False), - dbus.UInt16(condition), - dbus.String(command)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(False), + dbus.UInt16(condition), + dbus.String(command)) else: logger.info(u"Checker for %(name)s failed", vars(self)) - # Emit D-Bus signal - self.CheckerCompleted(dbus.Boolean(False), - dbus.UInt16(condition), - dbus.String(command)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerCompleted(dbus.Boolean(False), + dbus.UInt16(condition), + dbus.String(command)) def bump_timeout(self): """Bump up the timeout for this client. @@ -368,11 +354,14 @@ self.last_checked_ok = datetime.datetime.utcnow() gobject.source_remove(self.disable_initiator_tag) self.disable_initiator_tag = (gobject.timeout_add - (self._timeout_milliseconds, + (self.timeout_milliseconds(), self.disable)) - self.PropertyChanged(dbus.String(u"last_checked_ok"), - (_datetime_to_dbus(self.last_checked_ok, - variant_level=1))) + if self.use_dbus: + # Emit D-Bus signal + self.PropertyChanged( + dbus.String(u"last_checked_ok"), + (_datetime_to_dbus(self.last_checked_ok, + variant_level=1))) def start_checker(self): """Start a new checker subprocess if one is not running. @@ -411,10 +400,12 @@ self.checker = subprocess.Popen(command, close_fds=True, shell=True, cwd="/") - # Emit D-Bus signal - self.CheckerStarted(command) - self.PropertyChanged(dbus.String("checker_running"), - dbus.Boolean(True, variant_level=1)) + if self.use_dbus: + # Emit D-Bus signal + self.CheckerStarted(command) + self.PropertyChanged( + dbus.String("checker_running"), + dbus.Boolean(True, variant_level=1)) self.checker_callback_tag = (gobject.child_watch_add (self.checker.pid, self.checker_callback, @@ -442,8 +433,9 @@ if error.errno != errno.ESRCH: # No such process raise self.checker = None - self.PropertyChanged(dbus.String(u"checker_running"), - dbus.Boolean(False, variant_level=1)) + if self.use_dbus: + self.PropertyChanged(dbus.String(u"checker_running"), + dbus.Boolean(False, variant_level=1)) def still_valid(self): """Has the timeout not yet passed for this client?""" @@ -500,10 +492,10 @@ if self.last_checked_ok is not None else dbus.Boolean (False, variant_level=1)), dbus.String("timeout"): - dbus.UInt64(self._timeout_milliseconds, + dbus.UInt64(self.timeout_milliseconds(), variant_level=1), dbus.String("interval"): - dbus.UInt64(self._interval_milliseconds, + dbus.UInt64(self.interval_milliseconds(), variant_level=1), dbus.String("checker"): dbus.String(self.checker_command, @@ -529,17 +521,28 @@ def SetChecker(self, checker): "D-Bus setter method" self.checker_command = checker + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"checker"), + dbus.String(self.checker_command, + variant_level=1)) # SetHost - method @dbus.service.method(_interface, in_signature="s") def SetHost(self, host): "D-Bus setter method" self.host = host + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"host"), + dbus.String(self.host, variant_level=1)) # SetInterval - method @dbus.service.method(_interface, in_signature="t") def SetInterval(self, milliseconds): - self.interval = datetime.timdeelta(0, 0, 0, milliseconds) + self.interval = datetime.timedelta(0, 0, 0, milliseconds) + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"interval"), + (dbus.UInt64(self.interval_milliseconds(), + variant_level=1))) # SetSecret - method @dbus.service.method(_interface, in_signature="ay", @@ -552,6 +555,10 @@ @dbus.service.method(_interface, in_signature="t") def SetTimeout(self, milliseconds): self.timeout = datetime.timedelta(0, 0, 0, milliseconds) + # Emit D-Bus signal + self.PropertyChanged(dbus.String(u"timeout"), + (dbus.UInt64(self.timeout_milliseconds(), + variant_level=1))) # Enable - method Enable = dbus.service.method(_interface)(enable) @@ -889,7 +896,7 @@ help="Address to listen for requests on") parser.add_option("-p", "--port", type="int", help="Port number to receive requests on") - parser.add_option("--check", action="store_true", default=False, + parser.add_option("--check", action="store_true", help="Run self-test") parser.add_option("--debug", action="store_true", help="Debug mode; run in foreground and log to" @@ -902,6 +909,10 @@ default="/etc/mandos", metavar="DIR", help="Directory to search for configuration" " files") + parser.add_option("--no-dbus", action="store_false", + dest="use_dbus", + help="Do not provide D-Bus system bus" + " interface") options = parser.parse_args()[0] if options.check: @@ -917,6 +928,7 @@ "priority": "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", "servicename": "Mandos", + "use_dbus": "True", } # Parse config file for server-global settings @@ -925,22 +937,27 @@ server_config.read(os.path.join(options.configdir, "mandos.conf")) # Convert the SafeConfigParser object to a dict server_settings = server_config.defaults() - # Use getboolean on the boolean config option + # Use getboolean on the boolean config options server_settings["debug"] = (server_config.getboolean ("DEFAULT", "debug")) + server_settings["use_dbus"] = (server_config.getboolean + ("DEFAULT", "use_dbus")) del server_config # Override the settings from the config file with command line # options, if set. for option in ("interface", "address", "port", "debug", - "priority", "servicename", "configdir"): + "priority", "servicename", "configdir", + "use_dbus"): value = getattr(options, option) if value is not None: server_settings[option] = value del options # Now we have our good server settings in "server_settings" + # For convenience debug = server_settings["debug"] + use_dbus = server_settings["use_dbus"] if not debug: syslogger.setLevel(logging.WARNING) @@ -1019,11 +1036,14 @@ avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) # End of Avahi example code - bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", bus) + if use_dbus: + bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", + bus) clients.update(Set(Client(name = section, config - = dict(client_config.items(section))) + = dict(client_config.items(section)), + use_dbus = use_dbus) for section in client_config.sections())) if not clients: logger.critical(u"No clients defined") @@ -1075,51 +1095,57 @@ signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit()) signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) - class MandosServer(dbus.service.Object): - """A D-Bus proxy object""" - def __init__(self): - dbus.service.Object.__init__(self, bus, - "/Mandos") - _interface = u"org.mandos_system.Mandos" - - @dbus.service.signal(_interface, signature="oa{sv}") - def ClientAdded(self, objpath, properties): - "D-Bus signal" - pass - - @dbus.service.signal(_interface, signature="o") - def ClientRemoved(self, objpath): - "D-Bus signal" - pass - - @dbus.service.method(_interface, out_signature="ao") - def GetAllClients(self): - return dbus.Array(c.dbus_object_path for c in clients) - - @dbus.service.method(_interface, out_signature="a{oa{sv}}") - def GetAllClientsWithProperties(self): - return dbus.Dictionary( - ((c.dbus_object_path, c.GetAllProperties()) - for c in clients), - signature="oa{sv}") - - @dbus.service.method(_interface, in_signature="o") - def RemoveClient(self, object_path): - for c in clients: - if c.dbus_object_path == object_path: - c.disable() - clients.remove(c) - return - raise KeyError - - del _interface + if use_dbus: + class MandosServer(dbus.service.Object): + """A D-Bus proxy object""" + def __init__(self): + dbus.service.Object.__init__(self, bus, + "/Mandos") + _interface = u"org.mandos_system.Mandos" + + @dbus.service.signal(_interface, signature="oa{sv}") + def ClientAdded(self, objpath, properties): + "D-Bus signal" + pass + + @dbus.service.signal(_interface, signature="o") + def ClientRemoved(self, objpath): + "D-Bus signal" + pass + + @dbus.service.method(_interface, out_signature="ao") + def GetAllClients(self): + return dbus.Array(c.dbus_object_path for c in clients) + + @dbus.service.method(_interface, out_signature="a{oa{sv}}") + def GetAllClientsWithProperties(self): + return dbus.Dictionary( + ((c.dbus_object_path, c.GetAllProperties()) + for c in clients), + signature="oa{sv}") + + @dbus.service.method(_interface, in_signature="o") + def RemoveClient(self, object_path): + for c in clients: + if c.dbus_object_path == object_path: + clients.remove(c) + # Don't signal anything except ClientRemoved + c.use_dbus = False + c.disable() + # Emit D-Bus signal + self.ClientRemoved(object_path) + return + raise KeyError + + del _interface - mandos_server = MandosServer() + mandos_server = MandosServer() for client in clients: - # Emit D-Bus signal - mandos_server.ClientAdded(client.dbus_object_path, - client.GetAllProperties()) + if use_dbus: + # Emit D-Bus signal + mandos_server.ClientAdded(client.dbus_object_path, + client.GetAllProperties()) client.enable() tcp_server.enable() === modified file 'mandos-options.xml' --- mandos-options.xml 2008-09-30 03:19:39 +0000 +++ mandos-options.xml 2008-12-29 02:44:54 +0000 @@ -65,5 +65,11 @@ rename itself to Mandos #2, and so on; therefore, this option is not needed in that case. + + + This option controls whether the server will provide a D-Bus + system bus interface. The default is to provide such an + interface. + === modified file 'mandos.conf' --- mandos.conf 2008-08-18 23:55:28 +0000 +++ mandos.conf 2008-12-29 02:44:54 +0000 @@ -36,3 +36,6 @@ # If there are name collisions on the same *network*, the server will # rename itself to "Mandos #2", etc. ;servicename = Mandos + +# Whether to provide a D-Bus system bus interface or not +;use_dbus = True === modified file 'mandos.conf.xml' --- mandos.conf.xml 2008-09-30 07:23:39 +0000 +++ mandos.conf.xml 2008-12-29 02:44:54 +0000 @@ -3,7 +3,7 @@ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ /etc/mandos/mandos.conf"> - + %common; ]> @@ -130,6 +130,17 @@ + + + + + + + @@ -172,6 +183,7 @@ debug = true priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP servicename = Daena +use_dbus = False === modified file 'mandos.xml' --- mandos.xml 2008-10-04 01:55:56 +0000 +++ mandos.xml 2008-12-29 02:44:54 +0000 @@ -2,7 +2,7 @@ - + %common; ]> @@ -84,6 +84,8 @@ DIRECTORY + + &COMMANDNAME; @@ -228,6 +230,15 @@ + + + + + + + See also . + + @@ -323,6 +334,16 @@ + + D-BUS INTERFACE + + The server will by default provide a D-Bus system bus interface. + This interface will only be accessible by the root user or a + Mandos-specific user, if such a user exists. + + + + EXIT STATUS