chiark / gitweb /
logind: add support for automatic suspend/hibernate/shutdown on idle
authorLennart Poettering <lennart@poettering.net>
Sun, 23 Dec 2012 21:32:48 +0000 (22:32 +0100)
committerLennart Poettering <lennart@poettering.net>
Sun, 23 Dec 2012 23:29:40 +0000 (00:29 +0100)
13 files changed:
Makefile.am
src/login/logind-action.c [new file with mode: 0644]
src/login/logind-action.h [new file with mode: 0644]
src/login/logind-button.c
src/login/logind-button.h
src/login/logind-dbus.c
src/login/logind-gperf.gperf
src/login/logind-session.c
src/login/logind.c
src/login/logind.conf
src/login/logind.h
src/shared/dbus-common.h
src/shared/util.c

index 163a180..477b3a6 100644 (file)
@@ -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 (file)
index 0000000..bd5664e
--- /dev/null
@@ -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 (file)
index 0000000..7ab4464
--- /dev/null
@@ -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
index 8bbd731..dbf3d3c 100644 (file)
@@ -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");
index b76ca32..1c5a845 100644 (file)
 
 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
index 89021ab..77a06f2 100644 (file)
         "  <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, }
index 1bd1b28..076d116 100644 (file)
@@ -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)
index 5d9401b..b64a5d3 100644 (file)
@@ -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) {
index 9cce481..6438631 100644 (file)
@@ -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;
index 2757fba..0861d73 100644 (file)
@@ -24,3 +24,5 @@
 #SuspendKeyIgnoreInhibited=no
 #HibernateKeyIgnoreInhibited=no
 #LidSwitchIgnoreInhibited=yes
+#IdleAction=ignore
+#IdleActionSec=30min
index f415dfb..816635d 100644 (file)
@@ -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);
index a9a4dcc..bcbf18f 100644 (file)
@@ -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"
index 9ec6e2f..d01c206 100644 (file)
@@ -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;