From d093c3ac55649e9a30504cfeaff506329ffd6ec7 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 4 Jan 2019 11:57:18 +0000 Subject: [PATCH] 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. --- include/swaybar/tray/item.h | 4 ++ swaybar/tray/item.c | 94 ++++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index 9bba7951..4238bdb8 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -35,6 +35,10 @@ struct swaybar_sni { bool item_is_menu; char *menu; char *icon_theme_path; // non-standard KDE property + + sd_bus_slot *new_icon_slot; + sd_bus_slot *new_attention_icon_slot; + sd_bus_slot *new_status_slot; }; struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); 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);