bus: properly detect and handle if a callback is installed/removed from within a callback
This commit is contained in:
parent
6807947e56
commit
7286037fd4
|
@ -46,6 +46,8 @@ struct filter_callback {
|
||||||
sd_bus_message_handler_t callback;
|
sd_bus_message_handler_t callback;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
|
|
||||||
|
unsigned last_iteration;
|
||||||
|
|
||||||
LIST_FIELDS(struct filter_callback, callbacks);
|
LIST_FIELDS(struct filter_callback, callbacks);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,6 +57,8 @@ struct object_callback {
|
||||||
|
|
||||||
char *path;
|
char *path;
|
||||||
bool is_fallback;
|
bool is_fallback;
|
||||||
|
|
||||||
|
unsigned last_iteration;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum bus_state {
|
enum bus_state {
|
||||||
|
@ -86,6 +90,9 @@ struct sd_bus {
|
||||||
bool prefer_readv:1;
|
bool prefer_readv:1;
|
||||||
bool prefer_writev:1;
|
bool prefer_writev:1;
|
||||||
bool processing:1;
|
bool processing:1;
|
||||||
|
bool match_callbacks_modified:1;
|
||||||
|
bool filter_callbacks_modified:1;
|
||||||
|
bool object_callbacks_modified:1;
|
||||||
|
|
||||||
void *rbuffer;
|
void *rbuffer;
|
||||||
size_t rbuffer_size;
|
size_t rbuffer_size;
|
||||||
|
@ -139,6 +146,7 @@ struct sd_bus {
|
||||||
char **exec_argv;
|
char **exec_argv;
|
||||||
|
|
||||||
uint64_t hello_serial;
|
uint64_t hello_serial;
|
||||||
|
unsigned iteration_counter;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void bus_unrefp(sd_bus **b) {
|
static inline void bus_unrefp(sd_bus **b) {
|
||||||
|
|
|
@ -212,6 +212,9 @@ int bus_match_run(
|
||||||
if (!node)
|
if (!node)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (bus && bus->match_callbacks_modified)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Not these special semantics: when traversing the tree we
|
/* Not these special semantics: when traversing the tree we
|
||||||
* usually let bus_match_run() when called for a node
|
* usually let bus_match_run() when called for a node
|
||||||
* recursively invoke bus_match_run(). There's are two
|
* recursively invoke bus_match_run(). There's are two
|
||||||
|
@ -241,6 +244,13 @@ int bus_match_run(
|
||||||
|
|
||||||
case BUS_MATCH_LEAF:
|
case BUS_MATCH_LEAF:
|
||||||
|
|
||||||
|
if (bus) {
|
||||||
|
if (node->leaf.last_iteration == bus->iteration_counter)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
node->leaf.last_iteration = bus->iteration_counter;
|
||||||
|
}
|
||||||
|
|
||||||
/* Run the callback. And then invoke siblings. */
|
/* Run the callback. And then invoke siblings. */
|
||||||
assert(node->leaf.callback);
|
assert(node->leaf.callback);
|
||||||
r = node->leaf.callback(bus, ret, m, node->leaf.userdata);
|
r = node->leaf.callback(bus, ret, m, node->leaf.userdata);
|
||||||
|
@ -323,6 +333,9 @@ int bus_match_run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bus && bus->match_callbacks_modified)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* And now, let's invoke our siblings */
|
/* And now, let's invoke our siblings */
|
||||||
return bus_match_run(bus, node->next, ret, m);
|
return bus_match_run(bus, node->next, ret, m);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,12 +54,13 @@ struct bus_match_node {
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
uint8_t u8;
|
|
||||||
char *str;
|
char *str;
|
||||||
|
uint8_t u8;
|
||||||
} value;
|
} value;
|
||||||
struct {
|
struct {
|
||||||
sd_bus_message_handler_t callback;
|
sd_bus_message_handler_t callback;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
|
unsigned last_iteration;
|
||||||
} leaf;
|
} leaf;
|
||||||
struct {
|
struct {
|
||||||
/* If this is set, then the child is NULL */
|
/* If this is set, then the child is NULL */
|
||||||
|
|
|
@ -1517,20 +1517,47 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) {
|
||||||
assert(bus);
|
assert(bus);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
|
do {
|
||||||
r = l->callback(bus, 0, m, l->userdata);
|
bus->filter_callbacks_modified = false;
|
||||||
if (r != 0)
|
|
||||||
return r;
|
LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
|
||||||
}
|
|
||||||
|
if (bus->filter_callbacks_modified)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Don't run this more than once per iteration */
|
||||||
|
if (l->last_iteration == bus->iteration_counter)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
l->last_iteration = bus->iteration_counter;
|
||||||
|
|
||||||
|
r = l->callback(bus, 0, m, l->userdata);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (bus->filter_callbacks_modified);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_match(sd_bus *bus, sd_bus_message *m) {
|
static int process_match(sd_bus *bus, sd_bus_message *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(bus);
|
assert(bus);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
return bus_match_run(bus, &bus->match_callbacks, 0, m);
|
do {
|
||||||
|
bus->match_callbacks_modified = false;
|
||||||
|
|
||||||
|
r = bus_match_run(bus, &bus->match_callbacks, 0, m);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
} while (bus->match_callbacks_modified);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_builtin(sd_bus *bus, sd_bus_message *m) {
|
static int process_builtin(sd_bus *bus, sd_bus_message *m) {
|
||||||
|
@ -1588,9 +1615,9 @@ static int process_object(sd_bus *bus, sd_bus_message *m) {
|
||||||
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT;
|
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT;
|
||||||
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
||||||
struct object_callback *c;
|
struct object_callback *c;
|
||||||
char *p;
|
|
||||||
int r;
|
int r;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
size_t pl;
|
||||||
|
|
||||||
assert(bus);
|
assert(bus);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
@ -1601,35 +1628,53 @@ static int process_object(sd_bus *bus, sd_bus_message *m) {
|
||||||
if (hashmap_isempty(bus->object_callbacks))
|
if (hashmap_isempty(bus->object_callbacks))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
c = hashmap_get(bus->object_callbacks, m->path);
|
pl = strlen(m->path);
|
||||||
if (c) {
|
|
||||||
r = c->callback(bus, 0, m, c->userdata);
|
|
||||||
if (r != 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
found = true;
|
do {
|
||||||
}
|
char p[pl+1];
|
||||||
|
|
||||||
/* Look for fallback prefixes */
|
bus->object_callbacks_modified = false;
|
||||||
p = strdupa(m->path);
|
|
||||||
for (;;) {
|
|
||||||
char *e;
|
|
||||||
|
|
||||||
e = strrchr(p, '/');
|
c = hashmap_get(bus->object_callbacks, m->path);
|
||||||
if (e == p || !e)
|
if (c && c->last_iteration != bus->iteration_counter) {
|
||||||
break;
|
|
||||||
|
|
||||||
*e = 0;
|
c->last_iteration = bus->iteration_counter;
|
||||||
|
|
||||||
c = hashmap_get(bus->object_callbacks, p);
|
|
||||||
if (c && c->is_fallback) {
|
|
||||||
r = c->callback(bus, 0, m, c->userdata);
|
r = c->callback(bus, 0, m, c->userdata);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/* Look for fallback prefixes */
|
||||||
|
strcpy(p, m->path);
|
||||||
|
for (;;) {
|
||||||
|
char *e;
|
||||||
|
|
||||||
|
if (bus->object_callbacks_modified)
|
||||||
|
break;
|
||||||
|
|
||||||
|
e = strrchr(p, '/');
|
||||||
|
if (e == p || !e)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*e = 0;
|
||||||
|
|
||||||
|
c = hashmap_get(bus->object_callbacks, p);
|
||||||
|
if (c && c->last_iteration != bus->iteration_counter && c->is_fallback) {
|
||||||
|
|
||||||
|
c->last_iteration = bus->iteration_counter;
|
||||||
|
|
||||||
|
r = c->callback(bus, 0, m, c->userdata);
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (bus->object_callbacks_modified);
|
||||||
|
|
||||||
/* We found some handlers but none wanted to take this, then
|
/* We found some handlers but none wanted to take this, then
|
||||||
* return this -- with one exception, we can handle
|
* return this -- with one exception, we can handle
|
||||||
|
@ -1750,6 +1795,8 @@ static int process_message(sd_bus *bus, sd_bus_message *m) {
|
||||||
assert(bus);
|
assert(bus);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
|
bus->iteration_counter++;
|
||||||
|
|
||||||
r = process_hello(bus, m);
|
r = process_hello(bus, m);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1995,6 +2042,7 @@ int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *user
|
||||||
f->callback = callback;
|
f->callback = callback;
|
||||||
f->userdata = userdata;
|
f->userdata = userdata;
|
||||||
|
|
||||||
|
bus->filter_callbacks_modified = true;
|
||||||
LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f);
|
LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2009,6 +2057,7 @@ int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *u
|
||||||
|
|
||||||
LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
|
LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
|
||||||
if (f->callback == callback && f->userdata == userdata) {
|
if (f->callback == callback && f->userdata == userdata) {
|
||||||
|
bus->filter_callbacks_modified = true;
|
||||||
LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f);
|
LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f);
|
||||||
free(f);
|
free(f);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -2053,6 +2102,7 @@ static int bus_add_object(
|
||||||
c->userdata = userdata;
|
c->userdata = userdata;
|
||||||
c->is_fallback = fallback;
|
c->is_fallback = fallback;
|
||||||
|
|
||||||
|
bus->object_callbacks_modified = true;
|
||||||
r = hashmap_put(bus->object_callbacks, c->path, c);
|
r = hashmap_put(bus->object_callbacks, c->path, c);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
free(c->path);
|
free(c->path);
|
||||||
|
@ -2086,6 +2136,7 @@ static int bus_remove_object(
|
||||||
if (c->callback != callback || c->userdata != userdata || c->is_fallback != fallback)
|
if (c->callback != callback || c->userdata != userdata || c->is_fallback != fallback)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
bus->object_callbacks_modified = true;
|
||||||
assert_se(c == hashmap_remove(bus->object_callbacks, c->path));
|
assert_se(c == hashmap_remove(bus->object_callbacks, c->path));
|
||||||
|
|
||||||
free(c->path);
|
free(c->path);
|
||||||
|
@ -2125,6 +2176,7 @@ int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t ca
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
|
bus->match_callbacks_modified = true;
|
||||||
r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL);
|
r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
|
||||||
|
@ -2147,8 +2199,10 @@ int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t
|
||||||
if (bus->bus_client)
|
if (bus->bus_client)
|
||||||
r = bus_remove_match_internal(bus, match);
|
r = bus_remove_match_internal(bus, match);
|
||||||
|
|
||||||
if (callback)
|
if (callback) {
|
||||||
|
bus->match_callbacks_modified = true;
|
||||||
q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
|
q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -33,8 +33,6 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* - allow registration/removl of callbacks from within callbacks
|
|
||||||
*
|
|
||||||
* - add page donation logic
|
* - add page donation logic
|
||||||
* - api for appending/reading fixed arrays
|
* - api for appending/reading fixed arrays
|
||||||
* - merge busctl into systemctl or so?
|
* - merge busctl into systemctl or so?
|
||||||
|
|
Loading…
Reference in a new issue