From a520bb665417af7aa98dcb983f3583659bbab807 Mon Sep 17 00:00:00 2001 From: Yegor Alexeyev Date: Sat, 12 Jun 2021 01:18:07 +0300 Subject: [PATCH] logind: allow binding different operation to reboot key long presses --- man/logind.conf.xml | 23 ++++-- src/login/logind-button.c | 154 ++++++++++++++++++++++++++++++----- src/login/logind-core.c | 20 ++++- src/login/logind-gperf.gperf | 6 +- src/login/logind.c | 2 + src/login/logind.conf.in | 1 + src/login/logind.h | 12 ++- src/systemd/sd-messages.h | 13 +++ 8 files changed, 198 insertions(+), 33 deletions(-) diff --git a/man/logind.conf.xml b/man/logind.conf.xml index be62b6b5723..5dc24a3f053 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -194,12 +194,16 @@ HandlePowerKey= + HandlePowerKeyLongPress= + HandleRebootKey= + HandleRebootKeyLongPress= HandleSuspendKey= + HandleSuspendKeyLongPress= HandleHibernateKey= + HandleHibernateKeyLongPress= HandleLidSwitch= HandleLidSwitchExternalPower= HandleLidSwitchDocked= - HandleRebootKey= Controls how logind shall handle the system power, reboot and sleep keys and the lid switch to trigger @@ -221,17 +225,20 @@ power-switch udev tag will be watched for key/lid switch events. HandlePowerKey= defaults to poweroff, HandleRebootKey= - defaults to reboot. - HandleSuspendKey= and - HandleLidSwitch= default to + defaults to reboot, HandleSuspendKey= + defaults to suspend, HandleHibernateKey= + defaults to hibernate, HandlePowerKeyLongPress= + defaults to ignore, HandleRebootKeyLongPress= + defaults to poweroff, HandleSuspendKeyLongPress= + defaults to hibernate, HandleHibernateKeyLongPress= + defaults to ignore. + HandleLidSwitch= defaults to suspend. HandleLidSwitchExternalPower= is completely ignored by default (for backwards compatibility) — an explicit value must be set before it will be used to determine behaviour. HandleLidSwitchDocked= defaults - to ignore. - HandleHibernateKey= defaults to - hibernate. If the system is inserted in a + to ignore. If the system is inserted in a docking station, or if more than one display is connected, the action specified by HandleLidSwitchDocked= occurs; if the system is on external power the action (if any) @@ -243,7 +250,7 @@ sleep keys and the lid switch by taking a low-level inhibitor lock (handle-power-key, handle-suspend-key, handle-hibernate-key, handle-lid-switch, - handle-reboot-switch). + handle-reboot-key). This is most commonly used by graphical desktop environments to take over suspend and hibernation handling, and to use their own configuration mechanisms. If a low-level inhibitor lock is taken, logind will not take any diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 0ee6702068b..a2eb4c5269f 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -19,6 +19,8 @@ #define ULONG_BITS (sizeof(unsigned long)*8) +#define LONG_PRESS_DURATION (5 * USEC_PER_SEC) + static bool bitset_get(const unsigned long *bits, unsigned i) { return (bits[i / ULONG_BITS] >> (i % ULONG_BITS)) & 1UL; } @@ -117,6 +119,89 @@ static int button_install_check_event_source(Button *b) { return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1); } +static int long_press_of_power_key_handler(sd_event_source *e, uint64_t usec, void *userdata) { + Manager *m = userdata; + + assert(e); + assert(m); + + m->power_key_long_press_event_source = sd_event_source_unref(m->power_key_long_press_event_source); + + log_struct(LOG_INFO, + LOG_MESSAGE("Power key pressed long."), + "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_LONG_PRESS_STR); + + manager_handle_action(m, INHIBIT_HANDLE_POWER_KEY, m->handle_power_key_long_press, m->power_key_ignore_inhibited, true); + return 0; +} + +static int long_press_of_reboot_key_handler(sd_event_source *e, uint64_t usec, void *userdata) { + Manager *m = userdata; + + assert(e); + assert(m); + + m->reboot_key_long_press_event_source = sd_event_source_unref(m->reboot_key_long_press_event_source); + + log_struct(LOG_INFO, + LOG_MESSAGE("Reboot key pressed long."), + "MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR); + + manager_handle_action(m, INHIBIT_HANDLE_REBOOT_KEY, m->handle_reboot_key_long_press, m->reboot_key_ignore_inhibited, true); + return 0; +} + +static int long_press_of_suspend_key_handler(sd_event_source *e, uint64_t usec, void *userdata) { + Manager *m = userdata; + + assert(e); + assert(m); + + m->suspend_key_long_press_event_source = sd_event_source_unref(m->suspend_key_long_press_event_source); + + log_struct(LOG_INFO, + LOG_MESSAGE("Suspend key pressed long."), + "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR); + + manager_handle_action(m, INHIBIT_HANDLE_SUSPEND_KEY, m->handle_suspend_key_long_press, m->suspend_key_ignore_inhibited, true); + return 0; +} + +static int long_press_of_hibernate_key_handler(sd_event_source *e, uint64_t usec, void *userdata) { + Manager *m = userdata; + + assert(e); + assert(m); + + m->hibernate_key_long_press_event_source = sd_event_source_unref(m->hibernate_key_long_press_event_source); + + log_struct(LOG_INFO, + LOG_MESSAGE("Hibernate key pressed long."), + "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR); + + manager_handle_action(m, INHIBIT_HANDLE_HIBERNATE_KEY, m->handle_hibernate_key_long_press, m->hibernate_key_ignore_inhibited, true); + return 0; +} + +static void start_long_press(Manager *m, sd_event_source **e, sd_event_time_handler_t callback) { + int r; + + assert(m); + assert(e); + + if (*e) + return; + + r = sd_event_add_time_relative( + m->event, + e, + CLOCK_MONOTONIC, + LONG_PRESS_DURATION, 0, + callback, m); + if (r < 0) + log_warning_errno(r, "Failed to add long press timer event, ignoring: %m"); +} + static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) { Button *b = userdata; struct input_event ev; @@ -138,11 +223,14 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_POWER: case KEY_POWER2: - log_struct(LOG_INFO, - LOG_MESSAGE("Power key pressed."), - "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR); - - manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); + if (b->manager->handle_power_key_long_press != HANDLE_IGNORE && b->manager->handle_power_key_long_press != b->manager->handle_power_key) { + log_debug("Power key pressed. Further action depends on the key press duration."); + start_long_press(b->manager, &b->manager->power_key_long_press_event_source, long_press_of_power_key_handler); + } else { + log_struct(LOG_INFO, LOG_MESSAGE("Power key pressed short."), + "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR); + manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); + } break; /* The kernel naming is a bit confusing here: @@ -151,11 +239,14 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u */ case KEY_RESTART: - log_struct(LOG_INFO, - LOG_MESSAGE("Reboot key pressed."), - "MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_STR); - - manager_handle_action(b->manager, INHIBIT_HANDLE_REBOOT_KEY, b->manager->handle_reboot_key, b->manager->reboot_key_ignore_inhibited, true); + if (b->manager->handle_reboot_key_long_press != HANDLE_IGNORE && b->manager->handle_reboot_key_long_press != b->manager->handle_reboot_key) { + log_debug("Reboot key pressed. Further action depends on the key press duration."); + start_long_press(b->manager, &b->manager->reboot_key_long_press_event_source, long_press_of_reboot_key_handler); + } else { + log_struct(LOG_INFO, LOG_MESSAGE("Reboot key pressed short."), + "MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_STR); + manager_handle_action(b->manager, INHIBIT_HANDLE_REBOOT_KEY, b->manager->handle_reboot_key, b->manager->reboot_key_ignore_inhibited, true); + } break; /* The kernel naming is a bit confusing here: @@ -165,22 +256,45 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u */ case KEY_SLEEP: - log_struct(LOG_INFO, - LOG_MESSAGE("Suspend key pressed."), - "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR); - - manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); + if (b->manager->handle_suspend_key_long_press != HANDLE_IGNORE && b->manager->handle_suspend_key_long_press != b->manager->handle_suspend_key) { + log_debug("Suspend key pressed. Further action depends on the key press duration."); + start_long_press(b->manager, &b->manager->suspend_key_long_press_event_source, long_press_of_suspend_key_handler); + } else { + log_struct(LOG_INFO, LOG_MESSAGE("Suspend key pressed short."), + "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR); + manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); + } break; case KEY_SUSPEND: - log_struct(LOG_INFO, - LOG_MESSAGE("Hibernate key pressed."), - "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR); - - manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); + if (b->manager->handle_hibernate_key_long_press != HANDLE_IGNORE && b->manager->handle_hibernate_key_long_press != b->manager->handle_hibernate_key) { + log_debug("Hibernate key pressed. Further action depends on the key press duration."); + start_long_press(b->manager, &b->manager->hibernate_key_long_press_event_source, long_press_of_hibernate_key_handler); + } else { + log_struct(LOG_INFO, LOG_MESSAGE("Hibernate key pressed short."), + "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR); + manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); + } break; } + } else if (ev.type == EV_KEY && ev.value == 0) { + if (ev.code == KEY_RESTART) { + if (b->manager->reboot_key_long_press_event_source) { + /* Long press event timer is still pending and key release + event happened. This means that key press duration was + insufficient to trigger a long press event + */ + log_struct(LOG_INFO, + LOG_MESSAGE("Reboot key pressed short."), + "MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_STR); + + b->manager->reboot_key_long_press_event_source = sd_event_source_unref(b->manager->reboot_key_long_press_event_source); + + manager_handle_action(b->manager, INHIBIT_HANDLE_REBOOT_KEY, b->manager->handle_reboot_key, b->manager->reboot_key_ignore_inhibited, true); + } + } + } else if (ev.type == EV_SW && ev.value > 0) { if (ev.code == SW_LID) { diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 22031f485a0..9d98d36b9f6 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -39,12 +39,18 @@ void manager_reset_config(Manager *m) { m->user_stop_delay = 10 * USEC_PER_SEC; m->handle_power_key = HANDLE_POWEROFF; + m->handle_power_key_long_press = HANDLE_IGNORE; + m->handle_reboot_key = HANDLE_REBOOT; + m->handle_reboot_key_long_press = HANDLE_POWEROFF; m->handle_suspend_key = HANDLE_SUSPEND; + m->handle_suspend_key_long_press = HANDLE_HIBERNATE; m->handle_hibernate_key = HANDLE_HIBERNATE; + m->handle_hibernate_key_long_press = HANDLE_IGNORE; + m->handle_lid_switch = HANDLE_SUSPEND; m->handle_lid_switch_ep = _HANDLE_ACTION_INVALID; m->handle_lid_switch_docked = HANDLE_IGNORE; - m->handle_reboot_key = HANDLE_REBOOT; + m->power_key_ignore_inhibited = false; m->suspend_key_ignore_inhibited = false; m->hibernate_key_ignore_inhibited = false; @@ -670,18 +676,26 @@ bool manager_all_buttons_ignored(Manager *m) { if (m->handle_power_key != HANDLE_IGNORE) return false; + if (m->handle_power_key_long_press != HANDLE_IGNORE) + return false; if (m->handle_suspend_key != HANDLE_IGNORE) return false; + if (m->handle_suspend_key_long_press != HANDLE_IGNORE) + return false; if (m->handle_hibernate_key != HANDLE_IGNORE) return false; + if (m->handle_hibernate_key_long_press != HANDLE_IGNORE) + return false; + if (m->handle_reboot_key != HANDLE_IGNORE) + return false; + if (m->handle_reboot_key_long_press != HANDLE_IGNORE) + return false; if (m->handle_lid_switch != HANDLE_IGNORE) return false; if (!IN_SET(m->handle_lid_switch_ep, _HANDLE_ACTION_INVALID, HANDLE_IGNORE)) return false; if (m->handle_lid_switch_docked != HANDLE_IGNORE) return false; - if (m->handle_reboot_key != HANDLE_IGNORE) - return false; return true; } diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 25e429c5a3e..867db365336 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -26,12 +26,16 @@ Login.KillExcludeUsers, config_parse_strv, 0, offse Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay) Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) +Login.HandlePowerKeyLongPress, config_parse_handle_action, 0, offsetof(Manager, handle_power_key_long_press) +Login.HandleRebootKey, config_parse_handle_action, 0, offsetof(Manager, handle_reboot_key) +Login.HandleRebootKeyLongPress, config_parse_handle_action, 0, offsetof(Manager, handle_reboot_key_long_press) Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) +Login.HandleSuspendKeyLongPress, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key_long_press) Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) +Login.HandleHibernateKeyLongPress, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key_long_press) Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch) Login.HandleLidSwitchExternalPower, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_ep) Login.HandleLidSwitchDocked, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch_docked) -Login.HandleRebootKey, config_parse_handle_action, 0, offsetof(Manager, handle_reboot_key) Login.PowerKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, power_key_ignore_inhibited) Login.SuspendKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, suspend_key_ignore_inhibited) Login.HibernateKeyIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, hibernate_key_ignore_inhibited) diff --git a/src/login/logind.c b/src/login/logind.c index ec52a57acb5..478779900fa 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -140,6 +140,8 @@ static Manager* manager_unref(Manager *m) { sd_event_source_unref(m->console_active_event_source); sd_event_source_unref(m->lid_switch_ignore_event_source); + sd_event_source_unref(m->reboot_key_long_press_event_source); + #if ENABLE_UTMP sd_event_source_unref(m->utmp_event_source); #endif diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in index 27ba77ce796..2d084e134df 100644 --- a/src/login/logind.conf.in +++ b/src/login/logind.conf.in @@ -29,6 +29,7 @@ #HandleLidSwitchExternalPower=suspend #HandleLidSwitchDocked=ignore #HandleRebootKey=reboot +#HandleRebootKeyLongPress=poweroff #PowerKeyIgnoreInhibited=no #SuspendKeyIgnoreInhibited=no #HibernateKeyIgnoreInhibited=no diff --git a/src/login/logind.h b/src/login/logind.h index 761763a4762..730c14a46ae 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -102,12 +102,17 @@ struct Manager { HandleAction idle_action; HandleAction handle_power_key; + HandleAction handle_power_key_long_press; + HandleAction handle_reboot_key; + HandleAction handle_reboot_key_long_press; HandleAction handle_suspend_key; + HandleAction handle_suspend_key_long_press; HandleAction handle_hibernate_key; + HandleAction handle_hibernate_key_long_press; + HandleAction handle_lid_switch; HandleAction handle_lid_switch_ep; HandleAction handle_lid_switch_docked; - HandleAction handle_reboot_key; bool power_key_ignore_inhibited; bool suspend_key_ignore_inhibited; @@ -122,6 +127,11 @@ struct Manager { usec_t holdoff_timeout_usec; sd_event_source *lid_switch_ignore_event_source; + sd_event_source *power_key_long_press_event_source; + sd_event_source *reboot_key_long_press_event_source; + sd_event_source *suspend_key_long_press_event_source; + sd_event_source *hibernate_key_long_press_event_source; + uint64_t runtime_dir_size; uint64_t runtime_dir_inodes; uint64_t sessions_max; diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index aee0ddb6863..eb285efe94d 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -143,12 +143,25 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) #define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) #define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_POWER_KEY_LONG_PRESS SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b) +#define SD_MESSAGE_POWER_KEY_LONG_PRESS_STR \ + SD_ID128_MAKE_STR(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b) #define SD_MESSAGE_REBOOT_KEY SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0) #define SD_MESSAGE_REBOOT_KEY_STR SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0) +#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75) +#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR \ + SD_ID128_MAKE_STR(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75) #define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) #define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8) +#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR \ + SD_ID128_MAKE_STR(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8) #define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) #define SD_MESSAGE_HIBERNATE_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) +#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS \ + SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45) +#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR \ + SD_ID128_MAKE_STR(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45) #define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) #define SD_MESSAGE_INVALID_CONFIGURATION_STR \