return b;
}
+static void stop_long_press(Button *b, sd_event_source **e) {
+ assert(b);
+ assert(e);
+
+ /* The long press timers are Manager-scoped but armed with the Button that started the
+ * press as userdata (see start_long_press()). Cancel the one this Button owns before it is
+ * freed. */
+ if (*e && sd_event_source_get_userdata(*e) == b)
+ *e = sd_event_source_unref(*e);
+}
+
Button *button_free(Button *b) {
if (!b)
return NULL;
hashmap_remove(b->manager->buttons, b->name);
+ stop_long_press(b, &b->manager->power_key_long_press_event_source);
+ stop_long_press(b, &b->manager->reboot_key_long_press_event_source);
+ stop_long_press(b, &b->manager->suspend_key_long_press_event_source);
+ stop_long_press(b, &b->manager->hibernate_key_long_press_event_source);
+
sd_event_source_unref(b->io_event_source);
sd_event_source_unref(b->check_event_source);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "sd-event.h"
+
+#include "alloc-util.h"
+#include "hash-funcs.h"
+#include "hashmap.h"
+#include "logind.h"
#include "logind-action.h"
+#include "logind-button.h"
#include "logind-session.h"
#include "logind-user.h"
#include "sleep-config.h"
#include "test-tables.h"
#include "tests.h"
+#include "time-util.h"
static void test_sleep_handle_action(void) {
for (HandleAction action = _HANDLE_ACTION_SLEEP_FIRST; action < _HANDLE_ACTION_SLEEP_LAST; action++) {
}
}
+static int dummy_time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ return 0;
+}
+
+/* Verify that button_free() cancels the Manager-scoped long-press timer the Button armed, and at the same
+ * time leaves a timer armed by a different Button alone. */
+static void test_button_free_cancels_long_press(void) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(hashmap_freep) Hashmap *buttons = NULL;
+ _cleanup_free_ Manager *m = NULL;
+ Button *b, *b2;
+
+ assert_se(m = new0(Manager, 1));
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(buttons = hashmap_new(&string_hash_ops));
+ m->event = e;
+ m->buttons = buttons;
+
+ assert_se(b = button_new(m, "test-button"));
+ assert_se(b2 = button_new(m, "other-button"));
+
+ /* Arm a long-press timer owned by b, exactly as start_long_press() does: the source lives in
+ * a Manager-scoped field and carries the Button as userdata. */
+ assert_se(sd_event_add_time_relative(e, &m->power_key_long_press_event_source, CLOCK_MONOTONIC,
+ 10 * USEC_PER_SEC, 0, dummy_time_handler, b) >= 0);
+ /* ... and another one owned by b2. */
+ assert_se(sd_event_add_time_relative(e, &m->reboot_key_long_press_event_source, CLOCK_MONOTONIC,
+ 10 * USEC_PER_SEC, 0, dummy_time_handler, b2) >= 0);
+
+ /* Freeing b must cancel the timer b armed. */
+ button_free(b);
+ assert_se(!m->power_key_long_press_event_source);
+
+ /* ... but freeing b must not touch a timer owned by another Button. */
+ assert_se(m->reboot_key_long_press_event_source);
+
+ button_free(b2);
+ assert_se(!m->reboot_key_long_press_event_source);
+}
+
int main(int argc, char **argv) {
test_setup_logging(LOG_DEBUG);
test_table(UserState, user_state, USER_STATE);
test_sleep_handle_action();
+ test_button_free_cancels_long_press();
return EXIT_SUCCESS;
}