aboutsummaryrefslogtreecommitdiff
path: root/swaybar/tray
diff options
context:
space:
mode:
authorIan Fan <ianfan0@gmail.com>2019-01-04 11:57:18 +0000
committerIan Fan <ianfan0@gmail.com>2019-01-08 12:18:59 +0000
commitd093c3ac55649e9a30504cfeaff506329ffd6ec7 (patch)
tree6ce91378ead75a72910744a1e694d33accc6215d /swaybar/tray
parentb666fc76a6de9d4eb24ca9a1e458736b1ffee974 (diff)
swaybar: handle SNI signals better
This fixes a crash caused by callbacks not matching the right sender, and frees old values later, before they are re-assigned.
Diffstat (limited to 'swaybar/tray')
-rw-r--r--swaybar/tray/item.c94
1 files changed, 61 insertions, 33 deletions
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index 130496ec..44fd876c 100644
--- a/swaybar/tray/item.c
+++ b/swaybar/tray/item.c
@@ -78,6 +78,7 @@ static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni,
sd_bus_message_exit_container(msg);
}
+ list_free_items_and_destroy(*dest);
*dest = pixmaps;
return ret;
@@ -121,6 +122,10 @@ static int get_property_callback(sd_bus_message *msg, void *data,
goto cleanup;
}
} else {
+ if (*type == 's' || *type == 'o') {
+ free(*(char **)dest);
+ }
+
ret = sd_bus_message_read(msg, type, dest);
if (ret < 0) {
wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop,
@@ -156,55 +161,73 @@ static void sni_get_property_async(struct swaybar_sni *sni, const char *prop,
}
}
+/*
+ * There is a quirk in sd-bus that in some systems, it is unable to get the
+ * well-known names on the bus, so it cannot identify if an incoming signal,
+ * which uses the sender's unique name, actually matches the callback's matching
+ * sender if the callback uses a well-known name, in which case it just calls
+ * the callback and hopes for the best, resulting in false positives. In the
+ * case of NewIcon & NewAttentionIcon, this doesn't affect anything, but it
+ * means that for NewStatus, if the SNI does not definitely match the sender,
+ * then the safe thing to do is to query the status independently.
+ * This function returns 1 if the SNI definitely matches the signal sender,
+ * which is returned by the calling function to indicate that signal matching
+ * can stop since it has already found the required callback, otherwise, it
+ * returns 0, which allows matching to continue.
+ */
+static int sni_check_msg_sender(struct swaybar_sni *sni, sd_bus_message *msg,
+ const char *signal) {
+ bool has_well_known_names =
+ sd_bus_creds_get_mask(sd_bus_message_get_creds(msg)) & SD_BUS_CREDS_WELL_KNOWN_NAMES;
+ if (sni->service[0] == ':' || has_well_known_names) {
+ wlr_log(WLR_DEBUG, "%s has new %s", sni->watcher_id, signal);
+ return 1;
+ } else {
+ wlr_log(WLR_DEBUG, "%s may have new %s", sni->watcher_id, signal);
+ return 0;
+ }
+}
+
static int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) {
struct swaybar_sni *sni = data;
- wlr_log(WLR_DEBUG, "%s has new IconName", sni->watcher_id);
-
- free(sni->icon_name);
- sni->icon_name = NULL;
sni_get_property_async(sni, "IconName", "s", &sni->icon_name);
-
- list_free_items_and_destroy(sni->icon_pixmap);
- sni->icon_pixmap = NULL;
sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap);
-
- return 0;
+ return sni_check_msg_sender(sni, msg, "icon");
}
static int handle_new_attention_icon(sd_bus_message *msg, void *data,
sd_bus_error *error) {
struct swaybar_sni *sni = data;
- wlr_log(WLR_DEBUG, "%s has new AttentionIconName", sni->watcher_id);
-
- free(sni->attention_icon_name);
- sni->attention_icon_name = NULL;
sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name);
-
- list_free_items_and_destroy(sni->attention_icon_pixmap);
- sni->attention_icon_pixmap = NULL;
sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap);
-
- return 0;
+ return sni_check_msg_sender(sni, msg, "attention icon");
}
static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) {
- char *status;
- int ret = sd_bus_message_read(msg, "s", &status);
- if (ret < 0) {
- wlr_log(WLR_DEBUG, "Failed to read new status message: %s", strerror(-ret));
+ struct swaybar_sni *sni = data;
+ int ret = sni_check_msg_sender(sni, msg, "status");
+ if (ret == 1) {
+ char *status;
+ int r = sd_bus_message_read(msg, "s", &status);
+ if (r < 0) {
+ wlr_log(WLR_ERROR, "Failed to read new status message: %s", strerror(-ret));
+ ret = r;
+ } else {
+ free(sni->status);
+ sni->status = strdup(status);
+ wlr_log(WLR_DEBUG, "%s has new status: %s", sni->watcher_id, status);
+ set_sni_dirty(sni);
+ }
} else {
- struct swaybar_sni *sni = data;
- free(sni->status);
- sni->status = strdup(status);
- wlr_log(WLR_DEBUG, "%s has new Status '%s'", sni->watcher_id, status);
- set_sni_dirty(sni);
+ sni_get_property_async(sni, "Status", "s", &sni->status);
}
+
return ret;
}
-static void sni_match_signal(struct swaybar_sni *sni, char *signal,
- sd_bus_message_handler_t callback) {
- int ret = sd_bus_match_signal(sni->tray->bus, NULL, sni->service, sni->path,
+static void sni_match_signal(struct swaybar_sni *sni, sd_bus_slot **slot,
+ char *signal, sd_bus_message_handler_t callback) {
+ int ret = sd_bus_match_signal(sni->tray->bus, slot, sni->service, sni->path,
sni->interface, signal, callback, sni);
if (ret < 0) {
wlr_log(WLR_DEBUG, "Failed to subscribe to signal %s: %s", signal,
@@ -241,9 +264,10 @@ struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) {
sni_get_property_async(sni, "ItemIsMenu", "b", &sni->item_is_menu);
sni_get_property_async(sni, "Menu", "o", &sni->menu);
- sni_match_signal(sni, "NewIcon", handle_new_icon);
- sni_match_signal(sni, "NewAttentionIcon", handle_new_attention_icon);
- sni_match_signal(sni, "NewStatus", handle_new_status);
+ sni_match_signal(sni, &sni->new_icon_slot, "NewIcon", handle_new_icon);
+ sni_match_signal(sni, &sni->new_attention_icon_slot, "NewAttentionIcon",
+ handle_new_attention_icon);
+ sni_match_signal(sni, &sni->new_status_slot, "NewStatus", handle_new_status);
return sni;
}
@@ -253,6 +277,10 @@ void destroy_sni(struct swaybar_sni *sni) {
return;
}
+ sd_bus_slot_unref(sni->new_icon_slot);
+ sd_bus_slot_unref(sni->new_attention_icon_slot);
+ sd_bus_slot_unref(sni->new_status_slot);
+
free(sni->watcher_id);
free(sni->service);
free(sni->path);