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.
This commit is contained in:
Daniel Mack 2015-08-05 17:47:45 +02:00
parent d5972272d2
commit bbc2908635
5 changed files with 83 additions and 56 deletions

View 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,

View 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;

View 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);

View 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) {

View 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);