chiark / gitweb /
logind: implement idle hint logic
authorLennart Poettering <lennart@poettering.net>
Fri, 17 Jun 2011 13:59:18 +0000 (15:59 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 21 Jun 2011 17:29:45 +0000 (19:29 +0200)
14 files changed:
src/logind-dbus.c
src/logind-seat-dbus.c
src/logind-seat.c
src/logind-seat.h
src/logind-session-dbus.c
src/logind-session.c
src/logind-session.h
src/logind-user-dbus.c
src/logind-user.c
src/logind-user.h
src/logind.c
src/logind.h
src/util.c
src/util.h

index 90db94184fb3f6f21cfcff2f1501dc985adff71e..42374d7fdb07826f38d8cd6152821f7441359286 100644 (file)
@@ -19,6 +19,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <errno.h>
+#include <string.h>
+
 #include "logind.h"
 #include "dbus-common.h"
 
@@ -62,6 +65,9 @@
         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
         "  </method>\n"                                                 \
+        "  <method name=\"ActivateSession\">\n"                         \
+        "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
+        "  </method>\n"                                                 \
         "  <method name=\"TerminateSession\">\n"                        \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
         "  </method>\n"                                                 \
         "  <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
         "  <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
         "  <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
+        "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
+        "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
+        "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
         " </interface>\n"
 
 #define INTROSPECTION_BEGIN                                             \
         BUS_GENERIC_INTERFACES_LIST                  \
         "org.freedesktop.login1.Manager\0"
 
+static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
+        Manager *m = data;
+        bool b;
+
+        assert(i);
+        assert(property);
+        assert(m);
+
+        b = manager_get_idle_hint(m, NULL);
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
+        Manager *m = data;
+        dual_timestamp t;
+        uint64_t u;
+
+        assert(i);
+        assert(property);
+        assert(m);
+
+        manager_get_idle_hint(m, &t);
+        u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static DBusHandlerResult manager_message_handler(
                 DBusConnection *connection,
                 DBusMessage *message,
@@ -127,13 +169,16 @@ static DBusHandlerResult manager_message_handler(
         Manager *m = userdata;
 
         const BusProperty properties[] = {
-                { "org.freedesktop.login1.Manager", "ControlGroupHierarchy", bus_property_append_string,   "s",  m->cgroup_path          },
-                { "org.freedesktop.login1.Manager", "Controllers",           bus_property_append_strv,     "as", m->controllers          },
-                { "org.freedesktop.login1.Manager", "ResetControllers",      bus_property_append_strv,     "as", m->reset_controllers    },
-                { "org.freedesktop.login1.Manager", "NAutoVTs",              bus_property_append_unsigned, "u",  &m->n_autovts           },
-                { "org.freedesktop.login1.Manager", "KillOnlyUsers",         bus_property_append_strv,     "as", m->kill_only_users      },
-                { "org.freedesktop.login1.Manager", "KillExcludeUsers",      bus_property_append_strv,     "as", m->kill_exclude_users   },
-                { "org.freedesktop.login1.Manager", "KillUserProcesses",     bus_property_append_bool,     "b",  &m->kill_user_processes },
+                { "org.freedesktop.login1.Manager", "ControlGroupHierarchy",  bus_property_append_string,   "s",  m->cgroup_path          },
+                { "org.freedesktop.login1.Manager", "Controllers",            bus_property_append_strv,     "as", m->controllers          },
+                { "org.freedesktop.login1.Manager", "ResetControllers",       bus_property_append_strv,     "as", m->reset_controllers    },
+                { "org.freedesktop.login1.Manager", "NAutoVTs",               bus_property_append_unsigned, "u",  &m->n_autovts           },
+                { "org.freedesktop.login1.Manager", "KillOnlyUsers",          bus_property_append_strv,     "as", m->kill_only_users      },
+                { "org.freedesktop.login1.Manager", "KillExcludeUsers",       bus_property_append_strv,     "as", m->kill_exclude_users   },
+                { "org.freedesktop.login1.Manager", "KillUserProcesses",      bus_property_append_bool,     "b",  &m->kill_user_processes },
+                { "org.freedesktop.login1.Manager", "IdleHint",               bus_manager_append_idle_hint, "b",  m                       },
+                { "org.freedesktop.login1.Manager", "IdleSinceHint",          bus_manager_append_idle_hint_since, "t", m                  },
+                { "org.freedesktop.login1.Manager", "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", m                  },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
index 4937d65f745adc60f78c6e30baf7f9e248b1d12c..32171ce4010d1a5d15243e558835ebb3440acaa9 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include <errno.h>
+#include <string.h>
 
 #include "logind.h"
 #include "logind-seat.h"
@@ -36,6 +37,9 @@
         "  <property name=\"ActiveSession\" type=\"so\" access=\"read\"/>\n" \
         "  <property name=\"CanActivateSessions\" type=\"b\" access=\"read\"/>\n" \
         "  <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
+        "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
+        "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
+        "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
         " </interface>\n"                                               \
 
 #define INTROSPECTION                                                   \
@@ -129,7 +133,6 @@ static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, vo
         return 0;
 }
 
-
 static int bus_seat_append_can_activate(DBusMessageIter *i, const char *property, void *data) {
         Seat *s = data;
         dbus_bool_t b;
@@ -138,7 +141,7 @@ static int bus_seat_append_can_activate(DBusMessageIter *i, const char *property
         assert(property);
         assert(s);
 
-        b = s->manager->vtconsole == s;
+        b = seat_is_vtconsole(s);
 
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
                 return -ENOMEM;
@@ -146,6 +149,39 @@ static int bus_seat_append_can_activate(DBusMessageIter *i, const char *property
         return 0;
 }
 
+static int bus_seat_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
+        Seat *s = data;
+        bool b;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        b = seat_get_idle_hint(s, NULL);
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_seat_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
+        Seat *s = data;
+        dual_timestamp t;
+        uint64_t k;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        seat_get_idle_hint(s, &t);
+        k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
         Seat *s;
         char *id;
@@ -181,14 +217,73 @@ static DBusHandlerResult seat_message_dispatch(
                 { "org.freedesktop.login1.Seat", "ActiveSession",       bus_seat_append_active,       "(so)",  s     },
                 { "org.freedesktop.login1.Seat", "CanActivateSessions", bus_seat_append_can_activate, "b",     s     },
                 { "org.freedesktop.login1.Seat", "Sessions",            bus_seat_append_sessions,     "a(so)", s     },
+                { "org.freedesktop.login1.Seat", "IdleHint",            bus_seat_append_idle_hint,    "b",     s     },
+                { "org.freedesktop.login1.Seat", "IdleSinceHint",          bus_seat_append_idle_hint_since, "t", s   },
+                { "org.freedesktop.login1.Seat", "IdleSinceHintMonotonic", bus_seat_append_idle_hint_since, "t", s   },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
+        DBusError error;
+        DBusMessage *reply = NULL;
+        int r;
+
         assert(s);
         assert(connection);
         assert(message);
 
-        return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+        dbus_error_init(&error);
+
+        if (dbus_message_is_method_call(message, "org.freedesktop.login1.Seat", "Terminate")) {
+
+                r = seat_stop_sessions(s);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, NULL, r);
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Seat", "ActivateSession")) {
+                const char *name;
+                Session *session;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &name,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                session = hashmap_get(s->manager->sessions, name);
+                if (!session || session->seat != s)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                r = session_activate(session);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, NULL, r);
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+        } else
+                return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+
+        if (reply) {
+                if (!dbus_connection_send(connection, reply, NULL))
+                        goto oom;
+
+                dbus_message_unref(reply);
+        }
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
 }
 
 static DBusHandlerResult seat_message_handler(
index 2ba3060be33940fb4a691778696898c43486e80d..751f59a48d6f3d74239263f5be67e4761cccf8bd 100644 (file)
@@ -99,7 +99,7 @@ int seat_save(Seat *s) {
         fprintf(f,
                 "# This is private data. Do not parse.\n"
                 "IS_VTCONSOLE=%i\n",
-                s->manager->vtconsole == s);
+                seat_is_vtconsole(s));
 
         if (s->active) {
                 assert(s->active->user);
@@ -158,6 +158,8 @@ finish:
 int seat_load(Seat *s) {
         assert(s);
 
+        /* There isn't actually anything to read here ... */
+
         return 0;
 }
 
@@ -191,7 +193,7 @@ static int seat_preallocate_vts(Seat *s) {
         if (s->manager->n_autovts <= 0)
                 return 0;
 
-        if (s->manager->vtconsole != s)
+        if (!seat_is_vtconsole(s))
                 return 0;
 
         for (i = 1; i < s->manager->n_autovts; i++) {
@@ -230,7 +232,7 @@ int seat_active_vt_changed(Seat *s, int vtnr) {
         assert(s);
         assert(vtnr >= 1);
 
-        if (s->manager->vtconsole != s)
+        if (!seat_is_vtconsole(s))
                 return -EINVAL;
 
         log_debug("VT changed to %i", vtnr);
@@ -260,7 +262,7 @@ int seat_read_active_vt(Seat *s) {
 
         assert(s);
 
-        if (s->manager->vtconsole != s)
+        if (!seat_is_vtconsole(s))
                 return 0;
 
         lseek(s->manager->console_active_fd, SEEK_SET, 0);
@@ -316,8 +318,7 @@ int seat_start(Seat *s) {
 }
 
 int seat_stop(Seat *s) {
-        Session *session;
-        int r = 0, k;
+        int r = 0;
 
         assert(s);
 
@@ -326,24 +327,96 @@ int seat_stop(Seat *s) {
 
         log_info("Removed seat %s.", s->id);
 
+        seat_stop_sessions(s);
+
+        unlink(s->state_file);
+        seat_add_to_gc_queue(s);
+
+        s->started = false;
+
+        return r;
+}
+
+int seat_stop_sessions(Seat *s) {
+        Session *session;
+        int r = 0, k;
+
+        assert(s);
+
         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
                 k = session_stop(session);
                 if (k < 0)
                         r = k;
         }
 
-        unlink(s->state_file);
-        seat_add_to_gc_queue(s);
+        return r;
+}
 
-        s->started = false;
+int seat_attach_session(Seat *s, Session *session) {
+        assert(s);
+        assert(session);
+        assert(!session->seat);
 
-        return r;
+        if (!seat_is_vtconsole(s)) {
+                if (s->sessions)
+                        return -EEXIST;
+
+                assert(!s->active);
+                s->active = session;
+        }
+
+        session->seat = s;
+        LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
+
+        return 0;
+}
+
+bool seat_is_vtconsole(Seat *s) {
+        assert(s);
+
+        return s->manager->vtconsole == s;
+}
+
+int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
+        Session *session;
+        bool idle_hint = true;
+        dual_timestamp ts = { 0, 0 };
+
+        assert(s);
+
+        LIST_FOREACH(sessions_by_seat, session, s->sessions) {
+                dual_timestamp k;
+                int ih;
+
+                ih = session_get_idle_hint(session, &k);
+                if (ih < 0)
+                        return ih;
+
+                if (!ih) {
+                        if (!idle_hint) {
+                                if (k.monotonic < ts.monotonic)
+                                        ts = k;
+                        } else {
+                                idle_hint = false;
+                                ts = k;
+                        }
+                } else if (idle_hint) {
+
+                        if (k.monotonic > ts.monotonic)
+                                ts = k;
+                }
+        }
+
+        if (t)
+                *t = ts;
+
+        return idle_hint;
 }
 
 int seat_check_gc(Seat *s) {
         assert(s);
 
-        if (s->manager->vtconsole == s)
+        if (seat_is_vtconsole(s))
                 return 1;
 
         return !!s->devices;
index b045bde931ab0079ce24c38342657855d9cf40cb..dfbb2a2a0a6e698cf1e3413b80564093b545ee09 100644 (file)
@@ -57,8 +57,14 @@ int seat_apply_acls(Seat *s, Session *old_active);
 int seat_active_vt_changed(Seat *s, int vtnr);
 int seat_read_active_vt(Seat *s);
 
+int seat_attach_session(Seat *s, Session *session);
+
+bool seat_is_vtconsole(Seat *s);
+int seat_get_idle_hint(Seat *s, dual_timestamp *t);
+
 int seat_start(Seat *s);
 int seat_stop(Seat *s);
+int seat_stop_sessions(Seat *s);
 
 int seat_check_gc(Seat *s);
 void seat_add_to_gc_queue(Seat *s);
index 539384b7d4dac3b19cabdbf689a68b0f3ed2e4aa..8b5b3ad51f8210d83572d5480c3506c0c5e71593 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include <errno.h>
+#include <string.h>
 
 #include "logind.h"
 #include "logind-session.h"
@@ -156,6 +157,39 @@ static int bus_session_append_active(DBusMessageIter *i, const char *property, v
         return 0;
 }
 
+static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
+        Session *s = data;
+        bool b;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        b = session_get_idle_hint(s, NULL);
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
+        Session *s = data;
+        dual_timestamp t;
+        uint64_t u;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        session_get_idle_hint(s, &t);
+        u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
 
 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
@@ -189,24 +223,29 @@ static DBusHandlerResult session_message_dispatch(
                 DBusMessage *message) {
 
         const BusProperty properties[] = {
-                { "org.freedesktop.login1.Session", "Id",               bus_property_append_string, "s",    s->id                },
-                { "org.freedesktop.login1.Session", "User",             bus_session_append_user,    "(uo)", s                    },
-                { "org.freedesktop.login1.Session", "Name",             bus_property_append_string, "s",    s->user->name        },
-                { "org.freedesktop.login1.Session", "ControlGroupPath", bus_property_append_string, "s",    s->cgroup_path       },
-                { "org.freedesktop.login1.Session", "VTNr",             bus_property_append_uint32, "u",    &s->vtnr             },
-                { "org.freedesktop.login1.Session", "Seat",             bus_session_append_seat,    "(so)", s                    },
-                { "org.freedesktop.login1.Session", "TTY",              bus_property_append_string, "s",    s->tty               },
-                { "org.freedesktop.login1.Session", "Display",          bus_property_append_string, "s",    s->display           },
-                { "org.freedesktop.login1.Session", "Remote",           bus_property_append_bool,   "b",    &s->remote           },
-                { "org.freedesktop.login1.Session", "RemoteUser",       bus_property_append_string, "s",    s->remote_user       },
-                { "org.freedesktop.login1.Session", "RemoteHost",       bus_property_append_string, "s",    s->remote_host       },
-                { "org.freedesktop.login1.Session", "Leader",           bus_property_append_pid,    "u",    &s->leader           },
-                { "org.freedesktop.login1.Session", "Audit",            bus_property_append_uint32, "u",    &s->audit_id         },
-                { "org.freedesktop.login1.Session", "Type",             bus_session_append_type,    "s",    &s->type             },
-                { "org.freedesktop.login1.Session", "Active",           bus_session_append_active,  "b",    s                    },
-                { "org.freedesktop.login1.Session", "Controllers",      bus_property_append_strv,   "as",   s->controllers       },
-                { "org.freedesktop.login1.Session", "ResetControllers", bus_property_append_strv,   "as",   s->reset_controllers },
-                { "org.freedesktop.login1.Session", "KillProcesses",    bus_property_append_bool,   "b",    &s->kill_processes   },
+                { "org.freedesktop.login1.Session", "Id",                 bus_property_append_string,   "s",    s->id                   },
+                { "org.freedesktop.login1.Session", "User",               bus_session_append_user,      "(uo)", s                       },
+                { "org.freedesktop.login1.Session", "Name",               bus_property_append_string,   "s",    s->user->name           },
+                { "org.freedesktop.login1.Session", "Timestamp",          bus_property_append_usec,     "t",    &s->timestamp.realtime  },
+                { "org.freedesktop.login1.Session", "TimestampMonotonic", bus_property_append_usec,     "t",    &s->timestamp.monotonic },
+                { "org.freedesktop.login1.Session", "ControlGroupPath",   bus_property_append_string,   "s",    s->cgroup_path          },
+                { "org.freedesktop.login1.Session", "VTNr",               bus_property_append_uint32,   "u",    &s->vtnr                },
+                { "org.freedesktop.login1.Session", "Seat",               bus_session_append_seat,      "(so)", s                       },
+                { "org.freedesktop.login1.Session", "TTY",                bus_property_append_string,   "s",    s->tty                  },
+                { "org.freedesktop.login1.Session", "Display",            bus_property_append_string,   "s",    s->display              },
+                { "org.freedesktop.login1.Session", "Remote",             bus_property_append_bool,     "b",    &s->remote              },
+                { "org.freedesktop.login1.Session", "RemoteUser",         bus_property_append_string,   "s",    s->remote_user          },
+                { "org.freedesktop.login1.Session", "RemoteHost",         bus_property_append_string,   "s",    s->remote_host          },
+                { "org.freedesktop.login1.Session", "Leader",             bus_property_append_pid,      "u",    &s->leader              },
+                { "org.freedesktop.login1.Session", "Audit",              bus_property_append_uint32,   "u",    &s->audit_id            },
+                { "org.freedesktop.login1.Session", "Type",               bus_session_append_type,      "s",    &s->type                },
+                { "org.freedesktop.login1.Session", "Active",             bus_session_append_active,    "b",    s                       },
+                { "org.freedesktop.login1.Session", "Controllers",        bus_property_append_strv,     "as",   s->controllers          },
+                { "org.freedesktop.login1.Session", "ResetControllers",   bus_property_append_strv,     "as",   s->reset_controllers    },
+                { "org.freedesktop.login1.Session", "KillProcesses",      bus_property_append_bool,     "b",    &s->kill_processes      },
+                { "org.freedesktop.login1.Session", "IdleHint",           bus_session_append_idle_hint, "b",    s                       },
+                { "org.freedesktop.login1.Session", "IdleSinceHint",          bus_session_append_idle_hint_since, "t", s                },
+                { "org.freedesktop.login1.Session", "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", s                },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
index 6b3b277897544084b844aef1331e6b8c02ada2d0..89fe02c06112b764255fc84dec0bde7ecee73ffe 100644 (file)
@@ -28,6 +28,8 @@
 #include "util.h"
 #include "cgroup-util.h"
 
+#define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE)
+
 Session* session_new(Manager *m, User *u, const char *id) {
         Session *s;
 
@@ -153,7 +155,7 @@ int session_save(Session *s) {
                         "REMOTE_USER=%s\n",
                         s->remote_user);
 
-        if (s->seat && s->seat->manager->vtconsole == s->seat)
+        if (s->seat && seat_is_vtconsole(s->seat))
                 fprintf(f,
                         "VTNR=%i\n",
                         s->vtnr);
@@ -187,9 +189,87 @@ finish:
 }
 
 int session_load(Session *s) {
+        char *remote = NULL,
+                *kill_processes = NULL,
+                *seat = NULL,
+                *vtnr = NULL,
+                *leader = NULL,
+                *audit_id = NULL;
+
+        int k, r;
+
         assert(s);
 
-        return 0;
+        r = parse_env_file(s->state_file, NEWLINE,
+                           "REMOTE",         &remote,
+                           "KILL_PROCESSES", &kill_processes,
+                           "CGROUP",         &s->cgroup_path,
+                           "SEAT",           &seat,
+                           "TTY",            &s->tty,
+                           "DISPLAY",        &s->display,
+                           "REMOTE_HOST",    &s->remote_host,
+                           "REMOTE_USER",    &s->remote_user,
+                           "VTNR",           &vtnr,
+                           "LEADER",         &leader,
+                           "AUDIT_ID",       &audit_id,
+                           NULL);
+
+        if (r < 0)
+                goto finish;
+
+        if (remote) {
+                k = parse_boolean(remote);
+                if (k >= 0)
+                        s->remote = k;
+        }
+
+        if (kill_processes) {
+                k = parse_boolean(kill_processes);
+                if (k >= 0)
+                        s->kill_processes = k;
+        }
+
+        if (seat) {
+                Seat *o;
+
+                o = hashmap_get(s->manager->seats, seat);
+                if (o)
+                        seat_attach_session(o, s);
+        }
+
+        if (vtnr && s->seat && seat_is_vtconsole(s->seat)) {
+                int v;
+
+                k = safe_atoi(vtnr, &v);
+                if (k >= 0 && v >= 1)
+                        s->vtnr = v;
+        }
+
+        if (leader) {
+                pid_t pid;
+
+                k = parse_pid(leader, &pid);
+                if (k >= 0 && pid >= 1)
+                        s->leader = pid;
+        }
+
+        if (audit_id) {
+                uint32_t l;
+
+                k = safe_atou32(audit_id, &l);
+                if (k >= 0 && l >= l)
+                        s->audit_id = l;
+        }
+
+finish:
+        free(remote);
+        free(kill_processes);
+        free(seat);
+        free(vtnr);
+        free(leader);
+        free(audit_id);
+
+        return r;
 }
 
 int session_activate(Session *s) {
@@ -207,7 +287,7 @@ int session_activate(Session *s) {
         if (s->seat->active == s)
                 return 0;
 
-        assert(s->manager->vtconsole == s->seat);
+        assert(seat_is_vtconsole(s->seat));
 
         r = chvt(s->vtnr);
         if (r < 0)
@@ -462,6 +542,59 @@ bool session_is_active(Session *s) {
         return s->seat->active == s;
 }
 
+int session_get_idle_hint(Session *s, dual_timestamp *t) {
+        char *p;
+        struct stat st;
+        usec_t u, n;
+        bool b;
+        int k;
+
+        assert(s);
+
+        if (s->idle_hint) {
+                if (t)
+                        *t = s->idle_hint_timestamp;
+
+                return s->idle_hint;
+        }
+
+        if (isempty(s->tty))
+                goto dont_know;
+
+        if (s->tty[0] != '/') {
+                p = strappend("/dev/", s->tty);
+                if (!p)
+                        return -ENOMEM;
+        } else
+                p = NULL;
+
+        if (!startswith(p ? p : s->tty, "/dev/")) {
+                free(p);
+                goto dont_know;
+        }
+
+        k = lstat(p ? p : s->tty, &st);
+        free(p);
+
+        if (k < 0)
+                goto dont_know;
+
+        u = timespec_load(&st.st_atim);
+        n = now(CLOCK_REALTIME);
+        b = u + IDLE_THRESHOLD_USEC < n;
+
+        if (t)
+                dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
+
+        return b;
+
+dont_know:
+        if (t)
+                *t = s->idle_hint_timestamp;
+
+        return 0;
+}
+
 int session_check_gc(Session *s) {
         int r;
 
index 60ac1c54e603976739cb2cd1964fb9a8480a33f3..76823931daa47402d5c9c013bcfdc91068efa9de 100644 (file)
@@ -67,6 +67,9 @@ struct Session {
         char *cgroup_path;
         char **controllers, **reset_controllers;
 
+        bool idle_hint;
+        dual_timestamp idle_hint_timestamp;
+
         bool kill_processes;
         bool in_gc_queue:1;
 
@@ -82,6 +85,7 @@ int session_check_gc(Session *s);
 void session_add_to_gc_queue(Session *s);
 int session_activate(Session *s);
 bool session_is_active(Session *s);
+int session_get_idle_hint(Session *s, dual_timestamp *t);
 int session_start(Session *s);
 int session_stop(Session *s);
 int session_save(Session *s);
index 7c8bb27c6d3ee44fd20af2a77d812814b7045d6e..3be2c05d263f2b2d7c6c562d7489290d2fa51b4f 100644 (file)
@@ -20,6 +20,7 @@
 ***/
 
 #include <errno.h>
+#include <string.h>
 
 #include "logind.h"
 #include "logind-user.h"
         "  <property name=\"UID\" type=\"u\" access=\"read\"/>\n"       \
         "  <property name=\"GID\" type=\"u\" access=\"read\"/>\n"       \
         "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"      \
+        "  <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
+        "  <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"RuntimePath\" type=\"s\" access=\"read\"/>\n" \
-        "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n"   \
         "  <property name=\"ControlGroupPath\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n"   \
         "  <property name=\"Display\" type=\"(so)\" access=\"read\"/>\n" \
         "  <property name=\"State\" type=\"s\" access=\"read\"/>\n"     \
         "  <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
+        "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
+        "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
+        "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
         " </interface>\n"                                               \
 
 #define INTROSPECTION                                                   \
@@ -146,6 +152,39 @@ static int bus_user_append_sessions(DBusMessageIter *i, const char *property, vo
         return 0;
 }
 
+static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
+        User *u = data;
+        bool b;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        b = user_get_idle_hint(u, NULL);
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
+        User *u = data;
+        dual_timestamp t;
+        uint64_t k;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        user_get_idle_hint(u, &t);
+        k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static int get_user_for_path(Manager *m, const char *path, User **_u) {
         User *u;
         unsigned long lu;
@@ -176,23 +215,59 @@ static DBusHandlerResult user_message_dispatch(
                 DBusMessage *message) {
 
         const BusProperty properties[] = {
-                { "org.freedesktop.login1.User", "UID",              bus_property_append_uid,    "u",     &u->uid         },
-                { "org.freedesktop.login1.User", "GID",              bus_property_append_gid,    "u",     &u->gid         },
-                { "org.freedesktop.login1.User", "Name",             bus_property_append_string, "s",     u->name         },
-                { "org.freedesktop.login1.User", "RuntimePath",      bus_property_append_string, "s",     u->runtime_path },
-                { "org.freedesktop.login1.User", "ControlGroupPath", bus_property_append_string, "s",     u->cgroup_path  },
-                { "org.freedesktop.login1.User", "Service",          bus_property_append_string, "s",     u->service      },
-                { "org.freedesktop.login1.User", "Display",          bus_user_append_display,    "(so)",  u               },
-                { "org.freedesktop.login1.User", "State",            bus_user_append_state,      "s",     u               },
-                { "org.freedesktop.login1.User", "Sessions",         bus_user_append_sessions,   "a(so)", u               },
+                { "org.freedesktop.login1.User", "UID",                bus_property_append_uid,    "u",     &u->uid                 },
+                { "org.freedesktop.login1.User", "GID",                bus_property_append_gid,    "u",     &u->gid                 },
+                { "org.freedesktop.login1.User", "Name",               bus_property_append_string, "s",     u->name                 },
+                { "org.freedesktop.login1.User", "Timestamp",          bus_property_append_usec,   "t",     &u->timestamp.realtime  },
+                { "org.freedesktop.login1.User", "TimestampMonotonic", bus_property_append_usec,   "t",     &u->timestamp.monotonic },
+                { "org.freedesktop.login1.User", "RuntimePath",        bus_property_append_string, "s",     u->runtime_path         },
+                { "org.freedesktop.login1.User", "ControlGroupPath",   bus_property_append_string, "s",     u->cgroup_path          },
+                { "org.freedesktop.login1.User", "Service",            bus_property_append_string, "s",     u->service              },
+                { "org.freedesktop.login1.User", "Display",            bus_user_append_display,    "(so)",  u                       },
+                { "org.freedesktop.login1.User", "State",              bus_user_append_state,      "s",     u                       },
+                { "org.freedesktop.login1.User", "Sessions",           bus_user_append_sessions,   "a(so)", u                       },
+                { "org.freedesktop.login1.User", "IdleHint",           bus_user_append_idle_hint,  "b",     u                       },
+                { "org.freedesktop.login1.User", "IdleSinceHint",          bus_user_append_idle_hint_since, "t", u                  },
+                { "org.freedesktop.login1.User", "IdleSinceHintMonotonic", bus_user_append_idle_hint_since, "t", u                  },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
+        DBusError error;
+        DBusMessage *reply = NULL;
+        int r;
+
         assert(u);
         assert(connection);
         assert(message);
 
-        return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+        if (dbus_message_is_method_call(message, "org.freedesktop.login1.User", "Terminate")) {
+
+                r = user_stop(u);
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, NULL, r);
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+        } else
+                return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+
+        if (reply) {
+                if (!dbus_connection_send(connection, reply, NULL))
+                        goto oom;
+
+                dbus_message_unref(reply);
+        }
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
 }
 
 static DBusHandlerResult user_message_handler(
index 7d6df8db7a8d6989be36b4b70a44db5d6c0fc7d2..206064f1039224339330627be7f7128bab6c6edf 100644 (file)
@@ -156,7 +156,7 @@ int user_load(User *u) {
 
         assert(u);
 
-        r = parse_env_file(u->state_file, "r",
+        r = parse_env_file(u->state_file, NEWLINE,
                            "CGROUP", &u->cgroup_path,
                            "RUNTIME", &u->runtime_path,
                            "SERVICE", &u->service,
@@ -386,6 +386,42 @@ int user_stop(User *u) {
         return r;
 }
 
+int user_get_idle_hint(User *u, dual_timestamp *t) {
+        Session *s;
+        bool idle_hint = true;
+        dual_timestamp ts = { 0, 0 };
+
+        assert(u);
+
+        LIST_FOREACH(sessions_by_user, s, u->sessions) {
+                dual_timestamp k;
+                int ih;
+
+                ih = session_get_idle_hint(s, &k);
+                if (ih < 0)
+                        return ih;
+
+                if (!ih) {
+                        if (!idle_hint) {
+                                if (k.monotonic < ts.monotonic)
+                                        ts = k;
+                        } else {
+                                idle_hint = false;
+                                ts = k;
+                        }
+                } else if (idle_hint) {
+
+                        if (k.monotonic > ts.monotonic)
+                                ts = k;
+                }
+        }
+
+        if (t)
+                *t = ts;
+
+        return idle_hint;
+}
+
 int user_check_gc(User *u) {
         int r;
         char *p;
index 7f58aa2b0701a0986dffddc614181dba86556831..c891119e274f897fefa41354692a0134d99d61b3 100644 (file)
@@ -67,6 +67,7 @@ void user_add_to_gc_queue(User *u);
 int user_start(User *u);
 int user_stop(User *u);
 UserState user_get_state(User *u);
+int user_get_idle_hint(User *u, dual_timestamp *t);
 int user_save(User *u);
 int user_load(User *u);
 
index a62802895c1feec12c54adc2fc7c9b0d1a1f9cec..d72a5bfa8b11e77b0cfb83ae8a2edbaaa63f2211 100644 (file)
@@ -853,6 +853,44 @@ void manager_gc(Manager *m) {
         }
 }
 
+int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
+        Session *s;
+        bool idle_hint = true;
+        dual_timestamp ts = { 0, 0 };
+        Iterator i;
+
+        assert(m);
+
+        HASHMAP_FOREACH(s, m->sessions, i) {
+                dual_timestamp k;
+                int ih;
+
+                ih = session_get_idle_hint(s, &k);
+                if (ih < 0)
+                        return ih;
+
+                if (!ih) {
+                        if (!idle_hint) {
+                                if (k.monotonic < ts.monotonic)
+                                        ts = k;
+                        } else {
+                                idle_hint = false;
+                                ts = k;
+                        }
+                } else if (idle_hint) {
+
+                        if (k.monotonic > ts.monotonic)
+                                ts = k;
+                }
+        }
+
+        if (t)
+                *t = ts;
+
+        return idle_hint;
+}
+
+
 int manager_startup(Manager *m) {
         int r;
         Seat *seat;
index fdc780feb66cbcd9b3445b171b0ca79be564615e..e18a357c9a633091a61c7aacd01f08562b0ba7c0 100644 (file)
@@ -108,6 +108,8 @@ int manager_spawn_autovt(Manager *m, int vtnr);
 
 void manager_gc(Manager *m);
 
+int manager_get_idle_hint(Manager *m, dual_timestamp *t);
+
 bool x11_display_is_local(const char *display);
 
 extern const DBusObjectPathVTable bus_manager_vtable;
index 2047ebd7bd841bd8b045c86258327dab81f4fcf4..08529cc235ba3513afc52c85cef1b2ff536d1d00 100644 (file)
@@ -108,6 +108,28 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
         return ts;
 }
 
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+        int64_t delta;
+        assert(ts);
+
+        ts->realtime = u;
+
+        if (u == 0)
+                ts->monotonic = 0;
+        else {
+                delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+
+                ts->monotonic = now(CLOCK_MONOTONIC);
+
+                if ((int64_t) ts->monotonic > delta)
+                        ts->monotonic -= delta;
+                else
+                        ts->monotonic = 0;
+        }
+
+        return ts;
+}
+
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
index 3863a08b6346c5829aedbb1576b368a96de4d720..76e1d4faa0e34a2b0fa7ad2ca911b4bf456861a5 100644 (file)
@@ -75,6 +75,7 @@ typedef struct dual_timestamp {
 usec_t now(clockid_t clock);
 
 dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 
 #define dual_timestamp_is_set(ts) ((ts)->realtime > 0)