]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: dbus: track bus names per unit 894/head
authorDaniel Mack <daniel@zonque.org>
Wed, 5 Aug 2015 15:47:45 +0000 (17:47 +0200)
committerDaniel Mack <daniel@zonque.org>
Thu, 6 Aug 2015 08:14:41 +0000 (10:14 +0200)
Currently, PID1 installs an unfiltered NameOwnerChanged signal match, and
dispatches the signals itself. This does not scale, as right now, PID1
wakes up every time a bus client connects.

To fix this, install individual matches once they are requested by
unit_watch_bus_name(), and remove the watches again through their slot in
unit_unwatch_bus_name().

If the bus is not available during unit_watch_bus_name(), just store
name in the 'watch_bus' hashmap, and let bus_setup_api() do the installing
later.

src/core/dbus.c
src/core/manager.c
src/core/manager.h
src/core/unit.c
src/core/unit.h

index 057653a8b5f2faaa9bec61ea7ee723b56613009e..44bf5cab285baa169d5ce32c563ad92fc1b98792 100644 (file)
@@ -140,28 +140,6 @@ static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_e
         return 0;
 }
 
-static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        const char *name, *old_owner, *new_owner;
-        Manager *m = userdata;
-        int r;
-
-        assert(message);
-        assert(m);
-
-        r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
-        if (r < 0) {
-                bus_log_parse_error(r);
-                return 0;
-        }
-
-        manager_dispatch_bus_name_owner_changed(
-                        m, name,
-                        isempty(old_owner) ? NULL : old_owner,
-                        isempty(new_owner) ? NULL : new_owner);
-
-        return 0;
-}
-
 static int signal_activation_request(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
@@ -762,13 +740,21 @@ static int bus_list_names(Manager *m, sd_bus *bus) {
         /* This is a bit hacky, we say the owner of the name is the
          * name itself, because we don't want the extra traffic to
          * figure out the real owner. */
-        STRV_FOREACH(i, names)
-                manager_dispatch_bus_name_owner_changed(m, *i, NULL, *i);
+        STRV_FOREACH(i, names) {
+                Unit *u;
+
+                u = hashmap_get(m->watch_bus, *i);
+                if (u)
+                        UNIT_VTABLE(u)->bus_name_owner_change(u, *i, NULL, *i);
+        }
 
         return 0;
 }
 
 static int bus_setup_api(Manager *m, sd_bus *bus) {
+        Iterator i;
+        char *name;
+        Unit *u;
         int r;
 
         assert(m);
@@ -786,17 +772,11 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_add_match(
-                        bus,
-                        NULL,
-                        "type='signal',"
-                        "sender='org.freedesktop.DBus',"
-                        "path='/org/freedesktop/DBus',"
-                        "interface='org.freedesktop.DBus',"
-                        "member='NameOwnerChanged'",
-                        signal_name_owner_changed, m);
-        if (r < 0)
-                log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+        HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
+                r = unit_install_bus_match(bus, u, name);
+                if (r < 0)
+                        log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+        }
 
         r = sd_bus_add_match(
                         bus,
index ba107d461568d1f48f41ca9603f6a08f901476c7..ecea89c377a6145bb6764766469371a8303f3bb9 100644 (file)
@@ -2187,24 +2187,6 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
                         log_error_errno(errno, "Failed to write Plymouth message: %m");
 }
 
-void manager_dispatch_bus_name_owner_changed(
-                Manager *m,
-                const char *name,
-                const char* old_owner,
-                const char *new_owner) {
-
-        Unit *u;
-
-        assert(m);
-        assert(name);
-
-        u = hashmap_get(m->watch_bus, name);
-        if (!u)
-                return;
-
-        UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
-}
-
 int manager_open_serialization(Manager *m, FILE **_f) {
         const char *path;
         int fd = -1;
index 4ef869d14aae7eb8191176a4412e6184e4209e02..1e01f2bdef0145b012eb5a6a11b83df7cbcffc5c 100644 (file)
@@ -329,8 +329,6 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
 
 int manager_loop(Manager *m);
 
-void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner);
-
 int manager_open_serialization(Manager *m, FILE **_f);
 
 int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root);
