summaryrefslogtreecommitdiff
path: root/src/core/unit.c
diff options
context:
space:
mode:
authorDaniel Mack <daniel@zonque.org>2015-08-05 17:47:45 +0200
committerDaniel Mack <daniel@zonque.org>2015-08-06 10:14:41 +0200
commitbbc2908635ca3ded9162504683fa126809f0ec14 (patch)
treed667bb2afaaef914cbc262e3b6e4220325722bbf /src/core/unit.c
parentd5972272d2b725ab6a2789899f8b6c1d2a8113e3 (diff)
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.
Diffstat (limited to 'src/core/unit.c')
-rw-r--r--src/core/unit.c65
1 files changed, 64 insertions, 1 deletions
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) {