diff options
author | Lennart Poettering <lennart@poettering.net> | 2012-12-23 22:32:48 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2012-12-24 00:29:40 +0100 |
commit | 23406ce58aa7142e8df3c5c9e5ac34a01e90e3e0 (patch) | |
tree | 5175b16d46a7dfe59afaf2c8dd85f1b4081f7979 | |
parent | 0ad1271f564b9c956685938167f7ea8c301e835e (diff) |
logind: add support for automatic suspend/hibernate/shutdown on idle
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/login/logind-action.c | 124 | ||||
-rw-r--r-- | src/login/logind-action.h | 54 | ||||
-rw-r--r-- | src/login/logind-button.c | 95 | ||||
-rw-r--r-- | src/login/logind-button.h | 19 | ||||
-rw-r--r-- | src/login/logind-dbus.c | 14 | ||||
-rw-r--r-- | src/login/logind-gperf.gperf | 10 | ||||
-rw-r--r-- | src/login/logind-session.c | 110 | ||||
-rw-r--r-- | src/login/logind.c | 88 | ||||
-rw-r--r-- | src/login/logind.conf | 2 | ||||
-rw-r--r-- | src/login/logind.h | 16 | ||||
-rw-r--r-- | src/shared/dbus-common.h | 1 | ||||
-rw-r--r-- | src/shared/util.c | 6 |
13 files changed, 394 insertions, 147 deletions
diff --git a/Makefile.am b/Makefile.am index 163a180dc4..477b3a6e89 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3481,6 +3481,8 @@ systemd_logind_SOURCES = \ src/login/logind-device.h \ src/login/logind-button.c \ src/login/logind-button.h \ + src/login/logind-action.c \ + src/login/logind-action.h \ src/login/logind-seat.c \ src/login/logind-seat.h \ src/login/logind-session.c \ diff --git a/src/login/logind-action.c b/src/login/logind-action.c new file mode 100644 index 0000000000..bd5664e905 --- /dev/null +++ b/src/login/logind-action.c @@ -0,0 +1,124 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "conf-parser.h" +#include "special.h" +#include "dbus-common.h" +#include "logind-action.h" + +int manager_handle_action( + Manager *m, + InhibitWhat inhibit_key, + HandleAction handle, + bool ignore_inhibited, + bool is_edge) { + + static const char * const message_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = "Powering Off...", + [HANDLE_REBOOT] = "Rebooting...", + [HANDLE_HALT] = "Halting...", + [HANDLE_KEXEC] = "Rebooting via kexec...", + [HANDLE_SUSPEND] = "Suspending...", + [HANDLE_HIBERNATE] = "Hibernating...", + [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..." + }; + + static const char * const target_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, + [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, + [HANDLE_HALT] = SPECIAL_HALT_TARGET, + [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, + [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, + [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET + }; + + DBusError error; + int r; + InhibitWhat inhibit_operation; + + assert(m); + + /* If the key handling is turned off, don't do anything */ + if (handle == HANDLE_IGNORE) { + log_debug("Refusing operation, as it is turned off."); + return 0; + } + + /* If the key handling is inhibited, don't do anything */ + if (inhibit_key > 0) { + if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0)) { + log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key)); + return 0; + } + } + + /* Locking is handled differently from the rest. */ + if (handle == HANDLE_LOCK) { + log_info("Locking sessions..."); + session_send_lock_all(m, true); + return 1; + } + + inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; + + /* If the actual operation is inhibited, warn and fail */ + if (!ignore_inhibited && + manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0)) { + + /* If this is just a recheck of the lid switch then don't warn about anything */ + if (!is_edge) { + log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation)); + return 0; + } + + log_error("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation)); + warn_melody(); + return -EPERM; + } + + log_info("%s", message_table[handle]); + + dbus_error_init(&error); + r = bus_manager_shutdown_or_sleep_now_or_later(m, target_table[handle], inhibit_operation, &error); + if (r < 0) { + log_error("Failed to execute operation: %s", bus_error_message(&error)); + dbus_error_free(&error); + return r; + } + + return 1; +} + +static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { + [HANDLE_IGNORE] = "ignore", + [HANDLE_POWEROFF] = "poweroff", + [HANDLE_REBOOT] = "reboot", + [HANDLE_HALT] = "halt", + [HANDLE_KEXEC] = "kexec", + [HANDLE_SUSPEND] = "suspend", + [HANDLE_HIBERNATE] = "hibernate", + [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", + [HANDLE_LOCK] = "lock" +}; + +DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); +DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); diff --git a/src/login/logind-action.h b/src/login/logind-action.h new file mode 100644 index 0000000000..7ab44644f2 --- /dev/null +++ b/src/login/logind-action.h @@ -0,0 +1,54 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologindactionhfoo +#define foologindactionhfoo + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef enum HandleAction { + HANDLE_IGNORE, + HANDLE_POWEROFF, + HANDLE_REBOOT, + HANDLE_HALT, + HANDLE_KEXEC, + HANDLE_SUSPEND, + HANDLE_HIBERNATE, + HANDLE_HYBRID_SLEEP, + HANDLE_LOCK, + _HANDLE_ACTION_MAX, + _HANDLE_ACTION_INVALID = -1 +} HandleAction; + +#include "logind.h" +#include "logind-inhibit.h" + +int manager_handle_action( + Manager *m, + InhibitWhat inhibit_key, + HandleAction handle, + bool ignore_inhibited, + bool is_edge); + +const char* handle_action_to_string(HandleAction h); +HandleAction handle_action_from_string(const char *s); + +int config_parse_handle_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +#endif diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 8bbd731ae4..dbf3d3c446 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -153,88 +153,21 @@ fail: static int button_handle( Button *b, InhibitWhat inhibit_key, - HandleButton handle, + HandleAction handle, bool ignore_inhibited, bool is_edge) { - static const char * const message_table[_HANDLE_BUTTON_MAX] = { - [HANDLE_POWEROFF] = "Powering Off...", - [HANDLE_REBOOT] = "Rebooting...", - [HANDLE_HALT] = "Halting...", - [HANDLE_KEXEC] = "Rebooting via kexec...", - [HANDLE_SUSPEND] = "Suspending...", - [HANDLE_HIBERNATE] = "Hibernating...", - [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..." - }; - - static const char * const target_table[_HANDLE_BUTTON_MAX] = { - [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, - [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, - [HANDLE_HALT] = SPECIAL_HALT_TARGET, - [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, - [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, - [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET - }; - - DBusError error; int r; - InhibitWhat inhibit_operation; assert(b); - /* If the key handling is turned off, don't do anything */ - if (handle == HANDLE_IGNORE) { - log_debug("Refusing key handling, as it is turned off."); - return 0; - } - - /* If the key handling is inhibited, don't do anything */ - if (manager_is_inhibited(b->manager, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0)) { - log_debug("Refusing key handling, %s is inhibited.", inhibit_what_to_string(inhibit_key)); - return 0; - } + r = manager_handle_action(b->manager, inhibit_key, handle, ignore_inhibited, is_edge); + if (r > 0) + /* We are executing the operation, so make sure we don't + * execute another one until the lid is opened/closed again */ + b->lid_close_queued = false; - /* Locking is handled differently from the rest. */ - if (handle == HANDLE_LOCK) { - log_info("Locking sessions..."); - session_send_lock_all(b->manager, true); - return 1; - } - - inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; - - /* If the actual operation is inhibited, warn and fail */ - if (!ignore_inhibited && - manager_is_inhibited(b->manager, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0)) { - - - /* If this is just a recheck of the lid switch then don't warn about anything */ - if (!is_edge) { - log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation)); - return 0; - } - - log_error("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_operation)); - warn_melody(); - return -EPERM; - } - - log_info("%s", message_table[handle]); - - /* We are executing the operation, so make sure we don't - * execute another one until the lid is opened/closed again */ - b->lid_close_queued = false; - - dbus_error_init(&error); - r = bus_manager_shutdown_or_sleep_now_or_later(b->manager, target_table[handle], inhibit_operation, &error); - if (r < 0) { - log_error("Failed to execute operation: %s", bus_error_message(&error)); - dbus_error_free(&error); - return r; - } - - return 1; + return r; } int button_process(Button *b) { @@ -306,17 +239,3 @@ int button_recheck(Button *b) { return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false); } - -static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = { - [HANDLE_IGNORE] = "ignore", - [HANDLE_POWEROFF] = "poweroff", - [HANDLE_REBOOT] = "reboot", - [HANDLE_HALT] = "halt", - [HANDLE_KEXEC] = "kexec", - [HANDLE_SUSPEND] = "suspend", - [HANDLE_HIBERNATE] = "hibernate", - [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", - [HANDLE_LOCK] = "lock" -}; -DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton); -DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting"); diff --git a/src/login/logind-button.h b/src/login/logind-button.h index b76ca32c08..1c5a84553d 100644 --- a/src/login/logind-button.h +++ b/src/login/logind-button.h @@ -24,20 +24,6 @@ typedef struct Button Button; -typedef enum HandleButton { - HANDLE_IGNORE, - HANDLE_POWEROFF, - HANDLE_REBOOT, - HANDLE_HALT, - HANDLE_KEXEC, - HANDLE_SUSPEND, - HANDLE_HIBERNATE, - HANDLE_HYBRID_SLEEP, - HANDLE_LOCK, - _HANDLE_BUTTON_MAX, - _HANDLE_BUTTON_INVALID = -1 -} HandleButton; - #include "list.h" #include "util.h" #include "logind.h" @@ -59,9 +45,4 @@ int button_process(Button *b); int button_recheck(Button *b); int button_set_seat(Button *b, const char *sn); -const char* handle_button_to_string(HandleButton h); -HandleButton handle_button_from_string(const char *s); - -int config_parse_handle_button(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); - #endif diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 89021ab15b..77a06f2ce4 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -220,6 +220,8 @@ " <property name=\"HandleSuspendKey\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"HandleHibernateKey\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"HandleLidSwitch\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"IdleAction\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"IdleActionUSec\" type=\"t\" access=\"read\"/>\n" \ " <property name=\"PreparingForShutdown\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"PreparingForSleep\" type=\"b\" access=\"read\"/>\n" \ " </interface>\n" @@ -1339,7 +1341,7 @@ static int bus_manager_do_shutdown_or_sleep( return 0; } -static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_button, handle_button, HandleButton); +static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_action, handle_action, HandleAction); static const BusProperty bus_login_manager_properties[] = { { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true }, @@ -1355,10 +1357,12 @@ static const BusProperty bus_login_manager_properties[] = { { "BlockInhibited", bus_manager_append_inhibited, "s", 0 }, { "DelayInhibited", bus_manager_append_inhibited, "s", 0 }, { "InhibitDelayMaxUSec", bus_property_append_usec, "t", offsetof(Manager, inhibit_delay_max) }, - { "HandlePowerKey", bus_manager_append_handle_button, "s", offsetof(Manager, handle_power_key) }, - { "HandleSuspendKey", bus_manager_append_handle_button, "s", offsetof(Manager, handle_suspend_key) }, - { "HandleHibernateKey", bus_manager_append_handle_button, "s", offsetof(Manager, handle_hibernate_key)}, - { "HandleLidSwitch", bus_manager_append_handle_button, "s", offsetof(Manager, handle_lid_switch) }, + { "HandlePowerKey", bus_manager_append_handle_action, "s", offsetof(Manager, handle_power_key) }, + { "HandleSuspendKey", bus_manager_append_handle_action, "s", offsetof(Manager, handle_suspend_key) }, + { "HandleHibernateKey", bus_manager_append_handle_action, "s", offsetof(Manager, handle_hibernate_key)}, + { "HandleLidSwitch", bus_manager_append_handle_action, "s", offsetof(Manager, handle_lid_switch) }, + { "IdleAction", bus_manager_append_handle_action, "s", offsetof(Manager, idle_action) }, + { "IdleActionUSec", bus_property_append_usec, "t", offsetof(Manager, idle_action_usec) }, { "PreparingForShutdown", bus_manager_append_preparing, "b", 0 }, { "PreparingForSleep", bus_manager_append_preparing, "b", 0 }, { NULL, } diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 1bd1b285d8..076d116161 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -22,11 +22,13 @@ Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manag Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers) Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers) Login.InhibitDelayMaxSec, config_parse_usec, 0, offsetof(Manager, inhibit_delay_max) -Login.HandlePowerKey, config_parse_handle_button, 0, offsetof(Manager, handle_power_key) -Login.HandleSuspendKey, config_parse_handle_button, 0, offsetof(Manager, handle_suspend_key) -Login.HandleHibernateKey, config_parse_handle_button, 0, offsetof(Manager, handle_hibernate_key) -Login.HandleLidSwitch, config_parse_handle_button, 0, offsetof(Manager, handle_lid_switch) +Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) +Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key) +Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key) +Login.HandleLidSwitch, config_parse_handle_action, 0, offsetof(Manager, handle_lid_switch) 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) Login.LidSwitchIgnoreInhibited, config_parse_bool, 0, offsetof(Manager, lid_switch_ignore_inhibited) +Login.IdleAction, config_parse_handle_action, 0, offsetof(Manager, idle_action) +Login.IdleActionSec, config_parse_usec, 0, offsetof(Manager, idle_action_usec) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 5d9401b223..b64a5d302d 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -34,8 +34,6 @@ #include "cgroup-util.h" #include "logind-session.h" -#define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE) - Session* session_new(Manager *m, User *u, const char *id) { Session *s; @@ -736,14 +734,51 @@ bool session_is_active(Session *s) { return s->seat->active == s; } -int session_get_idle_hint(Session *s, dual_timestamp *t) { - char *p; +static int get_tty_atime(const char *tty, usec_t *atime) { + _cleanup_free_ char *p = NULL; struct stat st; - usec_t u, n; - int k; + + assert(tty); + assert(atime); + + if (!path_is_absolute(tty)) { + p = strappend("/dev/", tty); + if (!p) + return -ENOMEM; + + tty = p; + } else if (!path_startswith(tty, "/dev/")) + return -ENOENT; + + if (lstat(tty, &st) < 0) + return -errno; + + *atime = timespec_load(&st.st_atim); + return 0; +} + +static int get_process_ctty_atime(pid_t pid, usec_t *atime) { + _cleanup_free_ char *p = NULL; + int r; + + assert(pid > 0); + assert(atime); + + r = get_ctty(pid, NULL, &p); + if (r < 0) + return r; + + return get_tty_atime(p, atime); +} + +int session_get_idle_hint(Session *s, dual_timestamp *t) { + _cleanup_free_ char *p = NULL; + usec_t atime = 0, n; + int r; assert(s); + /* Explicit idle hint is set */ if (s->idle_hint) { if (t) *t = s->idle_hint_timestamp; @@ -751,40 +786,65 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) { return s->idle_hint; } - if (isempty(s->tty)) + /* Graphical sessions really should really implement a real + * idle hint logic */ + if (s->display) goto dont_know; - if (s->tty[0] != '/') { - p = strappend("/dev/", s->tty); - if (!p) - return -ENOMEM; - } else - p = NULL; + /* For sessions with an explicitly configured tty, let's check + * its atime */ + if (s->tty) { + r = get_tty_atime(s->tty, &atime); + if (r >= 0) + goto found_atime; + } - if (!startswith(p ? p : s->tty, "/dev/")) { - free(p); - goto dont_know; + /* For sessions with a leader but no explicitly configured + * tty, let's check the controlling tty of the leader */ + if (s->leader > 0) { + r = get_process_ctty_atime(s->leader, &atime); + if (r >= 0) + goto found_atime; } - k = lstat(p ? p : s->tty, &st); - free(p); + /* For other TTY sessions, let's find the most recent atime of + * the ttys of any of the processes of the session */ + if (s->cgroup_path) { + _cleanup_fclose_ FILE *f = NULL; - if (k < 0) - goto dont_know; + if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &f) >= 0) { + pid_t pid; - u = timespec_load(&st.st_atim); - n = now(CLOCK_REALTIME); + atime = 0; + while (cg_read_pid(f, &pid) > 0) { + usec_t a; - if (t) - dual_timestamp_from_realtime(t, u); + if (get_process_ctty_atime(pid, &a) >= 0) + if (atime == 0 || atime < a) + atime = a; + } - return u + IDLE_THRESHOLD_USEC < n; + if (atime != 0) + goto found_atime; + } + } dont_know: if (t) *t = s->idle_hint_timestamp; return 0; + +found_atime: + if (t) + dual_timestamp_from_realtime(t, atime); + + n = now(CLOCK_REALTIME); + + if (s->manager->idle_action_usec <= 0) + return 0; + + return atime + s->manager->idle_action_usec <= n; } void session_set_idle_hint(Session *s, bool b) { diff --git a/src/login/logind.c b/src/login/logind.c index 9cce481340..6438631b59 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -28,6 +28,7 @@ #include <sys/epoll.h> #include <sys/ioctl.h> #include <linux/vt.h> +#include <sys/timerfd.h> #include <systemd/sd-daemon.h> @@ -61,6 +62,11 @@ Manager *manager_new(void) { m->handle_lid_switch = HANDLE_SUSPEND; m->lid_switch_ignore_inhibited = true; + m->idle_action_fd = -1; + m->idle_action_usec = 30 * USEC_PER_MINUTE; + m->idle_action = HANDLE_IGNORE; + m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); + m->devices = hashmap_new(string_hash_func, string_compare_func); m->seats = hashmap_new(string_hash_func, string_compare_func); m->sessions = hashmap_new(string_hash_func, string_compare_func); @@ -173,6 +179,9 @@ void manager_free(Manager *m) { if (m->reserve_vt_fd >= 0) close_nointr_nofail(m->reserve_vt_fd); + if (m->idle_action_fd >= 0) + close_nointr_nofail(m->idle_action_fd); + strv_free(m->controllers); strv_free(m->reset_controllers); strv_free(m->kill_only_users); @@ -1441,6 +1450,79 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) { return idle_hint; } +int manager_dispatch_idle_action(Manager *m) { + struct dual_timestamp since; + struct itimerspec its; + int r; + usec_t n; + + assert(m); + + if (m->idle_action == HANDLE_IGNORE || + m->idle_action_usec <= 0) { + r = 0; + goto finish; + } + + zero(its); + n = now(CLOCK_MONOTONIC); + + r = manager_get_idle_hint(m, &since); + if (r <= 0) + /* Not idle. Let's check if after a timeout it it might be idle then. */ + timespec_store(&its.it_value, n + m->idle_action_usec); + else { + /* Idle! Let's see if it's time to do something, or if + * we shall sleep for longer. */ + + if (n >= since.monotonic + m->idle_action_usec && + (m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) { + log_info("System idle. Taking action."); + + manager_handle_action(m, 0, m->idle_action, false, false); + m->idle_action_not_before_usec = n; + } + + timespec_store(&its.it_value, MAX(since.monotonic, m->idle_action_not_before_usec) + m->idle_action_usec); + } + + if (m->idle_action_fd < 0) { + struct epoll_event ev; + + m->idle_action_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->idle_action_fd < 0) { + log_error("Failed to create idle action timer: %m"); + r = -errno; + goto finish; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.u32 = FD_IDLE_ACTION; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_action_fd, &ev) < 0) { + log_error("Failed to add idle action timer to epoll: %m"); + r = -errno; + goto finish; + } + } + + if (timerfd_settime(m->idle_action_fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { + log_error("Failed to reset timerfd: %m"); + r = -errno; + goto finish; + } + + return 0; + +finish: + if (m->idle_action_fd >= 0) { + close_nointr_nofail(m->idle_action_fd); + m->idle_action_fd = -1; + } + + return r; +} int manager_startup(Manager *m) { int r; Seat *seat; @@ -1506,6 +1588,8 @@ int manager_startup(Manager *m) { HASHMAP_FOREACH(inhibitor, m->inhibitors, i) inhibitor_start(inhibitor); + manager_dispatch_idle_action(m); + return 0; } @@ -1589,6 +1673,10 @@ int manager_run(Manager *m) { manager_dispatch_console(m); break; + case FD_IDLE_ACTION: + manager_dispatch_idle_action(m); + break; + case FD_BUS: bus_loop_dispatch(m->bus_fd); break; diff --git a/src/login/logind.conf b/src/login/logind.conf index 2757fba30a..0861d73e0b 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -24,3 +24,5 @@ #SuspendKeyIgnoreInhibited=no #HibernateKeyIgnoreInhibited=no #LidSwitchIgnoreInhibited=yes +#IdleAction=ignore +#IdleActionSec=30min diff --git a/src/login/logind.h b/src/login/logind.h index f415dfbcbf..816635dcfc 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -40,6 +40,7 @@ typedef struct Manager Manager; #include "logind-user.h" #include "logind-inhibit.h" #include "logind-button.h" +#include "logind-action.h" struct Manager { DBusConnection *bus; @@ -99,10 +100,15 @@ struct Manager { usec_t inhibit_delay_max; - HandleButton handle_power_key; - HandleButton handle_suspend_key; - HandleButton handle_hibernate_key; - HandleButton handle_lid_switch; + int idle_action_fd; + usec_t idle_action_usec; + usec_t idle_action_not_before_usec; + HandleAction idle_action; + + HandleAction handle_power_key; + HandleAction handle_suspend_key; + HandleAction handle_hibernate_key; + HandleAction handle_lid_switch; bool power_key_ignore_inhibited; bool suspend_key_ignore_inhibited; @@ -116,6 +122,7 @@ enum { FD_BUTTON_UDEV, FD_CONSOLE, FD_BUS, + FD_IDLE_ACTION, FD_OTHER_BASE }; @@ -138,6 +145,7 @@ int manager_dispatch_seat_udev(Manager *m); int manager_dispatch_vcsa_udev(Manager *m); int manager_dispatch_button_udev(Manager *m); int manager_dispatch_console(Manager *m); +int manager_dispatch_idle_action(Manager *m); int manager_enumerate_devices(Manager *m); int manager_enumerate_buttons(Manager *m); diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h index a9a4dcca6b..bcbf18ffab 100644 --- a/src/shared/dbus-common.h +++ b/src/shared/dbus-common.h @@ -23,6 +23,7 @@ #include <dbus/dbus.h> #include <inttypes.h> +#include <sys/types.h> #ifndef DBUS_ERROR_UNKNOWN_OBJECT #define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" diff --git a/src/shared/util.c b/src/shared/util.c index 9ec6e2fe2f..d01c2061b4 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -2869,7 +2869,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr)); char_array_0(fn); - if ((k = readlink_malloc(fn, &s)) < 0) { + k = readlink_malloc(fn, &s); + if (k < 0) { if (k != -ENOENT) return k; @@ -2890,7 +2891,8 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { * symlink in /dev/char. Let's return something * vaguely useful. */ - if (!(b = strdup(fn + 5))) + b = strdup(fn + 5); + if (!b) return -ENOMEM; *r = b; |