[systemd-commits] 8 commits - Makefile.am TODO src/login src/shared src/systemctl

Lennart Poettering lennart at kemper.freedesktop.org
Sun Dec 23 15:45:53 PST 2012


 Makefile.am                  |    2 
 TODO                         |   20 +-----
 src/login/logind-action.c    |  141 +++++++++++++++++++++++++++++++++++++++++++
 src/login/logind-action.h    |   54 ++++++++++++++++
 src/login/logind-button.c    |   95 ++--------------------------
 src/login/logind-button.h    |   19 -----
 src/login/logind-dbus.c      |   14 ++--
 src/login/logind-gperf.gperf |   10 +--
 src/login/logind-session.c   |  110 +++++++++++++++++++++++++--------
 src/login/logind.c           |   88 ++++++++++++++++++++++++++
 src/login/logind.conf        |    2 
 src/login/logind.h           |   16 +++-
 src/login/pam-module.c       |   19 ++++-
 src/shared/cgroup-show.c     |    8 --
 src/shared/cgroup-util.c     |   14 ++++
 src/shared/cgroup-util.h     |    1 
 src/shared/dbus-common.h     |    1 
 src/shared/util.c            |   22 +++++-
 src/systemctl/systemctl.c    |    3 
 19 files changed, 464 insertions(+), 175 deletions(-)

New commits:
commit 95b4be171988fc2ea33377b1b4450e5d410add7b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 22:38:21 2012 +0100

    update TODO

diff --git a/TODO b/TODO
index 5591125..8526d69 100644
--- a/TODO
+++ b/TODO
@@ -19,6 +19,8 @@ F18:
 
 Features:
 
+* logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around
+
 * service: when killing a service with SIGKILL always kill all processes, even if for SIGTERM we only killed the main process
 
 * rewrite #!/usr/bin/python line in python scripts to use @PYTHON@ instead
@@ -377,12 +379,6 @@ Features:
 
 * automount: implement expire
 
-* logind: auto-suspend, auto-shutdown:
-        IdleAction=(none|suspend|opportunistic|hibernate|poweroff)
-        IdleActionDelay=...
-        SessionIdleMode=(explicit|ignore|login)
-        ForceShutdown=(yes|no)
-
 * services which create their own subcgroups break cgroup-empty notification (needs to be fixed in the kernel)
 
 * don't delete /tmp/systemd-namespace-* before a process is gone down

commit dc3a1b76a6a6f9dfe9b451f534587251b50a0685
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 22:12:01 2012 +0100

    logind: don't allow suspending/hibernating if the kernel doesn't support it

diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index bd5664e..e1517d6 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -19,6 +19,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <unistd.h>
+
 #include "conf-parser.h"
 #include "special.h"
 #include "dbus-common.h"
@@ -54,6 +56,7 @@ int manager_handle_action(
         DBusError error;
         int r;
         InhibitWhat inhibit_operation;
+        bool supported = true;
 
         assert(m);
 
@@ -63,6 +66,20 @@ int manager_handle_action(
                 return 0;
         }
 
+        if (handle == HANDLE_SUSPEND)
+                supported = can_sleep("mem") > 0;
+        else if (handle == HANDLE_HIBERNATE)
+                supported = can_sleep("disk") > 0;
+        else if (handle == HANDLE_HYBRID_SLEEP)
+                supported = can_sleep("disk") > 0 && can_sleep_disk("suspend") > 0;
+        else if (handle == HANDLE_KEXEC)
+                supported = access("/sbin/kexec", X_OK) >= 0;
+
+        if (!supported) {
+                log_warning("Requested operation not supported, ignoring.");
+                return -ENOTSUP;
+        }
+
         /* 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)) {

commit 679b7d791241eb52e15e4914e0d5709f67ff5123
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 21:58:37 2012 +0100

    util: if /sys mounted read-only we can't suspend/hibernate

diff --git a/src/shared/util.c b/src/shared/util.c
index 59d8544..1779625 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5282,9 +5282,13 @@ int can_sleep(const char *type) {
 
         assert(type);
 
+        /* If /sys is read-only we cannot sleep */
+        if (access("/sys/power/state", W_OK) < 0)
+                return false;
+
         r = read_one_line_file("/sys/power/state", &p);
         if (r < 0)
-                return r == -ENOENT ? 0 : r;
+                return false;
 
         k = strlen(type);
         FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
@@ -5302,9 +5306,14 @@ int can_sleep_disk(const char *type) {
 
         assert(type);
 
+        /* If /sys is read-only we cannot sleep */
+        if (access("/sys/power/state", W_OK) < 0 ||
+            access("/sys/power/disk", W_OK) < 0)
+                return false;
+
         r = read_one_line_file("/sys/power/disk", &p);
         if (r < 0)
-                return r == -ENOENT ? 0 : r;
+                return false;
 
         k = strlen(type);
         FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {

commit 11dc5d2b649afaf9f4470ad97929ebe844604f44
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 17:28:17 2012 +0100

    util: when determining controlling tty of a process properly handle processes without

diff --git a/src/shared/util.c b/src/shared/util.c
index d01c206..59d8544 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -2851,6 +2851,9 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
                    &ttynr) != 1)
                 return -EIO;
 
+        if (major(ttynr) == 0 && minor(ttynr) == 0)
+                return -ENOENT;
+
         *d = (dev_t) ttynr;
         return 0;
 }

