From bbc2908635ca3ded9162504683fa126809f0ec14 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 5 Aug 2015 17:47:45 +0200 Subject: core: dbus: track bus names per unit 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 | 50 +++++++++++++---------------------------- src/core/manager.c | 18 --------------- src/core/manager.h | 2 -- src/core/unit.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/core/unit.h | 4 ++++ 5 files changed, 83 insertions(+), 56 deletions(-) diff --git a/src/core/dbus.c b/src/core/dbus.c index 057653a8b5..44bf5cab28 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -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, diff --git a/src/core/manager.c b/src/core/manager.c index ba107d4615..ecea89c377 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -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; diff --git a/src/core/manager.h b/src/core/manager.h index 4ef869d14a..1e01f2bdef 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -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); diff --git a/src/core/unit.c b/src/core/unit.c index dd5e801285..6cc5824eb2 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -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) { diff --git a/src/core/unit.h b/src/core/unit.h index e60168267f..9df5a7e6fb 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -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); -- cgit v1.2.3-54-g00ecf