From: Lennart Poettering Date: Fri, 17 Jun 2011 13:59:18 +0000 (+0200) Subject: logind: implement idle hint logic X-Git-Tag: v30~166 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=a185c5aa2d8bef98716f8cf160da263c17e588b2 logind: implement idle hint logic --- diff --git a/src/logind-dbus.c b/src/logind-dbus.c index 90db94184..42374d7fd 100644 --- a/src/logind-dbus.c +++ b/src/logind-dbus.c @@ -19,6 +19,9 @@ along with systemd; If not, see . ***/ +#include +#include + #include "logind.h" #include "dbus-common.h" @@ -62,6 +65,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -102,6 +108,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" #define INTROSPECTION_BEGIN \ @@ -119,6 +128,39 @@ 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 } }; diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c index 4937d65f7..32171ce40 100644 --- a/src/logind-seat-dbus.c +++ b/src/logind-seat-dbus.c @@ -20,6 +20,7 @@ ***/ #include +#include #include "logind.h" #include "logind-seat.h" @@ -36,6 +37,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \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( diff --git a/src/logind-seat.c b/src/logind-seat.c index 2ba3060be..751f59a48 100644 --- a/src/logind-seat.c +++ b/src/logind-seat.c @@ -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; diff --git a/src/logind-seat.h b/src/logind-seat.h index b045bde93..dfbb2a2a0 100644 --- a/src/logind-seat.h +++ b/src/logind-seat.h @@ -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); diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c index 539384b7d..8b5b3ad51 100644 --- a/src/logind-session-dbus.c +++ b/src/logind-session-dbus.c @@ -20,6 +20,7 @@ ***/ #include +#include #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 } }; diff --git a/src/logind-session.c b/src/logind-session.c index 6b3b27789..89fe02c06 100644 --- a/src/logind-session.c +++ b/src/logind-session.c @@ -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; diff --git a/src/logind-session.h b/src/logind-session.h index 60ac1c54e..76823931d 100644 --- a/src/logind-session.h +++ b/src/logind-session.h @@ -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); diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c index 7c8bb27c6..3be2c05d2 100644 --- a/src/logind-user-dbus.c +++ b/src/logind-user-dbus.c @@ -20,6 +20,7 @@ ***/ #include +#include #include "logind.h" #include "logind-user.h" @@ -31,12 +32,17 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ " \n" \ - " \n" \ " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \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( diff --git a/src/logind-user.c b/src/logind-user.c index 7d6df8db7..206064f10 100644 --- a/src/logind-user.c +++ b/src/logind-user.c @@ -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; diff --git a/src/logind-user.h b/src/logind-user.h index 7f58aa2b0..c891119e2 100644 --- a/src/logind-user.h +++ b/src/logind-user.h @@ -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); diff --git a/src/logind.c b/src/logind.c index a62802895..d72a5bfa8 100644 --- a/src/logind.c +++ b/src/logind.c @@ -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; diff --git a/src/logind.h b/src/logind.h index fdc780feb..e18a357c9 100644 --- a/src/logind.h +++ b/src/logind.h @@ -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; diff --git a/src/util.c b/src/util.c index 2047ebd7b..08529cc23 100644 --- a/src/util.c +++ b/src/util.c @@ -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); diff --git a/src/util.h b/src/util.h index 3863a08b6..76e1d4faa 100644 --- a/src/util.h +++ b/src/util.h @@ -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)