commit 23406ce58aa7142e8df3c5c9e5ac34a01e90e3e0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 22:32:48 2012 +0100

    logind: add support for automatic suspend/hibernate/shutdown on idle

diff --git a/Makefile.am b/Makefile.am
index 163a180..477b3a6 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 0000000..bd5664e
--- /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 0000000..7ab4464
--- /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 8bbd731..dbf3d3c 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 b76ca32..1c5a845 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 89021ab..77a06f2 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 1bd1b28..076d116 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 5d9401b..b64a5d3 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 9cce481..6438631 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 2757fba..0861d73 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 f415dfb..816635d 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 a9a4dcc..bcbf18f 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 9ec6e2f..d01c206 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;

commit 0ad1271f564b9c956685938167f7ea8c301e835e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 22:31:17 2012 +0100

    pam: properly handle SSH logins lacking the PAM tty field

diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index 08a9328..e6764a1 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -322,7 +322,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         struct passwd *pw;
         bool kill_processes = false, debug = false;
-        const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type, *class, *cvtnr = NULL;
+        const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class, *cvtnr = NULL;
         char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
         DBusError error;
         uint32_t uid, pid;
@@ -453,9 +453,17 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         display = tty;
                 tty = "";
         } else if (streq(tty, "cron")) {
-                /* cron has been setting PAM_TTY to "cron" for a very long time
-                 * and it cannot stop doing that for compatibility reasons. */
+                /* cron has been setting PAM_TTY to "cron" for a very
+                 * long time and it probably shouldn't stop doing that
+                 * for compatibility reasons. */
                 tty = "";
+                type = "unspecified";
+        } else if (streq(tty, "ssh")) {
+                /* ssh has been setting PAM_TTY to "ssh" for a very
+                 * long time and probably shouldn't stop doing that
+                 * for compatibility reasons. */
+                tty = "";
+                type ="tty";
         }
 
         /* If this fails vtnr will be 0, that's intended */
@@ -469,8 +477,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         get_seat_from_display(display, NULL, &vtnr);
         }
 
-        type = !isempty(display) ? "x11" :
-                   !isempty(tty) ? "tty" : "unspecified";
+        if (!type)
+                type = !isempty(display) ? "x11" :
+                        !isempty(tty) ? "tty" : "unspecified";
 
         class = pam_getenv(handle, "XDG_SESSION_CLASS");
         if (isempty(class))

commit 15abdb9a6f34628b04b887e0b9649fa582d6cd37
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 11:56:22 2012 +0100

    update TODO

diff --git a/TODO b/TODO
index 86f90ea..5591125 100644
--- a/TODO
+++ b/TODO
@@ -19,7 +19,7 @@ F18:
 
 Features:
 
-* service: when killing a service with SIGKILL always kill all processes
+* service: when killing a service with SIGKILL always kill all processes, even if for SIGTERM we only killed the main process
 
 * rewrite #!/usr/bin/python line in python scripts to use @PYTHON@ instead
 