index dd5e80128512766ce2b6c133bcd1127155e49d08..6cc5824eb219f379bb590c79f2e33e65ceb46a23 100644 (file)
@@ -48,6 +48,7 @@
 #include "dropin.h"
 #include "formats-util.h"
 #include "process-util.h"
+#include "bus-util.h"
 
 const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = &service_vtable,
@@ -477,6 +478,7 @@ void unit_free(Unit *u) {
         if (u->manager->n_reloading <= 0)
                 unit_remove_transient(u);
 
+        sd_bus_slot_unref(u->match_bus_slot);
         bus_unit_send_removed_signal(u);
 
         unit_done(u);
@@ -2500,14 +2502,74 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
         return r;
 }
 
+static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        const char *name, *old_owner, *new_owner;
+        Unit *u = userdata;
+        int r;
+
+        assert(message);
+        assert(u);
+
+        r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
+        if (r < 0) {
+                bus_log_parse_error(r);
+                return 0;
+        }
+
+        if (UNIT_VTABLE(u)->bus_name_owner_change)
+                UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+
+        return 0;
+}
+
+int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name) {
+        _cleanup_free_ char *match = NULL;
+        Manager *m = u->manager;
+
+        assert(m);
+
+        if (u->match_bus_slot)
+                return -EBUSY;
+
+        match = strjoin("type='signal',"
+                        "sender='org.freedesktop.DBus',"
+                        "path='/org/freedesktop/DBus',"
+                        "interface='org.freedesktop.DBus',"
+                        "member='NameOwnerChanged',"
+                        "arg0='",
+                        name,
+                        "'",
+                        NULL);
+        if (!match)
+                return -ENOMEM;
+
+        return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
+}
+
 int unit_watch_bus_name(Unit *u, const char *name) {
+        int r;
+
         assert(u);
         assert(name);
 
         /* Watch a specific name on the bus. We only support one unit
          * watching each name for now. */
 
-        return hashmap_put(u->manager->watch_bus, name, u);
+        if (u->manager->api_bus) {
+                /* If the bus is already available, install the match directly.
+                 * Otherwise, just put the name in the list. bus_setup_api() will take care later. */
+                r = unit_install_bus_match(u->manager->api_bus, u, name);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+        }
+
+        r = hashmap_put(u->manager->watch_bus, name, u);
+        if (r < 0) {
+                u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+                return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
+        }
+
+        return 0;
 }
 
 void unit_unwatch_bus_name(Unit *u, const char *name) {
@@ -2515,6 +2577,7 @@ void unit_unwatch_bus_name(Unit *u, const char *name) {
         assert(name);
 
         hashmap_remove_value(u->manager->watch_bus, name, u);
+        u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
 }
 
 bool unit_can_serialize(Unit *u) {
index e60168267f0eab84f51f537fb4496c8e13ca3f85..9df5a7e6fb351ab1eddbd3c182051e990a9b57dd 100644 (file)
@@ -115,6 +115,9 @@ struct Unit {
         /* JOB_NOP jobs are special and can be installed without disturbing the real job. */
         Job *nop_job;
 
+        /* The slot used for watching NameOwnerChanged signals */
+        sd_bus_slot *match_bus_slot;
+
         /* Job timeout and action to take */
         usec_t job_timeout;
         FailureAction job_timeout_action;
@@ -522,6 +525,7 @@ void unit_unwatch_all_pids(Unit *u);
 
 void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2);
 
+int unit_install_bus_match(sd_bus *bus, Unit *u, const char *name);
 int unit_watch_bus_name(Unit *u, const char *name);
 void unit_unwatch_bus_name(Unit *u, const char *name);