@@ -45,7 +45,7 @@ Features:
 
 * check if we can make journalctl by default use --follow mode inside of less if called without args?
 
-* Addd a verbose mode to "systemctl start" and friends that explains what is being done or not done
+* Add a verbose mode to "systemctl start" and friends that explains what is being done or not done
 
 * "systemctl is-failed" to join "systemctl is-active" and "systemctl is-enabled".
 
@@ -57,7 +57,7 @@ Features:
 
 * use polkit "imply" for binding hostname actions together
 
-* journal: when waiting for journal additions always sleep at least 1s or so, in order to minimize wakeups
+* journal: when waiting for journal additions in the client always sleep at least 1s or so, in order to minimize wakeups
 
 * When shutdown.target is queued begin with an asynchronous sync()?
 
@@ -150,8 +150,6 @@ Features:
 
 * document that people can use file system ACLs to manage access to journal files, with example
 
-* don't show cgroup in "systemctl status" if empty/non-existent, especially for foreign .mount units
-
 * timedated: export boolean that clarifies whether NTP is even available
 
 * timedated: refuse time changes when NTP is on
@@ -192,7 +190,7 @@ Features:
 
 * WorkingDirectory: support env var replacements like in ExecStart= so that people can use $HOME
 
-* refuse boot if /etc/machine-id is not useful
+* refuse boot if /etc/machine-id is not useful (or set taint?)
 
 * nspawn: consider changing users for -u with su, so that NSS resolving works correctly
 
@@ -217,8 +215,6 @@ Features:
 * move cryptsetup key caching into kernel keyctl?
   https://bugs.freedesktop.org/show_bug.cgi?id=54982
 
-* make nspawn work without terminal
-
 * hw watchdog: optionally try to use the preset watchdog timeout instead of always overriding it
   https://bugs.freedesktop.org/show_bug.cgi?id=54712
 

commit b08121d00467874ac9aa0dc6f59787877f1bb427
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 23 11:56:13 2012 +0100

    systemctl: don't show cgroup field for a unit if cgroup is empty

diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 437d097..2b79f37 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -335,7 +335,7 @@ int show_cgroup_and_extra(const char *controller, const char *path, const char *
 
 int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, bool all, const pid_t extra_pids[], unsigned n_extra_pids) {
         int r;
-        char *controller, *path;
+        _cleanup_free_ char *controller = NULL, *path = NULL;
 
         assert(spec);
 
@@ -343,9 +343,5 @@ int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned
         if (r < 0)
                 return r;
 
-        r = show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, all, extra_pids, n_extra_pids);
-        free(controller);
-        free(path);
-
-        return r;
+        return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, all, extra_pids, n_extra_pids);
 }
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index b0d378d..18cbf04 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -934,6 +934,20 @@ int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
         return !found;
 }
 
+int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
+        int r;
+        _cleanup_free_ char *controller = NULL, *path = NULL;
+
+        assert(spec);
+
+        r = cg_split_spec(spec, &controller, &path);
+        if (r < 0)
+                return r;
+
+        return cg_is_empty(controller, path, ignore_self);
+}
+
+
 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
         int r;
         DIR *d = NULL;
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
index 697669d..af2efc3 100644
--- a/src/shared/cgroup-util.h
+++ b/src/shared/cgroup-util.h
@@ -65,6 +65,7 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui
 int cg_install_release_agent(const char *controller, const char *agent);
 
 int cg_is_empty(const char *controller, const char *path, bool ignore_self);
+int cg_is_empty_by_spec(const char *spec, bool ignore_self);
 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
 
 int cg_get_user_path(char **path);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 3abd7dc..876e782 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2265,7 +2265,8 @@ static void print_status_info(UnitStatusInfo *i) {
         if (i->status_text)
                 printf("\t  Status: \"%s\"\n", i->status_text);
 
-        if (i->default_control_group) {
+        if (i->default_control_group &&
+            (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_by_spec(i->default_control_group, false) == 0)) {
                 unsigned c;
 
                 printf("\t  CGroup: %s\n", i->default_control_group);



More information about the systemd-commits mailing list