[systemd-commits] 43 commits - configure.ac .gitignore Makefile.am man/systemd.exec.xml org.freedesktop.hostname1.xml org.freedesktop.locale1.xml org.freedesktop.timedate1.xml src/70-uaccess.rules src/71-seat.rules src/99-systemd.rules src/automount.c src/dbus.c src/dbus-common.c src/dbus-common.h src/dbus.h src/dbus-loop.c src/dbus-loop.h src/dbus-manager.c src/hostnamed.c src/localed.c src/logind-acl.c src/logind-acl.h src/logind.c src/logind-dbus.c src/logind-device.c src/logind-device.h src/logind.h src/logind-seat.c src/logind-seat-dbus.c src/logind-seat.h src/logind-session.c src/logind-session-dbus.c src/logind-session.h src/logind-user.c src/logind-user-dbus.c src/logind-user.h src/main.c src/manager.c src/manager.h src/mount.c src/org.freedesktop.locale1.conf src/org.freedesktop.locale1.policy src/org.freedesktop.locale1.service src/org.freedesktop.login1.conf src/org.freedesktop.login1.policy src/org.freedesktop.login1.service src/org.freedesktop.timedate1.conf src/org.freed esktop.timedate1.policy src/org.freedesktop.timedate1.service src/pam-module.c src/path.c src/polkit.c src/polkit.h src/service.c src/service.h src/shutdownd.c src/socket.c src/swap.c src/timedated.c src/uaccess.c src/unit.c src/unit.h src/user-sessions.c src/util.c src/util.h TODO units/.gitignore units/systemd-localed.service.in units/systemd-logind.service.in units/systemd-timedated.service.in

Lennart Poettering lennart at kemper.freedesktop.org
Mon Jun 27 04:48:16 PDT 2011


 .gitignore                            |    4 
 Makefile.am                           |  136 +++-
 TODO                                  |   20 
 configure.ac                          |   38 +
 man/systemd.exec.xml                  |    2 
 org.freedesktop.hostname1.xml         |   26 
 org.freedesktop.locale1.xml           |   11 
 org.freedesktop.timedate1.xml         |   22 
 src/70-uaccess.rules                  |   72 ++
 src/71-seat.rules                     |   20 
 src/99-systemd.rules                  |    2 
 src/automount.c                       |    2 
 src/dbus-common.c                     |  135 +++-
 src/dbus-common.h                     |   10 
 src/dbus-loop.c                       |  263 ++++++++
 src/dbus-loop.h                       |   30 
 src/dbus-manager.c                    |   62 +
 src/dbus.c                            |  146 +---
 src/dbus.h                            |    4 
 src/hostnamed.c                       |  253 ++------
 src/localed.c                         |  612 +++++++++++++++++++
 src/logind-acl.c                      |  282 +++++++++
 src/logind-acl.h                      |   40 +
 src/logind-dbus.c                     | 1006 ++++++++++++++++++++++++++++++++
 src/logind-device.c                   |   85 ++
 src/logind-device.h                   |   48 +
 src/logind-seat-dbus.c                |  403 ++++++++++++
 src/logind-seat.c                     |  491 +++++++++++++++
 src/logind-seat.h                     |   81 ++
 src/logind-session-dbus.c             |  454 ++++++++++++++
 src/logind-session.c                  |  830 ++++++++++++++++++++++++++
 src/logind-session.h                  |  111 +++
 src/logind-user-dbus.c                |  387 ++++++++++++
 src/logind-user.c                     |  515 ++++++++++++++++
 src/logind-user.h                     |   85 ++
 src/logind.c                          | 1064 ++++++++++++++++++++++++++++++++++
 src/logind.h                          |  135 ++++
 src/main.c                            |    7 
 src/manager.c                         |   47 -
 src/manager.h                         |    2 
 src/mount.c                           |   16 
 src/org.freedesktop.locale1.conf      |   27 
 src/org.freedesktop.locale1.policy    |   29 
 src/org.freedesktop.locale1.service   |   12 
 src/org.freedesktop.login1.conf       |   27 
 src/org.freedesktop.login1.policy     |   29 
 src/org.freedesktop.login1.service    |   12 
 src/org.freedesktop.timedate1.conf    |   27 
 src/org.freedesktop.timedate1.policy  |   50 +
 src/org.freedesktop.timedate1.service |   12 
 src/pam-module.c                      |  627 +++++---------------
 src/path.c                            |    2 
 src/polkit.c                          |  190 ++++++
 src/polkit.h                          |   35 +
 src/service.c                         |   52 +
 src/service.h                         |    1 
 src/shutdownd.c                       |    2 
 src/socket.c                          |    6 
 src/swap.c                            |    6 
 src/timedated.c                       |  615 +++++++++++++++++++
 src/uaccess.c                         |   87 ++
 src/unit.c                            |   30 
 src/unit.h                            |    5 
 src/user-sessions.c                   |    2 
 src/util.c                            |  757 ++++++++++++++++++------
 src/util.h                            |   36 -
 units/.gitignore                      |    3 
 units/systemd-localed.service.in      |   17 
 units/systemd-logind.service.in       |   18 
 units/systemd-timedated.service.in    |   17 
 70 files changed, 9692 insertions(+), 1000 deletions(-)

New commits:
commit 18fa6b2705aaef42041942f47f013e426640b6a4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jun 27 13:47:03 2011 +0200

    dbus: send our finished signal when we are finished booting

diff --git a/TODO b/TODO
index d7c92e1..2700c7a 100644
--- a/TODO
+++ b/TODO
@@ -56,8 +56,6 @@ Features:
 
 * add prefix match to sysctl, tmpfiles, ...
 
-* send out "finished" signal when we are finished booting
-
 * drop /.readahead on bigger upgrades with yum
 
 * add inode stat() check to readahead to suppress preloading changed files
diff --git a/src/dbus-manager.c b/src/dbus-manager.c
index cc2b4d0..b4e2f86 100644
--- a/src/dbus-manager.c
+++ b/src/dbus-manager.c
@@ -151,6 +151,12 @@
         "   <arg name=\"id\" type=\"u\"/>\n"                            \
         "   <arg name=\"job\" type=\"o\"/>\n"                           \
         "   <arg name=\"result\" type=\"s\"/>\n"                        \
+        "  </signal>"                                                   \
+        "  <signal name=\"StartupFinished\">\n"                         \
+        "   <arg name=\"kernel\" type=\"t\"/>\n"                        \
+        "   <arg name=\"initrd\" type=\"t\"/>\n"                        \
+        "   <arg name=\"userspace\" type=\"t\"/>\n"                     \
+        "   <arg name=\"total\" type=\"t\"/>\n"                         \
         "  </signal>"
 
 #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL                        \
diff --git a/src/dbus.c b/src/dbus.c
index 93a19a4..764c65c 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -1274,3 +1274,42 @@ int bus_fdset_add_all(Manager *m, FDSet *fds) {
 
         return 0;
 }
+
+void bus_broadcast_finished(
+                Manager *m,
+                usec_t kernel_usec,
+                usec_t initrd_usec,
+                usec_t userspace_usec,
+                usec_t total_usec) {
+
+        DBusMessage *message;
+
+        assert(m);
+
+        message = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished");
+        if (!message) {
+                log_error("Out of memory.");
+                return;
+        }
+
+        assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+        if (!dbus_message_append_args(message,
+                                      DBUS_TYPE_UINT64, &kernel_usec,
+                                      DBUS_TYPE_UINT64, &initrd_usec,
+                                      DBUS_TYPE_UINT64, &userspace_usec,
+                                      DBUS_TYPE_UINT64, &total_usec,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Out of memory.");
+                goto finish;
+        }
+
+
+        if (bus_broadcast(m, message) < 0) {
+                log_error("Out of memory.");
+                goto finish;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(message);
+}
diff --git a/src/dbus.h b/src/dbus.h
index c47e782..bd539d0 100644
--- a/src/dbus.h
+++ b/src/dbus.h
@@ -43,6 +43,8 @@ bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
 
 int bus_fdset_add_all(Manager *m, FDSet *fds);
 
+void bus_broadcast_finished(Manager *m, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
+
 #define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot)
 #define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot)
 
diff --git a/src/logind.h b/src/logind.h
index d512c3e..be8bb1d 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -37,7 +37,7 @@
  * recreate VTs when disallocated
  * spawn user systemd
  * direct client API
- * D-Bus method: AttachDevice(seat, device);
+ * D-Bus method: AttachDevices(seat, devices[]);
  * D-Bus method: SetLinger(user, bool b);
  *
  * non-local X11 server
diff --git a/src/manager.c b/src/manager.c
index 19172a2..0830e80 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -2899,6 +2899,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) {
 
 void manager_check_finished(Manager *m) {
         char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
+        usec_t kernel_usec = 0, initrd_usec = 0, userspace_usec = 0, total_usec = 0;
 
         assert(m);
 
@@ -2912,29 +2913,37 @@ void manager_check_finished(Manager *m) {
 
         if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
 
+                userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
+                total_usec = m->finish_timestamp.monotonic;
+
                 if (dual_timestamp_is_set(&m->initrd_timestamp)) {
+
+                        kernel_usec = m->initrd_timestamp.monotonic;
+                        initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic;
+
                         log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
-                                 format_timespan(kernel, sizeof(kernel),
-                                                 m->initrd_timestamp.monotonic),
-                                 format_timespan(initrd, sizeof(initrd),
-                                                 m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic),
-                                 format_timespan(userspace, sizeof(userspace),
-                                                 m->finish_timestamp.monotonic - m->startup_timestamp.monotonic),
-                                 format_timespan(sum, sizeof(sum),
-                                                 m->finish_timestamp.monotonic));
-                } else
+                                 format_timespan(kernel, sizeof(kernel), kernel_usec),
+                                 format_timespan(initrd, sizeof(initrd), initrd_usec),
+                                 format_timespan(userspace, sizeof(userspace), userspace_usec),
+                                 format_timespan(sum, sizeof(sum), total_usec));
+                } else {
+                        kernel_usec = m->startup_timestamp.monotonic;
+                        initrd_usec = 0;
+
                         log_info("Startup finished in %s (kernel) + %s (userspace) = %s.",
-                                 format_timespan(kernel, sizeof(kernel),
-                                                 m->startup_timestamp.monotonic),
-                                 format_timespan(userspace, sizeof(userspace),
-                                                 m->finish_timestamp.monotonic - m->startup_timestamp.monotonic),
-                                 format_timespan(sum, sizeof(sum),
-                                                 m->finish_timestamp.monotonic));
-        } else
+                                 format_timespan(kernel, sizeof(kernel), kernel_usec),
+                                 format_timespan(userspace, sizeof(userspace), userspace_usec),
+                                 format_timespan(sum, sizeof(sum), total_usec));
+                }
+        } else {
+                userspace_usec = initrd_usec = kernel_usec = 0;
+                total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
+
                 log_debug("Startup finished in %s.",
-                          format_timespan(userspace, sizeof(userspace),
-                                          m->finish_timestamp.monotonic - m->startup_timestamp.monotonic));
+                          format_timespan(sum, sizeof(sum), total_usec));
+        }
 
+        bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec);
 }
 
 void manager_run_generators(Manager *m) {

commit 3084a7c453680fac7bdca9cf76bde397ec6a67de
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 23:52:02 2011 +0200

    logind: add more necessary caps to the service

diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in
index d4734c3..82a2c6a 100644
--- a/units/systemd-logind.service.in
+++ b/units/systemd-logind.service.in
@@ -14,5 +14,5 @@ Description=Login Service
 ExecStart=@rootlibexecdir@/systemd-logind
 Type=dbus
 BusName=org.freedesktop.login1
-CapabilityBoundingSet=CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL
+CapabilityBoundingSet=CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER
 StandardOutput=syslog

commit ee8545b06c2325e5dca7afd9ea8dbc35342a7768
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 23:51:49 2011 +0200

    pam: fix up tty if it is actually a display

diff --git a/src/pam-module.c b/src/pam-module.c
index eba59f6..1658348 100644
--- a/src/pam-module.c
+++ b/src/pam-module.c
@@ -382,6 +382,16 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         remote_host = strempty(remote_host);
         seat = strempty(seat);
 
+        if (strchr(tty, ':')) {
+                /* A tty with a colon is usually an X11 display, place
+                 * there to show up in utmp. We rearrange things and
+                 * don't pretend that an X display was a tty */
+
+                if (isempty(display))
+                        display = tty;
+                tty = NULL;
+        }
+
         type = !isempty(display) ? "x11" :
                    !isempty(tty) ? "tty" : "other";
 

commit a91e4e5337a46db3f0a4f1415c1b60285e5c33a3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 23:50:39 2011 +0200

    logind: save/restore session type

diff --git a/src/logind-session.c b/src/logind-session.c
index 16d6c17..e71ff4f 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -136,6 +136,11 @@ int session_save(Session *s) {
                 s->remote,
                 s->kill_processes);
 
+        if (s->type >= 0)
+                fprintf(f,
+                        "TYPE=%s\n",
+                        session_type_to_string(s->type));
+
         if (s->cgroup_path)
                 fprintf(f,
                         "CGROUP=%s\n",
@@ -210,7 +215,8 @@ int session_load(Session *s) {
                 *seat = NULL,
                 *vtnr = NULL,
                 *leader = NULL,
-                *audit_id = NULL;
+                *audit_id = NULL,
+                *type = NULL;
 
         int k, r;
 
@@ -228,6 +234,7 @@ int session_load(Session *s) {
                            "SERVICE",        &s->service,
                            "VTNR",           &vtnr,
                            "LEADER",         &leader,
+                           "TYPE",           &type,
                            NULL);
 
         if (r < 0)
@@ -272,6 +279,14 @@ int session_load(Session *s) {
                 }
         }
 
+        if (type) {
+                SessionType t;
+
+                t = session_type_from_string(type);
+                if (t >= 0)
+                        s->type = t;
+        }
+
 finish:
         free(remote);
         free(kill_processes);
@@ -809,7 +824,7 @@ void session_add_to_gc_queue(Session *s) {
 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
         [SESSION_TTY] = "tty",
         [SESSION_X11] = "x11",
-        [SESSION_OTHER] = "other"
+        [SESSION_UNSPECIFIED] = "unspecified"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
diff --git a/src/logind-session.h b/src/logind-session.h
index 7a8001e..72f85ca 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -31,9 +31,9 @@ typedef struct Session Session;
 #include "logind-user.h"
 
 typedef enum SessionType {
+        SESSION_UNSPECIFIED,
         SESSION_TTY,
         SESSION_X11,
-        SESSION_OTHER,
         _SESSION_TYPE_MAX,
         _SESSION_TYPE_INVALID = -1
 } SessionType;

commit 31b79c2b4a34961eefc3b3680704124d8490d105
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 23:25:28 2011 +0200

    logind: use pipe fd to detect when a session is dead

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index d48d68c..136f610 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -437,7 +437,9 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                 goto fail;
         }
 
-        session->pipe_fd = pipe_fds[0];
+        r = session_set_pipe_fd(session, pipe_fds[0]);
+        if (r < 0)
+                goto fail;
         pipe_fds[0] = -1;
 
         if (s) {
diff --git a/src/logind-session.c b/src/logind-session.c
index 9278f30..16d6c17 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/epoll.h>
 
 #include "logind-session.h"
 #include "strv.h"
@@ -97,8 +98,7 @@ void session_free(Session *s) {
 
         hashmap_remove(s->manager->sessions, s->id);
 
-        if (s->pipe_fd >= 0)
-                close_nointr_nofail(s->pipe_fd);
+        session_unset_pipe_fd(s);
 
         free(s->state_file);
         free(s);
@@ -729,6 +729,45 @@ void session_set_idle_hint(Session *s, bool b) {
                              "IdleSinceHintMonotonic\0");
 }
 
+int session_set_pipe_fd(Session *s, int fd) {
+        struct epoll_event ev;
+        int r;
+
+        assert(s);
+        assert(fd >= 0);
+        assert(s->pipe_fd < 0);
+
+        r = hashmap_put(s->manager->pipe_fds, INT_TO_PTR(fd + 1), s);
+        if (r < 0)
+                return r;
+
+        zero(ev);
+        ev.events = 0;
+        ev.data.u32 = FD_PIPE_BASE + fd;
+
+        if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+                assert_se(hashmap_remove(s->manager->pipe_fds, INT_TO_PTR(fd + 1)) == s);
+                return -errno;
+        }
+
+        s->pipe_fd = fd;
+        return 0;
+}
+
+void session_unset_pipe_fd(Session *s) {
+        assert(s);
+
+        if (s->pipe_fd < 0)
+                return;
+
+        assert_se(hashmap_remove(s->manager->pipe_fds, INT_TO_PTR(s->pipe_fd + 1)) == s);
+
+        assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->pipe_fd, NULL) == 0);
+
+        close_nointr_nofail(s->pipe_fd);
+        s->pipe_fd = -1;
+}
+
 int session_check_gc(Session *s) {
         int r;
 
diff --git a/src/logind-session.h b/src/logind-session.h
index 01c9504..7a8001e 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -91,6 +91,8 @@ int session_activate(Session *s);
 bool session_is_active(Session *s);
 int session_get_idle_hint(Session *s, dual_timestamp *t);
 void session_set_idle_hint(Session *s, bool b);
+int session_set_pipe_fd(Session *s, int fd);
+void session_unset_pipe_fd(Session *s);
 int session_start(Session *s);
 int session_stop(Session *s);
 int session_save(Session *s);
diff --git a/src/logind.c b/src/logind.c
index 2665ab9..2577320 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -33,12 +33,6 @@
 #include "dbus-common.h"
 #include "dbus-loop.h"
 
-enum {
-        FD_UDEV,
-        FD_CONSOLE,
-        FD_BUS
-};
-
 Manager *manager_new(void) {
         Manager *m;
 
@@ -57,6 +51,7 @@ Manager *manager_new(void) {
         m->sessions = hashmap_new(string_hash_func, string_compare_func);
         m->users = hashmap_new(trivial_hash_func, trivial_compare_func);
         m->cgroups = hashmap_new(string_hash_func, string_compare_func);
+        m->pipe_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
 
         if (!m->devices || !m->seats || !m->sessions || !m->users) {
                 manager_free(m);
@@ -102,6 +97,7 @@ void manager_free(Manager *m) {
         hashmap_free(m->devices);
         hashmap_free(m->seats);
         hashmap_free(m->cgroups);
+        hashmap_free(m->pipe_fds);
 
         if (m->console_active_fd >= 0)
                 close_nointr_nofail(m->console_active_fd);
@@ -714,6 +710,19 @@ void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
         free(p);
 }
 
+static void manager_pipe_notify_eof(Manager *m, int fd) {
+        Session *s;
+
+        assert_se(m);
+        assert_se(fd >= 0);
+
+        assert_se(s = hashmap_get(m->pipe_fds, INT_TO_PTR(fd + 1)));
+        assert(s->pipe_fd == fd);
+        session_unset_pipe_fd(s);
+
+        session_add_to_gc_queue(s);
+}
+
 static int manager_connect_bus(Manager *m) {
         DBusError error;
         int r;
@@ -1006,6 +1015,10 @@ int manager_run(Manager *m) {
                 case FD_BUS:
                         bus_loop_dispatch(m->bus_fd);
                         break;
+
+                default:
+                        if (event.data.u32 >= FD_PIPE_BASE)
+                                manager_pipe_notify_eof(m, event.data.u32 - FD_PIPE_BASE);
                 }
         }
 
diff --git a/src/logind.h b/src/logind.h
index d8674e7..d512c3e 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -37,9 +37,8 @@
  * recreate VTs when disallocated
  * spawn user systemd
  * direct client API
- * subscribe to fd HUP
  * D-Bus method: AttachDevice(seat, device);
- * D-Bus method: PermitLinger(user, bool b);
+ * D-Bus method: SetLinger(user, bool b);
  *
  * non-local X11 server
  * reboot/shutdown halt management
@@ -86,6 +85,14 @@ struct Manager {
         unsigned long session_counter;
 
         Hashmap *cgroups;
+        Hashmap *pipe_fds;
+};
+
+enum {
+        FD_UDEV,
+        FD_CONSOLE,
+        FD_BUS,
+        FD_PIPE_BASE
 };
 
 Manager *manager_new(void);

commit 094062918c50cd5a34f7b6510fe206bf78d7cc58
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 22:55:51 2011 +0200

    logind: fix set of capabilities

diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in
index 52c4acf..d4734c3 100644
--- a/units/systemd-logind.service.in
+++ b/units/systemd-logind.service.in
@@ -14,4 +14,5 @@ Description=Login Service
 ExecStart=@rootlibexecdir@/systemd-logind
 Type=dbus
 BusName=org.freedesktop.login1
-CapabilityBoundingSet=
+CapabilityBoundingSet=CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL
+StandardOutput=syslog

commit 21c390ccd1b4f7bc962c16549df929ad518a1d37
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 22:55:39 2011 +0200

    logind: properly handle if two session with identical loginuids are attempted to be created

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 2bad549..d48d68c 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -314,9 +314,53 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                         goto fail;
                 }
 
-                if (hashmap_get(m->sessions, id)) {
-                        r = -EEXIST;
-                        goto fail;
+                session = hashmap_get(m->sessions, id);
+
+                if (session) {
+
+                        /* Session already exists, client is probably
+                         * something like "su" which changes uid but
+                         * is still the same audit session */
+
+                        reply = dbus_message_new_method_return(message);
+                        if (!reply) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        /* Create a throw-away fd */
+                        if (pipe(pipe_fds) < 0) {
+                                r = -errno;
+                                goto fail;
+                        }
+
+                        close_nointr_nofail(pipe_fds[0]);
+                        pipe_fds[0] = -1;
+
+                        p = session_bus_path(session);
+                        if (!p) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        b = dbus_message_append_args(
+                                        reply,
+                                        DBUS_TYPE_STRING, &session->id,
+                                        DBUS_TYPE_OBJECT_PATH, &p,
+                                        DBUS_TYPE_STRING, &session->user->runtime_path,
+                                        DBUS_TYPE_UNIX_FD, &pipe_fds[1],
+                                        DBUS_TYPE_INVALID);
+                        free(p);
+
+                        if (!b) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                        close_nointr_nofail(pipe_fds[1]);
+                        *_reply = reply;
+
+                        return 0;
                 }
 
         } else {
diff --git a/src/logind.h b/src/logind.h
index 2b9b702..d8674e7 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -40,7 +40,6 @@
  * subscribe to fd HUP
  * D-Bus method: AttachDevice(seat, device);
  * D-Bus method: PermitLinger(user, bool b);
- * properly handle if two sessions with the same loginuid are attempted to be created
  *
  * non-local X11 server
  * reboot/shutdown halt management
diff --git a/src/pam-module.c b/src/pam-module.c
index dc7c001..eba59f6 100644
--- a/src/pam-module.c
+++ b/src/pam-module.c
@@ -457,10 +457,12 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 goto finish;
         }
 
-        r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
-        if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
-                return r;
+        if (session_fd >= 0) {
+                r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
+                        return r;
+                }
         }
 
         session_fd = -1;

commit 0771475394887e3635e67196fa6f56486fa2126c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 20:46:22 2011 +0200

    logind: when generating session ids with a counter, retry if session is already allocated

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 0f3de41..2bad549 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -176,7 +176,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
         Seat *s;
         DBusMessageIter iter;
         int r;
-        char *id, *p;
+        char *id = NULL, *p;
         int vtnr = -1;
         int pipe_fds[2] = { -1, -1 };
         DBusMessage *reply = NULL;
@@ -306,19 +306,30 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
 
         audit_session_from_pid(leader, &audit_id);
 
-        if (audit_id > 0)
+        if (audit_id > 0) {
                 asprintf(&id, "%lu", (unsigned long) audit_id);
-        else
-                asprintf(&id, "c%lu", ++m->session_counter);
 
-        if (!id) {
-                r = -ENOMEM;
-                goto fail;
-        }
+                if (!id) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
 
-        if (hashmap_get(m->sessions, id)) {
-                r = -EEXIST;
-                goto fail;
+                if (hashmap_get(m->sessions, id)) {
+                        r = -EEXIST;
+                        goto fail;
+                }
+
+        } else {
+                do {
+                        free(id);
+                        asprintf(&id, "c%lu", ++m->session_counter);
+
+                        if (!id) {
+                                r = -ENOMEM;
+                                goto fail;
+                        }
+
+                } while (hashmap_get(m->sessions, id));
         }
 
         r = manager_add_session(m, user, id, &session);
diff --git a/src/logind.h b/src/logind.h
index d8674e7..2b9b702 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -40,6 +40,7 @@
  * subscribe to fd HUP
  * D-Bus method: AttachDevice(seat, device);
  * D-Bus method: PermitLinger(user, bool b);
+ * properly handle if two sessions with the same loginuid are attempted to be created
  *
  * non-local X11 server
  * reboot/shutdown halt management

commit 1713813de365486617ab87899f950e9b6ec928ef
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 20:41:56 2011 +0200

    logind: remove a session when its cgroup is gone

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 2a85749..0f3de41 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -896,6 +896,36 @@ const DBusObjectPathVTable bus_manager_vtable = {
         .message_function = manager_message_handler
 };
 
+DBusHandlerResult bus_message_filter(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        Manager *m = userdata;
+        DBusError error;
+
+        assert(m);
+        assert(connection);
+        assert(message);
+
+        dbus_error_init(&error);
+
+        if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
+                const char *cgroup;
+
+                if (!dbus_message_get_args(message, &error,
+                                           DBUS_TYPE_STRING, &cgroup,
+                                           DBUS_TYPE_INVALID))
+                        log_error("Failed to parse Released message: %s", bus_error_message(&error));
+                else
+                        manager_cgroup_notify_empty(m, cgroup);
+        }
+
+        dbus_error_free(&error);
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
 int manager_send_changed(Manager *manager, const char *properties) {
         DBusMessage *m;
         int r = -ENOMEM;
diff --git a/src/logind-session.c b/src/logind-session.c
index 74f8ad1..9278f30 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -83,6 +83,9 @@ void session_free(Session *s) {
                 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
         }
 
+        if (s->cgroup_path)
+                hashmap_remove(s->manager->cgroups, s->cgroup_path);
+
         free(s->cgroup_path);
         strv_free(s->controllers);
 
@@ -468,6 +471,8 @@ static int session_create_cgroup(Session *s) {
                 }
         }
 
+        hashmap_put(s->manager->cgroups, s->cgroup_path, s);
+
         return 0;
 }
 
@@ -562,6 +567,8 @@ static int session_kill_cgroup(Session *s) {
         STRV_FOREACH(k, s->user->manager->controllers)
                 cg_trim(*k, s->cgroup_path, true);
 
+        hashmap_remove(s->manager->cgroups, s->cgroup_path);
+
         free(s->cgroup_path);
         s->cgroup_path = NULL;
 
diff --git a/src/logind.c b/src/logind.c
index cdb4da7..2665ab9 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -56,6 +56,7 @@ Manager *manager_new(void) {
         m->seats = hashmap_new(string_hash_func, string_compare_func);
         m->sessions = hashmap_new(string_hash_func, string_compare_func);
         m->users = hashmap_new(trivial_hash_func, trivial_compare_func);
+        m->cgroups = hashmap_new(string_hash_func, string_compare_func);
 
         if (!m->devices || !m->seats || !m->sessions || !m->users) {
                 manager_free(m);
@@ -100,6 +101,7 @@ void manager_free(Manager *m) {
         hashmap_free(m->users);
         hashmap_free(m->devices);
         hashmap_free(m->seats);
+        hashmap_free(m->cgroups);
 
         if (m->console_active_fd >= 0)
                 close_nointr_nofail(m->console_active_fd);
@@ -682,12 +684,34 @@ int manager_spawn_autovt(Manager *m, int vtnr) {
         return 0;
 }
 
-static DBusHandlerResult login_message_filter(
-                DBusConnection *connection,
-                DBusMessage *message,
-                void *userdata) {
+void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
+        Session *s;
+        char *p;
+
+        assert(m);
+        assert(cgroup);
+
+        p = strdup(cgroup);
+        if (!p) {
+                log_error("Out of memory.");
+                return;
+        }
+
+        for (;;) {
+                char *e;
 
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                if (isempty(p) || streq(p, "/"))
+                        break;
+
+                s = hashmap_get(m->cgroups, p);
+                if (s)
+                        session_add_to_gc_queue(s);
+
+                assert_se(e = strrchr(p, '/'));
+                *e = 0;
+        }
+
+        free(p);
 }
 
 static int manager_connect_bus(Manager *m) {
@@ -712,7 +736,7 @@ static int manager_connect_bus(Manager *m) {
             !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/seat", &bus_seat_vtable, m) ||
             !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/session", &bus_session_vtable, m) ||
             !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/user", &bus_user_vtable, m) ||
-            !dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) {
+            !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
                 log_error("Not enough memory");
                 r = -ENOMEM;
                 goto fail;
@@ -958,6 +982,8 @@ int manager_run(Manager *m) {
                 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
                         continue;
 
+                manager_gc(m);
+
                 n = epoll_wait(m->epoll_fd, &event, 1, -1);
                 if (n < 0) {
                         if (errno == EINTR || errno == EAGAIN)
diff --git a/src/logind.h b/src/logind.h
index 22eab55..d8674e7 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -37,7 +37,7 @@
  * recreate VTs when disallocated
  * spawn user systemd
  * direct client API
- * subscribe to cgroup changes, fd HUP
+ * subscribe to fd HUP
  * D-Bus method: AttachDevice(seat, device);
  * D-Bus method: PermitLinger(user, bool b);
  *
@@ -84,6 +84,8 @@ struct Manager {
         bool kill_user_processes;
 
         unsigned long session_counter;
+
+        Hashmap *cgroups;
 };
 
 Manager *manager_new(void);
@@ -109,6 +111,8 @@ int manager_startup(Manager *m);
 int manager_run(Manager *m);
 int manager_spawn_autovt(Manager *m, int vtnr);
 
+void manager_cgroup_notify_empty(Manager *m, const char *cgroup);
+
 void manager_gc(Manager *m);
 
 int manager_get_idle_hint(Manager *m, dual_timestamp *t);
@@ -117,6 +121,8 @@ bool x11_display_is_local(const char *display);
 
 extern const DBusObjectPathVTable bus_manager_vtable;
 
+DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, void *userdata);
+
 int manager_send_changed(Manager *manager, const char *properties);
 
 #endif

commit dec15e9263cadae02f4f51463860248af40475d0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 20:41:22 2011 +0200

    logind: fix generation of bus arrays

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 693906e..2a85749 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -589,7 +589,7 @@ static DBusHandlerResult manager_message_handler(
 
                 dbus_message_iter_init_append(reply, &iter);
 
-                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "susso", &sub))
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
                         goto oom;
 
                 HASHMAP_FOREACH(session, m->sessions, i) {
@@ -635,7 +635,7 @@ static DBusHandlerResult manager_message_handler(
 
                 dbus_message_iter_init_append(reply, &iter);
 
-                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "uso", &sub))
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
                         goto oom;
 
                 HASHMAP_FOREACH(user, m->users, i) {
@@ -679,7 +679,7 @@ static DBusHandlerResult manager_message_handler(
 
                 dbus_message_iter_init_append(reply, &iter);
 
-                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "so", &sub))
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
                         goto oom;
 
                 HASHMAP_FOREACH(seat, m->seats, i) {
diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
index ad0298e..669e83e 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -102,7 +102,7 @@ static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, vo
         assert(property);
         assert(s);
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "so", &sub))
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
                 return -ENOMEM;
 
         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c
index 6d7e8e7..7263d1b 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -121,7 +121,7 @@ static int bus_user_append_sessions(DBusMessageIter *i, const char *property, vo
         assert(property);
         assert(u);
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "so", &sub))
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
                 return -ENOMEM;
 
         LIST_FOREACH(sessions_by_user, session, u->sessions) {

commit 19bc719ec28e731159671b06254d9bfd49014894
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 20:40:00 2011 +0200

    logind: make sure we hand out write fd, and keep read fd for session end detection

diff --git a/TODO b/TODO
index aab4431..d7c92e1 100644
--- a/TODO
+++ b/TODO
@@ -80,6 +80,8 @@ Features:
 
 * avoid DefaultStandardOutput=syslog to have any effect on StandardInput=socket services
 
+* cgroup_notify_empty(): recursively check groups up the tree, too
+
 * fix alsa mixer restore to not print error when no config is stored
 
 * show enablement status in systemctl status
diff --git a/src/dbus.c b/src/dbus.c
index 2a379a2..93a19a4 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -446,7 +446,7 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, D
                 if (!dbus_message_get_args(message, &error,
                                            DBUS_TYPE_STRING, &cgroup,
                                            DBUS_TYPE_INVALID))
-                        log_error("Failed to parse Released message: %s", error.message);
+                        log_error("Failed to parse Released message: %s", bus_error_message(&error));
                 else
                         cgroup_notify_empty(m, cgroup);
         }
diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 10a826b..693906e 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -382,8 +382,8 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                 goto fail;
         }
 
-        session->pipe_fd = pipe_fds[1];
-        pipe_fds[1] = -1;
+        session->pipe_fd = pipe_fds[0];
+        pipe_fds[0] = -1;
 
         if (s) {
                 r = seat_attach_session(s, session);
@@ -412,7 +412,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                         DBUS_TYPE_STRING, &session->id,
                         DBUS_TYPE_OBJECT_PATH, &p,
                         DBUS_TYPE_STRING, &session->user->runtime_path,
-                        DBUS_TYPE_UNIX_FD, &pipe_fds[0],
+                        DBUS_TYPE_UNIX_FD, &pipe_fds[1],
                         DBUS_TYPE_INVALID);
         free(p);
 
@@ -421,7 +421,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
                 goto fail;
         }
 
-        close_nointr_nofail(pipe_fds[0]);
+        close_nointr_nofail(pipe_fds[1]);
         *_reply = reply;
 
         return 0;

commit ed18b08bed983b845c72a83666a7d7db546d89ad
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 19:42:45 2011 +0200

    logind: various clean-ups

diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
index 4a4527d..ad0298e 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -378,6 +378,9 @@ int seat_send_changed(Seat *s, const char *properties) {
 
         assert(s);
 
+        if (!s->started)
+                return 0;
+
         p = seat_bus_path(s);
         if (!p)
                 return -ENOMEM;
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 26c2bd4..c232a87 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -350,18 +350,17 @@ int seat_stop(Seat *s) {
 
         assert(s);
 
-        if (!s->started)
-                return 0;
-
-        log_info("Removed seat %s.", s->id);
-
-        seat_send_signal(s, false);
+        if (s->started)
+                log_info("Removed seat %s.", s->id);
 
         seat_stop_sessions(s);
 
         unlink(s->state_file);
         seat_add_to_gc_queue(s);
 
+        if (s->started)
+                seat_send_signal(s, false);
+
         s->started = false;
 
         return r;
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index 8d1e607..2435a65 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -429,6 +429,9 @@ int session_send_changed(Session *s, const char *properties) {
 
         assert(s);
 
+        if (!s->started)
+                return 0;
+
         p = session_bus_path(s);
         if (!p)
                 return -ENOMEM;
diff --git a/src/logind-session.c b/src/logind-session.c
index 42d2801..74f8ad1 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -480,6 +480,10 @@ int session_start(Session *s) {
         if (s->started)
                 return 0;
 
+        r = user_start(s->user);
+        if (r < 0)
+                return r;
+
         log_info("New session %s of user %s.", s->id, s->user->name);
 
         /* Create cgroup */
@@ -514,7 +518,16 @@ int session_start(Session *s) {
 static bool session_shall_kill(Session *s) {
         assert(s);
 
-        return s->kill_processes;
+        if (!s->kill_processes)
+                return false;
+
+        if (strv_contains(s->manager->kill_exclude_users, s->user->name))
+                return false;
+
+        if (strv_isempty(s->manager->kill_only_users))
+                return true;
+
+        return strv_contains(s->manager->kill_only_users, s->user->name);
 }
 
 static int session_kill_cgroup(Session *s) {
@@ -584,10 +597,8 @@ int session_stop(Session *s) {
 
         assert(s);
 
-        if (!s->started)
-                return 0;
-
-        log_info("Removed session %s.", s->id);
+        if (s->started)
+                log_info("Removed session %s.", s->id);
 
         /* Kill cgroup */
         k = session_kill_cgroup(s);
@@ -599,8 +610,10 @@ int session_stop(Session *s) {
 
         unlink(s->state_file);
         session_add_to_gc_queue(s);
+        user_add_to_gc_queue(s->user);
 
-        session_send_signal(s, false);
+        if (s->started)
+                session_send_signal(s, false);
 
         if (s->seat) {
                 if (s->seat->active == s)
diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c
index 5926dcf..6d7e8e7 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -362,6 +362,9 @@ int user_send_changed(User *u, const char *properties) {
 
         assert(u);
 
+        if (!u->started)
+                return 0;
+
         p = user_bus_path(u);
         if (!p)
                 return -ENOMEM;
diff --git a/src/logind-user.c b/src/logind-user.c
index 8ebd6ec..63033e0 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -265,6 +265,8 @@ int user_start(User *u) {
         if (u->started)
                 return 0;
 
+        log_info("New user %s logged in.", u->name);
+
         /* Make XDG_RUNTIME_DIR */
         r = user_mkdir_runtime_path(u);
         if (r < 0)
@@ -304,7 +306,16 @@ static int user_stop_service(User *u) {
 static int user_shall_kill(User *u) {
         assert(u);
 
-        return u->manager->kill_user_processes;
+        if (!u->manager->kill_user_processes)
+                return false;
+
+        if (strv_contains(u->manager->kill_exclude_users, u->name))
+                return false;
+
+        if (strv_isempty(u->manager->kill_only_users))
+                return true;
+
+        return strv_contains(u->manager->kill_only_users, u->name);
 }
 
 static int user_kill_cgroup(User *u) {
@@ -368,8 +379,8 @@ int user_stop(User *u) {
         int r = 0, k;
         assert(u);
 
-        if (!u->started)
-                return 0;
+        if (u->started)
+                log_info("User %s logged out.", u->name);
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 k = session_stop(s);
@@ -377,8 +388,6 @@ int user_stop(User *u) {
                         r = k;
         }
 
-        user_send_signal(u, false);
-
         /* Kill systemd */
         k = user_stop_service(u);
         if (k < 0)
@@ -397,6 +406,9 @@ int user_stop(User *u) {
         unlink(u->state_file);
         user_add_to_gc_queue(u);
 
+        if (u->started)
+                user_send_signal(u, false);
+
         u->started = false;
 
         return r;
diff --git a/src/logind.h b/src/logind.h
index 7de8e3b..22eab55 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -35,10 +35,11 @@
 /* TODO:
  *
  * recreate VTs when disallocated
- * PAM rewrite
  * spawn user systemd
  * direct client API
  * subscribe to cgroup changes, fd HUP
+ * D-Bus method: AttachDevice(seat, device);
+ * D-Bus method: PermitLinger(user, bool b);
  *
  * non-local X11 server
  * reboot/shutdown halt management
diff --git a/src/pam-module.c b/src/pam-module.c
index b742d64..dc7c001 100644
--- a/src/pam-module.c
+++ b/src/pam-module.c
@@ -60,7 +60,7 @@ static int parse_argv(pam_handle_t *handle,
 
                 if (startswith(argv[i], "kill-processes=")) {
                         if ((k = parse_boolean(argv[i] + 15)) < 0) {
-                                pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
+                                pam_syslog(handle, LOG_ERR, "Failed to parse kill-processes= argument.");
                                 return k;
                         }
 
@@ -304,26 +304,25 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 int flags,
                 int argc, const char **argv) {
 
-        const char *username = NULL;
         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;
         char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
-        int r;
         DBusError error;
         uint32_t uid, pid;
         DBusMessageIter iter;
         dbus_bool_t kp;
-        const char *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type;
         int session_fd = -1;
         DBusConnection *bus = NULL;
         DBusMessage *m = NULL, *reply = NULL;
         dbus_bool_t remote;
+        int r;
 
         assert(handle);
 
         dbus_error_init(&error);
 
-        pam_syslog(handle, LOG_ERR, "pam-systemd initializing");
+        /* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */
 
         /* Make this a NOP on non-systemd systems */
         if (sd_booted() <= 0)
@@ -333,8 +332,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                        argc, argv,
                        &controllers, &reset_controllers,
                        &kill_processes, &kill_only_users, &kill_exclude_users,
-                       &debug) < 0)
-                return PAM_SESSION_ERR;
+                       &debug) < 0) {
+                r = PAM_SESSION_ERR;
+                goto finish;
+        }
 
         r = get_user_data(handle, &username, &pw);
         if (r != PAM_SUCCESS)
@@ -343,6 +344,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (kill_processes)
                 kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
 
+        dbus_connection_set_change_sigpipe(FALSE);
+
         bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
         if (!bus) {
                 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
@@ -370,18 +373,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         pam_get_item(handle, PAM_TTY, (const void**) &tty);
         pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
         pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
+        seat = pam_getenv(handle, "XDG_SEAT");
 
-        if (isempty(tty))
-                service = "";
-        if (isempty(tty))
-                tty = "";
-        if (isempty(display))
-                display = "";
-        if (isempty(remote_user))
-                remote_user = "";
-        if (isempty(remote_host))
-                remote_host = "";
-        seat = "";
+        service = strempty(service);
+        tty = strempty(tty);
+        display = strempty(display);
+        remote_user = strempty(remote_user);
+        remote_host = strempty(remote_host);
+        seat = strempty(seat);
 
         type = !isempty(display) ? "x11" :
                    !isempty(tty) ? "tty" : "other";
@@ -481,12 +480,12 @@ finish:
                 dbus_connection_unref(bus);
         }
 
-        if (reply)
-                dbus_message_unref(reply);
-
         if (m)
                 dbus_message_unref(m);
 
+        if (reply)
+                dbus_message_unref(reply);
+
         if (session_fd >= 0)
                 close_nointr_nofail(session_fd);
 

commit 98a28fef2618e54a644614c759f371f297381b70
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 24 18:50:50 2011 +0200

    logind: hook up PAM module with logind

diff --git a/Makefile.am b/Makefile.am
index a2d7397..bfc1079 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1248,10 +1248,12 @@ systemd_tty_ask_password_agent_LDADD = \
 
 pam_systemd_la_SOURCES = \
 	src/pam-module.c \
-	src/cgroup-util.c
+        src/dbus-common.c
 
 pam_systemd_la_CFLAGS = \
-	$(AM_CFLAGS)
+	$(AM_CFLAGS) \
+        $(PAM_CFLAGS) \
+        $(DBUS_CFLAGS) \
 	-fvisibility=hidden
 
 pam_systemd_la_LDFLAGS = \
@@ -1264,7 +1266,8 @@ pam_systemd_la_LDFLAGS = \
 pam_systemd_la_LIBADD = \
 	libsystemd-basic.la \
 	libsystemd-daemon.la \
-	$(PAM_LIBS)
+	$(PAM_LIBS) \
+        $(DBUS_LIBS)
 
 SED_PROCESS = \
 	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
@@ -1510,7 +1513,7 @@ endif
 		$(LN_S) graphical.target runlevel5.target && \
 		$(LN_S) reboot.target runlevel6.target )
 	( cd $(DESTDIR)$(systemunitdir) && \
-		rm -f default.target ctrl-alt-del.target dbus-org.freedesktop.hostname1.service && \
+		rm -f default.target ctrl-alt-del.target dbus-org.freedesktop.hostname1.service dbus-org.freedesktop.locale1.service dbus-org.freedesktop.timedate1.service dbus-org.freedesktop.login1.service && \
 		$(LN_S) graphical.target default.target && \
 		$(LN_S) reboot.target ctrl-alt-del.target && \
                 $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service && \
diff --git a/src/dbus-common.c b/src/dbus-common.c
index e439a42..73f9e87 100644
--- a/src/dbus-common.c
+++ b/src/dbus-common.c
@@ -463,23 +463,12 @@ int bus_property_append_string(DBusMessageIter *i, const char *property, void *d
 }
 
 int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) {
-        DBusMessageIter sub;
         char **t = data;
 
         assert(i);
         assert(property);
 
-        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
-                return -ENOMEM;
-
-        STRV_FOREACH(t, t)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t))
-                        return -ENOMEM;
-
-        if (!dbus_message_iter_close_container(i, &sub))
-                return -ENOMEM;
-
-        return 0;
+        return bus_append_strv_iter(i, t);
 }
 
 int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) {
@@ -796,3 +785,21 @@ int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
 
         return 0;
 }
+
+int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
+        DBusMessageIter sub;
+
+        assert(iter);
+
+        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
+                return -ENOMEM;
+
+        STRV_FOREACH(l, l)
+                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
+                        return -ENOMEM;
+
+        if (!dbus_message_iter_close_container(iter, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
diff --git a/src/dbus-common.h b/src/dbus-common.h
index 9368f75..04485e5 100644
--- a/src/dbus-common.h
+++ b/src/dbus-common.h
@@ -161,4 +161,6 @@ unsigned bus_events_to_flags(uint32_t events);
 int bus_parse_strv(DBusMessage *m, char ***_l);
 int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l);
 
+int bus_append_strv_iter(DBusMessageIter *iter, char **l);
+
 #endif
diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 662ffd0..10a826b 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -21,9 +21,11 @@
 
 #include <errno.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "logind.h"
 #include "dbus-common.h"
+#include "strv.h"
 
 #define BUS_MANAGER_INTERFACE                                           \
         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
@@ -51,6 +53,7 @@
         "  <method name=\"CreateSession\">\n"                           \
         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
+        "   <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
@@ -60,9 +63,10 @@
         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
         "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
         "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
-        "   <arg name=\"kill_processes\" type=\"as\" direction=\"in\"/>\n" \
+        "   <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
+        "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
         "  </method>\n"                                                 \
         "  <method name=\"ActivateSession\">\n"                         \
@@ -161,6 +165,285 @@ static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *pr
         return 0;
 }
 
+static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
+        Session *session = NULL;
+        User *user = NULL;
+        const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
+        uint32_t uid, leader, audit_id = 0;
+        dbus_bool_t remote, kill_processes;
+        char **controllers = NULL, **reset_controllers = NULL;
+        SessionType t;
+        Seat *s;
+        DBusMessageIter iter;
+        int r;
+        char *id, *p;
+        int vtnr = -1;
+        int pipe_fds[2] = { -1, -1 };
+        DBusMessage *reply = NULL;
+        bool b;
+
+        assert(m);
+        assert(message);
+        assert(_reply);
+
+        if (!dbus_message_iter_init(message, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &uid);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &leader);
+
+        if (leader <= 0 ||
+            !dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &service);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &type);
+        t = session_type_from_string(type);
+
+        if (t < 0 ||
+            !dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &seat);
+
+        if (isempty(seat))
+                s = NULL;
+        else {
+                s = hashmap_get(m->seats, seat);
+                if (!s)
+                        return -ENOENT;
+        }
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &tty);
+
+        if (tty_is_vc(tty)) {
+
+                if (!s)
+                        s = m->vtconsole;
+                else if (s != m->vtconsole)
+                        return -EINVAL;
+
+                vtnr = vtnr_from_tty(tty);
+
+                if (vtnr <= 0)
+                        return vtnr < 0 ? vtnr : -EINVAL;
+
+        } else if (s == m->vtconsole)
+                return -EINVAL;
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &display);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &remote);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &remote_user);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        dbus_message_iter_get_basic(&iter, &remote_host);
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
+                return -EINVAL;
+
+        r = bus_parse_strv_iter(&iter, &controllers);
+        if (r < 0)
+                return -EINVAL;
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        r = bus_parse_strv_iter(&iter, &reset_controllers);
+        if (r < 0)
+                goto fail;
+
+        if (!dbus_message_iter_next(&iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
+                r = -EINVAL;
+                goto fail;
+        }
+
+        dbus_message_iter_get_basic(&iter, &kill_processes);
+
+        r = manager_add_user_by_uid(m, uid, &user);
+        if (r < 0)
+                goto fail;
+
+        audit_session_from_pid(leader, &audit_id);
+
+        if (audit_id > 0)
+                asprintf(&id, "%lu", (unsigned long) audit_id);
+        else
+                asprintf(&id, "c%lu", ++m->session_counter);
+
+        if (!id) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (hashmap_get(m->sessions, id)) {
+                r = -EEXIST;
+                goto fail;
+        }
+
+        r = manager_add_session(m, user, id, &session);
+        free(id);
+        if (r < 0)
+                goto fail;
+
+        session->leader = leader;
+        session->audit_id = audit_id;
+        session->type = t;
+        session->remote = remote;
+        session->controllers = controllers;
+        session->reset_controllers = reset_controllers;
+        session->kill_processes = kill_processes;
+        session->vtnr = vtnr;
+
+        controllers = reset_controllers = NULL;
+
+        if (!isempty(tty)) {
+                session->tty = strdup(tty);
+                if (!session->tty) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(display)) {
+                session->display = strdup(display);
+                if (!session->display) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(remote_user)) {
+                session->remote_user = strdup(remote_user);
+                if (!session->remote_user) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(remote_host)) {
+                session->remote_host = strdup(remote_host);
+                if (!session->remote_host) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (!isempty(service)) {
+                session->service = strdup(service);
+                if (!session->service) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        if (pipe(pipe_fds) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        session->pipe_fd = pipe_fds[1];
+        pipe_fds[1] = -1;
+
+        if (s) {
+                r = seat_attach_session(s, session);
+                if (r < 0)
+                        goto fail;
+        }
+
+        r = session_start(session);
+        if (r < 0)
+                goto fail;
+
+        reply = dbus_message_new_method_return(message);
+        if (!reply) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        p = session_bus_path(session);
+        if (!p) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        b = dbus_message_append_args(
+                        reply,
+                        DBUS_TYPE_STRING, &session->id,
+                        DBUS_TYPE_OBJECT_PATH, &p,
+                        DBUS_TYPE_STRING, &session->user->runtime_path,
+                        DBUS_TYPE_UNIX_FD, &pipe_fds[0],
+                        DBUS_TYPE_INVALID);
+        free(p);
+
+        if (!b) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        close_nointr_nofail(pipe_fds[0]);
+        *_reply = reply;
+
+        return 0;
+
+fail:
+        strv_free(controllers);
+        strv_free(reset_controllers);
+
+        if (session)
+                session_add_to_gc_queue(session);
+
+        if (user)
+                user_add_to_gc_queue(user);
+
+        close_pipe(pipe_fds);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return r;
+}
+
 static DBusHandlerResult manager_message_handler(
                 DBusConnection *connection,
                 DBusMessage *message,
@@ -171,7 +454,6 @@ static DBusHandlerResult manager_message_handler(
         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   },
@@ -425,6 +707,15 @@ static DBusHandlerResult manager_message_handler(
                 if (!dbus_message_iter_close_container(&iter, &sub))
                         goto oom;
 
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
+
+                r = bus_manager_create_session(m, message, &reply);
+                if (r == -ENOMEM)
+                        goto oom;
+
+                if (r < 0)
+                        return bus_send_error_reply(connection, message, &error, r);
+
         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
                 const char *name;
                 Session *session;
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 12a1b80..26c2bd4 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -41,7 +41,7 @@ Seat *seat_new(Manager *m, const char *id) {
         if (!s)
                 return NULL;
 
-        s->state_file = strappend("/run/systemd/seat/", id);
+        s->state_file = strappend("/run/systemd/seats/", id);
         if (!s->state_file) {
                 free(s);
                 return NULL;
@@ -86,7 +86,7 @@ int seat_save(Seat *s) {
 
         assert(s);
 
-        r = safe_mkdir("/run/systemd/seat", 0755, 0, 0);
+        r = safe_mkdir("/run/systemd/seats", 0755, 0, 0);
         if (r < 0)
                 goto finish;
 
@@ -246,6 +246,14 @@ int seat_set_active(Seat *s, Session *session) {
         if (!session || session->started)
                 seat_send_changed(s, "ActiveSession\0");
 
+        seat_save(s);
+
+        if (session)
+                session_save(session);
+
+        if (old_active)
+                session_save(old_active);
+
         return 0;
 }
 
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index b0e592d..8d1e607 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -49,6 +49,7 @@
         "  <property name=\"Remote\" type=\"b\" access=\"read\"/>\n"    \
         "  <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"Leader\" type=\"u\" access=\"read\"/>\n"    \
         "  <property name=\"Audit\" type=\"u\" access=\"read\"/>\n"     \
         "  <property name=\"Type\" type=\"s\" access=\"read\"/>\n"      \
@@ -236,6 +237,7 @@ static DBusHandlerResult session_message_dispatch(
                 { "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", "Service",            bus_property_append_string,   "s",    s->service              },
                 { "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                },
diff --git a/src/logind-session.c b/src/logind-session.c
index 5ba6b21..42d2801 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -40,7 +40,7 @@ Session* session_new(Manager *m, User *u, const char *id) {
         if (!s)
                 return NULL;
 
-        s->state_file = strappend("/run/systemd/session/", id);
+        s->state_file = strappend("/run/systemd/sessions/", id);
         if (!s->state_file) {
                 free(s);
                 return NULL;
@@ -90,9 +90,13 @@ void session_free(Session *s) {
         free(s->display);
         free(s->remote_host);
         free(s->remote_user);
+        free(s->service);
 
         hashmap_remove(s->manager->sessions, s->id);
 
+        if (s->pipe_fd >= 0)
+                close_nointr_nofail(s->pipe_fd);
+
         free(s->state_file);
         free(s);
 }
@@ -104,7 +108,7 @@ int session_save(Session *s) {
 
         assert(s);
 
-        r = safe_mkdir("/run/systemd/session", 0755, 0, 0);
+        r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
         if (r < 0)
                 goto finish;
 
@@ -159,6 +163,11 @@ int session_save(Session *s) {
                         "REMOTE_USER=%s\n",
                         s->remote_user);
 
+        if (s->service)
+                fprintf(f,
+                        "SERVICE=%s\n",
+                        s->service);
+
         if (s->seat && seat_is_vtconsole(s->seat))
                 fprintf(f,
                         "VTNR=%i\n",
@@ -213,9 +222,9 @@ int session_load(Session *s) {
                            "DISPLAY",        &s->display,
                            "REMOTE_HOST",    &s->remote_host,
                            "REMOTE_USER",    &s->remote_user,
+                           "SERVICE",        &s->service,
                            "VTNR",           &vtnr,
                            "LEADER",         &leader,
-                           "AUDIT_ID",       &audit_id,
                            NULL);
 
         if (r < 0)
@@ -253,16 +262,11 @@ int session_load(Session *s) {
                 pid_t pid;
 
                 k = parse_pid(leader, &pid);
-                if (k >= 0 && pid >= 1)
+                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;
+                        audit_session_from_pid(pid, &s->audit_id);
+                }
         }
 
 finish:
@@ -384,6 +388,28 @@ done:
         return 0;
 }
 
+static int session_create_one_group(Session *s, const char *controller, const char *path) {
+        int r;
+
+        assert(s);
+        assert(controller);
+        assert(path);
+
+        if (s->leader > 0)
+                r = cg_create_and_attach(controller, path, s->leader);
+        else
+                r = cg_create(controller, path);
+
+        if (r < 0)
+                return r;
+
+        r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid);
+        if (r >= 0)
+                r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
+
+        return r;
+}
+
 static int session_create_cgroup(Session *s) {
         char **k;
         char *p;
@@ -401,11 +427,7 @@ static int session_create_cgroup(Session *s) {
         } else
                 p = s->cgroup_path;
 
-        if (s->leader > 0)
-                r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, p, s->leader);
-        else
-                r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
-
+        r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
         if (r < 0) {
                 free(p);
                 s->cgroup_path = NULL;
@@ -415,14 +437,35 @@ static int session_create_cgroup(Session *s) {
 
         s->cgroup_path = p;
 
+        STRV_FOREACH(k, s->controllers) {
+
+                if (strv_contains(s->reset_controllers, *k))
+                        continue;
+
+                r = session_create_one_group(s, *k, p);
+                if (r < 0)
+                        log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
+        }
+
         STRV_FOREACH(k, s->manager->controllers) {
-                if (s->leader > 0)
-                        r = cg_create_and_attach(*k, p, s->leader);
-                else
-                        r = cg_create(*k, p);
 
+                if (strv_contains(s->reset_controllers, *k) ||
+                    strv_contains(s->controllers, *k))
+                        continue;
+
+                r = session_create_one_group(s, *k, p);
                 if (r < 0)
-                        log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
+                        log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
+        }
+
+        if (s->leader > 0) {
+
+                STRV_FOREACH(k, s->reset_controllers) {
+                        r = cg_attach(*k, "/", s->leader);
+                        if (r < 0)
+                                log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
+
+                }
         }
 
         return 0;
@@ -437,6 +480,8 @@ int session_start(Session *s) {
         if (s->started)
                 return 0;
 
+        log_info("New session %s of user %s.", s->id, s->user->name);
+
         /* Create cgroup */
         r = session_create_cgroup(s);
         if (r < 0)
@@ -542,6 +587,8 @@ int session_stop(Session *s) {
         if (!s->started)
                 return 0;
 
+        log_info("Removed session %s.", s->id);
+
         /* Kill cgroup */
         k = session_kill_cgroup(s);
         if (k < 0)
@@ -702,7 +749,8 @@ void session_add_to_gc_queue(Session *s) {
 
 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
         [SESSION_TTY] = "tty",
-        [SESSION_X11] = "x11"
+        [SESSION_X11] = "x11",
+        [SESSION_OTHER] = "other"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
diff --git a/src/logind-session.h b/src/logind-session.h
index d2f2552..01c9504 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -33,6 +33,7 @@ typedef struct Session Session;
 typedef enum SessionType {
         SESSION_TTY,
         SESSION_X11,
+        SESSION_OTHER,
         _SESSION_TYPE_MAX,
         _SESSION_TYPE_INVALID = -1
 } SessionType;
@@ -56,6 +57,8 @@ struct Session {
         char *remote_user;
         char *remote_host;
 
+        char *service;
+
         int vtnr;
         Seat *seat;
 
diff --git a/src/logind-user.c b/src/logind-user.c
index cb3e441..8ebd6ec 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -45,7 +45,7 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
                 return NULL;
         }
 
-        if (asprintf(&u->state_file, "/run/systemd/user/%lu", (unsigned long) uid) < 0) {
+        if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
                 free(u->name);
                 free(u);
                 return NULL;
@@ -94,7 +94,7 @@ int user_save(User *u) {
         assert(u);
         assert(u->state_file);
 
-        r = safe_mkdir("/run/systemd/user", 0755, 0, 0);
+        r = safe_mkdir("/run/systemd/users", 0755, 0, 0);
         if (r < 0)
                 goto finish;
 
@@ -152,7 +152,7 @@ finish:
 int user_load(User *u) {
         int r;
         char *display = NULL;
-        Session *s;
+        Session *s = NULL;
 
         assert(u);
 
@@ -172,8 +172,10 @@ int user_load(User *u) {
                 return r;
         }
 
-        s = hashmap_get(u->manager->sessions, display);
-        free(display);
+        if (display) {
+                s = hashmap_get(u->manager->sessions, display);
+                free(display);
+        }
 
         if (s && s->display && x11_display_is_local(s->display))
                 u->display = s;
diff --git a/src/logind.h b/src/logind.h
index 1b7a2b2..7de8e3b 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -37,8 +37,8 @@
  * recreate VTs when disallocated
  * PAM rewrite
  * spawn user systemd
- * dbus API
  * direct client API
+ * subscribe to cgroup changes, fd HUP
  *
  * non-local X11 server
  * reboot/shutdown halt management
@@ -76,11 +76,13 @@ struct Manager {
         Seat *vtconsole;
 
         char *cgroup_path;
-        char **controllers, **reset_controllers;
+        char **controllers;
 
         char **kill_only_users, **kill_exclude_users;
 
         bool kill_user_processes;
+
+        unsigned long session_counter;
 };
 
 Manager *manager_new(void);
diff --git a/src/pam-module.c b/src/pam-module.c
index bdf6133..b742d64 100644
--- a/src/pam-module.c
+++ b/src/pam-module.c
@@ -33,18 +33,17 @@
 #include <security/pam_misc.h>
 
 #include "util.h"
-#include "cgroup-util.h"
 #include "macro.h"
 #include "sd-daemon.h"
 #include "strv.h"
+#include "dbus-common.h"
+#include "def.h"
 
 static int parse_argv(pam_handle_t *handle,
                       int argc, const char **argv,
-                      bool *create_session,
-                      bool *kill_session,
-                      bool *kill_user,
                       char ***controllers,
                       char ***reset_controllers,
+                      bool *kill_processes,
                       char ***kill_only_users,
                       char ***kill_exclude_users,
                       bool *debug) {
@@ -59,32 +58,25 @@ static int parse_argv(pam_handle_t *handle,
         for (i = 0; i < (unsigned) argc; i++) {
                 int k;
 
-                if (startswith(argv[i], "create-session=")) {
+                if (startswith(argv[i], "kill-processes=")) {
                         if ((k = parse_boolean(argv[i] + 15)) < 0) {
-                                pam_syslog(handle, LOG_ERR, "Failed to parse create-session= argument.");
+                                pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
                                 return k;
                         }
 
-                        if (create_session)
-                                *create_session = k;
+                        if (kill_processes)
+                                *kill_processes = k;
 
                 } else if (startswith(argv[i], "kill-session=")) {
+                        /* As compatibility for old versions */
+
                         if ((k = parse_boolean(argv[i] + 13)) < 0) {
                                 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
                                 return k;
                         }
 
-                        if (kill_session)
-                                *kill_session = k;
-
-                } else if (startswith(argv[i], "kill-user=")) {
-                        if ((k = parse_boolean(argv[i] + 10)) < 0) {
-                                pam_syslog(handle, LOG_ERR, "Failed to parse kill-user= argument.");
-                                return k;
-                        }
-
-                        if (kill_user)
-                                *kill_user = k;
+                        if (kill_processes)
+                                *kill_processes = k;
 
                 } else if (startswith(argv[i], "controllers=")) {
 
@@ -155,6 +147,11 @@ static int parse_argv(pam_handle_t *handle,
                         if (debug)
                                 *debug = k;
 
+                } else if (startswith(argv[i], "create-session=") ||
+                           startswith(argv[i], "kill-user=")) {
+
+                        pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]);
+
                 } else {
                         pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
                         return -EINVAL;
@@ -173,13 +170,10 @@ static int parse_argv(pam_handle_t *handle,
         }
 
         if (controllers)
-                strv_remove(*controllers, "name=systemd");
+                strv_remove(*controllers, SYSTEMD_CGROUP_CONTROLLER);
 
         if (reset_controllers)
-                strv_remove(*reset_controllers, "name=systemd");
-
-        if (kill_session && *kill_session && kill_user)
-                *kill_user = true;
+                strv_remove(*reset_controllers, SYSTEMD_CGROUP_CONTROLLER);
 
         if (!kill_exclude_users_set && kill_exclude_users) {
                 char **l;
@@ -195,96 +189,6 @@ static int parse_argv(pam_handle_t *handle,
         return 0;
 }
 
-static int open_file_and_lock(const char *fn) {
-        int fd;
-
-        assert(fn);
-
-        if ((fd = open(fn, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_CREAT, 0600)) < 0)
-                return -errno;
-
-        /* The BSD socket semantics are a lot nicer than those of
-         * POSIX locks. Which is why we use flock() here. BSD locking
-         * does not work across NFS which however is not needed here
-         * as the filesystems in question should be local, and only
-         * locally accessible, and most likely even tmpfs. */
-
-        if (flock(fd, LOCK_EX) < 0) {
-                close_nointr_nofail(fd);
-                return -errno;
-        }
-
-        return fd;
-}
-
-enum {
-        SESSION_ID_AUDIT = 'a',
-        SESSION_ID_COUNTER = 'c',
-        SESSION_ID_RANDOM = 'r'
-};
-
-static uint64_t get_session_id(int *mode) {
-        char *s;
-        int fd;
-
-        assert(mode);
-
-        /* First attempt: let's use the session ID of the audit
-         * system, if it is available. */
-        if (have_effective_cap(CAP_AUDIT_CONTROL) > 0)
-                if (read_one_line_file("/proc/self/sessionid", &s) >= 0) {
-                        uint32_t u;
-                        int r;
-
-                        r = safe_atou32(s, &u);
-                        free(s);
-
-                        if (r >= 0 && u != (uint32_t) -1 && u > 0) {
-                                *mode = SESSION_ID_AUDIT;
-                                return (uint64_t) u;
-                        }
-                }
-
-        /* Second attempt, use our own counter. */
-        if ((fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-session")) >= 0) {
-                uint64_t counter;
-                ssize_t r;
-
-                /* We do a bit of endianess swapping here, just to be
-                 * sure. /run should be machine specific anyway, and
-                 * even mounted from tmpfs, so this byteswapping
-                 * should really not be necessary. But then again, you
-                 * never know, so let's avoid any risk. */
-
-                if (loop_read(fd, &counter, sizeof(counter), false) != sizeof(counter))
-                        counter = 1;
-                else
-                        counter = le64toh(counter) + 1;
-
-                if (lseek(fd, 0, SEEK_SET) == 0) {
-                        uint64_t swapped = htole64(counter);
-
-                        r = loop_write(fd, &swapped, sizeof(swapped), false);
-
-                        if (r != sizeof(swapped))
-                                r = -EIO;
-                } else
-                        r = -errno;
-
-                close_nointr_nofail(fd);
-
-                if (r >= 0) {
-                        *mode = SESSION_ID_COUNTER;
-                        return counter;
-                }
-        }
-
-        *mode = SESSION_ID_RANDOM;
-
-        /* Last attempt, pick a random value */
-        return (uint64_t) random_ull();
-}
-
 static int get_user_data(
                 pam_handle_t *handle,
                 const char **ret_username,
@@ -346,232 +250,6 @@ static int get_user_data(
         return PAM_SUCCESS;
 }
 
-static int create_user_group(
-                pam_handle_t *handle,
-                const char *controller,
-                const char *group,
-                struct passwd *pw,
-                bool attach,
-                bool remember) {
-
-        int r;
-
-        assert(handle);
-        assert(group);
-
-        if (attach)
-                r = cg_create_and_attach(controller, group, 0);
-        else
-                r = cg_create(controller, group);
-
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to create cgroup: %s", strerror(-r));
-                return PAM_SESSION_ERR;
-        }
-
-        if (r > 0 && remember) {
-                /* Remember that it was us who created this group, and
-                 * that hence we need to remove it too. This is a
-                 * protection against removing the cgroup when run
-                 * recursively. */
-                if ((r = pam_set_data(handle, "systemd.created", INT_TO_PTR(1), NULL)) != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to install created variable.");
-                        return r;
-                }
-        }
-
-        if ((r = cg_set_task_access(controller, group, 0644, pw->pw_uid, pw->pw_gid)) < 0 ||
-            (r = cg_set_group_access(controller, group, 0755, pw->pw_uid, pw->pw_gid)) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to change access modes: %s", strerror(-r));
-                return PAM_SESSION_ERR;
-        }
-
-        return PAM_SUCCESS;
-}
-
-static int reset_group(
-                pam_handle_t *handle,
-                const char *controller) {
-
-        int r;
-
-        assert(handle);
-
-        if ((r = cg_attach(controller, "/", 0)) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to reset cgroup for controller %s: %s", controller, strerror(-r));
-                return PAM_SESSION_ERR;
-        }
-
-        return PAM_SUCCESS;
-}
-
-_public_ PAM_EXTERN int pam_sm_open_session(
-                pam_handle_t *handle,
-                int flags,
-                int argc, const char **argv) {
-
-        const char *username = NULL;
-        struct passwd *pw;
-        int r;
-        char *buf = NULL;
-        int lock_fd = -1;
-        bool create_session = true;
-        bool debug = false;
-        char **controllers = NULL, **reset_controllers = NULL, **c;
-        char *cgroup_user_tree = NULL;
-
-        assert(handle);
-
-        /* pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing"); */
-
-        /* Make this a NOP on non-systemd systems */
-        if (sd_booted() <= 0)
-                return PAM_SUCCESS;
-
-        if (parse_argv(handle,
-                       argc, argv,
-                       &create_session, NULL, NULL,
-                       &controllers, &reset_controllers,
-                       NULL, NULL, &debug) < 0)
-                return PAM_SESSION_ERR;
-
-        if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
-                goto finish;
-
-        if ((r = cg_get_user_path(&cgroup_user_tree)) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to determine user cgroup tree: %s", strerror(-r));
-                r = PAM_SYSTEM_ERR;
-                goto finish;
-        }
-
-        if (safe_mkdir(RUNTIME_DIR "/user", 0755, 0, 0) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to create runtime directory: %m");
-                r = PAM_SYSTEM_ERR;
-                goto finish;
-        }
-
-        if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m");
-                r = PAM_SYSTEM_ERR;
-                goto finish;
-        }
-
-        /* Create /run/user/$USER */
-        free(buf);
-        if (asprintf(&buf, RUNTIME_DIR "/user/%s", username) < 0) {
-                r = PAM_BUF_ERR;
-                goto finish;
-        }
-
-        if (safe_mkdir(buf, 0700, pw->pw_uid, pw->pw_gid) < 0) {
-                pam_syslog(handle, LOG_WARNING, "Failed to create runtime directory: %m");
-                r = PAM_SYSTEM_ERR;
-                goto finish;
-        } else if ((r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", buf, 0)) != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
-                goto finish;
-        }
-
-        free(buf);
-        buf = NULL;
-
-        if (create_session) {
-                const char *id;
-
-                /* Reuse or create XDG session ID */
-                if (!(id = pam_getenv(handle, "XDG_SESSION_ID"))) {
-                        int mode;
-
-                        if (asprintf(&buf, "%llux", (unsigned long long) get_session_id(&mode)) < 0) {
-                                r = PAM_BUF_ERR;
-                                goto finish;
-                        }
-
-                        /* To avoid id clashes we add the session id
-                         * source to our session ids. Note that the
-                         * session id source might change during
-                         * runtime, because a filesystem became
-                         * writable or the system reconfigured. */
-                        buf[strlen(buf)-1] =
-                                mode != SESSION_ID_AUDIT ? (char) mode : 0;
-
-                        if ((r = pam_misc_setenv(handle, "XDG_SESSION_ID", buf, 0)) != PAM_SUCCESS) {
-                                pam_syslog(handle, LOG_ERR, "Failed to set session id.");
-                                goto finish;
-                        }
-
-                        if (!(id = pam_getenv(handle, "XDG_SESSION_ID"))) {
-                                pam_syslog(handle, LOG_ERR, "Failed to get session id.");
-                                r = PAM_SESSION_ERR;
-                                goto finish;
-                        }
-                }
-
-                r = asprintf(&buf, "%s/%s/%s", cgroup_user_tree, username, id);
-        } else
-                r = asprintf(&buf, "%s/%s/master", cgroup_user_tree, username);
-
-        if (r < 0) {
-                r = PAM_BUF_ERR;
-                goto finish;
-        }
-
-        if (debug)
-                pam_syslog(handle, LOG_DEBUG, "Moving new user session for %s into control group %s.", username, buf);
-
-        if ((r = create_user_group(handle, SYSTEMD_CGROUP_CONTROLLER, buf, pw, true, true)) != PAM_SUCCESS)
-                goto finish;
-
-        /* The additional controllers don't really matter, so we
-         * ignore the return value */
-        STRV_FOREACH(c, controllers)
-                create_user_group(handle, *c, buf, pw, true, false);
-
-        STRV_FOREACH(c, reset_controllers)
-                reset_group(handle, *c);
-
-        r = PAM_SUCCESS;
-
-finish:
-        free(buf);
-
-        if (lock_fd >= 0)
-                close_nointr_nofail(lock_fd);
-
-        strv_free(controllers);
-        strv_free(reset_controllers);
-
-        free(cgroup_user_tree);
-
-        return r;
-}
-
-static int session_remains(pam_handle_t *handle, const char *user_path) {
-        int r;
-        bool remains = false;
-        DIR *d;
-        char *subgroup;
-
-        if ((r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, user_path, &d)) < 0)
-                return r;
-
-        while ((r = cg_read_subgroup(d, &subgroup)) > 0) {
-
-                remains = !streq(subgroup, "master");
-                free(subgroup);
-
-                if (remains)
-                        break;
-        }
-
-        closedir(d);
-
-        if (r < 0)
-                return r;
-
-        return !!remains;
-}
-
 static bool check_user_lists(
                 pam_handle_t *handle,
                 uid_t uid,
@@ -588,7 +266,8 @@ static bool check_user_lists(
         else {
                 struct passwd *pw;
 
-                if ((pw = pam_modutil_getpwuid(handle, uid)))
+                pw = pam_modutil_getpwuid(handle, uid);
+                if (pw)
                         name = pw->pw_name;
         }
 
@@ -620,158 +299,211 @@ static bool check_user_lists(
         return false;
 }
 
-_public_ PAM_EXTERN int pam_sm_close_session(
+_public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
                 int argc, const char **argv) {
 
         const char *username = NULL;
-        bool kill_session = false;
-        bool kill_user = false;
-        bool debug = false;
-        int lock_fd = -1, r;
-        char *session_path = NULL, *nosession_path = NULL, *user_path = NULL;
-        const char *id;
         struct passwd *pw;
-        const void *created = NULL;
-        char **controllers = NULL, **c, **kill_only_users = NULL, **kill_exclude_users = NULL;
-        char *cgroup_user_tree = NULL;
+        bool kill_processes = false, debug = false;
+        char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
+        int r;
+        DBusError error;
+        uint32_t uid, pid;
+        DBusMessageIter iter;
+        dbus_bool_t kp;
+        const char *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type;
+        int session_fd = -1;
+        DBusConnection *bus = NULL;
+        DBusMessage *m = NULL, *reply = NULL;
+        dbus_bool_t remote;
 
         assert(handle);
 
+        dbus_error_init(&error);
+
+        pam_syslog(handle, LOG_ERR, "pam-systemd initializing");
+
         /* Make this a NOP on non-systemd systems */
         if (sd_booted() <= 0)
                 return PAM_SUCCESS;
 
         if (parse_argv(handle,
                        argc, argv,
-                       NULL, &kill_session, &kill_user,
-                       &controllers, NULL,
-                       &kill_only_users, &kill_exclude_users, &debug) < 0)
+                       &controllers, &reset_controllers,
+                       &kill_processes, &kill_only_users, &kill_exclude_users,
+                       &debug) < 0)
                 return PAM_SESSION_ERR;
 
-        if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
+        r = get_user_data(handle, &username, &pw);
+        if (r != PAM_SUCCESS)
                 goto finish;
 
-        if ((r = cg_get_user_path(&cgroup_user_tree)) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to determine user cgroup tree: %s", strerror(-r));
-                r = PAM_SYSTEM_ERR;
-                goto finish;
-        }
+        if (kill_processes)
+                kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
 
-        if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m");
-                r = PAM_SYSTEM_ERR;
+        bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+        if (!bus) {
+                pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
+                r = PAM_SESSION_ERR;
                 goto finish;
         }
 
-        /* We are probably still in some session/user dir. Move ourselves out of the way as first step */
-        if ((r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, cgroup_user_tree, 0)) < 0)
-                pam_syslog(handle, LOG_ERR, "Failed to move us away: %s", strerror(-r));
-
-        STRV_FOREACH(c, controllers)
-                if ((r = cg_attach(*c, cgroup_user_tree, 0)) < 0)
-                        pam_syslog(handle, LOG_ERR, "Failed to move us away in %s hierarchy: %s", *c, strerror(-r));
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.login1",
+                        "/org/freedesktop/login1",
+                        "org.freedesktop.login1.Manager",
+                        "CreateSession");
 
-        if (asprintf(&user_path, "%s/%s", cgroup_user_tree, username) < 0) {
+        if (!m) {
+                pam_syslog(handle, LOG_ERR, "Could not allocate create session message.");
                 r = PAM_BUF_ERR;
                 goto finish;
         }
 
-        pam_get_data(handle, "systemd.created", &created);
-
-        if ((id = pam_getenv(handle, "XDG_SESSION_ID")) && created) {
+        uid = pw->pw_uid;
+        pid = getpid();
+
+        pam_get_item(handle, PAM_SERVICE, (const void**) &service);
+        pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
+        pam_get_item(handle, PAM_TTY, (const void**) &tty);
+        pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
+        pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
+
+        if (isempty(tty))
+                service = "";
+        if (isempty(tty))
+                tty = "";
+        if (isempty(display))
+                display = "";
+        if (isempty(remote_user))
+                remote_user = "";
+        if (isempty(remote_host))
+                remote_host = "";
+        seat = "";
+
+        type = !isempty(display) ? "x11" :
+                   !isempty(tty) ? "tty" : "other";
+
+        remote = !isempty(remote_host) && !streq(remote_host, "localhost") && !streq(remote_host, "localhost.localdomain");
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_UINT32, &uid,
+                                      DBUS_TYPE_UINT32, &pid,
+                                      DBUS_TYPE_STRING, &service,
+                                      DBUS_TYPE_STRING, &type,
+                                      DBUS_TYPE_STRING, &seat,
+                                      DBUS_TYPE_STRING, &tty,
+                                      DBUS_TYPE_STRING, &display,
+                                      DBUS_TYPE_BOOLEAN, &remote,
+                                      DBUS_TYPE_STRING, &remote_user,
+                                      DBUS_TYPE_STRING, &remote_host,
+                                      DBUS_TYPE_INVALID)) {
+                pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
+                r = PAM_BUF_ERR;
+                goto finish;
+        }
 
-                if (asprintf(&session_path, "%s/%s/%s", cgroup_user_tree, username, id) < 0 ||
-                    asprintf(&nosession_path, "%s/%s/master", cgroup_user_tree, username) < 0) {
-                        r = PAM_BUF_ERR;
-                        goto finish;
-                }
+        dbus_message_iter_init_append(m, &iter);
 
-                if (kill_session && check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users))  {
-                        if (debug)
-                                pam_syslog(handle, LOG_DEBUG, "Killing remaining processes of user session %s of %s.", id, username);
+        r = bus_append_strv_iter(&iter, controllers);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
+                r = PAM_BUF_ERR;
+                goto finish;
+        }
 
-                        /* Kill processes in session cgroup, and delete it */
-                        if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, session_path, true)) < 0)
-                                pam_syslog(handle, LOG_ERR, "Failed to kill session cgroup: %s", strerror(-r));
-                } else {
-                        if (debug)
-                                pam_syslog(handle, LOG_DEBUG, "Moving remaining processes of user session %s of %s into control group %s.", id, username, nosession_path);
+        r = bus_append_strv_iter(&iter, reset_controllers);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
+                r = PAM_BUF_ERR;
+                goto finish;
+        }
 
-                        /* Migrate processes from session to user
-                         * cgroup. First, try to create the user group
-                         * in case it doesn't exist yet. Also, delete
-                         * the session group. */
-                        create_user_group(handle, SYSTEMD_CGROUP_CONTROLLER, nosession_path, pw, false, false);
+        kp = kill_processes;
+        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
+                pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
+                r = PAM_BUF_ERR;
+                goto finish;
+        }
 
-                        if ((r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, session_path, nosession_path, false, true)) < 0)
-                                pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup: %s", strerror(-r));
-                }
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
+                pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
+                r = PAM_SESSION_ERR;
+                goto finish;
+        }
 
-                STRV_FOREACH(c, controllers) {
-                        create_user_group(handle, *c, nosession_path, pw, false, false);
+        if (!dbus_message_get_args(reply, &error,
+                                   DBUS_TYPE_STRING, &id,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
+                                   DBUS_TYPE_STRING, &runtime_path,
+                                   DBUS_TYPE_UNIX_FD, &session_fd,
+                                   DBUS_TYPE_INVALID)) {
+                pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
+                r = PAM_SESSION_ERR;
+                goto finish;
+        }
 
-                        if ((r = cg_migrate_recursive(*c, session_path, nosession_path, false, true)) < 0)
-                                pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup in hierarchy %s: %s", *c, strerror(-r));
-                }
+        r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set session id.");
+                goto finish;
         }
 
-        /* GC user tree */
-        cg_trim(SYSTEMD_CGROUP_CONTROLLER, user_path, false);
+        r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
+                goto finish;
+        }
 
-        if ((r = session_remains(handle, user_path)) < 0)
-                pam_syslog(handle, LOG_ERR, "Failed to determine whether a session remains: %s", strerror(-r));
+        r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
+                return r;
+        }
 
-        /* Kill user processes not attached to any session */
-        if (kill_user && r == 0 && check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users)) {
+        session_fd = -1;
 
-                /* Kill user cgroup */
-                if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0)
-                        pam_syslog(handle, LOG_ERR, "Failed to kill user cgroup: %s", strerror(-r));
-        } else {
+        r = PAM_SUCCESS;
 
-                if ((r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0)
-                        pam_syslog(handle, LOG_ERR, "Failed to check user cgroup: %s", strerror(-r));
+finish:
+        strv_free(controllers);
+        strv_free(reset_controllers);
+        strv_free(kill_only_users);
+        strv_free(kill_exclude_users);
 
-                /* Remove user cgroup */
-                if (r > 0) {
-                        if ((r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, user_path)) < 0)
-                                pam_syslog(handle, LOG_ERR, "Failed to delete user cgroup: %s", strerror(-r));
+        dbus_error_free(&error);
 
-                /* If we managed to find somebody, don't cleanup the cgroup. */
-                } else if (r == 0)
-                        r = -EBUSY;
+        if (bus) {
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
         }
 
-        STRV_FOREACH(c, controllers)
-                cg_trim(*c, user_path, true);
-
-        if (r >= 0) {
-                const char *runtime_dir;
+        if (reply)
+                dbus_message_unref(reply);
 
-                if ((runtime_dir = pam_getenv(handle, "XDG_RUNTIME_DIR")))
-                        if ((r = rm_rf(runtime_dir, false, true)) < 0)
-                                pam_syslog(handle, LOG_ERR, "Failed to remove runtime directory: %s", strerror(-r));
-        }
+        if (m)
+                dbus_message_unref(m);
 
-        /* pam_syslog(handle, LOG_DEBUG, "pam-systemd done"); */
+        if (session_fd >= 0)
+                close_nointr_nofail(session_fd);
 
-        r = PAM_SUCCESS;
+        return r;
+}
 
-finish:
-        if (lock_fd >= 0)
-                close_nointr_nofail(lock_fd);
+_public_ PAM_EXTERN int pam_sm_close_session(
+                pam_handle_t *handle,
+                int flags,
+                int argc, const char **argv) {
 
-        free(session_path);
-        free(nosession_path);
-        free(user_path);
+        const void *p = NULL;
 
-        strv_free(controllers);
-        strv_free(kill_exclude_users);
-        strv_free(kill_only_users);
+        pam_get_data(handle, "systemd.session-fd", &p);
 
-        free(cgroup_user_tree);
+        if (p)
+                close_nointr(PTR_TO_INT(p) - 1);
 
-        return r;
+        return PAM_SUCCESS;
 }
diff --git a/src/uaccess.c b/src/uaccess.c
index e55ab51..524e4f0 100644
--- a/src/uaccess.c
+++ b/src/uaccess.c
@@ -45,7 +45,7 @@ int main(int argc, char *argv[]) {
         path = argv[1];
         seat = argv[2];
 
-        p = strappend("/run/systemd/seat/", seat);
+        p = strappend("/run/systemd/seats/", seat);
         if (!p) {
                 log_error("Out of memory.");
                 goto finish;
diff --git a/src/util.c b/src/util.c
index 08529cc..5d57d52 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4040,8 +4040,31 @@ bool tty_is_vc(const char *tty) {
         if (startswith(tty, "/dev/"))
                 tty += 5;
 
-        return startswith(tty, "tty") &&
-                tty[3] >= '0' && tty[3] <= '9';
+        return vtnr_from_tty(tty) >= 0;
+}
+
+int vtnr_from_tty(const char *tty) {
+        int i, r;
+
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        if (!startswith(tty, "tty") )
+                return -EINVAL;
+
+        if (tty[3] < '0' || tty[3] > '9')
+                return -EINVAL;
+
+        r = safe_atoi(tty+3, &i);
+        if (r < 0)
+                return r;
+
+        if (i < 0 || i > 63)
+                return -EINVAL;
+
+        return i;
 }
 
 const char *default_term_for_tty(const char *tty) {
@@ -5068,6 +5091,38 @@ int symlink_or_copy_atomic(const char *from, const char *to) {
         return r;
 }
 
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+        char *p, *s;
+        uint32_t u;
+        int r;
+
+        assert(pid >= 1);
+        assert(id);
+
+        if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+                return -ENOENT;
+
+        if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
+                return -ENOMEM;
+
+        r = read_one_line_file(p, &s);
+        free(p);
+        if (r < 0)
+                return r;
+
+        r = safe_atou32(s, &u);
+        free(s);
+
+        if (r < 0)
+                return r;
+
+        if (u == (uint32_t) -1 || u <= 0)
+                return -ENOENT;
+
+        *id = u;
+        return 0;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/util.h b/src/util.h
index 76e1d4f..ceabe92 100644
--- a/src/util.h
+++ b/src/util.h
@@ -396,6 +396,7 @@ char *fstab_node_to_udev_node(const char *p);
 void filter_environ(const char *prefix);
 
 bool tty_is_vc(const char *tty);
+int vtnr_from_tty(const char *tty);
 const char *default_term_for_tty(const char *tty);
 
 int detect_vm(const char **id);
@@ -439,6 +440,8 @@ int hwclock_reset_localtime_delta(void);
 int hwclock_get_time(struct tm *tm);
 int hwclock_set_time(const struct tm *tm);
 
+int audit_session_from_pid(pid_t pid, uint32_t *id);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 

commit 77527da0a02029ce9c5ec86d5db5ea42147a658f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 21 22:29:25 2011 +0200

    logind: make idle hint logic work

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index f73307f..662ffd0 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -130,13 +130,13 @@
 
 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
         Manager *m = data;
-        bool b;
+        dbus_bool_t b;
 
         assert(i);
         assert(property);
         assert(m);
 
-        b = manager_get_idle_hint(m, NULL);
+        b = manager_get_idle_hint(m, NULL) > 0;
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
                 return -ENOMEM;
 
diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
index 1895fe6..4a4527d 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -151,13 +151,13 @@ static int bus_seat_append_can_activate(DBusMessageIter *i, const char *property
 
 static int bus_seat_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
         Seat *s = data;
-        bool b;
+        dbus_bool_t b;
 
         assert(i);
         assert(property);
         assert(s);
 
-        b = seat_get_idle_hint(s, NULL);
+        b = seat_get_idle_hint(s, NULL) > 0;
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
                 return -ENOMEM;
 
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 09356b6..12a1b80 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -230,8 +230,7 @@ int seat_set_active(Seat *s, Session *session) {
         Session *old_active;
 
         assert(s);
-        assert(session);
-        assert(session->seat == s);
+        assert(!session || session->seat == s);
 
         if (session == s->active)
                 return 0;
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index 0ce880c..b0e592d 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -144,7 +144,7 @@ static int bus_session_append_user(DBusMessageIter *i, const char *property, voi
 
 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
         Session *s = data;
-        bool b;
+        dbus_bool_t b;
 
         assert(i);
         assert(property);
@@ -159,13 +159,13 @@ static int bus_session_append_active(DBusMessageIter *i, const char *property, v
 
 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
         Session *s = data;
-        bool b;
+        int b;
 
         assert(i);
         assert(property);
         assert(s);
 
-        b = session_get_idle_hint(s, NULL);
+        b = session_get_idle_hint(s, NULL) > 0;
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
                 return -ENOMEM;
 
diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c
index 623f2c9..5926dcf 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -154,13 +154,14 @@ static int bus_user_append_sessions(DBusMessageIter *i, const char *property, vo
 
 static int bus_user_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
         User *u = data;
-        bool b;
+        dbus_bool_t b;
 
         assert(i);
         assert(property);
         assert(u);
 
-        b = user_get_idle_hint(u, NULL);
+        b = user_get_idle_hint(u, NULL) > 0;
+
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
                 return -ENOMEM;
 

commit 9418f14772e7e646fe981d45506b3bbce68d6ccf
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 21 21:46:13 2011 +0200

    logind: send out PropertyChanged signals where appropriate

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index f9ee7e5..f73307f 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -604,3 +604,25 @@ oom:
 const DBusObjectPathVTable bus_manager_vtable = {
         .message_function = manager_message_handler
 };
+
+int manager_send_changed(Manager *manager, const char *properties) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+
+        assert(manager);
+
+        m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
+        if (!m)
+                goto finish;
+
+        if (!dbus_connection_send(manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        return r;
+}
diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
index 33eafe5..1895fe6 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -370,3 +370,31 @@ finish:
 
         return r;
 }
+
+int seat_send_changed(Seat *s, const char *properties) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+        char *p = NULL;
+
+        assert(s);
+
+        p = seat_bus_path(s);
+        if (!p)
+                return -ENOMEM;
+
+        m = bus_properties_changed_new(p, "org.freedesktop.login1.Seat", properties);
+        if (!m)
+                goto finish;
+
+        if (!dbus_connection_send(s->manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+        free(p);
+
+        return r;
+}
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 95c66dd..09356b6 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -226,8 +226,33 @@ int seat_apply_acls(Seat *s, Session *old_active) {
         return r;
 }
 
+int seat_set_active(Seat *s, Session *session) {
+        Session *old_active;
+
+        assert(s);
+        assert(session);
+        assert(session->seat == s);
+
+        if (session == s->active)
+                return 0;
+
+        old_active = s->active;
+        s->active = session;
+
+        seat_apply_acls(s, old_active);
+
+        if (session && session->started)
+                session_send_changed(session, "Active\0");
+
+        if (!session || session->started)
+                seat_send_changed(s, "ActiveSession\0");
+
+        return 0;
+}
+
 int seat_active_vt_changed(Seat *s, int vtnr) {
-        Session *i, *new_active = NULL, *old_active;
+        Session *i, *new_active = NULL;
+        int r;
 
         assert(s);
         assert(vtnr >= 1);
@@ -243,16 +268,10 @@ int seat_active_vt_changed(Seat *s, int vtnr) {
                         break;
                 }
 
-        if (new_active == s->active)
-                return 0;
-
-        old_active = s->active;
-        s->active = new_active;
-
-        seat_apply_acls(s, old_active);
+        r = seat_set_active(s, new_active);
         manager_spawn_autovt(s->manager, vtnr);
 
-        return 0;
+        return r;
 }
 
 int seat_read_active_vt(Seat *s) {
@@ -361,17 +380,19 @@ int seat_attach_session(Seat *s, Session *session) {
         assert(session);
         assert(!session->seat);
 
-        if (!seat_is_vtconsole(s)) {
-                if (s->sessions)
-                        return -EEXIST;
-
-                assert(!s->active);
-                s->active = session;
-        }
+        if (!seat_is_vtconsole(s) && s->sessions)
+                return -EEXIST;
 
         session->seat = s;
         LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
 
+        seat_send_changed(s, "Sessions\0");
+
+        if (!seat_is_vtconsole(s)) {
+                assert(!s->active);
+                seat_set_active(s, session);
+        }
+
         return 0;
 }
 
diff --git a/src/logind-seat.h b/src/logind-seat.h
index a7cd6c7..275939e 100644
--- a/src/logind-seat.h
+++ b/src/logind-seat.h
@@ -54,6 +54,7 @@ int seat_save(Seat *s);
 int seat_load(Seat *s);
 
 int seat_apply_acls(Seat *s, Session *old_active);
+int seat_set_active(Seat *s, Session *session);
 int seat_active_vt_changed(Seat *s, int vtnr);
 int seat_read_active_vt(Seat *s);
 
@@ -75,5 +76,6 @@ char *seat_bus_path(Seat *s);
 extern const DBusObjectPathVTable bus_seat_vtable;
 
 int seat_send_signal(Seat *s, bool new_seat);
+int seat_send_changed(Seat *s, const char *properties);
 
 #endif
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index edc96dd..0ce880c 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -419,3 +419,31 @@ finish:
 
         return r;
 }
+
+int session_send_changed(Session *s, const char *properties) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+        char *p = NULL;
+
+        assert(s);
+
+        p = session_bus_path(s);
+        if (!p)
+                return -ENOMEM;
+
+        m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
+        if (!m)
+                goto finish;
+
+        if (!dbus_connection_send(s->manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+        free(p);
+
+        return r;
+}
diff --git a/src/logind-session.c b/src/logind-session.c
index 26f857d..5ba6b21 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -76,8 +76,12 @@ void session_free(Session *s) {
                         s->user->display = NULL;
         }
 
-        if (s->seat)
+        if (s->seat) {
+                if (s->seat->active == s)
+                        s->seat->active = NULL;
+
                 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
+        }
 
         free(s->cgroup_path);
         strv_free(s->controllers);
@@ -229,7 +233,7 @@ int session_load(Session *s) {
                         s->kill_processes = k;
         }
 
-        if (seat) {
+        if (seat && !s->seat) {
                 Seat *o;
 
                 o = hashmap_get(s->manager->seats, seat);
@@ -430,6 +434,9 @@ int session_start(Session *s) {
         assert(s);
         assert(s->user);
 
+        if (s->started)
+                return 0;
+
         /* Create cgroup */
         r = session_create_cgroup(s);
         if (r < 0)
@@ -443,8 +450,19 @@ int session_start(Session *s) {
 
         dual_timestamp_get(&s->timestamp);
 
+        s->started = true;
+
         session_send_signal(s, true);
 
+        if (s->seat) {
+                if (s->seat->active == s)
+                        seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
+                else
+                        seat_send_changed(s->seat, "Sessions\0");
+        }
+
+        user_send_changed(s->user, "Sessions\0");
+
         return 0;
 }
 
@@ -521,7 +539,8 @@ int session_stop(Session *s) {
 
         assert(s);
 
-        session_send_signal(s, false);
+        if (!s->started)
+                return 0;
 
         /* Kill cgroup */
         k = session_kill_cgroup(s);
@@ -534,6 +553,19 @@ int session_stop(Session *s) {
         unlink(s->state_file);
         session_add_to_gc_queue(s);
 
+        session_send_signal(s, false);
+
+        if (s->seat) {
+                if (s->seat->active == s)
+                        seat_set_active(s->seat, NULL);
+
+                seat_send_changed(s->seat, "Sessions\0");
+        }
+
+        user_send_changed(s->user, "Sessions\0");
+
+        s->started = false;
+
         return r;
 }
 
@@ -607,6 +639,27 @@ void session_set_idle_hint(Session *s, bool b) {
 
         s->idle_hint = b;
         dual_timestamp_get(&s->idle_hint_timestamp);
+
+        session_send_changed(s,
+                             "IdleHint\0"
+                             "IdleSinceHint\0"
+                             "IdleSinceHintMonotonic\0");
+
+        if (s->seat)
+                seat_send_changed(s->seat,
+                                  "IdleHint\0"
+                                  "IdleSinceHint\0"
+                                  "IdleSinceHintMonotonic\0");
+
+        user_send_changed(s->user,
+                          "IdleHint\0"
+                          "IdleSinceHint\0"
+                          "IdleSinceHintMonotonic\0");
+
+        manager_send_changed(s->manager,
+                             "IdleHint\0"
+                             "IdleSinceHint\0"
+                             "IdleSinceHintMonotonic\0");
 }
 
 int session_check_gc(Session *s) {
diff --git a/src/logind-session.h b/src/logind-session.h
index a30c3e7..d2f2552 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -72,6 +72,7 @@ struct Session {
 
         bool kill_processes;
         bool in_gc_queue:1;
+        bool started:1;
 
         LIST_FIELDS(Session, sessions_by_user);
         LIST_FIELDS(Session, sessions_by_seat);
@@ -97,6 +98,7 @@ char *session_bus_path(Session *s);
 extern const DBusObjectPathVTable bus_session_vtable;
 
 int session_send_signal(Session *s, bool new_session);
+int session_send_changed(Session *s, const char *properties);
 
 const char* session_type_to_string(SessionType t);
 SessionType session_type_from_string(const char *s);
diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c
index c8d47de..623f2c9 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -353,3 +353,31 @@ finish:
 
         return r;
 }
+
+int user_send_changed(User *u, const char *properties) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+        char *p = NULL;
+
+        assert(u);
+
+        p = user_bus_path(u);
+        if (!p)
+                return -ENOMEM;
+
+        m = bus_properties_changed_new(p, "org.freedesktop.login1.User", properties);
+        if (!m)
+                goto finish;
+
+        if (!dbus_connection_send(u->manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+        free(p);
+
+        return r;
+}
diff --git a/src/logind-user.c b/src/logind-user.c
index 70d6a9a..cb3e441 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -260,6 +260,9 @@ int user_start(User *u) {
 
         assert(u);
 
+        if (u->started)
+                return 0;
+
         /* Make XDG_RUNTIME_DIR */
         r = user_mkdir_runtime_path(u);
         if (r < 0)
@@ -280,6 +283,8 @@ int user_start(User *u) {
 
         dual_timestamp_get(&u->timestamp);
 
+        u->started = true;
+
         user_send_signal(u, true);
 
         return 0;
@@ -361,6 +366,9 @@ int user_stop(User *u) {
         int r = 0, k;
         assert(u);
 
+        if (!u->started)
+                return 0;
+
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 k = session_stop(s);
                 if (k < 0)
@@ -387,6 +395,8 @@ int user_stop(User *u) {
         unlink(u->state_file);
         user_add_to_gc_queue(u);
 
+        u->started = false;
+
         return r;
 }
 
diff --git a/src/logind-user.h b/src/logind-user.h
index c6f1401..dd0dcad 100644
--- a/src/logind-user.h
+++ b/src/logind-user.h
@@ -55,6 +55,7 @@ struct User {
         dual_timestamp timestamp;
 
         bool in_gc_queue:1;
+        bool started:1;
 
         LIST_HEAD(Session, sessions);
         LIST_FIELDS(User, gc_queue);
@@ -76,6 +77,7 @@ char *user_bus_path(User *s);
 extern const DBusObjectPathVTable bus_user_vtable;
 
 int user_send_signal(User *u, bool new_user);
+int user_send_changed(User *u, const char *properties);
 
 const char* user_state_to_string(UserState s);
 UserState user_state_from_string(const char *s);
diff --git a/src/logind.c b/src/logind.c
index d72a5bf..cdb4da7 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -890,7 +890,6 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
         return idle_hint;
 }
 
-
 int manager_startup(Manager *m) {
         int r;
         Seat *seat;
diff --git a/src/logind.h b/src/logind.h
index e18a357..1b7a2b2 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -114,4 +114,6 @@ bool x11_display_is_local(const char *display);
 
 extern const DBusObjectPathVTable bus_manager_vtable;
 
+int manager_send_changed(Manager *manager, const char *properties);
+
 #endif

commit da11939561b1e6eeb1131b68dbf43042fd633a05
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 21 20:43:34 2011 +0200

    logind: send dbus signals when sessions/users/seats come and go

diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
index 32171ce..33eafe5 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -333,3 +333,40 @@ char *seat_bus_path(Seat *s) {
 
         return r;
 }
+
+int seat_send_signal(Seat *s, bool new_seat) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+        char *p = NULL;
+
+        assert(s);
+
+        m = dbus_message_new_signal("/org/freedesktop/login1",
+                                    "org.freedesktop.login1.Manager",
+                                    new_seat ? "SeatNew" : "SeatRemoved");
+
+        if (!m)
+                return -ENOMEM;
+
+        p = seat_bus_path(s);
+        if (!p)
+                goto finish;
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_STRING, &s->id,
+                            DBUS_TYPE_OBJECT_PATH, &p,
+                            DBUS_TYPE_INVALID))
+                goto finish;
+
+        if (!dbus_connection_send(s->manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        dbus_message_unref(m);
+        free(p);
+
+        return r;
+}
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 751f59a..95c66dd 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -314,6 +314,8 @@ int seat_start(Seat *s) {
 
         s->started = true;
 
+        seat_send_signal(s, true);
+
         return 0;
 }
 
@@ -327,6 +329,8 @@ int seat_stop(Seat *s) {
 
         log_info("Removed seat %s.", s->id);
 
+        seat_send_signal(s, false);
+
         seat_stop_sessions(s);
 
         unlink(s->state_file);
diff --git a/src/logind-seat.h b/src/logind-seat.h
index dfbb2a2..a7cd6c7 100644
--- a/src/logind-seat.h
+++ b/src/logind-seat.h
@@ -74,4 +74,6 @@ char *seat_bus_path(Seat *s);
 
 extern const DBusObjectPathVTable bus_seat_vtable;
 
+int seat_send_signal(Seat *s, bool new_seat);
+
 #endif
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index 6bded6a..edc96dd 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -382,3 +382,40 @@ char *session_bus_path(Session *s) {
 
         return r;
 }
+
+int session_send_signal(Session *s, bool new_session) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+        char *p = NULL;
+
+        assert(s);
+
+        m = dbus_message_new_signal("/org/freedesktop/login1",
+                                    "org.freedesktop.login1.Manager",
+                                    new_session ? "SessionNew" : "SessionRemoved");
+
+        if (!m)
+                return -ENOMEM;
+
+        p = session_bus_path(s);
+        if (!p)
+                goto finish;
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_STRING, &s->id,
+                            DBUS_TYPE_OBJECT_PATH, &p,
+                            DBUS_TYPE_INVALID))
+                goto finish;
+
+        if (!dbus_connection_send(s->manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        dbus_message_unref(m);
+        free(p);
+
+        return r;
+}
diff --git a/src/logind-session.c b/src/logind-session.c
index 0d34037..26f857d 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -443,6 +443,8 @@ int session_start(Session *s) {
 
         dual_timestamp_get(&s->timestamp);
 
+        session_send_signal(s, true);
+
         return 0;
 }
 
@@ -519,6 +521,8 @@ int session_stop(Session *s) {
 
         assert(s);
 
+        session_send_signal(s, false);
+
         /* Kill cgroup */
         k = session_kill_cgroup(s);
         if (k < 0)
diff --git a/src/logind-session.h b/src/logind-session.h
index 9f58165..a30c3e7 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -96,6 +96,8 @@ char *session_bus_path(Session *s);
 
 extern const DBusObjectPathVTable bus_session_vtable;
 
+int session_send_signal(Session *s, bool new_session);
+
 const char* session_type_to_string(SessionType t);
 SessionType session_type_from_string(const char *s);
 
diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c
index 3be2c05..c8d47de 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -313,3 +313,43 @@ char *user_bus_path(User *u) {
 
         return s;
 }
+
+int user_send_signal(User *u, bool new_user) {
+        DBusMessage *m;
+        int r = -ENOMEM;
+        char *p = NULL;
+        uint32_t uid;
+
+        assert(u);
+
+        m = dbus_message_new_signal("/org/freedesktop/login1",
+                                    "org.freedesktop.login1.Manager",
+                                    new_user ? "UserNew" : "UserRemoved");
+
+        if (!m)
+                return -ENOMEM;
+
+        p = user_bus_path(u);
+        if (!p)
+                goto finish;
+
+        uid = u->uid;
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_UINT32, &uid,
+                            DBUS_TYPE_OBJECT_PATH, &p,
+                            DBUS_TYPE_INVALID))
+                goto finish;
+
+        if (!dbus_connection_send(u->manager->bus, m, NULL))
+                goto finish;
+
+        r = 0;
+
+finish:
+        dbus_message_unref(m);
+        free(p);
+
+        return r;
+}
diff --git a/src/logind-user.c b/src/logind-user.c
index 206064f..70d6a9a 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -280,6 +280,8 @@ int user_start(User *u) {
 
         dual_timestamp_get(&u->timestamp);
 
+        user_send_signal(u, true);
+
         return 0;
 }
 
@@ -365,6 +367,8 @@ int user_stop(User *u) {
                         r = k;
         }
 
+        user_send_signal(u, false);
+
         /* Kill systemd */
         k = user_stop_service(u);
         if (k < 0)
diff --git a/src/logind-user.h b/src/logind-user.h
index c891119..c6f1401 100644
--- a/src/logind-user.h
+++ b/src/logind-user.h
@@ -75,6 +75,8 @@ char *user_bus_path(User *s);
 
 extern const DBusObjectPathVTable bus_user_vtable;
 
+int user_send_signal(User *u, bool new_user);
+
 const char* user_state_to_string(UserState s);
 UserState user_state_from_string(const char *s);
 

commit e1c9c2d53606f987110b4215cf08c236af637ff1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 21 20:14:24 2011 +0200

    logind: implement ListXXX bus methods

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index f7ec148..f9ee7e5 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -40,7 +40,7 @@
         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
         "  </method>\n"                                                 \
         "  <method name=\"ListSessions\">\n"                            \
-        "   <arg name=\"users\" type=\"a(sussso)\" direction=\"out\"/>\n" \
+        "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"ListUsers\">\n"                               \
         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
@@ -294,6 +294,137 @@ static DBusHandlerResult manager_message_handler(
                 if (!b)
                         goto oom;
 
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
+                char *p;
+                Session *session;
+                Iterator i;
+                DBusMessageIter iter, sub;
+                const char *empty = "";
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                dbus_message_iter_init_append(reply, &iter);
+
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "susso", &sub))
+                        goto oom;
+
+                HASHMAP_FOREACH(session, m->sessions, i) {
+                        DBusMessageIter sub2;
+                        uint32_t uid;
+
+                        if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                                goto oom;
+
+                        uid = session->user->uid;
+
+                        p = session_bus_path(session);
+                        if (!p)
+                                goto oom;
+
+                        if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                                free(p);
+                                goto oom;
+                        }
+
+                        free(p);
+
+                        if (!dbus_message_iter_close_container(&sub, &sub2))
+                                goto oom;
+                }
+
+                if (!dbus_message_iter_close_container(&iter, &sub))
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
+                char *p;
+                User *user;
+                Iterator i;
+                DBusMessageIter iter, sub;
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                dbus_message_iter_init_append(reply, &iter);
+
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "uso", &sub))
+                        goto oom;
+
+                HASHMAP_FOREACH(user, m->users, i) {
+                        DBusMessageIter sub2;
+                        uint32_t uid;
+
+                        if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                                goto oom;
+
+                        uid = user->uid;
+
+                        p = user_bus_path(user);
+                        if (!p)
+                                goto oom;
+
+                        if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                                free(p);
+                                goto oom;
+                        }
+
+                        free(p);
+
+                        if (!dbus_message_iter_close_container(&sub, &sub2))
+                                goto oom;
+                }
+
+                if (!dbus_message_iter_close_container(&iter, &sub))
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
+                char *p;
+                Seat *seat;
+                Iterator i;
+                DBusMessageIter iter, sub;
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                dbus_message_iter_init_append(reply, &iter);
+
+                if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "so", &sub))
+                        goto oom;
+
+                HASHMAP_FOREACH(seat, m->seats, i) {
+                        DBusMessageIter sub2;
+
+                        if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                                goto oom;
+
+                        p = seat_bus_path(seat);
+                        if (!p)
+                                goto oom;
+
+                        if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
+                            !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                                free(p);
+                                goto oom;
+                        }
+
+                        free(p);
+
+                        if (!dbus_message_iter_close_container(&sub, &sub2))
+                                goto oom;
+                }
+
+                if (!dbus_message_iter_close_container(&iter, &sub))
+                        goto oom;
+
         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
                 const char *name;
                 Session *session;

commit bef422ae1e7cbe77ad72dcbfe44798b0fe5e2931
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jun 21 19:20:05 2011 +0200

    logind: implement more dbus functionality

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 42374d7..f7ec148 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -27,26 +27,26 @@
 
 #define BUS_MANAGER_INTERFACE                                           \
         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
-        "  <method name=\"GetSeat\">\n"                                 \
+        "  <method name=\"GetSession\">\n"                              \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
-        "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
+        "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
         "  </method>\n"                                                 \
         "  <method name=\"GetUser\">\n"                                 \
-        "   <arg name=\"uid\" type=\"t\" direction=\"in\"/>\n"          \
+        "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
         "  </method>\n"                                                 \
-        "  <method name=\"GetSession\">\n"                              \
+        "  <method name=\"GetSeat\">\n"                                 \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
-        "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
+        "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
         "  </method>\n"                                                 \
-        "  <method name=\"ListSeats\">\n"                               \
-        "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
+        "  <method name=\"ListSessions\">\n"                            \
+        "   <arg name=\"users\" type=\"a(sussso)\" direction=\"out\"/>\n" \
         "  </method>\n"                                                 \
         "  <method name=\"ListUsers\">\n"                               \
         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
         "  </method>\n"                                                 \
-        "  <method name=\"ListSessions\">\n"                            \
-        "   <arg name=\"users\" type=\"a(sussso)\" direction=\"out\"/>\n" \
+        "  <method name=\"ListSeats\">\n"                               \
+        "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
         "  </method>\n"                                                 \
         "  <method name=\"CreateSession\">\n"                           \
         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
@@ -72,7 +72,7 @@
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
         "  </method>\n"                                                 \
         "  <method name=\"TerminateUser\">\n"                           \
-        "   <arg name=\"uid\" type=\"t\" direction=\"in\"/>\n"          \
+        "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
         "  </method>\n"                                                 \
         "  <method name=\"TerminateSeat\">\n"                           \
         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
@@ -184,6 +184,7 @@ static DBusHandlerResult manager_message_handler(
 
         DBusError error;
         DBusMessage *reply = NULL;
+        int r;
 
         assert(connection);
         assert(message);
@@ -191,7 +192,201 @@ static DBusHandlerResult manager_message_handler(
 
         dbus_error_init(&error);
 
-        if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
+                const char *name;
+                char *p;
+                Session *session;
+                bool b;
+
+                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(m->sessions, name);
+                if (!session)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                p = session_bus_path(session);
+                if (!p)
+                        goto oom;
+
+                b = dbus_message_append_args(
+                                reply,
+                                DBUS_TYPE_OBJECT_PATH, &p,
+                                DBUS_TYPE_INVALID);
+                free(p);
+
+                if (!b)
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
+                uint32_t uid;
+                char *p;
+                User *user;
+                bool b;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_UINT32, &uid,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
+                if (!user)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                p = user_bus_path(user);
+                if (!p)
+                        goto oom;
+
+                b = dbus_message_append_args(
+                                reply,
+                                DBUS_TYPE_OBJECT_PATH, &p,
+                                DBUS_TYPE_INVALID);
+                free(p);
+
+                if (!b)
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
+                const char *name;
+                char *p;
+                Seat *seat;
+                bool b;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &name,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                seat = hashmap_get(m->seats, name);
+                if (!seat)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+                p = seat_bus_path(seat);
+                if (!p)
+                        goto oom;
+
+                b = dbus_message_append_args(
+                                reply,
+                                DBUS_TYPE_OBJECT_PATH, &p,
+                                DBUS_TYPE_INVALID);
+                free(p);
+
+                if (!b)
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "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(m->sessions, name);
+                if (!session)
+                        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 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
+                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(m->sessions, name);
+                if (!session)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                r = session_stop(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 if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
+                uint32_t uid;
+                User *user;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_UINT32, &uid,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
+                if (!user)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                r = user_stop(user);
+                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.Manager", "TerminateSeat")) {
+                const char *name;
+                Seat *seat;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &name,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                seat = hashmap_get(m->seats, name);
+                if (!seat)
+                        return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+                r = seat_stop_sessions(seat);
+                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.DBus.Introspectable", "Introspect")) {
                 char *introspection = NULL;
                 FILE *f;
                 Iterator i;
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index 8b5b3ad..6bded6a 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -249,11 +249,90 @@ static DBusHandlerResult session_message_dispatch(
                 { 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.Session", "Terminate")) {
+
+                r = session_stop(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.Session", "Activate")) {
+
+                r = session_activate(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.Session", "Lock") ||
+                   dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
+                bool b;
+                DBusMessage *sig;
+
+                sig = dbus_message_new_signal(dbus_message_get_path(message), "org.freedesktop.login1.Session", dbus_message_get_member(message));
+                if (!sig)
+                        goto oom;
+
+                b = dbus_connection_send(connection, sig, NULL);
+                dbus_message_unref(sig);
+
+                if (!b)
+                        goto oom;
+
+                reply = dbus_message_new_method_return(message);
+                if (!reply)
+                        goto oom;
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
+                dbus_bool_t b;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_BOOLEAN, &b,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                session_set_idle_hint(s, b);
+
+                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 session_message_handler(
diff --git a/src/logind-session.c b/src/logind-session.c
index 89fe02c..0d34037 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -595,6 +595,16 @@ dont_know:
         return 0;
 }
 
+void session_set_idle_hint(Session *s, bool b) {
+        assert(s);
+
+        if (s->idle_hint == b)
+                return;
+
+        s->idle_hint = b;
+        dual_timestamp_get(&s->idle_hint_timestamp);
+}
+
 int session_check_gc(Session *s) {
         int r;
 
diff --git a/src/logind-session.h b/src/logind-session.h
index 7682393..9f58165 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -86,6 +86,7 @@ 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);
+void session_set_idle_hint(Session *s, bool b);
 int session_start(Session *s);
 int session_stop(Session *s);
 int session_save(Session *s);

commit a185c5aa2d8bef98716f8cf160da263c17e588b2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 17 15:59:18 2011 +0200

    logind: implement idle hint logic

diff --git a/src/logind-dbus.c b/src/logind-dbus.c
index 90db941..42374d7 100644
--- a/src/logind-dbus.c
+++ b/src/logind-dbus.c
@@ -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"                                                 \
@@ -102,6 +108,9 @@
         "  <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                                             \
@@ -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 4937d65..32171ce 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -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(
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 2ba3060..751f59a 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 b045bde..dfbb2a2 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 539384b..8b5b3ad 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -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 }
         };
 
diff --git a/src/logind-session.c b/src/logind-session.c
index 6b3b277..89fe02c 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 60ac1c5..7682393 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 7c8bb27..3be2c05 100644
--- a/src/logind-user-dbus.c
+++ b/src/logind-user-dbus.c
@@ -20,6 +20,7 @@
 ***/
 
 #include <errno.h>
+#include <string.h>
 
 #include "logind.h"
 #include "logind-user.h"
@@ -31,12 +32,17 @@
         "  <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(
diff --git a/src/logind-user.c b/src/logind-user.c
index 7d6df8d..206064f 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 7f58aa2..c891119 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 a628028..d72a5bf 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 fdc780f..e18a357 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 2047ebd..08529cc 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 3863a08..76e1d4f 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)
 

commit 91f9dcaf9270fe465525638cc08bd94590273349
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jun 17 00:15:02 2011 +0200

    dbus: add dbus introspection extraction

diff --git a/Makefile.am b/Makefile.am
index 0439957..a2d7397 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -203,13 +203,15 @@ dist_dbuspolicy_DATA = \
 	src/org.freedesktop.systemd1.conf \
         src/org.freedesktop.hostname1.conf \
         src/org.freedesktop.locale1.conf \
-        src/org.freedesktop.timedate1.conf
+        src/org.freedesktop.timedate1.conf \
+        src/org.freedesktop.login1.conf
 
 dist_dbussystemservice_DATA = \
 	src/org.freedesktop.systemd1.service \
         src/org.freedesktop.hostname1.service \
         src/org.freedesktop.locale1.service \
-        src/org.freedesktop.timedate1.service
+        src/org.freedesktop.timedate1.service \
+        src/org.freedesktop.login1.service
 
 dist_udevrules_DATA = \
 	src/99-systemd.rules
@@ -227,7 +229,10 @@ dbusinterface_DATA = \
 	org.freedesktop.systemd1.Automount.xml \
 	org.freedesktop.systemd1.Snapshot.xml \
 	org.freedesktop.systemd1.Swap.xml \
-	org.freedesktop.systemd1.Path.xml
+	org.freedesktop.systemd1.Path.xml \
+        org.freedesktop.hostname1.xml \
+        org.freedesktop.locale1.xml \
+        org.freedesktop.timedate1.xml
 
 dist_bashcompletion_DATA = \
 	src/systemctl-bash-completion.sh
@@ -316,6 +321,7 @@ nodist_systemunit_DATA = \
 	units/systemd-hostnamed.service \
 	units/systemd-localed.service \
 	units/systemd-timedated.service \
+	units/systemd-logind.service \
 	units/systemd-kmsg-syslogd.service \
 	units/systemd-modules-load.service \
 	units/systemd-vconsole-setup.service \
@@ -365,6 +371,7 @@ EXTRA_DIST = \
 	units/systemd-hostnamed.service.in \
 	units/systemd-localed.service.in \
 	units/systemd-timedated.service.in \
+	units/systemd-logind.service.in \
 	units/systemd-kmsg-syslogd.service.in \
 	units/systemd-modules-load.service.in \
 	units/systemd-vconsole-setup.service.in \
@@ -455,7 +462,8 @@ nodist_polkitpolicy_DATA = \
 dist_polkitpolicy_DATA = \
         src/org.freedesktop.hostname1.policy \
         src/org.freedesktop.locale1.policy \
-        src/org.freedesktop.timedate1.policy
+        src/org.freedesktop.timedate1.policy \
+        src/org.freedesktop.login1.policy
 
 noinst_LTLIBRARIES = \
 	libsystemd-basic.la \
@@ -1388,6 +1396,21 @@ org.freedesktop.systemd1.%.xml: systemd
 		$(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \
 		$(DBUS_PREPROCESS) -o $@ - && rm $@.tmp
 
+org.freedesktop.hostname1.xml: systemd-hostnamed
+	$(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.hostname1 $< $@.tmp && \
+		$(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \
+		$(DBUS_PREPROCESS) -o $@ - && rm $@.tmp
+
+org.freedesktop.locale1.xml: systemd-localed
+	$(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.locale1 $< $@.tmp && \
+		$(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \
+		$(DBUS_PREPROCESS) -o $@ - && rm $@.tmp
+
+org.freedesktop.timedate1.xml: systemd-timedated
+	$(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \
+		$(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \
+		$(DBUS_PREPROCESS) -o $@ - && rm $@.tmp
+
 CLEANFILES += \
 	$(dbusinterface_DATA)
 
@@ -1492,7 +1515,8 @@ endif
 		$(LN_S) reboot.target ctrl-alt-del.target && \
                 $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service && \
                 $(LN_S) systemd-localed.service dbus-org.freedesktop.locale1.service && \
-                $(LN_S) systemd-timedate.service dbus-org.freedesktop.timedate1.service )
+                $(LN_S) systemd-timedated.service dbus-org.freedesktop.timedate1.service && \
+                $(LN_S) systemd-logind.service dbus-org.freedesktop.login1.service )
 	( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \
 		rm -f getty.target systemd-user-sessions.service systemd-ask-password-wall.path && \
 		$(LN_S) ../getty.target getty.target && \
diff --git a/org.freedesktop.hostname1.xml b/org.freedesktop.hostname1.xml
new file mode 100644
index 0000000..e2d81a3
--- /dev/null
+++ b/org.freedesktop.hostname1.xml
@@ -0,0 +1,26 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.hostname1">
+  <property name="Hostname" type="s" access="read"/>
+  <property name="StaticHostname" type="s" access="read"/>
+  <property name="PrettyHostname" type="s" access="read"/>
+  <property name="IconName" type="s" access="read"/>
+  <method name="SetHostname">
+   <arg name="name" type="s" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+  <method name="SetStaticHostname">
+   <arg name="name" type="s" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+  <method name="SetPrettyHostname">
+   <arg name="name" type="s" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+  <method name="SetIconName">
+   <arg name="name" type="s" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+ </interface>
+</node>
diff --git a/org.freedesktop.locale1.xml b/org.freedesktop.locale1.xml
new file mode 100644
index 0000000..ca24e84
--- /dev/null
+++ b/org.freedesktop.locale1.xml
@@ -0,0 +1,11 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.locale1">
+  <property name="Locale" type="as" access="read"/>
+  <method name="SetLocale">
+   <arg name="locale" type="as" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+ </interface>
+</node>
diff --git a/org.freedesktop.timedate1.xml b/org.freedesktop.timedate1.xml
new file mode 100644
index 0000000..3799a65
--- /dev/null
+++ b/org.freedesktop.timedate1.xml
@@ -0,0 +1,22 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.timedate1">
+  <property name="Timezone" type="s" access="read"/>
+  <property name="LocalRTC" type="b" access="read"/>
+  <method name="SetTime">
+   <arg name="usec_utc" type="x" direction="in"/>
+   <arg name="relative" type="b" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+  <method name="SetTimezone">
+   <arg name="timezone" type="s" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+  <method name="SetLocalRTC">
+   <arg name="local_rtc" type="b" direction="in"/>
+   <arg name="fix_system" type="b" direction="in"/>
+   <arg name="user_interaction" type="b" direction="in"/>
+  </method>
+ </interface>
+</node>
diff --git a/src/hostnamed.c b/src/hostnamed.c
index f579e11..d05c902 100644
--- a/src/hostnamed.c
+++ b/src/hostnamed.c
@@ -31,9 +31,7 @@
 #include "dbus-common.h"
 #include "polkit.h"
 
-#define INTROSPECTION                                                   \
-        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
-        "<node>\n"                                                      \
+#define INTERFACE \
         " <interface name=\"org.freedesktop.hostname1\">\n"             \
         "  <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n"  \
         "  <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
@@ -55,7 +53,12 @@
         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
-        " </interface>\n"                                               \
+        " </interface>\n"
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        INTERFACE                                                       \
         BUS_PROPERTIES_INTERFACE                                        \
         BUS_INTROSPECTABLE_INTERFACE                                    \
         BUS_PEER_INTERFACE                                              \
@@ -65,6 +68,8 @@
         BUS_GENERIC_INTERFACES_LIST             \
         "org.freedesktop.hostname1\0"
 
+const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
+
 enum {
         PROP_HOSTNAME,
         PROP_STATIC_HOSTNAME,
@@ -547,6 +552,14 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        if (argc == 2 && streq(argv[1], "--introspect")) {
+                fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+                      "<node>\n", stdout);
+                fputs(hostname_interface, stdout);
+                fputs("</node>\n", stdout);
+                return 0;
+        }
+
         if (argc != 1) {
                 log_error("This program takes no arguments.");
                 r = -EINVAL;
diff --git a/src/localed.c b/src/localed.c
index 0fbe747..353e88e 100644
--- a/src/localed.c
+++ b/src/localed.c
@@ -30,16 +30,19 @@
 #include "dbus-common.h"
 #include "polkit.h"
 
-#define INTROSPECTION                                                   \
-        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
-        "<node>\n"                                                      \
+#define INTERFACE                                                       \
         " <interface name=\"org.freedesktop.locale1\">\n"               \
         "  <property name=\"Locale\" type=\"as\" access=\"read\"/>\n"   \
         "  <method name=\"SetLocale\">\n"                               \
         "   <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n"      \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
-        " </interface>\n"                                               \
+        " </interface>\n"
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        INTERFACE                                                       \
         BUS_PROPERTIES_INTERFACE                                        \
         BUS_INTROSPECTABLE_INTERFACE                                    \
         BUS_PEER_INTERFACE                                              \
@@ -49,6 +52,8 @@
         BUS_GENERIC_INTERFACES_LIST             \
         "org.freedesktop.locale1\0"
 
+const char locale_interface[] _introspect_("locale1") = INTERFACE;
+
 enum {
         /* We don't list LC_ALL here on purpose. People should be
          * using LANG instead. */
@@ -563,6 +568,14 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        if (argc == 2 && streq(argv[1], "--introspect")) {
+                fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+                      "<node>\n", stdout);
+                fputs(locale_interface, stdout);
+                fputs("</node>\n", stdout);
+                return 0;
+        }
+
         if (argc != 1) {
                 log_error("This program takes no arguments.");
                 r = -EINVAL;
diff --git a/src/logind.h b/src/logind.h
index 95db35d..fdc780f 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -38,6 +38,7 @@
  * PAM rewrite
  * spawn user systemd
  * dbus API
+ * direct client API
  *
  * non-local X11 server
  * reboot/shutdown halt management
diff --git a/src/org.freedesktop.login1.policy b/src/org.freedesktop.login1.policy
new file mode 100644
index 0000000..b81e488
--- /dev/null
+++ b/src/org.freedesktop.login1.policy
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+-->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+
+        <action id="org.freedesktop.login1.enable-user-linger">
+                <description>Allow non-logged-in users to run programs</description>
+                <message>Authentication is required to allow a non-logged-in user to run programs</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+</policyconfig>
diff --git a/src/org.freedesktop.login1.service b/src/org.freedesktop.login1.service
new file mode 100644
index 0000000..4a64177
--- /dev/null
+++ b/src/org.freedesktop.login1.service
@@ -0,0 +1,12 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+[D-BUS Service]
+Name=org.freedesktop.login1
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.freedesktop.login1.service
diff --git a/src/timedated.c b/src/timedated.c
index daa3d9c..a6ec262 100644
--- a/src/timedated.c
+++ b/src/timedated.c
@@ -33,9 +33,7 @@
 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
 
-#define INTROSPECTION                                                   \
-        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
-        "<node>\n"                                                      \
+#define INTERFACE                                                       \
         " <interface name=\"org.freedesktop.timedate1\">\n"             \
         "  <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n"  \
         "  <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n"  \
@@ -50,10 +48,15 @@
         "  </method>\n"                                                 \
         "  <method name=\"SetLocalRTC\">\n"                             \
         "   <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n"    \
-        "   <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \
+        "   <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n"   \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
-        " </interface>\n"                                               \
+        " </interface>\n"
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        INTERFACE                                                       \
         BUS_PROPERTIES_INTERFACE                                        \
         BUS_INTROSPECTABLE_INTERFACE                                    \
         BUS_PEER_INTERFACE                                              \
@@ -63,6 +66,8 @@
         BUS_GENERIC_INTERFACES_LIST             \
         "org.freedesktop.locale1\0"
 
+const char timedate_interface[] _introspect_("timedate1") = INTERFACE;
+
 static char *zone = NULL;
 static bool local_rtc = false;
 
@@ -566,6 +571,14 @@ int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        if (argc == 2 && streq(argv[1], "--introspect")) {
+                fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+                      "<node>\n", stdout);
+                fputs(timedate_interface, stdout);
+                fputs("</node>\n", stdout);
+                return 0;
+        }
+
         if (argc != 1) {
                 log_error("This program takes no arguments.");
                 r = -EINVAL;
diff --git a/units/.gitignore b/units/.gitignore
index 9634440..8da3804 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -1,3 +1,4 @@
+systemd-logind.service
 systemd-localed.service
 systemd-timedated.service
 systemd-hostnamed.service
diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in
new file mode 100644
index 0000000..52c4acf
--- /dev/null
+++ b/units/systemd-logind.service.in
@@ -0,0 +1,17 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Login Service
+
+[Service]
+ExecStart=@rootlibexecdir@/systemd-logind
+Type=dbus
+BusName=org.freedesktop.login1
+CapabilityBoundingSet=

commit 05a4abb9146eebd75f5d9b9cac38f183818a9f6d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 16 22:15:43 2011 +0200

    timedated: rename a few things for clarification

diff --git a/src/timedated.c b/src/timedated.c
index ad7b881..daa3d9c 100644
--- a/src/timedated.c
+++ b/src/timedated.c
@@ -50,7 +50,7 @@
         "  </method>\n"                                                 \
         "  <method name=\"SetLocalRTC\">\n"                             \
         "   <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n"    \
-        "   <arg name=\"correct_system\" type=\"b\" direction=\"in\"/>\n" \
+        "   <arg name=\"fix_system\" type=\"b\" direction=\"in\"/>\n" \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
         " </interface>\n"                                               \
@@ -347,14 +347,14 @@ static DBusHandlerResult timedate_message_handler(
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
                 dbus_bool_t lrtc;
-                dbus_bool_t correct_system;
+                dbus_bool_t fix_system;
                 dbus_bool_t interactive;
 
                 if (!dbus_message_get_args(
                                     message,
                                     &error,
                                     DBUS_TYPE_BOOLEAN, &lrtc,
-                                    DBUS_TYPE_BOOLEAN, &correct_system,
+                                    DBUS_TYPE_BOOLEAN, &fix_system,
                                     DBUS_TYPE_BOOLEAN, &interactive,
                                     DBUS_TYPE_INVALID))
                         return bus_send_error_reply(connection, message, &error, -EINVAL);
@@ -384,7 +384,7 @@ static DBusHandlerResult timedate_message_handler(
                         /* 3. Synchronize clocks */
                         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
 
-                        if (correct_system) {
+                        if (fix_system) {
                                 struct tm tm;
 
                                 /* Sync system clock from RTC; first,
@@ -422,7 +422,7 @@ static DBusHandlerResult timedate_message_handler(
                                 hwclock_set_time(tm);
                         }
 
-                        log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc));
+                        log_error("RTC configured to %s time.", local_rtc ? "local" : "UTC");
 
                         changed = bus_properties_changed_new(
                                         "/org/freedesktop/timedate1",

commit 2076cf883110bd6fc0f87b619005baf2117d6b95
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 16 21:52:11 2011 +0200

    timedated: sync clock down to RTC where necessary

diff --git a/TODO b/TODO
index 99b7707..aab4431 100644
--- a/TODO
+++ b/TODO
@@ -74,6 +74,10 @@ Features:
 
 * support wildcard expansion in EnvironmentFile= and friends
 
+* add JoinControllers= to system.conf to mount certain cgroup
+  controllers together in order to guarantee atomic creation/addition
+  of cgroups
+
 * avoid DefaultStandardOutput=syslog to have any effect on StandardInput=socket services
 
 * fix alsa mixer restore to not print error when no config is stored
diff --git a/src/main.c b/src/main.c
index 11379f6..0452033 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1050,11 +1050,14 @@ int main(int argc, char *argv[]) {
                 if (label_init() < 0)
                         goto finish;
 
-                if (hwclock_is_localtime()) {
+                if (hwclock_is_localtime() > 0) {
                         int min;
 
                         min = hwclock_apply_localtime_delta();
-                        log_info("Hwclock configured in localtime, applying delta of %i minutes to system time", min);
+                        if (min < 0)
+                                log_error("Failed to apply local time delta: %s", strerror(-min));
+                        else
+                                log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min);
                 }
         } else {
                 arg_running_as = MANAGER_USER;
diff --git a/src/timedated.c b/src/timedated.c
index 4749648..ad7b881 100644
--- a/src/timedated.c
+++ b/src/timedated.c
@@ -50,6 +50,7 @@
         "  </method>\n"                                                 \
         "  <method name=\"SetLocalRTC\">\n"                             \
         "   <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n"    \
+        "   <arg name=\"correct_system\" type=\"b\" direction=\"in\"/>\n" \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
         " </interface>\n"                                               \
@@ -151,7 +152,6 @@ static void verify_timezone(void) {
 
 static int read_data(void) {
         int r;
-        FILE *f;
 
         free_data();
 
@@ -161,25 +161,7 @@ static int read_data(void) {
 
         verify_timezone();
 
-        f = fopen("/etc/adjtime", "r");
-        if (f) {
-                char line[LINE_MAX];
-                bool b;
-
-                b = fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f);
-
-                fclose(f);
-
-                if (!b)
-                        return -EIO;
-
-                truncate_nl(line);
-                local_rtc = streq(line, "LOCAL");
-
-        } else if (errno != ENOENT)
-                return -errno;
+        local_rtc = hwclock_is_localtime() > 0;
 
         return 0;
 }
@@ -333,12 +315,26 @@ static DBusHandlerResult timedate_message_handler(
                         free(zone);
                         zone = t;
 
+                        /* 1. Write new configuration file */
                         r = write_data_timezone();
                         if (r < 0) {
                                 log_error("Failed to set timezone: %s", strerror(-r));
                                 return bus_send_error_reply(connection, message, NULL, r);
                         }
 
+                        if (local_rtc) {
+                                struct timespec ts;
+                                struct tm *tm;
+
+                                /* 2. Teach kernel new timezone */
+                                hwclock_apply_localtime_delta();
+
+                                /* 3. Sync RTC from system clock, with the new delta */
+                                assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+                                assert_se(tm = localtime(&ts.tv_sec));
+                                hwclock_set_time(tm);
+                        }
+
                         log_info("Changed timezone to '%s'.", zone);
 
                         changed = bus_properties_changed_new(
@@ -351,29 +347,81 @@ static DBusHandlerResult timedate_message_handler(
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
                 dbus_bool_t lrtc;
+                dbus_bool_t correct_system;
                 dbus_bool_t interactive;
 
                 if (!dbus_message_get_args(
                                     message,
                                     &error,
                                     DBUS_TYPE_BOOLEAN, &lrtc,
+                                    DBUS_TYPE_BOOLEAN, &correct_system,
                                     DBUS_TYPE_BOOLEAN, &interactive,
                                     DBUS_TYPE_INVALID))
                         return bus_send_error_reply(connection, message, &error, -EINVAL);
 
                 if (lrtc != local_rtc) {
+                        struct timespec ts;
+
                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error);
                         if (r < 0)
                                 return bus_send_error_reply(connection, message, &error, r);
 
                         local_rtc = lrtc;
 
+                        /* 1. Write new configuration file */
                         r = write_data_local_rtc();
                         if (r < 0) {
                                 log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
                                 return bus_send_error_reply(connection, message, NULL, r);
                         }
 
+                        /* 2. Teach kernel new timezone */
+                        if (local_rtc)
+                                hwclock_apply_localtime_delta();
+                        else
+                                hwclock_reset_localtime_delta();
+
+                        /* 3. Synchronize clocks */
+                        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+
+                        if (correct_system) {
+                                struct tm tm;
+
+                                /* Sync system clock from RTC; first,
+                                 * initialize the timezone fields of
+                                 * struct tm. */
+                                if (local_rtc)
+                                        tm = *localtime(&ts.tv_sec);
+                                else
+                                        tm = *gmtime(&ts.tv_sec);
+
+                                /* Override the main fields of
+                                 * struct tm, but not the timezone
+                                 * fields */
+                                if (hwclock_get_time(&tm) >= 0) {
+
+                                        /* And set the system clock
+                                         * with this */
+                                        if (local_rtc)
+                                                ts.tv_sec = mktime(&tm);
+                                        else
+                                                ts.tv_sec = timegm(&tm);
+
+                                        clock_settime(CLOCK_REALTIME, &ts);
+                                }
+
+                        } else {
+                                struct tm *tm;
+
+                                /* Sync RTC from system clock */
+                                if (local_rtc)
+                                        tm = localtime(&ts.tv_sec);
+                                else
+                                        tm = gmtime(&ts.tv_sec);
+
+                                hwclock_set_time(tm);
+                        }
+
                         log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc));
 
                         changed = bus_properties_changed_new(
@@ -403,6 +451,7 @@ static DBusHandlerResult timedate_message_handler(
 
                 if (!relative || utc != 0) {
                         struct timespec ts;
+                        struct tm* tm;
 
                         r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error);
                         if (r < 0)
@@ -413,11 +462,20 @@ static DBusHandlerResult timedate_message_handler(
                         else
                                 timespec_store(&ts, utc);
 
+                        /* Set system clock */
                         if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
                                 log_error("Failed to set local time: %m");
                                 return bus_send_error_reply(connection, message, NULL, -errno);
                         }
 
+                        /* Sync down to RTC */
+                        if (local_rtc)
+                                tm = localtime(&ts.tv_sec);
+                        else
+                                tm = gmtime(&ts.tv_sec);
+
+                        hwclock_set_time(tm);
+
                         log_info("Changed local time to %s", ctime(&ts.tv_sec));
                 }
 
diff --git a/src/util.c b/src/util.c
index dfb153b..2047ebd 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4785,40 +4785,50 @@ finish:
         return r;
 }
 
-bool hwclock_is_localtime(void) {
+int hwclock_is_localtime(void) {
         FILE *f;
-        char line[LINE_MAX];
         bool local = false;
 
         /*
          * The third line of adjtime is "UTC" or "LOCAL" or nothing.
          *   # /etc/adjtime
-         *   0.0 0 0.0
+         *   0.0 0 0
          *   0
          *   UTC
          */
         f = fopen("/etc/adjtime", "re");
         if (f) {
-                if (fgets(line, sizeof(line), f) &&
-                    fgets(line, sizeof(line), f) &&
-                    fgets(line, sizeof(line), f) ) {
-                            if (!strcmp(line, "LOCAL\n"))
-                                 local = true;
-                }
+                char line[LINE_MAX];
+                bool b;
+
+                b = fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f);
+
                 fclose(f);
-        }
+
+                if (!b)
+                        return -EIO;
+
+
+                truncate_nl(line);
+                local = streq(line, "LOCAL");
+
+        } else if (errno != -ENOENT)
+                return -errno;
+
         return local;
 }
 
 int hwclock_apply_localtime_delta(void) {
         const struct timeval *tv_null = NULL;
-        struct timeval tv;
+        struct timespec ts;
         struct tm *tm;
         int minuteswest;
         struct timezone tz;
 
-        gettimeofday(&tv, NULL);
-        tm = localtime(&tv.tv_sec);
+        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+        assert_se(tm = localtime(&ts.tv_sec));
         minuteswest = tm->tm_gmtoff / 60;
 
         tz.tz_minuteswest = -minuteswest;
@@ -4831,20 +4841,43 @@ int hwclock_apply_localtime_delta(void) {
          */
         if (settimeofday(tv_null, &tz) < 0)
                 return -errno;
-        else
-                return minuteswest;
+
+        return minuteswest;
+}
+
+int hwclock_reset_localtime_delta(void) {
+        const struct timeval *tv_null = NULL;
+        struct timezone tz;
+
+        tz.tz_minuteswest = 0;
+        tz.tz_dsttime = 0; /* DST_NONE*/
+
+        if (settimeofday(tv_null, &tz) < 0)
+                return -errno;
+
+        return 0;
 }
 
 int hwclock_get_time(struct tm *tm) {
         int fd;
         int err = 0;
 
+        assert(tm);
+
         fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
+
+        /* This leaves the timezone fields of struct tm
+         * uninitialized! */
         if (ioctl(fd, RTC_RD_TIME, tm) < 0)
                 err = -errno;
-        close(fd);
+
+        /* We don't now daylight saving, so we reset this in order not
+         * to confused mktime(). */
+        tm->tm_isdst = -1;
+
+        close_nointr_nofail(fd);
 
         return err;
 }
@@ -4853,12 +4886,16 @@ int hwclock_set_time(const struct tm *tm) {
         int fd;
         int err = 0;
 
+        assert(tm);
+
         fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
+
         if (ioctl(fd, RTC_SET_TIME, tm) < 0)
                 err = -errno;
-        close(fd);
+
+        close_nointr_nofail(fd);
 
         return err;
 }
diff --git a/src/util.h b/src/util.h
index bd98b65..3863a08 100644
--- a/src/util.h
+++ b/src/util.h
@@ -431,12 +431,11 @@ int fchmod_umask(int fd, mode_t mode);
 
 int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
 
-bool hwclock_is_localtime(void);
+int hwclock_is_localtime(void);
 
 int hwclock_apply_localtime_delta(void);
-
+int hwclock_reset_localtime_delta(void);
 int hwclock_get_time(struct tm *tm);
-
 int hwclock_set_time(const struct tm *tm);
 
 #define NULSTR_FOREACH(i, l)                                    \

commit d3fc81bd6a5a046b22600ac1204df220c93d2c15
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 15 15:39:10 2011 +0200

    update TODO

diff --git a/TODO b/TODO
index 71ad077..99b7707 100644
--- a/TODO
+++ b/TODO
@@ -27,6 +27,10 @@ Features:
 
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
 
+* readahead: use BTRFS_IOC_DEFRAG_RANGE instead of BTRFS_IOC_DEFRAG ioctl, with START_IO
+
+* readahead: check whether a btrfs volume includes ssd by checking mount flag "ssd"
+
 * hostnamed: make file updates atomic
 
 * support sd_notify() style notificatio when reload is finished (RELOADED=1)

commit f401e48c2db22ff9d1a05885b5599bebf19c2707
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 15 15:37:39 2011 +0200

    mechanisms: add mechanisms to change system locale and clock

diff --git a/.gitignore b/.gitignore
index 7bd22c5..92b8f73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+systemd-localed
+systemd-timedated
 systemd-uaccess
 systemd-logind
 systemd-hostnamed
diff --git a/Makefile.am b/Makefile.am
index 0bc5169..0439957 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -159,6 +159,8 @@ rootlibexec_PROGRAMS = \
 	systemd-detect-virt \
 	systemd-sysctl \
         systemd-hostnamed \
+        systemd-localed \
+        systemd-timedated \
         systemd-logind \
         systemd-uaccess
 
@@ -199,11 +201,15 @@ dist_pkgsysconf_DATA = \
 
 dist_dbuspolicy_DATA = \
 	src/org.freedesktop.systemd1.conf \
-        src/org.freedesktop.hostname1.conf
+        src/org.freedesktop.hostname1.conf \
+        src/org.freedesktop.locale1.conf \
+        src/org.freedesktop.timedate1.conf
 
 dist_dbussystemservice_DATA = \
 	src/org.freedesktop.systemd1.service \
-        src/org.freedesktop.hostname1.service
+        src/org.freedesktop.hostname1.service \
+        src/org.freedesktop.locale1.service \
+        src/org.freedesktop.timedate1.service
 
 dist_udevrules_DATA = \
 	src/99-systemd.rules
@@ -308,6 +314,8 @@ nodist_systemunit_DATA = \
 	units/systemd-logger.service \
 	units/systemd-shutdownd.service \
 	units/systemd-hostnamed.service \
+	units/systemd-localed.service \
+	units/systemd-timedated.service \
 	units/systemd-kmsg-syslogd.service \
 	units/systemd-modules-load.service \
 	units/systemd-vconsole-setup.service \
@@ -355,6 +363,8 @@ EXTRA_DIST = \
 	units/systemd-logger.service.in \
 	units/systemd-shutdownd.service.in \
 	units/systemd-hostnamed.service.in \
+	units/systemd-localed.service.in \
+	units/systemd-timedated.service.in \
 	units/systemd-kmsg-syslogd.service.in \
 	units/systemd-modules-load.service.in \
 	units/systemd-vconsole-setup.service.in \
@@ -443,7 +453,9 @@ nodist_polkitpolicy_DATA = \
 	src/org.freedesktop.systemd1.policy
 
 dist_polkitpolicy_DATA = \
-        src/org.freedesktop.hostname1.policy
+        src/org.freedesktop.hostname1.policy \
+        src/org.freedesktop.locale1.policy \
+        src/org.freedesktop.timedate1.policy
 
 noinst_LTLIBRARIES = \
 	libsystemd-basic.la \
@@ -797,7 +809,8 @@ systemd_shutdownd_LDADD = \
 
 systemd_hostnamed_SOURCES = \
 	src/hostnamed.c \
-        src/dbus-common.c
+        src/dbus-common.c \
+        src/polkit.c
 
 systemd_hostnamed_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -808,6 +821,34 @@ systemd_hostnamed_LDADD = \
 	libsystemd-daemon.la \
 	$(DBUS_LIBS)
 
+systemd_localed_SOURCES = \
+	src/localed.c \
+        src/dbus-common.c \
+        src/polkit.c
+
+systemd_localed_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+systemd_localed_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-daemon.la \
+	$(DBUS_LIBS)
+
+systemd_timedated_SOURCES = \
+	src/timedated.c \
+        src/dbus-common.c \
+        src/polkit.c
+
+systemd_timedated_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+systemd_timedated_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-daemon.la \
+	$(DBUS_LIBS)
+
 systemd_logind_SOURCES = \
 	src/logind.c \
 	src/logind-dbus.c \
@@ -1449,7 +1490,9 @@ endif
 		rm -f default.target ctrl-alt-del.target dbus-org.freedesktop.hostname1.service && \
 		$(LN_S) graphical.target default.target && \
 		$(LN_S) reboot.target ctrl-alt-del.target && \
-                $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service )
+                $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service && \
+                $(LN_S) systemd-localed.service dbus-org.freedesktop.locale1.service && \
+                $(LN_S) systemd-timedate.service dbus-org.freedesktop.timedate1.service )
 	( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \
 		rm -f getty.target systemd-user-sessions.service systemd-ask-password-wall.path && \
 		$(LN_S) ../getty.target getty.target && \
diff --git a/src/hostnamed.c b/src/hostnamed.c
index 68c5715..f579e11 100644
--- a/src/hostnamed.c
+++ b/src/hostnamed.c
@@ -29,6 +29,7 @@
 #include "util.h"
 #include "strv.h"
 #include "dbus-common.h"
+#include "polkit.h"
 
 #define INTROSPECTION                                                   \
         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
@@ -256,168 +257,6 @@ static int write_data_other(void) {
         return r;
 }
 
-/* This mimics dbus_bus_get_unix_user() */
-static pid_t get_unix_process_id(
-                DBusConnection *connection,
-                const char *name,
-                DBusError *error) {
-
-        DBusMessage *m = NULL, *reply = NULL;
-        uint32_t pid = 0;
-
-        m = dbus_message_new_method_call(
-                        DBUS_SERVICE_DBUS,
-                        DBUS_PATH_DBUS,
-                        DBUS_INTERFACE_DBUS,
-                        "GetConnectionUnixProcessID");
-        if (!m) {
-                dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(
-                            m,
-                            DBUS_TYPE_STRING, &name,
-                            DBUS_TYPE_INVALID)) {
-                dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
-                goto finish;
-        }
-
-        reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
-        if (!reply)
-                goto finish;
-
-        if (dbus_set_error_from_message(error, reply))
-                goto finish;
-
-        if (!dbus_message_get_args(
-                            reply, error,
-                            DBUS_TYPE_UINT32, &pid,
-                            DBUS_TYPE_INVALID))
-                goto finish;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        return (pid_t) pid;
-}
-
-static int verify_polkit(
-                DBusConnection *c,
-                DBusMessage *request,
-                const char *action,
-                bool interactive,
-                DBusError *error) {
-
-        DBusMessage *m = NULL, *reply = NULL;
-        const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
-        const char *sender;
-        uint32_t flags = interactive ? 1 : 0;
-        pid_t pid_raw;
-        uint32_t pid_u32;
-        unsigned long long starttime_raw;
-        uint64_t starttime_u64;
-        DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
-        int r;
-        dbus_bool_t authorized = FALSE;
-
-        assert(c);
-        assert(request);
-
-        sender = dbus_message_get_sender(request);
-        if (!sender)
-                return -EINVAL;
-
-        pid_raw = get_unix_process_id(c, sender, error);
-        if (pid_raw == 0)
-                return -EINVAL;
-
-        r = get_starttime_of_pid(pid_raw, &starttime_raw);
-        if (r < 0)
-                return r;
-
-        m = dbus_message_new_method_call(
-                        "org.freedesktop.PolicyKit1",
-                        "/org/freedesktop/PolicyKit1/Authority",
-                        "org.freedesktop.PolicyKit1.Authority",
-                        "CheckAuthorization");
-        if (!m)
-                return -ENOMEM;
-
-        dbus_message_iter_init_append(m, &iter_msg);
-
-        pid_u32 = (uint32_t) pid_raw;
-        starttime_u64 = (uint64_t) starttime_raw;
-
-        if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) ||
-            !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) ||
-            !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) ||
-            !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
-            !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) ||
-            !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) ||
-            !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) ||
-            !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
-            !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
-            !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
-            !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) ||
-            !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) ||
-            !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) ||
-            !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
-            !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
-            !dbus_message_iter_close_container(&iter_struct, &iter_array) ||
-            !dbus_message_iter_close_container(&iter_msg, &iter_struct) ||
-            !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) ||
-            !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) ||
-            !dbus_message_iter_close_container(&iter_msg, &iter_array) ||
-            !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) ||
-            !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
-        if (!reply) {
-                r = -EIO;
-                goto finish;
-        }
-
-        if (dbus_set_error_from_message(error, reply)) {
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter_msg) ||
-            dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter_msg, &iter_struct);
-
-        if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_get_basic(&iter_struct, &authorized);
-
-        r = authorized ? 0 : -EPERM;
-
-finish:
-
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        return r;
-}
-
 static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
         const char *name;
 
diff --git a/src/localed.c b/src/localed.c
new file mode 100644
index 0000000..0fbe747
--- /dev/null
+++ b/src/localed.c
@@ -0,0 +1,599 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "strv.h"
+#include "dbus-common.h"
+#include "polkit.h"
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        " <interface name=\"org.freedesktop.locale1\">\n"               \
+        "  <property name=\"Locale\" type=\"as\" access=\"read\"/>\n"   \
+        "  <method name=\"SetLocale\">\n"                               \
+        "   <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n"      \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
+        " </interface>\n"                                               \
+        BUS_PROPERTIES_INTERFACE                                        \
+        BUS_INTROSPECTABLE_INTERFACE                                    \
+        BUS_PEER_INTERFACE                                              \
+        "</node>\n"
+
+#define INTERFACES_LIST                         \
+        BUS_GENERIC_INTERFACES_LIST             \
+        "org.freedesktop.locale1\0"
+
+enum {
+        /* We don't list LC_ALL here on purpose. People should be
+         * using LANG instead. */
+
+        PROP_LANG,
+        PROP_LC_CTYPE,
+        PROP_LC_NUMERIC,
+        PROP_LC_TIME,
+        PROP_LC_COLLATE,
+        PROP_LC_MONETARY,
+        PROP_LC_MESSAGES,
+        PROP_LC_PAPER,
+        PROP_LC_NAME,
+        PROP_LC_ADDRESS,
+        PROP_LC_TELEPHONE,
+        PROP_LC_MEASUREMENT,
+        PROP_LC_IDENTIFICATION,
+        _PROP_MAX
+};
+
+static const char * const names[_PROP_MAX] = {
+        [PROP_LANG] = "LANG",
+        [PROP_LC_CTYPE] = "LC_CTYPE",
+        [PROP_LC_NUMERIC] = "LC_NUMERIC",
+        [PROP_LC_TIME] = "LC_TIME",
+        [PROP_LC_COLLATE] = "LC_COLLATE",
+        [PROP_LC_MONETARY] = "LC_MONETARY",
+        [PROP_LC_MESSAGES] = "LC_MESSAGES",
+        [PROP_LC_PAPER] = "LC_PAPER",
+        [PROP_LC_NAME] = "LC_NAME",
+        [PROP_LC_ADDRESS] = "LC_ADDRESS",
+        [PROP_LC_TELEPHONE] = "LC_TELEPHONE",
+        [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT",
+        [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
+};
+
+static char *data[_PROP_MAX] = {
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        NULL
+};
+
+static void free_data(void) {
+        int p;
+
+        for (p = 0; p < _PROP_MAX; p++) {
+                free(data[p]);
+                data[p] = NULL;
+        }
+}
+
+static void simplify(void) {
+        int p;
+
+        for (p = 1; p < _PROP_MAX; p++)
+                if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) {
+                        free(data[p]);
+                        data[p] = NULL;
+                }
+}
+
+static int read_data(void) {
+        int r;
+
+        free_data();
+
+        r = parse_env_file("/etc/locale.conf", NEWLINE,
+                           "LANG",              &data[PROP_LANG],
+                           "LC_CTYPE",          &data[PROP_LC_CTYPE],
+                           "LC_NUMERIC",        &data[PROP_LC_NUMERIC],
+                           "LC_TIME",           &data[PROP_LC_TIME],
+                           "LC_COLLATE",        &data[PROP_LC_COLLATE],
+                           "LC_MONETARY",       &data[PROP_LC_MONETARY],
+                           "LC_MESSAGES",       &data[PROP_LC_MESSAGES],
+                           "LC_PAPER",          &data[PROP_LC_PAPER],
+                           "LC_NAME",           &data[PROP_LC_NAME],
+                           "LC_ADDRESS",        &data[PROP_LC_ADDRESS],
+                           "LC_TELEPHONE",      &data[PROP_LC_TELEPHONE],
+                           "LC_MEASUREMENT",    &data[PROP_LC_MEASUREMENT],
+                           "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION],
+                           NULL);
+
+        if (r == -ENOENT) {
+                int p;
+
+                /* Fill in what we got passed from systemd. */
+
+                for (p = 0; p < _PROP_MAX; p++) {
+                        char *e, *d;
+
+                        assert(names[p]);
+
+                        e = getenv(names[p]);
+                        if (e) {
+                                d = strdup(e);
+                                if (!d)
+                                        return -ENOMEM;
+                        } else
+                                d = NULL;
+
+                        free(data[p]);
+                        data[p] = d;
+                }
+
+                r = 0;
+        }
+
+        simplify();
+        return r;
+}
+
+static int write_data(void) {
+        int r, p;
+        char **l = NULL;
+
+        r = load_env_file("/etc/locale.conf", &l);
+        if (r < 0 && r != -ENOENT)
+                return r;
+
+        for (p = 0; p < _PROP_MAX; p++) {
+                char *t, **u;
+
+                assert(names[p]);
+
+                if (isempty(data[p])) {
+                        l = strv_env_unset(l, names[p]);
+                        continue;
+                }
+
+                if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+
+                u = strv_env_set(l, t);
+                free(t);
+                strv_free(l);
+
+                if (!u)
+                        return -ENOMEM;
+
+                l = u;
+        }
+
+        if (strv_isempty(l)) {
+
+                if (unlink("/etc/locale.conf") < 0)
+                        return errno == ENOENT ? 0 : -errno;
+
+                return 0;
+        }
+
+        r = write_env_file("/etc/locale.conf", l);
+        strv_free(l);
+
+        return r;
+}
+
+static void push_data(DBusConnection *bus) {
+        char **l_set = NULL, **l_unset = NULL, **t;
+        int c_set = 0, c_unset = 0, p;
+        DBusError error;
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusMessageIter iter, sub;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        l_set = new0(char*, _PROP_MAX);
+        l_unset = new0(char*, _PROP_MAX);
+        if (!l_set || !l_unset) {
+                log_error("Out of memory");
+                goto finish;
+        }
+
+        for (p = 0; p < _PROP_MAX; p++) {
+                assert(names[p]);
+
+                if (isempty(data[p]))
+                        l_unset[c_set++] = (char*) names[p];
+                else {
+                        char *s;
+
+                        if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) {
+                                log_error("Out of memory");
+                                goto finish;
+                        }
+
+                        l_set[c_unset++] = s;
+                }
+        }
+
+        assert(c_set + c_unset == _PROP_MAX);
+        m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment");
+        if (!m) {
+                log_error("Could not allocate message.");
+                goto finish;
+        }
+
+        dbus_message_iter_init_append(m, &iter);
+
+        if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+                log_error("Out of memory.");
+                goto finish;
+        }
+
+        STRV_FOREACH(t, l_unset)
+                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
+                        log_error("Out of memory.");
+                        goto finish;
+                }
+
+        if (!dbus_message_iter_close_container(&iter, &sub) ||
+            !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+                log_error("Out of memory.");
+                goto finish;
+        }
+
+        STRV_FOREACH(t, l_set)
+                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) {
+                        log_error("Out of memory.");
+                        goto finish;
+                }
+
+        if (!dbus_message_iter_close_container(&iter, &sub)) {
+                log_error("Out of memory.");
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
+                log_error("Failed to set locale information: %s", bus_error_message(&error));
+                goto finish;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        strv_free(l_set);
+        free(l_unset);
+}
+
+static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
+        int r, c = 0, p;
+        char **l;
+
+        l = new0(char*, _PROP_MAX+1);
+        if (!l)
+                return -ENOMEM;
+
+        for (p = 0; p < _PROP_MAX; p++) {
+                char *t;
+
+                if (isempty(data[p]))
+                        continue;
+
+                if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+
+                l[c++] = t;
+        }
+
+        r = bus_property_append_strv(i, property, (void*) l);
+        strv_free(l);
+
+        return r;
+}
+
+static DBusHandlerResult locale_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        const BusProperty properties[] = {
+                { "org.freedesktop.locale1", "Locale", append_locale, "as", NULL},
+                { NULL, NULL, NULL, NULL, NULL }
+        };
+
+        DBusMessage *reply = NULL, *changed = NULL;
+        DBusError error;
+        int r;
+
+        assert(connection);
+        assert(message);
+
+        dbus_error_init(&error);
+
+        if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) {
+                char **l = NULL, **i;
+                dbus_bool_t interactive;
+                DBusMessageIter iter;
+                bool modified = false;
+                bool passed[_PROP_MAX];
+                int p;
+
+                if (!dbus_message_iter_init(message, &iter))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+                r = bus_parse_strv_iter(&iter, &l);
+                if (r < 0) {
+                        if (r == -ENOMEM)
+                                goto oom;
+
+                        return bus_send_error_reply(connection, message, NULL, r);
+                }
+
+                if (!dbus_message_iter_next(&iter) ||
+                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)  {
+                        strv_free(l);
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+                }
+
+                dbus_message_iter_get_basic(&iter, &interactive);
+
+                zero(passed);
+
+                /* Check whether a variable changed and if so valid */
+                STRV_FOREACH(i, l) {
+                        bool valid = false;
+
+                        for (p = 0; p < _PROP_MAX; p++) {
+                                size_t k;
+
+                                k = strlen(names[p]);
+                                if (startswith(*i, names[p]) && (*i)[k] == '=') {
+                                        valid = true;
+                                        passed[p] = true;
+
+                                        if (!streq_ptr(*i + k + 1, data[p]))
+                                                modified = true;
+
+                                        break;
+                                }
+                        }
+
+                        if (!valid) {
+                                strv_free(l);
+                                return bus_send_error_reply(connection, message, NULL, -EINVAL);
+                        }
+                }
+
+                /* Check whether a variable is unset */
+                if (!modified)  {
+                        for (p = 0; p < _PROP_MAX; p++)
+                                if (!isempty(data[p]) && !passed[p]) {
+                                        modified = true;
+                                        break;
+                                }
+                }
+
+                if (modified) {
+
+                        r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, &error);
+                        if (r < 0) {
+                                strv_free(l);
+                                return bus_send_error_reply(connection, message, &error, r);
+                        }
+
+                        STRV_FOREACH(i, l) {
+                                for (p = 0; p < _PROP_MAX; p++) {
+                                        size_t k;
+
+                                        k = strlen(names[p]);
+                                        if (startswith(*i, names[p]) && (*i)[k] == '=') {
+                                                char *t;
+
+                                                t = strdup(*i + k + 1);
+                                                if (!t) {
+                                                        strv_free(l);
+                                                        goto oom;
+                                                }
+
+                                                free(data[p]);
+                                                data[p] = t;
+
+                                                break;
+                                        }
+                                }
+                        }
+
+                        for (p = 0; p < _PROP_MAX; p++) {
+                                if (passed[p])
+                                        continue;
+
+                                free(data[p]);
+                                data[p] = NULL;
+                        }
+
+                        simplify();
+
+                        r = write_data();
+                        if (r < 0) {
+                                log_error("Failed to set locale: %s", strerror(-r));
+                                return bus_send_error_reply(connection, message, NULL, r);
+                        }
+
+                        push_data(connection);
+
+                        log_info("Changed locale information.");
+
+                        changed = bus_properties_changed_new(
+                                        "/org/freedesktop/locale1",
+                                        "org.freedesktop.locale1",
+                                        "Locale\0");
+                        if (!changed)
+                                goto oom;
+                }
+
+        } else
+                return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+
+        if (!(reply = dbus_message_new_method_return(message)))
+                goto oom;
+
+        if (!dbus_connection_send(connection, reply, NULL))
+                goto oom;
+
+        dbus_message_unref(reply);
+        reply = NULL;
+
+        if (changed) {
+
+                if (!dbus_connection_send(connection, changed, NULL))
+                        goto oom;
+
+                dbus_message_unref(changed);
+        }
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+        if (reply)
+                dbus_message_unref(reply);
+
+        if (changed)
+                dbus_message_unref(changed);
+
+        dbus_error_free(&error);
+
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static int connect_bus(DBusConnection **_bus) {
+        static const DBusObjectPathVTable locale_vtable = {
+                .message_function = locale_message_handler
+        };
+        DBusError error;
+        DBusConnection *bus = NULL;
+        int r;
+
+        assert(_bus);
+
+        dbus_error_init(&error);
+
+        bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+        if (!bus) {
+                log_error("Failed to get system D-Bus connection: %s", error.message);
+                r = -ECONNREFUSED;
+                goto fail;
+        }
+
+        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL)) {
+                log_error("Not enough memory");
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
+                log_error("Failed to register name on bus: %s", error.message);
+                r = -EEXIST;
+                goto fail;
+        }
+
+        if (_bus)
+                *_bus = bus;
+
+        return 0;
+
+fail:
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        DBusConnection *bus = NULL;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc != 1) {
+                log_error("This program takes no arguments.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        umask(0022);
+
+        r = read_data();
+        if (r < 0) {
+                log_error("Failed to read locale data: %s", strerror(-r));
+                goto finish;
+        }
+
+        r = connect_bus(&bus);
+        if (r < 0)
+                goto finish;
+
+        while (dbus_connection_read_write_dispatch(bus, -1))
+                ;
+
+        r = 0;
+
+finish:
+        free_data();
+
+        if (bus) {
+                dbus_connection_flush(bus);
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
+        }
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
index 63b1bd5..4937d65 100644
--- a/src/logind-seat-dbus.c
+++ b/src/logind-seat-dbus.c
@@ -29,8 +29,12 @@
 #define BUS_SEAT_INTERFACE \
         " <interface name=\"org.freedesktop.login1.Seat\">\n"           \
         "  <method name=\"Terminate\"/>\n"                              \
+        "  <method name=\"ActivateSession\">\n"                         \
+        "   <arg name=\"id\" type=\"s\"/>\n"                            \
+        "  </method>\n"                                                 \
         "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
-        "  <property name=\"Active\" type=\"so\" access=\"read\"/>\n"   \
+        "  <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" \
         " </interface>\n"                                               \
 
@@ -125,6 +129,23 @@ 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;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        b = s->manager->vtconsole == s;
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
         Seat *s;
         char *id;
@@ -156,9 +177,10 @@ static DBusHandlerResult seat_message_dispatch(
                 DBusMessage *message) {
 
         const BusProperty properties[] = {
-                { "org.freedesktop.login1.Seat", "Id",       bus_property_append_string, "s",     s->id },
-                { "org.freedesktop.login1.Seat", "Active",   bus_seat_append_active,     "(so)",  s     },
-                { "org.freedesktop.login1.Seat", "Sessions", bus_seat_append_sessions,   "a(so)", s     },
+                { "org.freedesktop.login1.Seat", "Id",                  bus_property_append_string,   "s",     s->id },
+                { "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     },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
index 41af658..539384b 100644
--- a/src/logind-session-dbus.c
+++ b/src/logind-session-dbus.c
@@ -30,9 +30,16 @@
         " <interface name=\"org.freedesktop.login1.Session\">\n"        \
         "  <method name=\"Terminate\"/>\n"                              \
         "  <method name=\"Activate\"/>\n"                               \
+        "  <method name=\"Lock\"/>\n"                                   \
+        "  <method name=\"Unlock\"/>\n"                                 \
+        "  <method name=\"SetIdleHint\">\n"                             \
+        "   <arg name=\"b\" type=\"b\"/>\n"                             \
+        "  </method>\n"                                                 \
         "  <property name=\"Id\" type=\"u\" access=\"read\"/>\n"        \
         "  <property name=\"User\" type=\"(uo)\" 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=\"ControlGroupPath\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n"      \
         "  <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n"   \
@@ -48,6 +55,9 @@
         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
         "  <property name=\"KillProcesses\" 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                                                   \
diff --git a/src/logind.h b/src/logind.h
index e4b7a3c..95db35d 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -35,11 +35,9 @@
 /* TODO:
  *
  * recreate VTs when disallocated
- * udev rules
  * PAM rewrite
  * spawn user systemd
  * dbus API
- * don't allow everybody to take logind name
  *
  * non-local X11 server
  * reboot/shutdown halt management
diff --git a/src/org.freedesktop.locale1.conf b/src/org.freedesktop.locale1.conf
new file mode 100644
index 0000000..6827331
--- /dev/null
+++ b/src/org.freedesktop.locale1.conf
@@ -0,0 +1,27 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+-->
+
+<busconfig>
+
+        <policy user="root">
+                <allow own="org.freedesktop.locale1"/>
+                <allow send_destination="org.freedesktop.locale1"/>
+                <allow receive_sender="org.freedesktop.locale1"/>
+        </policy>
+
+        <policy context="default">
+                <allow send_destination="org.freedesktop.locale1"/>
+                <allow receive_sender="org.freedesktop.locale1"/>
+        </policy>
+
+</busconfig>
diff --git a/src/org.freedesktop.locale1.policy b/src/org.freedesktop.locale1.policy
new file mode 100644
index 0000000..6c755fd
--- /dev/null
+++ b/src/org.freedesktop.locale1.policy
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+-->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+
+        <action id="org.freedesktop.locale1.set-locale">
+                <description>Set system locale</description>
+                <message>Authentication is required to set the system locale.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+</policyconfig>
diff --git a/src/org.freedesktop.locale1.service b/src/org.freedesktop.locale1.service
new file mode 100644
index 0000000..29bd582
--- /dev/null
+++ b/src/org.freedesktop.locale1.service
@@ -0,0 +1,12 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+[D-BUS Service]
+Name=org.freedesktop.locale1
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.freedesktop.locale1.service
diff --git a/src/org.freedesktop.login1.conf b/src/org.freedesktop.login1.conf
index 20008ea..ebc499d 100644
--- a/src/org.freedesktop.login1.conf
+++ b/src/org.freedesktop.login1.conf
@@ -20,7 +20,6 @@
         </policy>
 
         <policy context="default">
-                <allow own="org.freedesktop.login1"/>
                 <allow send_destination="org.freedesktop.login1"/>
                 <allow receive_sender="org.freedesktop.login1"/>
         </policy>
diff --git a/src/org.freedesktop.timedate1.conf b/src/org.freedesktop.timedate1.conf
new file mode 100644
index 0000000..c9c221b
--- /dev/null
+++ b/src/org.freedesktop.timedate1.conf
@@ -0,0 +1,27 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+-->
+
+<busconfig>
+
+        <policy user="root">
+                <allow own="org.freedesktop.timedate1"/>
+                <allow send_destination="org.freedesktop.timedate1"/>
+                <allow receive_sender="org.freedesktop.timedate1"/>
+        </policy>
+
+        <policy context="default">
+                <allow send_destination="org.freedesktop.timedate1"/>
+                <allow receive_sender="org.freedesktop.timedate1"/>
+        </policy>
+
+</busconfig>
diff --git a/src/org.freedesktop.timedate1.policy b/src/org.freedesktop.timedate1.policy
new file mode 100644
index 0000000..5010efd
--- /dev/null
+++ b/src/org.freedesktop.timedate1.policy
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+-->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+
+        <action id="org.freedesktop.timedate1.set-time">
+                <description>Set system time</description>
+                <message>Authentication is required to set the system time.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.timedate1.set-timezone">
+                <description>Set system timezone</description>
+                <message>Authentication is required to set the system timezone.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.timedate1.set-local-rtc">
+                <description>Set RTC to local timezone or UTC</description>
+                <message>Authentication is required to control whether
+                the RTC stores the local or UTC time.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+</policyconfig>
diff --git a/src/org.freedesktop.timedate1.service b/src/org.freedesktop.timedate1.service
new file mode 100644
index 0000000..c3120b6
--- /dev/null
+++ b/src/org.freedesktop.timedate1.service
@@ -0,0 +1,12 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+[D-BUS Service]
+Name=org.freedesktop.timedate1
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.freedesktop.timedate1.service
diff --git a/src/polkit.c b/src/polkit.c
new file mode 100644
index 0000000..5b67480
--- /dev/null
+++ b/src/polkit.c
@@ -0,0 +1,190 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include <errno.h>
+
+#include "util.h"
+#include "dbus-common.h"
+#include "polkit.h"
+
+/* This mimics dbus_bus_get_unix_user() */
+static pid_t get_unix_process_id(
+                DBusConnection *connection,
+                const char *name,
+                DBusError *error) {
+
+        DBusMessage *m = NULL, *reply = NULL;
+        uint32_t pid = 0;
+
+        m = dbus_message_new_method_call(
+                        DBUS_SERVICE_DBUS,
+                        DBUS_PATH_DBUS,
+                        DBUS_INTERFACE_DBUS,
+                        "GetConnectionUnixProcessID");
+        if (!m) {
+                dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(
+                            m,
+                            DBUS_TYPE_STRING, &name,
+                            DBUS_TYPE_INVALID)) {
+                dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
+        if (!reply)
+                goto finish;
+
+        if (dbus_set_error_from_message(error, reply))
+                goto finish;
+
+        if (!dbus_message_get_args(
+                            reply, error,
+                            DBUS_TYPE_UINT32, &pid,
+                            DBUS_TYPE_INVALID))
+                goto finish;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return (pid_t) pid;
+}
+
+int verify_polkit(
+                DBusConnection *c,
+                DBusMessage *request,
+                const char *action,
+                bool interactive,
+                DBusError *error) {
+
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = "";
+        const char *sender;
+        uint32_t flags = interactive ? 1 : 0;
+        pid_t pid_raw;
+        uint32_t pid_u32;
+        unsigned long long starttime_raw;
+        uint64_t starttime_u64;
+        DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant;
+        int r;
+        dbus_bool_t authorized = FALSE;
+
+        assert(c);
+        assert(request);
+
+        sender = dbus_message_get_sender(request);
+        if (!sender)
+                return -EINVAL;
+
+        pid_raw = get_unix_process_id(c, sender, error);
+        if (pid_raw == 0)
+                return -EINVAL;
+
+        r = get_starttime_of_pid(pid_raw, &starttime_raw);
+        if (r < 0)
+                return r;
+
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.PolicyKit1",
+                        "/org/freedesktop/PolicyKit1/Authority",
+                        "org.freedesktop.PolicyKit1.Authority",
+                        "CheckAuthorization");
+        if (!m)
+                return -ENOMEM;
+
+        dbus_message_iter_init_append(m, &iter_msg);
+
+        pid_u32 = (uint32_t) pid_raw;
+        starttime_u64 = (uint64_t) starttime_raw;
+
+        if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) ||
+            !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) ||
+            !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) ||
+            !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
+            !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) ||
+            !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) ||
+            !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) ||
+            !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
+            !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
+            !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) ||
+            !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) ||
+            !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) ||
+            !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) ||
+            !dbus_message_iter_close_container(&iter_dict, &iter_variant) ||
+            !dbus_message_iter_close_container(&iter_array, &iter_dict) ||
+            !dbus_message_iter_close_container(&iter_struct, &iter_array) ||
+            !dbus_message_iter_close_container(&iter_msg, &iter_struct) ||
+            !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) ||
+            !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) ||
+            !dbus_message_iter_close_container(&iter_msg, &iter_array) ||
+            !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) ||
+            !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
+        if (!reply) {
+                r = -EIO;
+                goto finish;
+        }
+
+        if (dbus_set_error_from_message(error, reply)) {
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter_msg) ||
+            dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) {
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter_msg, &iter_struct);
+
+        if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) {
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_get_basic(&iter_struct, &authorized);
+
+        r = authorized ? 0 : -EPERM;
+
+finish:
+
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return r;
+}
diff --git a/src/polkit.h b/src/polkit.h
new file mode 100644
index 0000000..fc4e771
--- /dev/null
+++ b/src/polkit.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foopolkithfoo
+#define foopolkithfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <dbus/dbus.h>
+
+int verify_polkit(
+                DBusConnection *c,
+                DBusMessage *request,
+                const char *action,
+                bool interactive,
+                DBusError *error);
+
+#endif
diff --git a/src/timedated.c b/src/timedated.c
new file mode 100644
index 0000000..4749648
--- /dev/null
+++ b/src/timedated.c
@@ -0,0 +1,544 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "strv.h"
+#include "dbus-common.h"
+#include "polkit.h"
+
+#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
+#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        " <interface name=\"org.freedesktop.timedate1\">\n"             \
+        "  <property name=\"Timezone\" type=\"s\" access=\"read\"/>\n"  \
+        "  <property name=\"LocalRTC\" type=\"b\" access=\"read\"/>\n"  \
+        "  <method name=\"SetTime\">\n"                                 \
+        "   <arg name=\"usec_utc\" type=\"x\" direction=\"in\"/>\n"     \
+        "   <arg name=\"relative\" type=\"b\" direction=\"in\"/>\n"     \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
+        "  <method name=\"SetTimezone\">\n"                             \
+        "   <arg name=\"timezone\" type=\"s\" direction=\"in\"/>\n"     \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
+        "  <method name=\"SetLocalRTC\">\n"                             \
+        "   <arg name=\"local_rtc\" type=\"b\" direction=\"in\"/>\n"    \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
+        " </interface>\n"                                               \
+        BUS_PROPERTIES_INTERFACE                                        \
+        BUS_INTROSPECTABLE_INTERFACE                                    \
+        BUS_PEER_INTERFACE                                              \
+        "</node>\n"
+
+#define INTERFACES_LIST                         \
+        BUS_GENERIC_INTERFACES_LIST             \
+        "org.freedesktop.locale1\0"
+
+static char *zone = NULL;
+static bool local_rtc = false;
+
+static void free_data(void) {
+        free(zone);
+        zone = NULL;
+
+        local_rtc = false;
+}
+
+static bool valid_timezone(const char *name) {
+        const char *p;
+        char *t;
+        bool slash = false;
+        int r;
+        struct stat st;
+
+        assert(name);
+
+        if (*name == '/' || *name == 0)
+                return false;
+
+        for (p = name; *p; p++) {
+                if (!(*p >= '0' && *p <= '9') &&
+                    !(*p >= 'a' && *p <= 'z') &&
+                    !(*p >= 'A' && *p <= 'Z') &&
+                    !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
+                        return false;
+
+                if (*p == '/') {
+
+                        if (slash)
+                                return false;
+
+                        slash = true;
+                } else
+                        slash = false;
+        }
+
+        if (slash)
+                return false;
+
+        t = strappend("/usr/share/zoneinfo/", name);
+        if (!t)
+                return false;
+
+        r = stat(t, &st);
+        free(t);
+
+        if (r < 0)
+                return false;
+
+        if (!S_ISREG(st.st_mode))
+                return false;
+
+        return true;
+}
+
+static void verify_timezone(void) {
+        char *p, *a = NULL, *b = NULL;
+        size_t l, q;
+        int j, k;
+
+        if (!zone)
+                return;
+
+        p = strappend("/usr/share/zoneinfo/", zone);
+        if (!p) {
+                log_error("Out of memory");
+                return;
+        }
+
+        j = read_full_file("/etc/localtime", &a, &l);
+        k = read_full_file(p, &b, &q);
+
+        free(p);
+
+        if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) {
+                log_warning("/etc/localtime and /etc/timezone out of sync.");
+                free(zone);
+                zone = NULL;
+        }
+
+        free(a);
+        free(b);
+}
+
+static int read_data(void) {
+        int r;
+        FILE *f;
+
+        free_data();
+
+        r = read_one_line_file("/etc/timezone", &zone);
+        if (r < 0 && r != -ENOENT)
+                return r;
+
+        verify_timezone();
+
+        f = fopen("/etc/adjtime", "r");
+        if (f) {
+                char line[LINE_MAX];
+                bool b;
+
+                b = fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f);
+
+                fclose(f);
+
+                if (!b)
+                        return -EIO;
+
+                truncate_nl(line);
+                local_rtc = streq(line, "LOCAL");
+
+        } else if (errno != ENOENT)
+                return -errno;
+
+        return 0;
+}
+
+static int write_data_timezone(void) {
+        int r = 0;
+        char *p;
+
+        if (!zone) {
+                if (unlink("/etc/timezone") < 0 && errno != ENOENT)
+                        r = -errno;
+
+                if (unlink("/etc/localtime") < 0 && errno != ENOENT)
+                        r = -errno;
+
+                return r;
+        }
+
+        p = strappend("/usr/share/zoneinfo/", zone);
+        if (!p) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        r = symlink_or_copy_atomic(p, "/etc/localtime");
+        free(p);
+
+        if (r < 0)
+                return r;
+
+        r = write_one_line_file_atomic("/etc/timezone", zone);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int write_data_local_rtc(void) {
+        int r;
+        char *s, *w;
+
+        r = read_full_file("/etc/adjtime", &s, NULL);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        return r;
+
+                if (!local_rtc)
+                        return 0;
+
+                w = strdup(NULL_ADJTIME_LOCAL);
+                if (!w)
+                        return -ENOMEM;
+        } else {
+                char *p, *e;
+                size_t a, b;
+
+                p = strchr(s, '\n');
+                if (!p) {
+                        free(s);
+                        return -EIO;
+                }
+
+                p = strchr(p+1, '\n');
+                if (!p) {
+                        free(s);
+                        return -EIO;
+                }
+
+                p++;
+                e = strchr(p, '\n');
+                if (!p) {
+                        free(s);
+                        return -EIO;
+                }
+
+                a = p - s;
+                b = strlen(e);
+
+                w = new(char, a + (local_rtc ? 5 : 3) + b + 1);
+                if (!w) {
+                        free(s);
+                        return -ENOMEM;
+                }
+
+                *(char*) mempcpy(stpcpy(mempcpy(w, s, a), local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
+
+                if (streq(w, NULL_ADJTIME_UTC)) {
+                        free(w);
+
+                        if (unlink("/etc/adjtime") < 0) {
+                                if (errno != ENOENT)
+                                        return -errno;
+                        }
+
+                        return 0;
+                }
+        }
+
+        r = write_one_line_file_atomic("/etc/adjtime", w);
+        free(w);
+
+        return r;
+}
+
+static DBusHandlerResult timedate_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        const BusProperty properties[] = {
+                { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone       },
+                { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool,   "b", &local_rtc },
+                { NULL, NULL, NULL, NULL, NULL }
+        };
+
+        DBusMessage *reply = NULL, *changed = NULL;
+        DBusError error;
+        int r;
+
+        assert(connection);
+        assert(message);
+
+        dbus_error_init(&error);
+
+        if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) {
+                const char *z;
+                dbus_bool_t interactive;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &z,
+                                    DBUS_TYPE_BOOLEAN, &interactive,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (!valid_timezone(z))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+                if (!streq_ptr(z, zone)) {
+                        char *t;
+
+                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        t = strdup(z);
+                        if (!t)
+                                goto oom;
+
+                        free(zone);
+                        zone = t;
+
+                        r = write_data_timezone();
+                        if (r < 0) {
+                                log_error("Failed to set timezone: %s", strerror(-r));
+                                return bus_send_error_reply(connection, message, NULL, r);
+                        }
+
+                        log_info("Changed timezone to '%s'.", zone);
+
+                        changed = bus_properties_changed_new(
+                                        "/org/freedesktop/timedate1",
+                                        "org.freedesktop.timedate1",
+                                        "Timezone\0");
+                        if (!changed)
+                                goto oom;
+                }
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) {
+                dbus_bool_t lrtc;
+                dbus_bool_t interactive;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_BOOLEAN, &lrtc,
+                                    DBUS_TYPE_BOOLEAN, &interactive,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (lrtc != local_rtc) {
+                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        local_rtc = lrtc;
+
+                        r = write_data_local_rtc();
+                        if (r < 0) {
+                                log_error("Failed to set RTC to local/UTC: %s", strerror(-r));
+                                return bus_send_error_reply(connection, message, NULL, r);
+                        }
+
+                        log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc));
+
+                        changed = bus_properties_changed_new(
+                                        "/org/freedesktop/timedate1",
+                                        "org.freedesktop.timedate1",
+                                        "LocalRTC\0");
+                        if (!changed)
+                                goto oom;
+                }
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) {
+                int64_t utc;
+                dbus_bool_t relative;
+                dbus_bool_t interactive;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_INT64, &utc,
+                                    DBUS_TYPE_BOOLEAN, &relative,
+                                    DBUS_TYPE_BOOLEAN, &interactive,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (!relative && utc <= 0)
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+                if (!relative || utc != 0) {
+                        struct timespec ts;
+
+                        r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        if (relative)
+                                timespec_store(&ts, now(CLOCK_REALTIME) + utc);
+                        else
+                                timespec_store(&ts, utc);
+
+                        if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
+                                log_error("Failed to set local time: %m");
+                                return bus_send_error_reply(connection, message, NULL, -errno);
+                        }
+
+                        log_info("Changed local time to %s", ctime(&ts.tv_sec));
+                }
+
+        } else
+                return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+
+        if (!(reply = dbus_message_new_method_return(message)))
+                goto oom;
+
+        if (!dbus_connection_send(connection, reply, NULL))
+                goto oom;
+
+        dbus_message_unref(reply);
+        reply = NULL;
+
+        if (changed) {
+
+                if (!dbus_connection_send(connection, changed, NULL))
+                        goto oom;
+
+                dbus_message_unref(changed);
+        }
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+        if (reply)
+                dbus_message_unref(reply);
+
+        if (changed)
+                dbus_message_unref(changed);
+
+        dbus_error_free(&error);
+
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static int connect_bus(DBusConnection **_bus) {
+        static const DBusObjectPathVTable timedate_vtable = {
+                .message_function = timedate_message_handler
+        };
+        DBusError error;
+        DBusConnection *bus = NULL;
+        int r;
+
+        assert(_bus);
+
+        dbus_error_init(&error);
+
+        bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+        if (!bus) {
+                log_error("Failed to get system D-Bus connection: %s", error.message);
+                r = -ECONNREFUSED;
+                goto fail;
+        }
+
+        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL)) {
+                log_error("Not enough memory");
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
+                log_error("Failed to register name on bus: %s", error.message);
+                r = -EEXIST;
+                goto fail;
+        }
+
+        if (_bus)
+                *_bus = bus;
+
+        return 0;
+
+fail:
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        DBusConnection *bus = NULL;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc != 1) {
+                log_error("This program takes no arguments.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        umask(0022);
+
+        r = read_data();
+        if (r < 0) {
+                log_error("Failed to read timezone data: %s", strerror(-r));
+                goto finish;
+        }
+
+        r = connect_bus(&bus);
+        if (r < 0)
+                goto finish;
+
+        while (dbus_connection_read_write_dispatch(bus, -1))
+                ;
+
+        r = 0;
+
+finish:
+        free_data();
+
+        if (bus) {
+                dbus_connection_flush(bus);
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
+        }
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/units/.gitignore b/units/.gitignore
index f969466..9634440 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -1,3 +1,5 @@
+systemd-localed.service
+systemd-timedated.service
 systemd-hostnamed.service
 console-shell.service
 systemd-sysctl.service
diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in
new file mode 100644
index 0000000..4be65df
--- /dev/null
+++ b/units/systemd-localed.service.in
@@ -0,0 +1,17 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Locale Service
+
+[Service]
+ExecStart=@rootlibexecdir@/systemd-localed
+Type=dbus
+BusName=org.freedesktop.locale1
+CapabilityBoundingSet=
diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in
new file mode 100644
index 0000000..90ff443
--- /dev/null
+++ b/units/systemd-timedated.service.in
@@ -0,0 +1,17 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Time & Date Service
+
+[Service]
+ExecStart=@rootlibexecdir@/systemd-timedated
+Type=dbus
+BusName=org.freedesktop.timedate1
+CapabilityBoundingSet=CAP_SYS_TIME

commit 34ca941cec76bbfdfd02c705b76bc1b53ea2bcd1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 15 15:35:23 2011 +0200

    util: make a couple of files we write atomic

diff --git a/src/hostnamed.c b/src/hostnamed.c
index cf2172f..68c5715 100644
--- a/src/hostnamed.c
+++ b/src/hostnamed.c
@@ -201,7 +201,7 @@ static int write_data_static_hostname(void) {
                 return 0;
         }
 
-        return write_one_line_file("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
+        return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
 }
 
 static int write_data_other(void) {
diff --git a/src/shutdownd.c b/src/shutdownd.c
index 8f765b4..1381941 100644
--- a/src/shutdownd.c
+++ b/src/shutdownd.c
@@ -320,7 +320,7 @@ int main(int argc, char *argv[]) {
 
                         log_info("Creating /run/nologin, blocking further logins...");
 
-                        if ((e = write_one_line_file("/run/nologin", "System is going down.")) < 0)
+                        if ((e = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
                                 log_error("Failed to create /run/nologin: %s", strerror(-e));
                         else
                                 unlink_nologin = true;
diff --git a/src/user-sessions.c b/src/user-sessions.c
index e045b88..ffb8657 100644
--- a/src/user-sessions.c
+++ b/src/user-sessions.c
@@ -67,7 +67,7 @@ int main(int argc, char*argv[]) {
                 int r, q;
                 char *cgroup_user_tree = NULL;
 
-                if ((r = write_one_line_file("/run/nologin", "System is going down.")) < 0)
+                if ((r = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
                         log_error("Failed to create /run/nologin: %s", strerror(-r));
 
                 if ((q = cg_get_user_path(&cgroup_user_tree)) < 0) {
diff --git a/src/util.c b/src/util.c
index 81d247c..dfb153b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -567,6 +567,7 @@ int write_one_line_file(const char *fn, const char *line) {
         if (!(f = fopen(fn, "we")))
                 return -errno;
 
+        errno = 0;
         if (fputs(line, f) < 0) {
                 r = -errno;
                 goto finish;
@@ -590,6 +591,64 @@ finish:
         return r;
 }
 
+int fchmod_umask(int fd, mode_t m) {
+        mode_t u;
+        int r;
+
+        u = umask(0777);
+        r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+        umask(u);
+
+        return r;
+}
+
+int write_one_line_file_atomic(const char *fn, const char *line) {
+        FILE *f;
+        int r;
+        char *p;
+
+        assert(fn);
+        assert(line);
+
+        r = fopen_temporary(fn, &f, &p);
+        if (r < 0)
+                return r;
+
+        fchmod_umask(fileno(f), 0644);
+
+        errno = 0;
+        if (fputs(line, f) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (!endswith(line, "\n"))
+                fputc('\n', f);
+
+        fflush(f);
+
+        if (ferror(f)) {
+                if (errno != 0)
+                        r = -errno;
+                else
+                        r = -EIO;
+        } else {
+                if (rename(p, fn) < 0)
+                        r = -errno;
+                else
+                        r = 0;
+        }
+
+finish:
+        if (r < 0)
+                unlink(p);
+
+        fclose(f);
+        free(p);
+
+        return r;
+}
+
 int read_one_line_file(const char *fn, char **line) {
         FILE *f;
         int r;
@@ -621,7 +680,7 @@ finish:
         return r;
 }
 
-int read_full_file(const char *fn, char **contents) {
+int read_full_file(const char *fn, char **contents, size_t *size) {
         FILE *f;
         int r;
         size_t n, l;
@@ -636,6 +695,12 @@ int read_full_file(const char *fn, char **contents) {
                 goto finish;
         }
 
+        /* Safety check */
+        if (st.st_size > 4*1024*1024) {
+                r = -E2BIG;
+                goto finish;
+        }
+
         n = st.st_size > 0 ? st.st_size : LINE_MAX;
         l = 0;
 
@@ -680,6 +745,9 @@ int read_full_file(const char *fn, char **contents) {
         *contents = buf;
         buf = NULL;
 
+        if (size)
+                *size = l;
+
         r = 0;
 
 finish:
@@ -699,7 +767,7 @@ int parse_env_file(
         assert(fname);
         assert(separator);
 
-        if ((r = read_full_file(fname, &contents)) < 0)
+        if ((r = read_full_file(fname, &contents, NULL)) < 0)
                 return r;
 
         p = contents;
@@ -838,15 +906,17 @@ finish:
 }
 
 int write_env_file(const char *fname, char **l) {
-
-        char **i;
+        char **i, *p;
         FILE *f;
         int r;
 
-        f = fopen(fname, "we");
-        if (!f)
-                return -errno;
+        r = fopen_temporary(fname, &f, &p);
+        if (r < 0)
+                return r;
 
+        fchmod_umask(fileno(f), 0644);
+
+        errno = 0;
         STRV_FOREACH(i, l) {
                 fputs(*i, f);
                 fputc('\n', f);
@@ -854,8 +924,23 @@ int write_env_file(const char *fname, char **l) {
 
         fflush(f);
 
-        r = ferror(f) ? -errno : 0;
+        if (ferror(f)) {
+                if (errno != 0)
+                        r = -errno;
+                else
+                        r = -EIO;
+        } else {
+                if (rename(p, fname) < 0)
+                        r = -errno;
+                else
+                        r = 0;
+        }
+
+        if (r < 0)
+                unlink(p);
+
         fclose(f);
+        free(p);
 
         return r;
 }
@@ -4778,6 +4863,152 @@ int hwclock_set_time(const struct tm *tm) {
         return err;
 }
 
+int copy_file(const char *from, const char *to) {
+        int r, fdf, fdt;
+
+        assert(from);
+        assert(to);
+
+        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fdf < 0)
+                return -errno;
+
+        fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
+        if (fdt < 0) {
+                close_nointr_nofail(fdf);
+                return -errno;
+        }
+
+        for (;;) {
+                char buf[PIPE_BUF];
+                ssize_t n, k;
+
+                n = read(fdf, buf, sizeof(buf));
+                if (n < 0) {
+                        r = -errno;
+
+                        close_nointr_nofail(fdf);
+                        close_nointr(fdt);
+                        unlink(to);
+
+                        return r;
+                }
+
+                if (n == 0)
+                        break;
+
+                errno = 0;
+                k = loop_write(fdt, buf, n, false);
+                if (n != k) {
+                        r = k < 0 ? k : (errno ? -errno : -EIO);
+
+                        close_nointr_nofail(fdf);
+                        close_nointr(fdt);
+
+                        unlink(to);
+                        return r;
+                }
+        }
+
+        close_nointr_nofail(fdf);
+        r = close_nointr(fdt);
+
+        if (r < 0) {
+                unlink(to);
+                return r;
+        }
+
+        return 0;
+}
+
+int symlink_or_copy(const char *from, const char *to) {
+        char *pf = NULL, *pt = NULL;
+        struct stat a, b;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        if (parent_of_path(from, &pf) < 0 ||
+            parent_of_path(to, &pt) < 0) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (stat(pf, &a) < 0 ||
+            stat(pt, &b) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (a.st_dev != b.st_dev) {
+                free(pf);
+                free(pt);
+
+                return copy_file(from, to);
+        }
+
+        if (symlink(from, to) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        free(pf);
+        free(pt);
+
+        return r;
+}
+
+int symlink_or_copy_atomic(const char *from, const char *to) {
+        char *t, *x;
+        const char *fn;
+        size_t k;
+        unsigned long long ull;
+        unsigned i;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        t = new(char, strlen(to) + 1 + 16 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        fn = file_name_from_path(to);
+        k = fn-to;
+        memcpy(t, to, k);
+        t[k] = '.';
+        x = stpcpy(t+k+1, fn);
+
+        ull = random_ull();
+        for (i = 0; i < 16; i++) {
+                *(x++) = hexchar(ull & 0xF);
+                ull >>= 4;
+        }
+
+        *x = 0;
+
+        r = symlink_or_copy(from, t);
+        if (r < 0) {
+                unlink(t);
+                free(t);
+                return r;
+        }
+
+        if (rename(t, to) < 0) {
+                r = -errno;
+                unlink(t);
+                free(t);
+                return r;
+        }
+
+        free(t);
+        return r;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/util.h b/src/util.h
index 15dfe17..bd98b65 100644
--- a/src/util.h
+++ b/src/util.h
@@ -200,8 +200,9 @@ pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
 int get_starttime_of_pid(pid_t pid, unsigned long long *st);
 
 int write_one_line_file(const char *fn, const char *line);
+int write_one_line_file_atomic(const char *fn, const char *line);
 int read_one_line_file(const char *fn, char **line);
-int read_full_file(const char *fn, char **contents);
+int read_full_file(const char *fn, char **contents, size_t *size);
 
 int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
 int load_env_file(const char *fname, char ***l);
@@ -422,6 +423,22 @@ int terminal_vhangup(const char *name);
 
 int vt_disallocate(const char *name);
 
+int copy_file(const char *from, const char *to);
+int symlink_or_copy(const char *from, const char *to);
+int symlink_or_copy_atomic(const char *from, const char *to);
+
+int fchmod_umask(int fd, mode_t mode);
+
+int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
+
+bool hwclock_is_localtime(void);
+
+int hwclock_apply_localtime_delta(void);
+
+int hwclock_get_time(struct tm *tm);
+
+int hwclock_set_time(const struct tm *tm);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 
@@ -454,14 +471,4 @@ int signal_from_string(const char *s);
 
 int signal_from_string_try_harder(const char *s);
 
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
-
-bool hwclock_is_localtime(void);
-
-int hwclock_apply_localtime_delta(void);
-
-int hwclock_get_time(struct tm *tm);
-
-int hwclock_set_time(const struct tm *tm);
-
 #endif

commit 5f4b19f4bc4b6e747ca19f53ef33a167ecf9ac0b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 15 15:34:19 2011 +0200

    service: check whether sysv scripts where changed

diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 9f71492..ffc2573 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -646,7 +646,7 @@
                                 removed. If the list of capabilities
                                 is prefixed with ~ all but the listed
                                 capabilities will be included, the
-                                effect of this assignment
+                                effect of the assignment
                                 inverted. Note that this option does
                                 not actually set or unset any
                                 capabilities in the effective,
diff --git a/src/service.c b/src/service.c
index 62027f3..4ee7900 100644
--- a/src/service.c
+++ b/src/service.c
@@ -470,6 +470,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 LSB_DESCRIPTION
         } state = NORMAL;
         char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
+        struct stat st;
 
         assert(s);
         assert(path);
@@ -481,12 +482,26 @@ static int service_load_sysv_path(Service *s, const char *path) {
                 goto finish;
         }
 
+        zero(st);
+        if (fstat(fileno(f), &st) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
         free(s->sysv_path);
         if (!(s->sysv_path = strdup(path))) {
                 r = -ENOMEM;
                 goto finish;
         }
 
+        s->sysv_mtime = timespec_load(&st.st_mtim);
+
+        if (null_or_empty(&st)) {
+                u->meta.load_state = UNIT_MASKED;
+                r = 0;
+                goto finish;
+        }
+
         while (!feof(f)) {
                 char l[LINE_MAX], *t;
 
@@ -3212,6 +3227,29 @@ static void service_reset_failed(Unit *u) {
         s->failure = false;
 }
 
+static bool service_need_daemon_reload(Unit *u) {
+        Service *s = SERVICE(u);
+
+        assert(s);
+
+#ifdef HAVE_SYSV_COMPAT
+        if (s->sysv_path) {
+                struct stat st;
+
+                zero(st);
+                if (stat(s->sysv_path, &st) < 0)
+                        /* What, cannot access this anymore? */
+                        return true;
+
+                if (s->sysv_mtime > 0 &&
+                    timespec_load(&st.st_mtim) != s->sysv_mtime)
+                        return true;
+        }
+#endif
+
+        return false;
+}
+
 static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
         Service *s = SERVICE(u);
         int r = 0;
@@ -3361,6 +3399,8 @@ const UnitVTable service_vtable = {
 
         .reset_failed = service_reset_failed,
 
+        .need_daemon_reload = service_need_daemon_reload,
+
         .cgroup_notify_empty = service_cgroup_notify_event,
         .notify_message = service_notify_message,
 
diff --git a/src/service.h b/src/service.h
index 55b9513..e28f74b 100644
--- a/src/service.h
+++ b/src/service.h
@@ -144,6 +144,7 @@ struct Service {
 
         char *sysv_path;
         char *sysv_runlevels;
+        usec_t sysv_mtime;
 #endif
 
         char *bus_name;
diff --git a/src/unit.c b/src/unit.c
index 057431c..87b7edf 100644
--- a/src/unit.c
+++ b/src/unit.c
@@ -2305,21 +2305,25 @@ void unit_status_printf(Unit *u, const char *format, ...) {
 }
 
 bool unit_need_daemon_reload(Unit *u) {
-        struct stat st;
-
         assert(u);
 
-        if (!u->meta.fragment_path)
-                return false;
+        if (u->meta.fragment_path) {
+                struct stat st;
 
-        zero(st);
-        if (stat(u->meta.fragment_path, &st) < 0)
-                /* What, cannot access this anymore? */
-                return true;
+                zero(st);
+                if (stat(u->meta.fragment_path, &st) < 0)
+                        /* What, cannot access this anymore? */
+                        return true;
 
-        return
-                u->meta.fragment_mtime &&
-                timespec_load(&st.st_mtim) != u->meta.fragment_mtime;
+                if (u->meta.fragment_mtime > 0 &&
+                    timespec_load(&st.st_mtim) != u->meta.fragment_mtime)
+                        return true;
+        }
+
+        if (UNIT_VTABLE(u)->need_daemon_reload)
+                return UNIT_VTABLE(u)->need_daemon_reload(u);
+
+        return false;
 }
 
 void unit_reset_failed(Unit *u) {
diff --git a/src/unit.h b/src/unit.h
index 1c8cf63..79f1510 100644
--- a/src/unit.h
+++ b/src/unit.h
@@ -317,6 +317,9 @@ struct UnitVTable {
         void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
         void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w);
 
+        /* Check whether unit needs a daemon reload */
+        bool (*need_daemon_reload)(Unit *u);
+
         /* Reset failed state if we are in failed state */
         void (*reset_failed)(Unit *u);
 

commit 8d0e38a2b966799af884e78a54fd6a2dffa44788
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 15 15:31:54 2011 +0200

    dbus: introduce UnsetAndSetEnvironment()

diff --git a/TODO b/TODO
index 8e43b38..71ad077 100644
--- a/TODO
+++ b/TODO
@@ -27,6 +27,10 @@ Features:
 
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
 
+* hostnamed: make file updates atomic
+
+* support sd_notify() style notificatio when reload is finished (RELOADED=1)
+
 * verify that the AF_UNIX sockets of a service in the fs still exist
   when we start a service in order to avoid confusion when a user
   assumes starting a service is enough to make it accessible
@@ -46,8 +50,6 @@ Features:
 
 * move /selinux to /sys/fs/selinux
 
-* unset cgroup agents on shutdown
-
 * add prefix match to sysctl, tmpfiles, ...
 
 * send out "finished" signal when we are finished booting
@@ -154,8 +156,6 @@ Features:
 
 * Support --test based on current system state
 
-* systemctl enable as D-Bus call
-
 * consider services with any kind of link in /etc/systemd/system enabled
 
 * show failure error string in "systemctl status"
diff --git a/src/dbus-common.c b/src/dbus-common.c
index cb43bbd..e439a42 100644
--- a/src/dbus-common.c
+++ b/src/dbus-common.c
@@ -737,3 +737,62 @@ unsigned bus_events_to_flags(uint32_t events) {
 
         return flags;
 }
+
+int bus_parse_strv(DBusMessage *m, char ***_l) {
+        DBusMessageIter iter;
+
+        assert(m);
+        assert(_l);
+
+        if (!dbus_message_iter_init(m, &iter))
+                return -EINVAL;
+
+        return bus_parse_strv_iter(&iter, _l);
+}
+
+int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
+        DBusMessageIter sub;
+        unsigned n = 0, i = 0;
+        char **l;
+
+        assert(iter);
+        assert(_l);
+
+        if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
+            return -EINVAL;
+
+        dbus_message_iter_recurse(iter, &sub);
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                n++;
+                dbus_message_iter_next(&sub);
+        }
+
+        if (!(l = new(char*, n+1)))
+                return -ENOMEM;
+
+        dbus_message_iter_recurse(iter, &sub);
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *s;
+
+                assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
+                dbus_message_iter_get_basic(&sub, &s);
+
+                if (!(l[i++] = strdup(s))) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+
+                dbus_message_iter_next(&sub);
+        }
+
+        assert(i == n);
+        l[i] = NULL;
+
+        if (_l)
+                *_l = l;
+
+        return 0;
+}
diff --git a/src/dbus-common.h b/src/dbus-common.h
index e321a2c..9368f75 100644
--- a/src/dbus-common.h
+++ b/src/dbus-common.h
@@ -158,4 +158,7 @@ DBusMessage* bus_properties_changed_new(const char *path, const char *interface,
 uint32_t bus_flags_to_events(DBusWatch *bus_watch);
 unsigned bus_events_to_flags(uint32_t events);
 
+int bus_parse_strv(DBusMessage *m, char ***_l);
+int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l);
+
 #endif
diff --git a/src/dbus-manager.c b/src/dbus-manager.c
index 797e53d..cc2b4d0 100644
--- a/src/dbus-manager.c
+++ b/src/dbus-manager.c
@@ -128,6 +128,10 @@
         "  </method>\n"                                                 \
         "  <method name=\"UnsetEnvironment\">\n"                        \
         "   <arg name=\"names\" type=\"as\" direction=\"in\"/>\n"       \
+        "  </method>\n"                                                 \
+        "  <method name=\"UnsetAndSetEnvironment\">\n"                  \
+        "   <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n"       \
+        "   <arg name=\"set\" type=\"as\" direction=\"in\"/>\n"         \
         "  </method>\n"
 
 #define BUS_MANAGER_INTERFACE_SIGNALS                                   \
@@ -1035,6 +1039,58 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                 strv_free(m->environment);
                 m->environment = e;
 
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
+                char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
+                DBusMessageIter iter;
+
+                if (!dbus_message_iter_init(message, &iter))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+                r = bus_parse_strv_iter(&iter, &l_unset);
+                if (r < 0) {
+                        if (r == -ENOMEM)
+                                goto oom;
+
+                        return bus_send_error_reply(connection, message, NULL, r);
+                }
+
+                if (!dbus_message_iter_next(&iter)) {
+                        strv_free(l_unset);
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
+                }
+
+                r = bus_parse_strv_iter(&iter, &l_set);
+                if (r < 0) {
+                        strv_free(l_unset);
+                        if (r == -ENOMEM)
+                                goto oom;
+
+                        return bus_send_error_reply(connection, message, NULL, r);
+                }
+
+                e = strv_env_delete(m->environment, 1, l_unset);
+                strv_free(l_unset);
+
+                if (!e) {
+                        strv_free(l_set);
+                        goto oom;
+                }
+
+                f = strv_env_merge(2, e, l_set);
+                strv_free(l_set);
+                strv_free(e);
+
+                if (!f)
+                        goto oom;
+
+                if (!(reply = dbus_message_new_method_return(message))) {
+                        strv_free(f);
+                        goto oom;
+                }
+
+                strv_free(m->environment);
+                m->environment = f;
+
         } else
                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties);
 
diff --git a/src/dbus.c b/src/dbus.c
index e153c35..2a379a2 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -1212,55 +1212,6 @@ int bus_broadcast(Manager *m, DBusMessage *message) {
         return oom ? -ENOMEM : 0;
 }
 
-int bus_parse_strv(DBusMessage *m, char ***_l) {
-        DBusMessageIter iter, sub;
-        unsigned n = 0, i = 0;
-        char **l;
-
-        assert(m);
-        assert(_l);
-
-        if (!dbus_message_iter_init(m, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
-            return -EINVAL;
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                n++;
-                dbus_message_iter_next(&sub);
-        }
-
-        if (!(l = new(char*, n+1)))
-                return -ENOMEM;
-
-        assert_se(dbus_message_iter_init(m, &iter));
-        dbus_message_iter_recurse(&iter, &sub);
-
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *s;
-
-                assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
-                dbus_message_iter_get_basic(&sub, &s);
-
-                if (!(l[i++] = strdup(s))) {
-                        strv_free(l);
-                        return -ENOMEM;
-                }
-
-                dbus_message_iter_next(&sub);
-        }
-
-        assert(i == n);
-        l[i] = NULL;
-
-        if (_l)
-                *_l = l;
-
-        return 0;
-}
-
 bool bus_has_subscriber(Manager *m) {
         Iterator i;
         DBusConnection *c;
diff --git a/src/dbus.h b/src/dbus.h
index 8387ffa..c47e782 100644
--- a/src/dbus.h
+++ b/src/dbus.h
@@ -38,8 +38,6 @@ int bus_query_pid(Manager *m, const char *name);
 
 int bus_broadcast(Manager *m, DBusMessage *message);
 
-int bus_parse_strv(DBusMessage *m, char ***_l);
-
 bool bus_has_subscriber(Manager *m);
 bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
 
diff --git a/src/service.c b/src/service.c
index 1f74868..62027f3 100644
--- a/src/service.c
+++ b/src/service.c
@@ -280,10 +280,10 @@ static int sysv_translate_facility(const char *name, const char *filename, char
                 /* LSB defined facilities */
                 "local_fs",             SPECIAL_LOCAL_FS_TARGET,
 #ifndef TARGET_MANDRIVA
-		/* Due to unfortunate name selection in Mandriva,
-		 * $network is provided by network-up which is ordered
-		 * after network which actually starts interfaces.
-		 * To break the loop, just ignore it */
+                /* Due to unfortunate name selection in Mandriva,
+                 * $network is provided by network-up which is ordered
+                 * after network which actually starts interfaces.
+                 * To break the loop, just ignore it */
                 "network",              SPECIAL_NETWORK_TARGET,
 #endif
                 "named",                SPECIAL_NSS_LOOKUP_TARGET,
diff --git a/src/util.c b/src/util.c
index b291e2f..81d247c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4692,6 +4692,7 @@ int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
         }
 
         qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
+
 finish:
         strv_free(dirs);
         hashmap_free(fh);

commit 48f82119ce55caa7671598fb1bd90df4eb00d150
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jun 15 10:16:09 2011 +0200

    util: return errno in close_nointr()

diff --git a/src/util.c b/src/util.c
index 156d32a..b291e2f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -230,11 +230,12 @@ int close_nointr(int fd) {
         for (;;) {
                 int r;
 
-                if ((r = close(fd)) >= 0)
+                r = close(fd);
+                if (r >= 0)
                         return r;
 
                 if (errno != EINTR)
-                        return r;
+                        return -errno;
         }
 }
 

commit 3f49d45a45c6c585098590174c3245d2d9bdde0a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu May 26 02:21:16 2011 +0200

    logind: implement D-Bus properties

diff --git a/Makefile.am b/Makefile.am
index f455f21..0bc5169 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -810,10 +810,14 @@ systemd_hostnamed_LDADD = \
 
 systemd_logind_SOURCES = \
 	src/logind.c \
+	src/logind-dbus.c \
         src/logind-device.c \
         src/logind-seat.c \
+        src/logind-seat-dbus.c \
         src/logind-session.c \
+        src/logind-session-dbus.c \
         src/logind-user.c \
+        src/logind-user-dbus.c \
         src/logind-acl.c \
         src/dbus-common.c \
         src/dbus-loop.c \
diff --git a/src/70-uaccess.rules b/src/70-uaccess.rules
new file mode 100644
index 0000000..6932492
--- /dev/null
+++ b/src/70-uaccess.rules
@@ -0,0 +1,72 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+ACTION=="remove", GOTO="uaccess_end"
+ENV{MAJOR}=="", GOTO="uaccess_end"
+
+# PTP/MTP protocol devices, cameras, portable media players
+SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="", ENV{DEVTYPE}=="usb_device", IMPORT{program}="usb_id --export %p"
+SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess"
+
+# Digicams with proprietary protocol
+ENV{ID_GPHOTO2}=="*?", TAG+="uaccess"
+
+# SCSI and USB scanners
+ENV{libsane_matched}=="yes", TAG+="uaccess"
+
+# HPLIP devices (necessary for ink level check and HP tool maintenance)
+ENV{ID_HPLIP}=="1", TAG+="uaccess"
+
+# optical drives
+SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
+
+# Sound devices
+SUBSYSTEM=="sound", TAG+="uaccess"
+
+# ffado is an userspace driver for firewire sound cards
+SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess"
+
+# Webcams, frame grabber, TV cards
+SUBSYSTEM=="video4linux", TAG+="uaccess"
+SUBSYSTEM=="dvb", TAG+="uaccess"
+
+# IIDC devices: industrial cameras and some webcams
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*",  TAG+="uaccess"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*",  TAG+="uaccess"
+# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess"
+
+# DRI video devices
+SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess"
+
+# KVM
+SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess"
+
+# smart-card readers
+ENV{ID_SMARTCARD_READER}=="*?", TAG+="uaccess"
+
+# PDA devices
+ENV{ID_PDA}=="*?", TAG+="uaccess"
+
+# Programmable remote control
+ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess"
+
+# joysticks
+SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess"
+
+# color measurement devices
+ENV{COLOR_MEASUREMENT_DEVICE}=="*?", TAG+="uaccess"
+
+# DDC/CI device, usually high-end monitors such as the DreamColor
+ENV{DDC_DEVICE}=="*?", TAG+="uaccess"
+
+# media player raw devices (for user-mode drivers, Android SDK, etc.)
+SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess"
+
+LABEL="uaccess_end"
diff --git a/src/71-seat.rules b/src/71-seat.rules
new file mode 100644
index 0000000..3969837
--- /dev/null
+++ b/src/71-seat.rules
@@ -0,0 +1,20 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+ACTION=="remove", GOTO="seat_end"
+
+TAG=="uaccess", TAG+="seat"
+SUBSYSTEM=="input", TAG+="seat"
+SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat"
+SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat"
+SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="0001", ENV{ID_AUTOSEAT}="1"
+
+IMPORT{parent}="ID_SEAT"
+ENV{ID_AUTOSEAT}=="1", ENV{ID_SEAT}=="", ENV{ID_SEAT}="seat-foo"
+ENV{ID_SEAT}!="", TAG+="seat-foo"
+
+LABEL="seat_end"
diff --git a/src/logind-dbus.c b/src/logind-dbus.c
new file mode 100644
index 0000000..90db941
--- /dev/null
+++ b/src/logind-dbus.c
@@ -0,0 +1,235 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "logind.h"
+#include "dbus-common.h"
+
+#define BUS_MANAGER_INTERFACE                                           \
+        " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
+        "  <method name=\"GetSeat\">\n"                                 \
+        "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
+        "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
+        "  </method>\n"                                                 \
+        "  <method name=\"GetUser\">\n"                                 \
+        "   <arg name=\"uid\" type=\"t\" direction=\"in\"/>\n"          \
+        "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
+        "  </method>\n"                                                 \
+        "  <method name=\"GetSession\">\n"                              \
+        "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
+        "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
+        "  </method>\n"                                                 \
+        "  <method name=\"ListSeats\">\n"                               \
+        "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
+        "  </method>\n"                                                 \
+        "  <method name=\"ListUsers\">\n"                               \
+        "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
+        "  </method>\n"                                                 \
+        "  <method name=\"ListSessions\">\n"                            \
+        "   <arg name=\"users\" type=\"a(sussso)\" direction=\"out\"/>\n" \
+        "  </method>\n"                                                 \
+        "  <method name=\"CreateSession\">\n"                           \
+        "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
+        "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
+        "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
+        "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
+        "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
+        "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
+        "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
+        "   <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n"  \
+        "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
+        "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
+        "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
+        "   <arg name=\"kill_processes\" type=\"as\" direction=\"in\"/>\n" \
+        "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
+        "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
+        "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
+        "  </method>\n"                                                 \
+        "  <method name=\"TerminateSession\">\n"                        \
+        "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
+        "  </method>\n"                                                 \
+        "  <method name=\"TerminateUser\">\n"                           \
+        "   <arg name=\"uid\" type=\"t\" direction=\"in\"/>\n"          \
+        "  </method>\n"                                                 \
+        "  <method name=\"TerminateSeat\">\n"                           \
+        "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
+        "  </method>\n"                                                 \
+        "  <signal name=\"SessionNew\">\n"                              \
+        "   <arg name=\"id\" type=\"s\"/>\n"                            \
+        "   <arg name=\"path\" type=\"o\"/>\n"                          \
+        "  </signal>\n"                                                 \
+        "  <signal name=\"SessionRemoved\">\n"                          \
+        "   <arg name=\"id\" type=\"s\"/>\n"                            \
+        "   <arg name=\"path\" type=\"o\"/>\n"                          \
+        "  </signal>\n"                                                 \
+        "  <signal name=\"UserNew\">\n"                                 \
+        "   <arg name=\"uid\" type=\"u\"/>\n"                           \
+        "   <arg name=\"path\" type=\"o\"/>\n"                          \
+        "  </signal>\n"                                                 \
+        "  <signal name=\"UserRemoved\">\n"                             \
+        "   <arg name=\"uid\" type=\"u\"/>\n"                           \
+        "   <arg name=\"path\" type=\"o\"/>\n"                          \
+        "  </signal>\n"                                                 \
+        "  <signal name=\"SeatNew\">\n"                                 \
+        "   <arg name=\"id\" type=\"s\"/>\n"                            \
+        "   <arg name=\"path\" type=\"o\"/>\n"                          \
+        "  </signal>\n"                                                 \
+        "  <signal name=\"SeatRemoved\">\n"                             \
+        "   <arg name=\"id\" type=\"s\"/>\n"                            \
+        "   <arg name=\"path\" type=\"o\"/>\n"                          \
+        "  </signal>\n"                                                 \
+        "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
+        "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
+        "  <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\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" \
+        " </interface>\n"
+
+#define INTROSPECTION_BEGIN                                             \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        BUS_MANAGER_INTERFACE                                           \
+        BUS_PROPERTIES_INTERFACE                                        \
+        BUS_PEER_INTERFACE                                              \
+        BUS_INTROSPECTABLE_INTERFACE
+
+#define INTROSPECTION_END                                               \
+        "</node>\n"
+
+#define INTERFACES_LIST                              \
+        BUS_GENERIC_INTERFACES_LIST                  \
+        "org.freedesktop.login1.Manager\0"
+
+static DBusHandlerResult manager_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        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 },
+                { NULL, NULL, NULL, NULL, NULL }
+        };
+
+        DBusError error;
+        DBusMessage *reply = NULL;
+
+        assert(connection);
+        assert(message);
+        assert(m);
+
+        dbus_error_init(&error);
+
+        if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+                char *introspection = NULL;
+                FILE *f;
+                Iterator i;
+                Session *session;
+                Seat *seat;
+                User *user;
+                size_t size;
+                char *p;
+
+                if (!(reply = dbus_message_new_method_return(message)))
+                        goto oom;
+
+                /* We roll our own introspection code here, instead of
+                 * relying on bus_default_message_handler() because we
+                 * need to generate our introspection string
+                 * dynamically. */
+
+                if (!(f = open_memstream(&introspection, &size)))
+                        goto oom;
+
+                fputs(INTROSPECTION_BEGIN, f);
+
+                HASHMAP_FOREACH(seat, m->seats, i) {
+                        p = bus_path_escape(seat->id);
+
+                        if (p) {
+                                fprintf(f, "<node name=\"seat/%s\"/>", p);
+                                free(p);
+                        }
+                }
+
+                HASHMAP_FOREACH(user, m->users, i)
+                        fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
+
+                HASHMAP_FOREACH(session, m->sessions, i) {
+                        p = bus_path_escape(session->id);
+
+                        if (p) {
+                                fprintf(f, "<node name=\"session/%s\"/>", p);
+                                free(p);
+                        }
+                }
+
+                fputs(INTROSPECTION_END, f);
+
+                if (ferror(f)) {
+                        fclose(f);
+                        free(introspection);
+                        goto oom;
+                }
+
+                fclose(f);
+
+                if (!introspection)
+                        goto oom;
+
+                if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
+                        free(introspection);
+                        goto oom;
+                }
+
+                free(introspection);
+        } else
+                return bus_default_message_handler(connection, message, NULL, 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;
+}
+
+const DBusObjectPathVTable bus_manager_vtable = {
+        .message_function = manager_message_handler
+};
diff --git a/src/logind-device.c b/src/logind-device.c
index 4e076c2..31afa4f 100644
--- a/src/logind-device.c
+++ b/src/logind-device.c
@@ -80,6 +80,6 @@ void device_attach(Device *d, Seat *s) {
         if (d->seat)
                 device_detach(d);
 
-        LIST_PREPEND(Device, devices, d->seat->devices, d);
         d->seat = s;
+        LIST_PREPEND(Device, devices, s->devices, d);
 }
diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c
new file mode 100644
index 0000000..63b1bd5
--- /dev/null
+++ b/src/logind-seat-dbus.c
@@ -0,0 +1,218 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "logind.h"
+#include "logind-seat.h"
+#include "dbus-common.h"
+#include "util.h"
+
+#define BUS_SEAT_INTERFACE \
+        " <interface name=\"org.freedesktop.login1.Seat\">\n"           \
+        "  <method name=\"Terminate\"/>\n"                              \
+        "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
+        "  <property name=\"Active\" type=\"so\" access=\"read\"/>\n"   \
+        "  <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
+        " </interface>\n"                                               \
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        BUS_SEAT_INTERFACE                                              \
+        BUS_PROPERTIES_INTERFACE                                        \
+        BUS_PEER_INTERFACE                                              \
+        BUS_INTROSPECTABLE_INTERFACE                                    \
+        "</node>\n"
+
+#define INTERFACES_LIST                              \
+        BUS_GENERIC_INTERFACES_LIST                  \
+        "org.freedesktop.login1.Seat\0"
+
+static int bus_seat_append_active(DBusMessageIter *i, const char *property, void *data) {
+        DBusMessageIter sub;
+        Seat *s = data;
+        const char *id, *path;
+        char *p = NULL;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
+                return -ENOMEM;
+
+        if (s->active) {
+                id = s->active->id;
+                path = p = session_bus_path(s->active);
+
+                if (!p)
+                        return -ENOMEM;
+        } else {
+                id = "";
+                path = "/";
+        }
+
+        if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
+                free(p);
+                return -ENOMEM;
+        }
+
+        free(p);
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, void *data) {
+        DBusMessageIter sub, sub2;
+        Seat *s = data;
+        Session *session;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "so", &sub))
+                return -ENOMEM;
+
+        LIST_FOREACH(sessions_by_seat, session, s->sessions) {
+                char *p;
+
+                if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                        return -ENOMEM;
+
+                p = session_bus_path(session);
+                if (!p)
+                        return -ENOMEM;
+
+                if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
+                    !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                        free(p);
+                        return -ENOMEM;
+                }
+
+                free(p);
+
+                if (!dbus_message_iter_close_container(&sub, &sub2))
+                        return -ENOMEM;
+        }
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
+        Seat *s;
+        char *id;
+
+        assert(m);
+        assert(path);
+        assert(_s);
+
+        if (!startswith(path, "/org/freedesktop/login1/seat/"))
+                return -EINVAL;
+
+        id = bus_path_unescape(path + 29);
+        if (!id)
+                return -ENOMEM;
+
+        s = hashmap_get(m->seats, id);
+        free(id);
+
+        if (!s)
+                return -ENOENT;
+
+        *_s = s;
+        return 0;
+}
+
+static DBusHandlerResult seat_message_dispatch(
+                Seat *s,
+                DBusConnection *connection,
+                DBusMessage *message) {
+
+        const BusProperty properties[] = {
+                { "org.freedesktop.login1.Seat", "Id",       bus_property_append_string, "s",     s->id },
+                { "org.freedesktop.login1.Seat", "Active",   bus_seat_append_active,     "(so)",  s     },
+                { "org.freedesktop.login1.Seat", "Sessions", bus_seat_append_sessions,   "a(so)", s     },
+                { NULL, NULL, NULL, NULL, NULL }
+        };
+
+        assert(s);
+        assert(connection);
+        assert(message);
+
+        return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+}
+
+static DBusHandlerResult seat_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        Manager *m = userdata;
+        Seat *s;
+        int r;
+
+        r = get_seat_for_path(m, dbus_message_get_path(message), &s);
+        if (r < 0) {
+
+                if (r == -ENOMEM)
+                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+                if (r == -ENOENT) {
+                        DBusError e;
+
+                        dbus_error_init(&e);
+                        dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown seat");
+                        return bus_send_error_reply(connection, message, &e, r);
+                }
+
+                return bus_send_error_reply(connection, message, NULL, r);
+        }
+
+        return seat_message_dispatch(s, connection, message);
+}
+
+const DBusObjectPathVTable bus_seat_vtable = {
+        .message_function = seat_message_handler
+};
+
+char *seat_bus_path(Seat *s) {
+        char *t, *r;
+
+        assert(s);
+
+        t = bus_path_escape(s->id);
+        if (!t)
+                return NULL;
+
+        r = strappend("/org/freedesktop/login1/seat/", t);
+        free(t);
+
+        return r;
+}
diff --git a/src/logind-seat.c b/src/logind-seat.c
index ae89ec9..2ba3060 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -182,7 +182,8 @@ static int vt_allocate(int vtnr) {
 }
 
 static int seat_preallocate_vts(Seat *s) {
-        int i, r = 0;
+        int r = 0;
+        unsigned i;
 
         assert(s);
         assert(s->manager);
@@ -295,6 +296,11 @@ int seat_read_active_vt(Seat *s) {
 int seat_start(Seat *s) {
         assert(s);
 
+        if (s->started)
+                return 0;
+
+        log_info("New seat %s.", s->id);
+
         /* Initialize VT magic stuff */
         seat_preallocate_vts(s);
 
@@ -304,6 +310,8 @@ int seat_start(Seat *s) {
         /* Save seat data */
         seat_save(s);
 
+        s->started = true;
+
         return 0;
 }
 
@@ -313,6 +321,11 @@ int seat_stop(Seat *s) {
 
         assert(s);
 
+        if (!s->started)
+                return 0;
+
+        log_info("Removed seat %s.", s->id);
+
         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
                 k = session_stop(session);
                 if (k < 0)
@@ -322,6 +335,8 @@ int seat_stop(Seat *s) {
         unlink(s->state_file);
         seat_add_to_gc_queue(s);
 
+        s->started = false;
+
         return r;
 }
 
@@ -343,3 +358,30 @@ void seat_add_to_gc_queue(Seat *s) {
         LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
         s->in_gc_queue = true;
 }
+
+static bool seat_name_valid_char(char c) {
+        return
+                (c >= 'a' && c <= 'z') ||
+                (c >= 'A' && c <= 'Z') ||
+                (c >= '0' && c <= '9') ||
+                c == '-' ||
+                c == '_';
+}
+
+bool seat_name_is_valid(const char *name) {
+        const char *p;
+
+        assert(name);
+
+        if (!startswith(name, "seat"))
+                return false;
+
+        if (!name[4])
+                return false;
+
+        for (p = name; *p; p++)
+                if (!seat_name_valid_char(*p))
+                        return false;
+
+        return true;
+}
diff --git a/src/logind-seat.h b/src/logind-seat.h
index b1a8d61..b045bde 100644
--- a/src/logind-seat.h
+++ b/src/logind-seat.h
@@ -42,6 +42,7 @@ struct Seat {
         LIST_HEAD(Session, sessions);
 
         bool in_gc_queue:1;
+        bool started:1;
 
         LIST_FIELDS(Seat, gc_queue);
 };
@@ -62,4 +63,9 @@ int seat_stop(Seat *s);
 int seat_check_gc(Seat *s);
 void seat_add_to_gc_queue(Seat *s);
 
+bool seat_name_is_valid(const char *name);
+char *seat_bus_path(Seat *s);
+
+extern const DBusObjectPathVTable bus_seat_vtable;
+
 #endif
diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c
new file mode 100644
index 0000000..41af658
--- /dev/null
+++ b/src/logind-session-dbus.c
@@ -0,0 +1,256 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "logind.h"
+#include "logind-session.h"
+#include "dbus-common.h"
+#include "util.h"
+
+#define BUS_SESSION_INTERFACE \
+        " <interface name=\"org.freedesktop.login1.Session\">\n"        \
+        "  <method name=\"Terminate\"/>\n"                              \
+        "  <method name=\"Activate\"/>\n"                               \
+        "  <property name=\"Id\" type=\"u\" access=\"read\"/>\n"        \
+        "  <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n"   \
+        "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"      \
+        "  <property name=\"ControlGroupPath\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n"      \
+        "  <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n"   \
+        "  <property name=\"TTY\" type=\"s\" access=\"read\"/>\n"       \
+        "  <property name=\"Display\" type=\"s\" access=\"read\"/>\n"   \
+        "  <property name=\"Remote\" type=\"b\" access=\"read\"/>\n"    \
+        "  <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"Leader\" type=\"u\" access=\"read\"/>\n"    \
+        "  <property name=\"Audit\" type=\"u\" access=\"read\"/>\n"     \
+        "  <property name=\"Type\" type=\"s\" access=\"read\"/>\n"      \
+        "  <property name=\"Active\" type=\"b\" access=\"read\"/>\n"    \
+        "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
+        "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
+        "  <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
+        " </interface>\n"
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        BUS_SESSION_INTERFACE                                           \
+        BUS_PROPERTIES_INTERFACE                                        \
+        BUS_PEER_INTERFACE                                              \
+        BUS_INTROSPECTABLE_INTERFACE                                    \
+        "</node>\n"
+
+#define INTERFACES_LIST                              \
+        BUS_GENERIC_INTERFACES_LIST                  \
+        "org.freedesktop.login1.Session\0"
+
+static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
+        DBusMessageIter sub;
+        Session *s = data;
+        const char *id, *path;
+        char *p = NULL;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
+                return -ENOMEM;
+
+        if (s->seat) {
+                id = s->seat->id;
+                path = p = seat_bus_path(s->seat);
+
+                if (!p)
+                        return -ENOMEM;
+        } else {
+                id = "";
+                path = "/";
+        }
+
+        if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
+                free(p);
+                return -ENOMEM;
+        }
+
+        free(p);
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
+        DBusMessageIter sub;
+        Session *s = data;
+        char *p = NULL;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
+                return -ENOMEM;
+
+        p = user_bus_path(s->user);
+        if (!p)
+                return -ENOMEM;
+
+        if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &s->user->uid) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
+                free(p);
+                return -ENOMEM;
+        }
+
+        free(p);
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
+        Session *s = data;
+        bool b;
+
+        assert(i);
+        assert(property);
+        assert(s);
+
+        b = session_is_active(s);
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+                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) {
+        Session *s;
+        char *id;
+
+        assert(m);
+        assert(path);
+        assert(_s);
+
+        if (!startswith(path, "/org/freedesktop/login1/session/"))
+                return -EINVAL;
+
+        id = bus_path_unescape(path + 32);
+        if (!id)
+                return -ENOMEM;
+
+        s = hashmap_get(m->sessions, id);
+        free(id);
+
+        if (!s)
+                return -ENOENT;
+
+        *_s = s;
+        return 0;
+}
+
+static DBusHandlerResult session_message_dispatch(
+                Session *s,
+                DBusConnection *connection,
+                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   },
+                { NULL, NULL, NULL, NULL, NULL }
+        };
+
+        assert(s);
+        assert(connection);
+        assert(message);
+
+        return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+}
+
+static DBusHandlerResult session_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        Manager *m = userdata;
+        Session *s;
+        int r;
+
+        r = get_session_for_path(m, dbus_message_get_path(message), &s);
+        if (r < 0) {
+
+                if (r == -ENOMEM)
+                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+                if (r == -ENOENT) {
+                        DBusError e;
+
+                        dbus_error_init(&e);
+                        dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
+                        return bus_send_error_reply(connection, message, &e, r);
+                }
+
+                return bus_send_error_reply(connection, message, NULL, r);
+        }
+
+        return session_message_dispatch(s, connection, message);
+}
+
+const DBusObjectPathVTable bus_session_vtable = {
+        .message_function = session_message_handler
+};
+
+char *session_bus_path(Session *s) {
+        char *t, *r;
+
+        assert(s);
+
+        t = bus_path_escape(s->id);
+        if (!t)
+                return NULL;
+
+        r = strappend("/org/freedesktop/login1/session/", t);
+        free(t);
+
+        return r;
+}
diff --git a/src/logind-session.c b/src/logind-session.c
index 8f1280d..6b3b277 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -83,6 +83,7 @@ void session_free(Session *s) {
         free(s->tty);
         free(s->display);
         free(s->remote_host);
+        free(s->remote_user);
 
         hashmap_remove(s->manager->sessions, s->id);
 
@@ -147,6 +148,11 @@ int session_save(Session *s) {
                         "REMOTE_HOST=%s\n",
                         s->remote_host);
 
+        if (s->remote_user)
+                fprintf(f,
+                        "REMOTE_USER=%s\n",
+                        s->remote_user);
+
         if (s->seat && s->seat->manager->vtconsole == s->seat)
                 fprintf(f,
                         "VTNR=%i\n",
@@ -495,7 +501,7 @@ void session_add_to_gc_queue(Session *s) {
 }
 
 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
-        [SESSION_TERMINAL] = "terminal",
+        [SESSION_TTY] = "tty",
         [SESSION_X11] = "x11"
 };
 
diff --git a/src/logind-session.h b/src/logind-session.h
index 4c6e768..60ac1c5 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -31,7 +31,7 @@ typedef struct Session Session;
 #include "logind-user.h"
 
 typedef enum SessionType {
-        SESSION_TERMINAL,
+        SESSION_TTY,
         SESSION_X11,
         _SESSION_TYPE_MAX,
         _SESSION_TYPE_INVALID = -1
@@ -53,20 +53,21 @@ struct Session {
         char *display;
 
         bool remote;
+        char *remote_user;
         char *remote_host;
 
         int vtnr;
         Seat *seat;
 
         pid_t leader;
-        uint64_t audit_id;
+        uint32_t audit_id;
 
         int pipe_fd;
 
         char *cgroup_path;
         char **controllers, **reset_controllers;
 
-        bool kill_processes:1;
+        bool kill_processes;
         bool in_gc_queue:1;
 
         LIST_FIELDS(Session, sessions_by_user);
@@ -86,6 +87,10 @@ int session_stop(Session *s);
 int session_save(Session *s);
 int session_load(Session *s);
 
+char *session_bus_path(Session *s);
+
+extern const DBusObjectPathVTable bus_session_vtable;
+
 const char* session_type_to_string(SessionType t);
 SessionType session_type_from_string(const char *s);
 
diff --git a/src/logind-user-dbus.c b/src/logind-user-dbus.c
new file mode 100644
index 0000000..7c8bb27
--- /dev/null
+++ b/src/logind-user-dbus.c
@@ -0,0 +1,240 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "logind.h"
+#include "logind-user.h"
+#include "dbus-common.h"
+
+#define BUS_USER_INTERFACE \
+        " <interface name=\"org.freedesktop.login1.User\">\n"           \
+        "  <method name=\"Terminate\"/>\n"                              \
+        "  <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=\"RuntimePath\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n"   \
+        "  <property name=\"ControlGroupPath\" 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" \
+        " </interface>\n"                                               \
+
+#define INTROSPECTION                                                   \
+        DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
+        "<node>\n"                                                      \
+        BUS_USER_INTERFACE                                              \
+        BUS_PROPERTIES_INTERFACE                                        \
+        BUS_PEER_INTERFACE                                              \
+        BUS_INTROSPECTABLE_INTERFACE                                    \
+        "</node>\n"
+
+#define INTERFACES_LIST                              \
+        BUS_GENERIC_INTERFACES_LIST                  \
+        "org.freedesktop.login1.User\0"
+
+static int bus_user_append_display(DBusMessageIter *i, const char *property, void *data) {
+        DBusMessageIter sub;
+        User *u = data;
+        const char *id, *path;
+        char *p = NULL;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
+                return -ENOMEM;
+
+        if (u->display) {
+                id = u->display->id;
+                path = p = session_bus_path(u->display);
+
+                if (!p)
+                        return -ENOMEM;
+        } else {
+                id = "";
+                path = "/";
+        }
+
+        if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
+                free(p);
+                return -ENOMEM;
+        }
+
+        free(p);
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_user_append_state(DBusMessageIter *i, const char *property, void *data) {
+        User *u = data;
+        const char *state;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        state = user_state_to_string(user_get_state(u));
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int bus_user_append_sessions(DBusMessageIter *i, const char *property, void *data) {
+        DBusMessageIter sub, sub2;
+        User *u = data;
+        Session *session;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "so", &sub))
+                return -ENOMEM;
+
+        LIST_FOREACH(sessions_by_user, session, u->sessions) {
+                char *p;
+
+                if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                        return -ENOMEM;
+
+                p = session_bus_path(session);
+                if (!p)
+                        return -ENOMEM;
+
+                if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
+                    !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+                        free(p);
+                        return -ENOMEM;
+                }
+
+                free(p);
+
+                if (!dbus_message_iter_close_container(&sub, &sub2))
+                        return -ENOMEM;
+        }
+
+        if (!dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
+static int get_user_for_path(Manager *m, const char *path, User **_u) {
+        User *u;
+        unsigned long lu;
+        int r;
+
+        assert(m);
+        assert(path);
+        assert(_u);
+
+        if (!startswith(path, "/org/freedesktop/login1/user/"))
+                return -EINVAL;
+
+        r = safe_atolu(path + 29, &lu);
+        if (r < 0)
+                return r;
+
+        u = hashmap_get(m->users, ULONG_TO_PTR(lu));
+        if (!u)
+                return -ENOENT;
+
+        *_u = u;
+        return 0;
+}
+
+static DBusHandlerResult user_message_dispatch(
+                User *u,
+                DBusConnection *connection,
+                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               },
+                { NULL, NULL, NULL, NULL, NULL }
+        };
+
+        assert(u);
+        assert(connection);
+        assert(message);
+
+        return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
+}
+
+static DBusHandlerResult user_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        Manager *m = userdata;
+        User *u;
+        int r;
+
+        r = get_user_for_path(m, dbus_message_get_path(message), &u);
+        if (r < 0) {
+
+                if (r == -ENOMEM)
+                        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+                if (r == -ENOENT) {
+                        DBusError e;
+
+                        dbus_error_init(&e);
+                        dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown user");
+                        return bus_send_error_reply(connection, message, &e, r);
+                }
+
+                return bus_send_error_reply(connection, message, NULL, r);
+        }
+
+        return user_message_dispatch(u, connection, message);
+}
+
+const DBusObjectPathVTable bus_user_vtable = {
+        .message_function = user_message_handler
+};
+
+char *user_bus_path(User *u) {
+        char *s;
+
+        assert(u);
+
+        if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
+                return NULL;
+
+        return s;
+}
diff --git a/src/logind-user.h b/src/logind-user.h
index fc48059..7f58aa2 100644
--- a/src/logind-user.h
+++ b/src/logind-user.h
@@ -70,6 +70,10 @@ UserState user_get_state(User *u);
 int user_save(User *u);
 int user_load(User *u);
 
+char *user_bus_path(User *s);
+
+extern const DBusObjectPathVTable bus_user_vtable;
+
 const char* user_state_to_string(UserState s);
 UserState user_state_from_string(const char *s);
 
diff --git a/src/logind.c b/src/logind.c
index 5d5181a..a628028 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -255,9 +255,15 @@ int manager_process_device(Manager *m, struct udev_device *d) {
 
         assert(m);
 
+        /* FIXME: drop this check as soon as libudev's enum support
+         * honours tags and subsystem matches at the same time */
+        if (!streq_ptr(udev_device_get_subsystem(d), "graphics"))
+                return 0;
+
         if (streq_ptr(udev_device_get_action(d), "remove")) {
 
-                device = hashmap_get(m->devices, udev_device_get_syspath(d));
+                /* FIXME: use syspath instead of sysname here, as soon as fb driver is fixed */
+                device = hashmap_get(m->devices, udev_device_get_sysname(d));
                 if (!device)
                         return 0;
 
@@ -268,14 +274,16 @@ int manager_process_device(Manager *m, struct udev_device *d) {
                 const char *sn;
                 Seat *seat;
 
-                sn = udev_device_get_property_value(d, "SEAT");
+                sn = udev_device_get_property_value(d, "ID_SEAT");
                 if (!sn)
                         sn = "seat0";
 
-                if (!startswith(sn, "seat"))
-                        return -EINVAL;
+                if (!seat_name_is_valid(sn)) {
+                        log_warning("Device with invalid seat name %s found, ignoring.", sn);
+                        return 0;
+                }
 
-                r = manager_add_device(m, udev_device_get_syspath(d), &device);
+                r = manager_add_device(m, udev_device_get_sysname(d), &device);
                 if (r < 0)
                         return r;
 
@@ -288,6 +296,7 @@ int manager_process_device(Manager *m, struct udev_device *d) {
                 }
 
                 device_attach(device, seat);
+                seat_start(seat);
         }
 
         return 0;
@@ -373,9 +382,6 @@ int manager_enumerate_seats(Manager *m) {
                 if (!dirent_is_file(de))
                         continue;
 
-                if (!startswith(de->d_name, "seat"))
-                        continue;
-
                 s = hashmap_get(m->seats, de->d_name);
                 if (!s) {
                         unlinkat(dirfd(d), de->d_name, 0);
@@ -676,27 +682,15 @@ int manager_spawn_autovt(Manager *m, int vtnr) {
         return 0;
 }
 
-static DBusHandlerResult login_message_handler(
-                DBusConnection *connection,
-                DBusMessage *message,
-                void *userdata) {
-
-        return DBUS_HANDLER_RESULT_HANDLED;
-}
-
 static DBusHandlerResult login_message_filter(
                 DBusConnection *connection,
                 DBusMessage *message,
                 void *userdata) {
 
-        return DBUS_HANDLER_RESULT_HANDLED;
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 static int manager_connect_bus(Manager *m) {
-        const DBusObjectPathVTable login_vtable = {
-                .message_function = login_message_handler
-        };
-
         DBusError error;
         int r;
         struct epoll_event ev;
@@ -714,13 +708,11 @@ static int manager_connect_bus(Manager *m) {
                 goto fail;
         }
 
-        if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &login_vtable, NULL)) {
-                log_error("Not enough memory");
-                r = -ENOMEM;
-                goto fail;
-        }
-
-        if (!dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) {
+        if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &bus_manager_vtable, m) ||
+            !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/seat", &bus_seat_vtable, m) ||
+            !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/session", &bus_session_vtable, m) ||
+            !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/user", &bus_user_vtable, m) ||
+            !dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) {
                 log_error("Not enough memory");
                 r = -ENOMEM;
                 goto fail;
@@ -931,6 +923,9 @@ int manager_run(Manager *m) {
 
                 n = epoll_wait(m->epoll_fd, &event, 1, -1);
                 if (n < 0) {
+                        if (errno == EINTR || errno == EAGAIN)
+                                continue;
+
                         log_error("epoll() failed: %m");
                         return -errno;
                 }
diff --git a/src/logind.h b/src/logind.h
index a8b387b..e4b7a3c 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -72,14 +72,14 @@ struct Manager {
         int bus_fd;
         int epoll_fd;
 
-        int n_autovts;
+        unsigned n_autovts;
 
         Seat *vtconsole;
 
         char *cgroup_path;
         char **controllers, **reset_controllers;
 
-        char **kill_only_users, **kill_exlude_users;
+        char **kill_only_users, **kill_exclude_users;
 
         bool kill_user_processes;
 };
@@ -111,4 +111,6 @@ void manager_gc(Manager *m);
 
 bool x11_display_is_local(const char *display);
 
+extern const DBusObjectPathVTable bus_manager_vtable;
+
 #endif

commit bd253d1b910a003c1d87f1f58a6afabd4f946ce5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu May 26 02:16:49 2011 +0200

    dbus: add api for append gid/uid properties

diff --git a/src/dbus-common.c b/src/dbus-common.c
index c2650fd..cb43bbd 100644
--- a/src/dbus-common.c
+++ b/src/dbus-common.c
@@ -503,7 +503,7 @@ int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *d
         assert(property);
         assert(data);
 
-        /* Let's ensure that pid_t is actually 64bit, and hence this
+        /* Let's ensure that usec_t is actually 64bit, and hence this
          * function can be used for usec_t */
         assert_cc(sizeof(uint64_t) == sizeof(usec_t));
 
@@ -518,11 +518,14 @@ int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *d
         assert(property);
         assert(data);
 
-        /* Let's ensure that pid_t and mode_t is actually 32bit, and
-         * hence this function can be used for pid_t/mode_t */
+        /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
+         * 32bit, and hence this function can be used for
+         * pid_t/mode_t/uid_t/gid_t */
         assert_cc(sizeof(uint32_t) == sizeof(pid_t));
         assert_cc(sizeof(uint32_t) == sizeof(mode_t));
         assert_cc(sizeof(uint32_t) == sizeof(unsigned));
+        assert_cc(sizeof(uint32_t) == sizeof(uid_t));
+        assert_cc(sizeof(uint32_t) == sizeof(gid_t));
 
         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
                 return -ENOMEM;
diff --git a/src/dbus-common.h b/src/dbus-common.h
index 1e5545f..e321a2c 100644
--- a/src/dbus-common.h
+++ b/src/dbus-common.h
@@ -129,6 +129,8 @@ int bus_property_append_long(DBusMessageIter *i, const char *property, void *dat
 
 #define bus_property_append_int bus_property_append_int32
 #define bus_property_append_pid bus_property_append_uint32
+#define bus_property_append_uid bus_property_append_uint32
+#define bus_property_append_gid bus_property_append_uint32
 #define bus_property_append_mode bus_property_append_uint32
 #define bus_property_append_unsigned bus_property_append_uint32
 #define bus_property_append_usec bus_property_append_uint64

commit c9f09cda7705a1ad583b0d9e95b5f1e1b091438d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu May 26 02:16:17 2011 +0200

    udev: simplify udev rules a bit

diff --git a/src/99-systemd.rules b/src/99-systemd.rules
index 186ef45..c079c35 100644
--- a/src/99-systemd.rules
+++ b/src/99-systemd.rules
@@ -5,7 +5,7 @@
 #  the Free Software Foundation; either version 2 of the License, or
 #  (at your option) any later version.
 
-ACTION!="add|change", GOTO="systemd_end"
+ACTION=="remove", GOTO="systemd_end"
 
 SUBSYSTEM=="tty", KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd"
 SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*", TAG+="systemd"

commit d2f92cdfd0189491387069da45734816effd8cbd
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed May 25 00:58:55 2011 +0200

    logind: unlink state files when stopping

diff --git a/src/logind-seat.c b/src/logind-seat.c
index 24a5d8f..ae89ec9 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -75,11 +75,7 @@ void seat_free(Seat *s) {
 
         hashmap_remove(s->manager->seats, s->id);
 
-        if (s->state_file) {
-                unlink(s->state_file);
-                free(s->state_file);
-        }
-
+        free(s->state_file);
         free(s);
 }
 
@@ -323,7 +319,7 @@ int seat_stop(Seat *s) {
                         r = k;
         }
 
-        seat_save(s);
+        unlink(s->state_file);
         seat_add_to_gc_queue(s);
 
         return r;
diff --git a/src/logind-session.c b/src/logind-session.c
index 566323d..8f1280d 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -86,11 +86,7 @@ void session_free(Session *s) {
 
         hashmap_remove(s->manager->sessions, s->id);
 
-        if (s->state_file) {
-                unlink(s->state_file);
-                free(s->state_file);
-        }
-
+        free(s->state_file);
         free(s);
 }
 
@@ -445,7 +441,8 @@ int session_stop(Session *s) {
         /* Remove X11 symlink */
         session_unlink_x11_socket(s);
 
-        session_save(s);
+        unlink(s->state_file);
+        session_add_to_gc_queue(s);
 
         return r;
 }
diff --git a/src/logind-user.c b/src/logind-user.c
index 19db746..7d6df8d 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -82,12 +82,7 @@ void user_free(User *u) {
         hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
 
         free(u->name);
-
-        if (u->state_file) {
-                unlink(u->state_file);
-                free(u->state_file);
-        }
-
+        free(u->state_file);
         free(u);
 }
 
@@ -385,6 +380,9 @@ int user_stop(User *u) {
         if (k < 0)
                 r = k;
 
+        unlink(u->state_file);
+        user_add_to_gc_queue(u);
+
         return r;
 }
 
diff --git a/src/logind.h b/src/logind.h
index 7a31216..a8b387b 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -39,6 +39,7 @@
  * PAM rewrite
  * spawn user systemd
  * dbus API
+ * don't allow everybody to take logind name
  *
  * non-local X11 server
  * reboot/shutdown halt management

commit 14c3baca3eb8b32fc266e46127851585bee5aff2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed May 25 00:55:58 2011 +0200

    logind: implement GC

diff --git a/src/logind-seat.c b/src/logind-seat.c
index 743ded6..24a5d8f 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -48,21 +48,23 @@ Seat *seat_new(Manager *m, const char *id) {
         }
 
         s->id = file_name_from_path(s->state_file);
+        s->manager = m;
 
         if (hashmap_put(m->seats, s->id, s) < 0) {
-                free(s->id);
+                free(s->state_file);
                 free(s);
                 return NULL;
         }
 
-        s->manager = m;
-
         return s;
 }
 
 void seat_free(Seat *s) {
         assert(s);
 
+        if (s->in_gc_queue)
+                LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s);
+
         while (s->sessions)
                 session_free(s->sessions);
 
@@ -73,25 +75,33 @@ void seat_free(Seat *s) {
 
         hashmap_remove(s->manager->seats, s->id);
 
-        free(s->state_file);
+        if (s->state_file) {
+                unlink(s->state_file);
+                free(s->state_file);
+        }
+
         free(s);
 }
 
 int seat_save(Seat *s) {
-        FILE *f;
         int r;
+        FILE *f;
+        char *temp_path;
 
         assert(s);
 
         r = safe_mkdir("/run/systemd/seat", 0755, 0, 0);
         if (r < 0)
-                return r;
+                goto finish;
+
+        r = fopen_temporary(s->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
 
-        f = fopen(s->state_file, "we");
-        if (!f)
-                return -errno;
+        fchmod(fileno(f), 0644);
 
         fprintf(f,
+                "# This is private data. Do not parse.\n"
                 "IS_VTCONSOLE=%i\n",
                 s->manager->vtconsole == s);
 
@@ -107,28 +117,45 @@ int seat_save(Seat *s) {
 
         if (s->sessions) {
                 Session *i;
-                fputs("OTHER_UIDS=", f);
 
+                fputs("OTHER=", f);
                 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
-                        assert(i->user);
+                        if (i == s->active)
+                                continue;
+
+                        fprintf(f,
+                                "%s%c",
+                                i->id,
+                                i->sessions_by_seat_next ? ' ' : '\n');
+                }
 
+                fputs("OTHER_UIDS=", f);
+                LIST_FOREACH(sessions_by_seat, i, s->sessions) {
                         if (i == s->active)
                                 continue;
 
                         fprintf(f,
-                                "%s%lu",
-                                i == s->sessions ? "" : " ",
-                                (unsigned long) i->user->uid);
+                                "%lu%c",
+                                (unsigned long) i->user->uid,
+                                i->sessions_by_seat_next ? ' ' : '\n');
                 }
         }
 
         fflush(f);
-        if (ferror(f)) {
+
+        if (ferror(f) || rename(temp_path, s->state_file) < 0) {
                 r = -errno;
                 unlink(s->state_file);
+                unlink(temp_path);
         }
 
         fclose(f);
+        free(temp_path);
+
+finish:
+        if (r < 0)
+                log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
+
         return r;
 }
 
@@ -158,7 +185,7 @@ static int vt_allocate(int vtnr) {
         return r;
 }
 
-int seat_preallocate_vts(Seat *s) {
+static int seat_preallocate_vts(Seat *s) {
         int i, r = 0;
 
         assert(s);
@@ -174,8 +201,10 @@ int seat_preallocate_vts(Seat *s) {
                 int q;
 
                 q = vt_allocate(i);
-                if (r >= 0 && q < 0)
+                if (q < 0) {
+                        log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
                         r = q;
+                }
         }
 
         return r;
@@ -199,44 +228,122 @@ int seat_apply_acls(Seat *s, Session *old_active) {
 }
 
 int seat_active_vt_changed(Seat *s, int vtnr) {
-        Session *i;
-        Session *old_active;
+        Session *i, *new_active = NULL, *old_active;
 
         assert(s);
         assert(vtnr >= 1);
-        assert(s->manager->vtconsole == s);
 
-        old_active = s->active;
-        s->active = NULL;
+        if (s->manager->vtconsole != s)
+                return -EINVAL;
+
+        log_debug("VT changed to %i", vtnr);
 
         LIST_FOREACH(sessions_by_seat, i, s->sessions)
                 if (i->vtnr == vtnr) {
-                        s->active = i;
+                        new_active = i;
                         break;
                 }
 
-        if (old_active == s->active)
+        if (new_active == s->active)
                 return 0;
 
+        old_active = s->active;
+        s->active = new_active;
+
         seat_apply_acls(s, old_active);
         manager_spawn_autovt(s->manager, vtnr);
 
         return 0;
 }
 
+int seat_read_active_vt(Seat *s) {
+        char t[64];
+        ssize_t k;
+        int r, vtnr;
+
+        assert(s);
+
+        if (s->manager->vtconsole != s)
+                return 0;
+
+        lseek(s->manager->console_active_fd, SEEK_SET, 0);
+
+        k = read(s->manager->console_active_fd, t, sizeof(t)-1);
+        if (k <= 0) {
+                log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
+                return k < 0 ? -errno : -EIO;
+        }
+
+        t[k] = 0;
+        truncate_nl(t);
+
+        if (!startswith(t, "tty")) {
+                log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
+                return -EIO;
+        }
+
+        r = safe_atoi(t+3, &vtnr);
+        if (r < 0) {
+                log_error("Failed to parse VT number %s", t+3);
+                return r;
+        }
+
+        if (vtnr <= 0) {
+                log_error("VT number invalid: %s", t+3);
+                return -EIO;
+        }
+
+        return seat_active_vt_changed(s, vtnr);
+}
+
+int seat_start(Seat *s) {
+        assert(s);
+
+        /* Initialize VT magic stuff */
+        seat_preallocate_vts(s);
+
+        /* Read current VT */
+        seat_read_active_vt(s);
+
+        /* Save seat data */
+        seat_save(s);
+
+        return 0;
+}
+
 int seat_stop(Seat *s) {
         Session *session;
-        int r = 0;
+        int r = 0, k;
 
         assert(s);
 
         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
-                int k;
-
                 k = session_stop(session);
                 if (k < 0)
                         r = k;
         }
 
+        seat_save(s);
+        seat_add_to_gc_queue(s);
+
         return r;
 }
+
+int seat_check_gc(Seat *s) {
+        assert(s);
+
+        if (s->manager->vtconsole == s)
+                return 1;
+
+        return !!s->devices;
+}
+
+void seat_add_to_gc_queue(Seat *s) {
+        assert(s);
+
+        if (s->in_gc_queue)
+                return;
+
+        LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
+        s->in_gc_queue = true;
+}
diff --git a/src/logind-seat.h b/src/logind-seat.h
index 4b6b340..b1a8d61 100644
--- a/src/logind-seat.h
+++ b/src/logind-seat.h
@@ -40,15 +40,26 @@ struct Seat {
 
         Session *active;
         LIST_HEAD(Session, sessions);
+
+        bool in_gc_queue:1;
+
+        LIST_FIELDS(Seat, gc_queue);
 };
 
 Seat *seat_new(Manager *m, const char *id);
 void seat_free(Seat *s);
-int seat_preallocate_vts(Seat *s);
-int seat_active_vt_changed(Seat *s, int vtnr);
-int seat_apply_acls(Seat *s, Session *old_active);
-int seat_stop(Seat *s);
+
 int seat_save(Seat *s);
 int seat_load(Seat *s);
 
+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_start(Seat *s);
+int seat_stop(Seat *s);
+
+int seat_check_gc(Seat *s);
+void seat_add_to_gc_queue(Seat *s);
+
 #endif
diff --git a/src/logind-session.c b/src/logind-session.c
index c10f5e6..566323d 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -34,7 +34,7 @@ Session* session_new(Manager *m, User *u, const char *id) {
         assert(m);
         assert(id);
 
-        s = new(Session, 1);
+        s = new0(Session, 1);
         if (!s)
                 return NULL;
 
@@ -56,7 +56,7 @@ Session* session_new(Manager *m, User *u, const char *id) {
         s->pipe_fd = -1;
         s->user = u;
 
-        dual_timestamp_get(&s->timestamp);
+        LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
 
         return s;
 }
@@ -64,6 +64,9 @@ Session* session_new(Manager *m, User *u, const char *id) {
 void session_free(Session *s) {
         assert(s);
 
+        if (s->in_gc_queue)
+                LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
+
         if (s->user) {
                 LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
 
@@ -83,26 +86,33 @@ void session_free(Session *s) {
 
         hashmap_remove(s->manager->sessions, s->id);
 
-        free(s->state_file);
+        if (s->state_file) {
+                unlink(s->state_file);
+                free(s->state_file);
+        }
+
         free(s);
 }
 
 int session_save(Session *s) {
         FILE *f;
         int r = 0;
+        char *temp_path;
 
         assert(s);
 
         r = safe_mkdir("/run/systemd/session", 0755, 0, 0);
         if (r < 0)
-                return r;
+                goto finish;
 
-        f = fopen(s->state_file, "we");
-        if (!f)
-                return -errno;
+        r = fopen_temporary(s->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
 
         assert(s->user);
 
+        fchmod(fileno(f), 0644);
+
         fprintf(f,
                 "# This is private data. Do not parse.\n"
                 "UID=%lu\n"
@@ -157,12 +167,20 @@ int session_save(Session *s) {
                         (unsigned long long) s->audit_id);
 
         fflush(f);
-        if (ferror(f)) {
+
+        if (ferror(f) || rename(temp_path, s->state_file) < 0) {
                 r = -errno;
                 unlink(s->state_file);
+                unlink(temp_path);
         }
 
         fclose(f);
+        free(temp_path);
+
+finish:
+        if (r < 0)
+                log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
+
         return r;
 }
 
@@ -196,10 +214,7 @@ int session_activate(Session *s) {
         old_active = s->seat->active;
         s->seat->active = s;
 
-        seat_apply_acls(s->seat, old_active);
-        manager_spawn_autovt(s->manager, s->vtnr);
-
-        return 0;
+        return seat_apply_acls(s->seat, old_active);
 }
 
 bool x11_display_is_local(const char *display) {
@@ -333,11 +348,6 @@ int session_start(Session *s) {
         assert(s);
         assert(s->user);
 
-        /* Create user first */
-        r = user_start(s->user);
-        if (r < 0)
-                return r;
-
         /* Create cgroup */
         r = session_create_cgroup(s);
         if (r < 0)
@@ -345,6 +355,12 @@ int session_start(Session *s) {
 
         /* Create X11 symlink */
         session_link_x11_socket(s);
+
+        /* Save session data */
+        session_save(s);
+
+        dual_timestamp_get(&s->timestamp);
+
         return 0;
 }
 
@@ -429,6 +445,8 @@ int session_stop(Session *s) {
         /* Remove X11 symlink */
         session_unlink_x11_socket(s);
 
+        session_save(s);
+
         return r;
 }
 
@@ -452,7 +470,7 @@ int session_check_gc(Session *s) {
                 if (r < 0)
                         return r;
 
-                if (r <= 0)
+                if (r == 0)
                         return 1;
         }
 
@@ -469,6 +487,16 @@ int session_check_gc(Session *s) {
         return 0;
 }
 
+void session_add_to_gc_queue(Session *s) {
+        assert(s);
+
+        if (s->in_gc_queue)
+                return;
+
+        LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
+        s->in_gc_queue = true;
+}
+
 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
         [SESSION_TERMINAL] = "terminal",
         [SESSION_X11] = "x11"
diff --git a/src/logind-session.h b/src/logind-session.h
index b3f0b5f..4c6e768 100644
--- a/src/logind-session.h
+++ b/src/logind-session.h
@@ -66,17 +66,21 @@ struct Session {
         char *cgroup_path;
         char **controllers, **reset_controllers;
 
-        bool kill_processes;
+        bool kill_processes:1;
+        bool in_gc_queue:1;
 
         LIST_FIELDS(Session, sessions_by_user);
         LIST_FIELDS(Session, sessions_by_seat);
+
+        LIST_FIELDS(Session, gc_queue);
 };
 
 Session *session_new(Manager *m, User *u, const char *id);
 void session_free(Session *s);
+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_check_gc(Session *s);
 int session_start(Session *s);
 int session_stop(Session *s);
 int session_save(Session *s);
diff --git a/src/logind-user.c b/src/logind-user.c
index e0a3842..19db746 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -35,7 +35,7 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
         assert(m);
         assert(name);
 
-        u = new(User, 1);
+        u = new0(User, 1);
         if (!u)
                 return NULL;
 
@@ -68,6 +68,9 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
 void user_free(User *u) {
         assert(u);
 
+        if (u->in_gc_queue)
+                LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
+
         while (u->sessions)
                 session_free(u->sessions);
 
@@ -79,26 +82,35 @@ void user_free(User *u) {
         hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
 
         free(u->name);
-        free(u->state_file);
+
+        if (u->state_file) {
+                unlink(u->state_file);
+                free(u->state_file);
+        }
+
         free(u);
 }
 
 int user_save(User *u) {
         FILE *f;
         int r;
+        char *temp_path;
 
         assert(u);
         assert(u->state_file);
 
         r = safe_mkdir("/run/systemd/user", 0755, 0, 0);
         if (r < 0)
-                return r;
+                goto finish;
 
-        f = fopen(u->state_file, "we");
-        if (!f)
-                return -errno;
+        r = fopen_temporary(u->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fchmod(fileno(f), 0644);
 
         fprintf(f,
+                "# This is private data. Do not parse.\n"
                 "NAME=%s\n"
                 "STATE=%s\n",
                 u->name,
@@ -125,12 +137,20 @@ int user_save(User *u) {
                         u->display->id);
 
         fflush(f);
-        if (ferror(f)) {
+
+        if (ferror(f) || rename(temp_path, u->state_file) < 0) {
                 r = -errno;
                 unlink(u->state_file);
+                unlink(temp_path);
         }
 
         fclose(f);
+        free(temp_path);
+
+finish:
+        if (r < 0)
+                log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
+
         return r;
 }
 
@@ -260,6 +280,9 @@ int user_start(User *u) {
         if (r < 0)
                 return r;
 
+        /* Save new user data */
+        user_save(u);
+
         dual_timestamp_get(&u->timestamp);
 
         return 0;
@@ -395,6 +418,16 @@ int user_check_gc(User *u) {
         return 0;
 }
 
+void user_add_to_gc_queue(User *u) {
+        assert(u);
+
+        if (u->in_gc_queue)
+                return;
+
+        LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
+        u->in_gc_queue = true;
+}
+
 UserState user_get_state(User *u) {
         Session *i;
 
diff --git a/src/logind-user.h b/src/logind-user.h
index 04392e0..fc48059 100644
--- a/src/logind-user.h
+++ b/src/logind-user.h
@@ -54,14 +54,18 @@ struct User {
 
         dual_timestamp timestamp;
 
+        bool in_gc_queue:1;
+
         LIST_HEAD(Session, sessions);
+        LIST_FIELDS(User, gc_queue);
 };
 
 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name);
 void user_free(User *u);
+int user_check_gc(User *u);
+void user_add_to_gc_queue(User *u);
 int user_start(User *u);
 int user_stop(User *u);
-int user_check_gc(User *u);
 UserState user_get_state(User *u);
 int user_save(User *u);
 int user_load(User *u);
diff --git a/src/logind.c b/src/logind.c
index 7e9b706..5d5181a 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -200,7 +200,6 @@ int manager_add_session(Manager *m, User *u, const char *id, Session **_session)
 
 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
         User *u;
-        int r;
 
         assert(m);
         assert(name);
@@ -217,16 +216,10 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
         if (!u)
                 return -ENOMEM;
 
-        r = user_start(u);
-        if (r < 0) {
-                user_stop(u);
-                user_free(u);
-        } else {
-                if (_user)
-                        *_user = u;
-        }
+        if (_user)
+                *_user = u;
 
-        return r;
+        return 0;
 }
 
 int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
@@ -257,33 +250,53 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
 }
 
 int manager_process_device(Manager *m, struct udev_device *d) {
-        const char *sn;
-        Seat *seat;
         Device *device;
         int r;
 
         assert(m);
 
-        sn = udev_device_get_property_value(d, "SEAT");
-        if (!sn)
-                sn = "seat0";
+        if (streq_ptr(udev_device_get_action(d), "remove")) {
 
-        r = manager_add_device(m, udev_device_get_syspath(d), &device);
-        if (r < 0)
-                return r;
+                device = hashmap_get(m->devices, udev_device_get_syspath(d));
+                if (!device)
+                        return 0;
 
-        r = manager_add_seat(m, sn, &seat);
-        if (r < 0)
-                return r;
+                seat_add_to_gc_queue(device->seat);
+                device_free(device);
+
+        } else {
+                const char *sn;
+                Seat *seat;
+
+                sn = udev_device_get_property_value(d, "SEAT");
+                if (!sn)
+                        sn = "seat0";
+
+                if (!startswith(sn, "seat"))
+                        return -EINVAL;
+
+                r = manager_add_device(m, udev_device_get_syspath(d), &device);
+                if (r < 0)
+                        return r;
+
+                r = manager_add_seat(m, sn, &seat);
+                if (r < 0) {
+                        if (!device->seat)
+                                device_free(device);
+
+                        return r;
+                }
+
+                device_attach(device, seat);
+        }
 
-        device_attach(device, seat);
         return 0;
 }
 
 int manager_enumerate_devices(Manager *m) {
         struct udev_list_entry *item = NULL, *first = NULL;
         struct udev_enumerate *e;
-        int r = -ENOMEM;
+        int r;
 
         assert(m);
 
@@ -291,16 +304,21 @@ int manager_enumerate_devices(Manager *m) {
          * necessary */
 
         e = udev_enumerate_new(m->udev);
-        if (!e)
+        if (!e) {
+                r = -ENOMEM;
                 goto finish;
+        }
 
-        if (udev_enumerate_add_match_subsystem(e, "graphics") < 0)
+        r = udev_enumerate_add_match_subsystem(e, "graphics");
+        if (r < 0)
                 goto finish;
 
-        if (udev_enumerate_add_match_tag(e, "seat") < 0)
+        r = udev_enumerate_add_match_tag(e, "seat");
+        if (r < 0)
                 goto finish;
 
-        if (udev_enumerate_scan_devices(e) < 0)
+        r = udev_enumerate_scan_devices(e);
+        if (r < 0)
                 goto finish;
 
         first = udev_enumerate_get_list_entry(e);
@@ -309,8 +327,10 @@ int manager_enumerate_devices(Manager *m) {
                 int k;
 
                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
-                if (!d)
+                if (!d) {
+                        r = -ENOMEM;
                         goto finish;
+                }
 
                 k = manager_process_device(m, d);
                 udev_device_unref(d);
@@ -353,6 +373,9 @@ int manager_enumerate_seats(Manager *m) {
                 if (!dirent_is_file(de))
                         continue;
 
+                if (!startswith(de->d_name, "seat"))
+                        continue;
+
                 s = hashmap_get(m->seats, de->d_name);
                 if (!s) {
                         unlinkat(dirfd(d), de->d_name, 0);
@@ -385,24 +408,61 @@ static int manager_enumerate_users_from_cgroup(Manager *m) {
 
         while ((r = cg_read_subgroup(d, &name)) > 0) {
                 User *user;
+                int k;
+
+                k = manager_add_user_by_name(m, name, &user);
+                if (k < 0) {
+                        free(name);
+                        r = k;
+                        continue;
+                }
+
+                user_add_to_gc_queue(user);
+
+                if (!user->cgroup_path)
+                        if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) {
+                                r = -ENOMEM;
+                                free(name);
+                                break;
+                        }
 
-                r = manager_add_user_by_name(m, name, &user);
                 free(name);
+        }
+
+        closedir(d);
+
+        return r;
+}
 
-                if (r < 0)
-                        break;
 
-                if (user->cgroup_path)
+static int manager_enumerate_linger_users(Manager *m) {
+        DIR *d;
+        struct dirent *de;
+        int r = 0;
+
+        d = opendir("/var/lib/systemd/linger");
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open /var/lib/systemd/linger/: %m");
+                return -errno;
+        }
+
+        while ((de = readdir(d))) {
+                int k;
+
+                if (!dirent_is_file(de))
                         continue;
 
-                if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) {
-                        r = -ENOMEM;
-                        break;
+                k = manager_add_user_by_name(m, de->d_name, NULL);
+                if (k < 0) {
+                        log_notice("Couldn't add lingering user %s: %s", de->d_name, strerror(-k));
+                        r = k;
                 }
         }
 
-        if (d)
-                closedir(d);
+        closedir(d);
 
         return r;
 }
@@ -410,12 +470,19 @@ static int manager_enumerate_users_from_cgroup(Manager *m) {
 int manager_enumerate_users(Manager *m) {
         DIR *d;
         struct dirent *de;
-        int r;
+        int r, k;
 
         assert(m);
 
+        /* First, enumerate user cgroups */
         r = manager_enumerate_users_from_cgroup(m);
 
+        /* Second, add lingering users on top */
+        k = manager_enumerate_linger_users(m);
+        if (k < 0)
+                r = k;
+
+        /* Third, read in user data stored on disk */
         d = opendir("/run/systemd/users");
         if (!d) {
                 if (errno == ENOENT)
@@ -428,7 +495,6 @@ int manager_enumerate_users(Manager *m) {
         while ((de = readdir(d))) {
                 unsigned long ul;
                 User *u;
-                int k;
 
                 if (!dirent_is_file(de))
                         continue;
@@ -465,6 +531,9 @@ static int manager_enumerate_sessions_from_cgroup(Manager *m) {
                 char *name;
                 int k;
 
+                if (!u->cgroup_path)
+                        continue;
+
                 k = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &d);
                 if (k < 0) {
                         if (k == -ENOENT)
@@ -479,18 +548,21 @@ static int manager_enumerate_sessions_from_cgroup(Manager *m) {
                         Session *session;
 
                         k = manager_add_session(m, u, name, &session);
-                        free(name);
-
-                        if (k < 0)
+                        if (k < 0) {
+                                free(name);
                                 break;
+                        }
 
-                        if (session->cgroup_path)
-                                continue;
+                        session_add_to_gc_queue(session);
 
-                        if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) {
-                                k = -ENOMEM;
-                                break;
-                        }
+                        if (!session->cgroup_path)
+                                if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) {
+                                        k = -ENOMEM;
+                                        free(name);
+                                        break;
+                                }
+
+                        free(name);
                 }
 
                 closedir(d);
@@ -509,8 +581,10 @@ int manager_enumerate_sessions(Manager *m) {
 
         assert(m);
 
+        /* First enumerate session cgroups */
         r = manager_enumerate_sessions_from_cgroup(m);
 
+        /* Second, read in session data stored on disk */
         d = opendir("/run/systemd/sessions");
         if (!d) {
                 if (errno == ENOENT)
@@ -543,38 +617,6 @@ int manager_enumerate_sessions(Manager *m) {
         return r;
 }
 
-int manager_start_linger_users(Manager *m) {
-        DIR *d;
-        struct dirent *de;
-        int r = 0;
-
-        d = opendir("/var/lib/systemd/linger");
-        if (!d) {
-                if (errno == ENOENT)
-                        return 0;
-
-                log_error("Failed to open /var/lib/systemd/linger/: %m");
-                return -errno;
-        }
-
-        while ((de = readdir(d))) {
-                int k;
-
-                if (!dirent_is_file(de))
-                        continue;
-
-                k = manager_add_user_by_name(m, de->d_name, NULL);
-                if (k < 0) {
-                        log_notice("Couldn't add lingering user %s: %s", de->d_name, strerror(-k));
-                        r = k;
-                }
-        }
-
-        closedir(d);
-
-        return r;
-}
-
 int manager_dispatch_udev(Manager *m) {
         struct udev_device *d;
         int r;
@@ -592,39 +634,10 @@ int manager_dispatch_udev(Manager *m) {
 }
 
 int manager_dispatch_console(Manager *m) {
-        char t[64];
-        ssize_t k;
-        int r, vtnr;
-
         assert(m);
 
-        lseek(m->console_active_fd, SEEK_SET, 0);
-
-        k = read(m->console_active_fd, t, sizeof(t)-1);
-        if (k <= 0) {
-                log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
-                return k < 0 ? -errno : -EIO;
-        }
-
-        t[k] = 0;
-        if (!startswith(t, "tty")) {
-                log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
-                return -EIO;
-        }
-
-        r = safe_atoi(t+3, &vtnr);
-        if (r < 0) {
-                log_error("Failed to parse VT number %s", t+3);
-                return r;
-        }
-
-        if (vtnr <= 0) {
-                log_error("VT number invalid: %s", t+3);
-                return -EIO;
-        }
-
         if (m->vtconsole)
-                seat_active_vt_changed(m->vtconsole, vtnr);
+                seat_read_active_vt(m->vtconsole);
 
         return 0;
 }
@@ -766,7 +779,7 @@ static int manager_connect_console(Manager *m) {
         }
 
         zero(ev);
-        ev.events = EPOLLIN;
+        ev.events = 0;
         ev.data.u32 = FD_CONSOLE;
 
         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->console_active_fd, &ev) < 0)
@@ -777,6 +790,7 @@ static int manager_connect_console(Manager *m) {
 
 static int manager_connect_udev(Manager *m) {
         struct epoll_event ev;
+        int r;
 
         assert(m);
         assert(!m->udev_monitor);
@@ -785,13 +799,17 @@ static int manager_connect_udev(Manager *m) {
         if (!m->udev_monitor)
                 return -ENOMEM;
 
-        udev_monitor_filter_add_match_tag(m->udev_monitor, "seat");
+        r = udev_monitor_filter_add_match_tag(m->udev_monitor, "seat");
+        if (r < 0)
+                return r;
 
-        if (udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "graphics", NULL) < 0)
-                return -ENOMEM;
+        r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "graphics", NULL);
+        if (r < 0)
+                return r;
 
-        if (udev_monitor_enable_receiving(m->udev_monitor) < 0)
-                return -ENOMEM;
+        r = udev_monitor_enable_receiving(m->udev_monitor);
+        if (r < 0)
+                return r;
 
         m->udev_fd = udev_monitor_get_fd(m->udev_monitor);
 
@@ -805,8 +823,50 @@ static int manager_connect_udev(Manager *m) {
         return 0;
 }
 
+void manager_gc(Manager *m) {
+        Seat *seat;
+        Session *session;
+        User *user;
+
+        assert(m);
+
+        while ((seat = m->seat_gc_queue)) {
+                LIST_REMOVE(Seat, gc_queue, m->seat_gc_queue, seat);
+                seat->in_gc_queue = false;
+
+                if (seat_check_gc(seat) == 0) {
+                        seat_stop(seat);
+                        seat_free(seat);
+                }
+        }
+
+        while ((session = m->session_gc_queue)) {
+                LIST_REMOVE(Session, gc_queue, m->session_gc_queue, session);
+                session->in_gc_queue = false;
+
+                if (session_check_gc(session) == 0) {
+                        session_stop(session);
+                        session_free(session);
+                }
+        }
+
+        while ((user = m->user_gc_queue)) {
+                LIST_REMOVE(User, gc_queue, m->user_gc_queue, user);
+                user->in_gc_queue = false;
+
+                if (user_check_gc(user) == 0) {
+                        user_stop(user);
+                        user_free(user);
+                }
+        }
+}
+
 int manager_startup(Manager *m) {
         int r;
+        Seat *seat;
+        Session *session;
+        User *user;
+        Iterator i;
 
         assert(m);
         assert(m->epoll_fd <= 0);
@@ -830,14 +890,29 @@ int manager_startup(Manager *m) {
         if (r < 0)
                 return r;
 
+        /* Instantiate magic seat 0 */
+        r = manager_add_seat(m, "seat0", &m->vtconsole);
+        if (r < 0)
+                return r;
+
         /* Deserialize state */
         manager_enumerate_devices(m);
         manager_enumerate_seats(m);
         manager_enumerate_users(m);
         manager_enumerate_sessions(m);
 
-        /* Spawn lingering users */
-        manager_start_linger_users(m);
+        /* Get rid of objects that are no longer used */
+        manager_gc(m);
+
+        /* And start everything */
+        HASHMAP_FOREACH(seat, m->seats, i)
+                seat_start(seat);
+
+        HASHMAP_FOREACH(user, m->users, i)
+                user_start(user);
+
+        HASHMAP_FOREACH(session, m->sessions, i)
+                session_start(session);
 
         return 0;
 }
@@ -849,6 +924,8 @@ int manager_run(Manager *m) {
                 struct epoll_event event;
                 int n;
 
+                manager_gc(m);
+
                 if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
                         continue;
 
diff --git a/src/logind.h b/src/logind.h
index be5dab7..7a31216 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -59,6 +59,10 @@ struct Manager {
         Hashmap *sessions;
         Hashmap *users;
 
+        LIST_HEAD(Seat, seat_gc_queue);
+        LIST_HEAD(Session, session_gc_queue);
+        LIST_HEAD(User, user_gc_queue);
+
         struct udev *udev;
         struct udev_monitor *udev_monitor;
 
@@ -81,25 +85,29 @@ struct Manager {
 
 Manager *manager_new(void);
 void manager_free(Manager *m);
+
 int manager_add_device(Manager *m, const char *sysfs, Device **_device);
 int manager_add_seat(Manager *m, const char *id, Seat **_seat);
 int manager_add_session(Manager *m, User *u, const char *id, Session **_session);
 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
 int manager_add_user_by_name(Manager *m, const char *name, User **_user);
 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
+
 int manager_process_device(Manager *m, struct udev_device *d);
 int manager_dispatch_udev(Manager *m);
 int manager_dispatch_console(Manager *m);
+
 int manager_enumerate_devices(Manager *m);
 int manager_enumerate_seats(Manager *m);
 int manager_enumerate_sessions(Manager *m);
 int manager_enumerate_users(Manager *m);
-int manager_start_one_linger_user(Manager *m, const char *user);
-int manager_start_linger_users(Manager *m);
+
 int manager_startup(Manager *m);
 int manager_run(Manager *m);
 int manager_spawn_autovt(Manager *m, int vtnr);
 
+void manager_gc(Manager *m);
+
 bool x11_display_is_local(const char *display);
 
 #endif
diff --git a/src/org.freedesktop.login1.conf b/src/org.freedesktop.login1.conf
new file mode 100644
index 0000000..20008ea
--- /dev/null
+++ b/src/org.freedesktop.login1.conf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!--
+  This file is part of systemd.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+-->
+
+<busconfig>
+
+        <policy user="root">
+                <allow own="org.freedesktop.login1"/>
+                <allow send_destination="org.freedesktop.login1"/>
+                <allow receive_sender="org.freedesktop.login1"/>
+        </policy>
+
+        <policy context="default">
+                <allow own="org.freedesktop.login1"/>
+                <allow send_destination="org.freedesktop.login1"/>
+                <allow receive_sender="org.freedesktop.login1"/>
+        </policy>
+
+</busconfig>

commit 5a3ab509b56bd79bf2be53cc259ce45c0be269e6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed May 25 00:54:32 2011 +0200

    util: add fopen_temporary()

diff --git a/src/util.c b/src/util.c
index ad1ca06..156d32a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4455,7 +4455,49 @@ int pipe_eof(int fd) {
         return pollfd.revents & POLLHUP;
 }
 
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+        FILE *f;
+        char *t;
+        const char *fn;
+        size_t k;
+        int fd;
+
+        assert(path);
+        assert(_f);
+        assert(_temp_path);
+
+        t = new(char, strlen(path) + 1 + 6 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        fn = file_name_from_path(path);
+        k = fn-path;
+        memcpy(t, path, k);
+        t[k] = '.';
+        stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
+
+        fd = mkostemp(t, O_WRONLY|O_CLOEXEC);
+        if (fd < 0) {
+                free(t);
+                return -errno;
+        }
+
+        f = fdopen(fd, "we");
+        if (!f) {
+                unlink(t);
+                free(t);
+                return -errno;
+        }
+
+        *_f = f;
+        *_temp_path = t;
+
+        return 0;
+}
+
 int terminal_vhangup_fd(int fd) {
+        assert(fd >= 0);
+
         if (ioctl(fd, TIOCVHANGUP) < 0)
                 return -errno;
 
diff --git a/src/util.h b/src/util.h
index 6076e69..15dfe17 100644
--- a/src/util.h
+++ b/src/util.h
@@ -330,6 +330,7 @@ int default_signals(int sig, ...);
 int sigaction_many(const struct sigaction *sa, ...);
 
 int close_pipe(int p[]);
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
 
 ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
 ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);

commit 5eda94dda25bccda928c4b33c790dbe748573a22
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue May 24 04:20:35 2011 +0200

    logind: implement ACL management

diff --git a/.gitignore b/.gitignore
index c2820b0..7bd22c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-uaccess
 systemd-logind
 systemd-hostnamed
 systemd-binfmt
diff --git a/Makefile.am b/Makefile.am
index 79ccbbe..f455f21 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -159,7 +159,8 @@ rootlibexec_PROGRAMS = \
 	systemd-detect-virt \
 	systemd-sysctl \
         systemd-hostnamed \
-        systemd-logind
+        systemd-logind \
+        systemd-uaccess
 
 if ENABLE_BINFMT
 rootlibexec_PROGRAMS += \
@@ -813,6 +814,7 @@ systemd_logind_SOURCES = \
         src/logind-seat.c \
         src/logind-session.c \
         src/logind-user.c \
+        src/logind-acl.c \
         src/dbus-common.c \
         src/dbus-loop.c \
         src/cgroup-util.c
@@ -820,13 +822,29 @@ systemd_logind_SOURCES = \
 systemd_logind_CFLAGS = \
 	$(AM_CFLAGS) \
 	$(DBUS_CFLAGS) \
-        $(UDEV_CFLAGS)
+        $(UDEV_CFLAGS) \
+        $(ACL_CFLAGS)
 
 systemd_logind_LDADD = \
 	libsystemd-basic.la \
 	libsystemd-daemon.la \
 	$(DBUS_LIBS) \
-        $(UDEV_LIBS)
+        $(UDEV_LIBS) \
+        $(ACL_LIBS)
+
+systemd_uaccess_SOURCES = \
+	src/uaccess.c \
+        src/logind-acl.c
+
+systemd_uaccess_CFLAGS = \
+	$(AM_CFLAGS) \
+        $(UDEV_CFLAGS) \
+        $(ACL_CFLAGS)
+
+systemd_uaccess_LDADD = \
+	libsystemd-basic.la \
+        $(UDEV_LIBS) \
+        $(ACL_LIBS)
 
 systemd_shutdown_SOURCES = \
 	src/mount-setup.c \
diff --git a/configure.ac b/configure.ac
index eb5fb6a..0dd185b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -193,6 +193,43 @@ fi
 AC_SUBST(PAM_LIBS)
 AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno])
 
+AC_ARG_ENABLE([acl],
+        AS_HELP_STRING([--disable-acl],[Disable optional ACL support]),
+                [case "${enableval}" in
+                        yes) have_acl=yes ;;
+                        no) have_acl=no ;;
+                        *) AC_MSG_ERROR(bad value ${enableval} for --disable-acl) ;;
+                esac],
+                [have_acl=auto])
+
+if test "x${have_acl}" != xno ; then
+        AC_CHECK_HEADERS(
+                [sys/acl.h acl/libacl.h],
+                [have_acl=yes],
+                [if test "x$have_acl" = xyes ; then
+                        AC_MSG_ERROR([*** ACL headers not found.])
+                fi])
+
+        AC_CHECK_LIB(
+                [acl],
+                [acl_get_file],
+                [have_acl=yes],
+                [if test "x$have_acl" = xyes ; then
+                        AC_MSG_ERROR([*** libacl not found.])
+                fi])
+
+        if test "x$have_acl" = xyes ; then
+                ACL_LIBS="-lacl"
+                AC_DEFINE(HAVE_ACL, 1, [ACL available])
+        else
+                have_acl=no
+        fi
+else
+        ACL_LIBS=
+fi
+AC_SUBST(ACL_LIBS)
+AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno])
+
 AC_ARG_ENABLE([audit],
         AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]),
                 [case "${enableval}" in
@@ -496,6 +533,7 @@ echo "
         PAM:                     ${have_pam}
         AUDIT:                   ${have_audit}
         SELinux:                 ${have_selinux}
+        ACL:                     ${have_acl}
         binfmt:                  ${have_binfmt}
         prefix:                  ${prefix}
         root dir:                ${with_rootdir}
diff --git a/src/logind-acl.c b/src/logind-acl.c
new file mode 100644
index 0000000..3df104f
--- /dev/null
+++ b/src/logind-acl.c
@@ -0,0 +1,282 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "logind-acl.h"
+#include "util.h"
+
+static int find_acl(acl_t acl, uid_t uid, acl_entry_t *entry) {
+        acl_entry_t i;
+        int found;
+
+        assert(acl);
+        assert(entry);
+
+        for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+             found > 0;
+             found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+
+                acl_tag_t tag;
+                uid_t *u;
+                bool b;
+
+                if (acl_get_tag_type(i, &tag) < 0)
+                        return -errno;
+
+                if (tag != ACL_USER)
+                        continue;
+
+                u = acl_get_qualifier(i);
+                if (!u)
+                        return -errno;
+
+                b = *u == uid;
+                free(u);
+
+                if (b) {
+                        *entry = i;
+                        return 1;
+                }
+        }
+
+        if (found < 0)
+                return -errno;
+
+        return 0;
+}
+
+static int flush_acl(acl_t acl) {
+        acl_entry_t i;
+        int found;
+        bool changed = false;
+
+        assert(acl);
+
+        for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+             found > 0;
+             found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+
+                acl_tag_t tag;
+
+                if (acl_get_tag_type(i, &tag) < 0)
+                        return -errno;
+
+                if (tag != ACL_USER)
+                        continue;
+
+                if (acl_delete_entry(acl, i) < 0)
+                        return -errno;
+
+                changed = true;
+        }
+
+        if (found < 0)
+                return -errno;
+
+        return changed;
+}
+
+int devnode_acl(const char *path,
+                bool flush,
+                bool del, uid_t old_uid,
+                bool add, uid_t new_uid) {
+
+        acl_t acl;
+        int r;
+        bool changed = false;
+
+        assert(path);
+
+        acl = acl_get_file(path, ACL_TYPE_ACCESS);
+        if (!acl)
+                return -errno;
+
+        if (flush) {
+
+                r = flush_acl(acl);
+                if (r < 0)
+                        goto finish;
+                if (r > 0)
+                        changed = true;
+
+        } else if (del && old_uid > 0) {
+                acl_entry_t entry;
+
+                r = find_acl(acl, old_uid, &entry);
+                if (r < 0)
+                        goto finish;
+
+                if (r > 0) {
+                        if (acl_delete_entry(acl, entry) < 0) {
+                                r = -errno;
+                                goto finish;
+                        }
+
+                        changed = true;
+                }
+        }
+
+        if (add && new_uid > 0) {
+                acl_entry_t entry;
+                acl_permset_t permset;
+                int rd, wt;
+
+                r = find_acl(acl, new_uid, &entry);
+                if (r < 0)
+                        goto finish;
+
+                if (r == 0) {
+                        if (acl_create_entry(&acl, &entry) < 0) {
+                                r = -errno;
+                                goto finish;
+                        }
+
+                        if (acl_set_tag_type(entry, ACL_USER) < 0 ||
+                            acl_set_qualifier(entry, &new_uid) < 0) {
+                                r = -errno;
+                                goto finish;
+                        }
+                }
+
+                if (acl_get_permset(entry, &permset) < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                rd = acl_get_perm(permset, ACL_READ);
+                if (rd < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                wt = acl_get_perm(permset, ACL_WRITE);
+                if (wt < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                if (!rd || !wt) {
+
+                        if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) {
+                                r = -errno;
+                                goto finish;
+                        }
+
+                        changed = true;
+                }
+        }
+
+        if (!changed)
+                goto finish;
+
+        if (acl_calc_mask(&acl) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        acl_free(acl);
+
+        return r;
+}
+
+int devnode_acl_all(struct udev *udev,
+                    const char *seat,
+                    bool flush,
+                    bool del, uid_t old_uid,
+                    bool add, uid_t new_uid) {
+
+        struct udev_list_entry *item = NULL, *first = NULL;
+        struct udev_enumerate *e;
+        int r;
+
+        assert(udev);
+
+        if (!seat)
+                seat = "seat0";
+
+        e = udev_enumerate_new(udev);
+        if (!e)
+                return -ENOMEM;
+
+        r = udev_enumerate_add_match_tag(e, "uaccess");
+        if (r < 0)
+                goto finish;
+
+        r = udev_enumerate_add_match_tag(e, seat);
+        if (r < 0)
+                goto finish;
+
+        r = udev_enumerate_scan_devices(e);
+        if (r < 0)
+                goto finish;
+
+        first = udev_enumerate_get_list_entry(e);
+        udev_list_entry_foreach(item, first) {
+                struct udev_device *d;
+                const char *node, *sn;
+
+                d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+                if (!d) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                sn = udev_device_get_property_value(d, "SEAT");
+                if (!sn)
+                        sn = "seat0";
+
+                if (!streq(seat, sn)) {
+                        udev_device_unref(d);
+                        continue;
+                }
+
+                node = udev_device_get_devnode(d);
+                udev_device_unref(d);
+
+                if (!node) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                r = devnode_acl(node, flush, del, old_uid, add, new_uid);
+                if (r < 0)
+                        goto finish;
+        }
+
+finish:
+        if (e)
+                udev_enumerate_unref(e);
+
+        return r;
+}
diff --git a/src/logind-acl.h b/src/logind-acl.h
new file mode 100644
index 0000000..9c88a80
--- /dev/null
+++ b/src/logind-acl.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologindaclhfoo
+#define foologindaclhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <libudev.h>
+
+int devnode_acl(const char *path,
+                bool flush,
+                bool del, uid_t old_uid,
+                bool add, uid_t new_uid);
+
+int devnode_acl_all(struct udev *udev,
+                    const char *seat,
+                    bool flush,
+                    bool del, uid_t old_uid,
+                    bool add, uid_t new_uid);
+
+#endif
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 3154900..743ded6 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -25,8 +25,10 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <linux/vt.h>
+#include <string.h>
 
 #include "logind-seat.h"
+#include "logind-acl.h"
 #include "util.h"
 
 Seat *seat_new(Manager *m, const char *id) {
@@ -179,40 +181,32 @@ int seat_preallocate_vts(Seat *s) {
         return r;
 }
 
-int seat_apply_acls(Seat *s) {
-        assert(s);
-
-
-        return 0;
-}
-
-static int vt_is_busy(int vtnr) {
-        struct vt_stat vt_stat;
-        int r = 0, fd;
-
-        assert(vtnr >= 1);
+int seat_apply_acls(Seat *s, Session *old_active) {
+        int r;
 
-        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
+        assert(s);
 
-        if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
-                r = -errno;
-        else
-                r = !!(vt_stat.v_state & (1 << vtnr));
+        r = devnode_acl_all(s->manager->udev,
+                            s->id,
+                            false,
+                            !!old_active, old_active ? old_active->user->uid : 0,
+                            !!s->active, s->active ? s->active->user->uid : 0);
 
-        close_nointr_nofail(fd);
+        if (r < 0)
+                log_error("Failed to apply ACLs: %s", strerror(-r));
 
         return r;
 }
 
-void seat_active_vt_changed(Seat *s, int vtnr) {
+int seat_active_vt_changed(Seat *s, int vtnr) {
         Session *i;
+        Session *old_active;
 
         assert(s);
         assert(vtnr >= 1);
         assert(s->manager->vtconsole == s);
 
+        old_active = s->active;
         s->active = NULL;
 
         LIST_FOREACH(sessions_by_seat, i, s->sessions)
@@ -221,10 +215,13 @@ void seat_active_vt_changed(Seat *s, int vtnr) {
                         break;
                 }
 
-        seat_apply_acls(s);
+        if (old_active == s->active)
+                return 0;
+
+        seat_apply_acls(s, old_active);
+        manager_spawn_autovt(s->manager, vtnr);
 
-        if (vt_is_busy(vtnr) == 0)
-                manager_spawn_autovt(s->manager, vtnr);
+        return 0;
 }
 
 int seat_stop(Seat *s) {
diff --git a/src/logind-seat.h b/src/logind-seat.h
index 2fe7949..4b6b340 100644
--- a/src/logind-seat.h
+++ b/src/logind-seat.h
@@ -45,8 +45,8 @@ struct Seat {
 Seat *seat_new(Manager *m, const char *id);
 void seat_free(Seat *s);
 int seat_preallocate_vts(Seat *s);
-void seat_active_vt_changed(Seat *s, int vtnr);
-int seat_apply_acls(Seat *s);
+int seat_active_vt_changed(Seat *s, int vtnr);
+int seat_apply_acls(Seat *s, Session *old_active);
 int seat_stop(Seat *s);
 int seat_save(Seat *s);
 int seat_load(Seat *s);
diff --git a/src/logind-session.c b/src/logind-session.c
index 7bdf487..c10f5e6 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -174,6 +174,7 @@ int session_load(Session *s) {
 
 int session_activate(Session *s) {
         int r;
+        Session *old_active;
 
         assert(s);
 
@@ -192,9 +193,13 @@ int session_activate(Session *s) {
         if (r < 0)
                 return r;
 
+        old_active = s->seat->active;
         s->seat->active = s;
 
-        return seat_apply_acls(s->seat);
+        seat_apply_acls(s->seat, old_active);
+        manager_spawn_autovt(s->manager, s->vtnr);
+
+        return 0;
 }
 
 bool x11_display_is_local(const char *display) {
diff --git a/src/logind.c b/src/logind.c
index 0c26aad..7e9b706 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -26,6 +26,8 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
 
 #include "logind.h"
 #include "dbus-common.h"
@@ -290,7 +292,7 @@ int manager_enumerate_devices(Manager *m) {
 
         e = udev_enumerate_new(m->udev);
         if (!e)
-                return -ENOMEM;
+                goto finish;
 
         if (udev_enumerate_add_match_subsystem(e, "graphics") < 0)
                 goto finish;
@@ -627,9 +629,37 @@ int manager_dispatch_console(Manager *m) {
         return 0;
 }
 
+static int vt_is_busy(int vtnr) {
+        struct vt_stat vt_stat;
+        int r = 0, fd;
+
+        assert(vtnr >= 1);
+
+        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
+                r = -errno;
+        else
+                r = !!(vt_stat.v_state & (1 << vtnr));
+
+        close_nointr_nofail(fd);
+
+        return r;
+}
+
 int manager_spawn_autovt(Manager *m, int vtnr) {
+        int r;
+
         assert(m);
 
+        r = vt_is_busy(vtnr);
+        if (r != 0)
+                return r;
+
+        /* ... */
+
         return 0;
 }
 
@@ -849,7 +879,7 @@ int manager_run(Manager *m) {
 
 int main(int argc, char *argv[]) {
         Manager *m = NULL;
-        int r = 0;
+        int r;
 
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
diff --git a/src/logind.h b/src/logind.h
index 0d3bd89..be5dab7 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -36,11 +36,12 @@
  *
  * recreate VTs when disallocated
  * udev rules
+ * PAM rewrite
  * spawn user systemd
+ * dbus API
+ *
  * non-local X11 server
- * udev-acl
  * reboot/shutdown halt management
- * PAM rewrite
  */
 
 typedef struct Manager Manager;
diff --git a/src/uaccess.c b/src/uaccess.c
new file mode 100644
index 0000000..e55ab51
--- /dev/null
+++ b/src/uaccess.c
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+
+#include "logind-acl.h"
+#include "util.h"
+#include "log.h"
+
+int main(int argc, char *argv[]) {
+        int r;
+        const char *path, *seat;
+        char *p, *active_uid = NULL;
+        unsigned long ul;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc != 2) {
+                log_error("This program expects two argument.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        path = argv[1];
+        seat = argv[2];
+
+        p = strappend("/run/systemd/seat/", seat);
+        if (!p) {
+                log_error("Out of memory.");
+                goto finish;
+        }
+
+        r = parse_env_file(p, NEWLINE,
+                           "ACTIVE_UID", &active_uid,
+                           NULL);
+        free(p);
+
+        if (r < 0) {
+                if (errno == ENOENT) {
+                        r = 0;
+                        goto finish;
+                }
+
+                log_error("Failed to read seat data for %s: %s", seat, strerror(-r));
+                goto finish;
+        }
+
+        r = safe_atolu(active_uid, &ul);
+        if (r < 0) {
+                log_error("Failed to parse active UID value %s: %s", active_uid, strerror(-r));
+                goto finish;
+        }
+
+        r = devnode_acl(path, true, false, 0, true, (uid_t) ul);
+        if (r < 0) {
+                log_error("Failed to apply ACL on %s: %s", path, strerror(-r));
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        free(active_uid);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}

commit 90821c935e5f4258dc21fc515cf721beb0914a85
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue May 24 00:19:22 2011 +0200

    logind: split up logind.h

diff --git a/src/logind-device.c b/src/logind-device.c
index 9084b5f..4e076c2 100644
--- a/src/logind-device.c
+++ b/src/logind-device.c
@@ -22,7 +22,7 @@
 #include <assert.h>
 #include <string.h>
 
-#include "logind.h"
+#include "logind-device.h"
 #include "util.h"
 
 Device* device_new(Manager *m, const char *sysfs) {
diff --git a/src/logind-device.h b/src/logind-device.h
new file mode 100644
index 0000000..e25a534
--- /dev/null
+++ b/src/logind-device.h
@@ -0,0 +1,48 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologinddevicehfoo
+#define foologinddevicehfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Device Device;
+
+#include "list.h"
+#include "util.h"
+#include "logind.h"
+#include "logind-seat.h"
+
+struct Device {
+        Manager *manager;
+
+        char *sysfs;
+        Seat *seat;
+
+        dual_timestamp timestamp;
+
+        LIST_FIELDS(struct Device, devices);
+};
+
+Device* device_new(Manager *m, const char *sysfs);
+void device_free(Device *d);
+void device_attach(Device *d, Seat *s);
+void device_detach(Device *d);
+
+#endif
diff --git a/src/logind-seat.c b/src/logind-seat.c
index dcf1c71..3154900 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -26,7 +26,7 @@
 #include <sys/ioctl.h>
 #include <linux/vt.h>
 
-#include "logind.h"
+#include "logind-seat.h"
 #include "util.h"
 
 Seat *seat_new(Manager *m, const char *id) {
diff --git a/src/logind-seat.h b/src/logind-seat.h
new file mode 100644
index 0000000..2fe7949
--- /dev/null
+++ b/src/logind-seat.h
@@ -0,0 +1,54 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologindseathfoo
+#define foologindseathfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Seat Seat;
+
+#include "list.h"
+#include "util.h"
+#include "logind.h"
+#include "logind-device.h"
+#include "logind-session.h"
+
+struct Seat {
+        Manager *manager;
+        char *id;
+
+        char *state_file;
+
+        LIST_HEAD(Device, devices);
+
+        Session *active;
+        LIST_HEAD(Session, sessions);
+};
+
+Seat *seat_new(Manager *m, const char *id);
+void seat_free(Seat *s);
+int seat_preallocate_vts(Seat *s);
+void seat_active_vt_changed(Seat *s, int vtnr);
+int seat_apply_acls(Seat *s);
+int seat_stop(Seat *s);
+int seat_save(Seat *s);
+int seat_load(Seat *s);
+
+#endif
diff --git a/src/logind-session.c b/src/logind-session.c
index 9af99d0..7bdf487 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "logind.h"
+#include "logind-session.h"
 #include "strv.h"
 #include "util.h"
 #include "cgroup-util.h"
diff --git a/src/logind-session.h b/src/logind-session.h
new file mode 100644
index 0000000..b3f0b5f
--- /dev/null
+++ b/src/logind-session.h
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologindsessionhfoo
+#define foologindsessionhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Session Session;
+
+#include "list.h"
+#include "util.h"
+#include "logind.h"
+#include "logind-seat.h"
+#include "logind-user.h"
+
+typedef enum SessionType {
+        SESSION_TERMINAL,
+        SESSION_X11,
+        _SESSION_TYPE_MAX,
+        _SESSION_TYPE_INVALID = -1
+} SessionType;
+
+struct Session {
+        Manager *manager;
+
+        char *id;
+        SessionType type;
+
+        char *state_file;
+
+        User *user;
+
+        dual_timestamp timestamp;
+
+        char *tty;
+        char *display;
+
+        bool remote;
+        char *remote_host;
+
+        int vtnr;
+        Seat *seat;
+
+        pid_t leader;
+        uint64_t audit_id;
+
+        int pipe_fd;
+
+        char *cgroup_path;
+        char **controllers, **reset_controllers;
+
+        bool kill_processes;
+
+        LIST_FIELDS(Session, sessions_by_user);
+        LIST_FIELDS(Session, sessions_by_seat);
+};
+
+Session *session_new(Manager *m, User *u, const char *id);
+void session_free(Session *s);
+int session_activate(Session *s);
+bool session_is_active(Session *s);
+int session_check_gc(Session *s);
+int session_start(Session *s);
+int session_stop(Session *s);
+int session_save(Session *s);
+int session_load(Session *s);
+
+const char* session_type_to_string(SessionType t);
+SessionType session_type_from_string(const char *s);
+
+#endif
diff --git a/src/logind-user.c b/src/logind-user.c
index 1292eed..e0a3842 100644
--- a/src/logind-user.c
+++ b/src/logind-user.c
@@ -23,7 +23,7 @@
 #include <unistd.h>
 #include <errno.h>
 
-#include "logind.h"
+#include "logind-user.h"
 #include "util.h"
 #include "cgroup-util.h"
 #include "hashmap.h"
diff --git a/src/logind-user.h b/src/logind-user.h
new file mode 100644
index 0000000..04392e0
--- /dev/null
+++ b/src/logind-user.h
@@ -0,0 +1,72 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologinduserhfoo
+#define foologinduserhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct User User;
+
+#include "list.h"
+#include "util.h"
+#include "logind.h"
+#include "logind-session.h"
+
+typedef enum UserState {
+        USER_OFFLINE,
+        USER_LINGERING,
+        USER_ONLINE,
+        USER_ACTIVE,
+        _USER_STATE_MAX,
+        _USER_STATE_INVALID = -1
+} UserState;
+
+struct User {
+        Manager *manager;
+
+        uid_t uid;
+        gid_t gid;
+        char *name;
+
+        char *state_file;
+        char *runtime_path;
+        char *service;
+        char *cgroup_path;
+
+        Session *display;
+
+        dual_timestamp timestamp;
+
+        LIST_HEAD(Session, sessions);
+};
+
+User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name);
+void user_free(User *u);
+int user_start(User *u);
+int user_stop(User *u);
+int user_check_gc(User *u);
+UserState user_get_state(User *u);
+int user_save(User *u);
+int user_load(User *u);
+
+const char* user_state_to_string(UserState s);
+UserState user_state_from_string(const char *s);
+
+#endif
diff --git a/src/logind.h b/src/logind.h
index 5e69a71..0d3bd89 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -44,103 +44,11 @@
  */
 
 typedef struct Manager Manager;
-typedef struct Device Device;
-typedef struct Seat Seat;
-typedef struct Session Session;
-typedef struct User User;
 
-struct Device {
-        Manager *manager;
-
-        char *sysfs;
-        Seat *seat;
-
-        dual_timestamp timestamp;
-
-        LIST_FIELDS(struct Device, devices);
-};
-
-struct Seat {
-        Manager *manager;
-        char *id;
-
-        char *state_file;
-
-        LIST_HEAD(Device, devices);
-
-        Session *active;
-        LIST_HEAD(Session, sessions);
-};
-
-typedef enum SessionType {
-        SESSION_TERMINAL,
-        SESSION_X11,
-        _SESSION_TYPE_MAX,
-        _SESSION_TYPE_INVALID = -1
-} SessionType;
-
-struct Session {
-        Manager *manager;
-
-        char *id;
-        SessionType type;
-
-        char *state_file;
-
-        User *user;
-
-        dual_timestamp timestamp;
-
-        char *tty;
-        char *display;
-
-        bool remote;
-        char *remote_host;
-
-        int vtnr;
-        Seat *seat;
-
-        pid_t leader;
-        uint64_t audit_id;
-
-        int pipe_fd;
-
-        char *cgroup_path;
-        char **controllers, **reset_controllers;
-
-        bool kill_processes;
-
-        LIST_FIELDS(Session, sessions_by_user);
-        LIST_FIELDS(Session, sessions_by_seat);
-};
-
-typedef enum UserState {
-        USER_OFFLINE,
-        USER_LINGERING,
-        USER_ONLINE,
-        USER_ACTIVE,
-        _USER_STATE_MAX,
-        _USER_STATE_INVALID = -1
-} UserState;
-
-struct User {
-        Manager *manager;
-
-        uid_t uid;
-        gid_t gid;
-        char *name;
-
-        char *state_file;
-        char *runtime_path;
-        char *service;
-        char *cgroup_path;
-
-        Session *display;
-
-        dual_timestamp timestamp;
-
-        LIST_HEAD(Session, sessions);
-};
+#include "logind-device.h"
+#include "logind-seat.h"
+#include "logind-session.h"
+#include "logind-user.h"
 
 struct Manager {
         DBusConnection *bus;
@@ -170,39 +78,6 @@ struct Manager {
         bool kill_user_processes;
 };
 
-Device* device_new(Manager *m, const char *sysfs);
-void device_free(Device *d);
-void device_attach(Device *d, Seat *s);
-void device_detach(Device *d);
-
-Seat *seat_new(Manager *m, const char *id);
-void seat_free(Seat *s);
-int seat_preallocate_vts(Seat *s);
-void seat_active_vt_changed(Seat *s, int vtnr);
-int seat_apply_acls(Seat *s);
-int seat_stop(Seat *s);
-int seat_save(Seat *s);
-int seat_load(Seat *s);
-
-Session *session_new(Manager *m, User *u, const char *id);
-void session_free(Session *s);
-int session_activate(Session *s);
-bool session_is_active(Session *s);
-int session_check_gc(Session *s);
-int session_start(Session *s);
-int session_stop(Session *s);
-int session_save(Session *s);
-int session_load(Session *s);
-
-User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name);
-void user_free(User *u);
-int user_start(User *u);
-int user_stop(User *u);
-int user_check_gc(User *u);
-UserState user_get_state(User *u);
-int user_save(User *u);
-int user_load(User *u);
-
 Manager *manager_new(void);
 void manager_free(Manager *m);
 int manager_add_device(Manager *m, const char *sysfs, Device **_device);
@@ -224,12 +99,6 @@ int manager_startup(Manager *m);
 int manager_run(Manager *m);
 int manager_spawn_autovt(Manager *m, int vtnr);
 
-const char* session_type_to_string(SessionType t);
-SessionType session_type_from_string(const char *s);
-
-const char* user_state_to_string(UserState s);
-UserState user_state_from_string(const char *s);
-
 bool x11_display_is_local(const char *display);
 
 #endif

commit 202630822f52e06dce8404633407329c38099278
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 23:55:06 2011 +0200

    logind: first version that compiles fine

diff --git a/.gitignore b/.gitignore
index 5669d74..c2820b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-logind
 systemd-hostnamed
 systemd-binfmt
 systemd-getty-generator
diff --git a/Makefile.am b/Makefile.am
index 7ca0ae8..79ccbbe 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -158,7 +158,8 @@ rootlibexec_PROGRAMS = \
 	systemd-ac-power \
 	systemd-detect-virt \
 	systemd-sysctl \
-        systemd-hostnamed
+        systemd-hostnamed \
+        systemd-logind
 
 if ENABLE_BINFMT
 rootlibexec_PROGRAMS += \
@@ -806,6 +807,27 @@ systemd_hostnamed_LDADD = \
 	libsystemd-daemon.la \
 	$(DBUS_LIBS)
 
+systemd_logind_SOURCES = \
+	src/logind.c \
+        src/logind-device.c \
+        src/logind-seat.c \
+        src/logind-session.c \
+        src/logind-user.c \
+        src/dbus-common.c \
+        src/dbus-loop.c \
+        src/cgroup-util.c
+
+systemd_logind_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(DBUS_CFLAGS) \
+        $(UDEV_CFLAGS)
+
+systemd_logind_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-daemon.la \
+	$(DBUS_LIBS) \
+        $(UDEV_LIBS)
+
 systemd_shutdown_SOURCES = \
 	src/mount-setup.c \
 	src/umount.c \
diff --git a/src/dbus-loop.c b/src/dbus-loop.c
new file mode 100644
index 0000000..8eb1d17
--- /dev/null
+++ b/src/dbus-loop.c
@@ -0,0 +1,263 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <assert.h>
+#include <sys/epoll.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+#include "dbus-loop.h"
+#include "dbus-common.h"
+#include "util.h"
+
+/* Minimal implementation of the dbus loop which integrates all dbus
+ * events into a single epoll fd which we can triviall integrate with
+ * other loops. Note that this is not used in the main systemd daemon
+ * since we run a more elaborate mainloop there. */
+
+typedef struct EpollData {
+        int fd;
+        void *object;
+        bool is_timeout:1;
+        bool fd_is_dupped:1;
+} EpollData;
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
+        EpollData *e;
+        struct epoll_event ev;
+
+        assert(watch);
+
+        e = new0(EpollData, 1);
+        if (!e)
+                return FALSE;
+
+        e->fd = dbus_watch_get_unix_fd(watch);
+        e->object = watch;
+        e->is_timeout = false;
+
+        zero(ev);
+        ev.events = bus_flags_to_events(watch);
+        ev.data.ptr = e;
+
+        if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
+
+                if (errno != EEXIST) {
+                        free(e);
+                        return FALSE;
+                }
+
+                /* Hmm, bloody D-Bus creates multiple watches on the
+                 * same fd. epoll() does not like that. As a dirty
+                 * hack we simply dup() the fd and hence get a second
+                 * one we can safely add to the epoll(). */
+
+                e->fd = dup(e->fd);
+                if (e->fd < 0) {
+                        free(e);
+                        return FALSE;
+                }
+
+                if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) {
+                        close_nointr_nofail(e->fd);
+                        free(e);
+                        return FALSE;
+                }
+
+                e->fd_is_dupped = true;
+        }
+
+        dbus_watch_set_data(watch, e, NULL);
+
+        return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data) {
+        EpollData *e;
+
+        assert(watch);
+
+        e = dbus_watch_get_data(watch);
+        if (!e)
+                return;
+
+        assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
+
+        if (e->fd_is_dupped)
+                close_nointr_nofail(e->fd);
+
+        free(e);
+}
+
+static void toggle_watch(DBusWatch *watch, void *data) {
+        EpollData *e;
+        struct epoll_event ev;
+
+        assert(watch);
+
+        e = dbus_watch_get_data(watch);
+        if (!e)
+                return;
+
+        zero(ev);
+        ev.events = bus_flags_to_events(watch);
+        ev.data.ptr = e;
+
+        assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0);
+}
+
+static int timeout_arm(EpollData *e) {
+        struct itimerspec its;
+
+        assert(e);
+        assert(e->is_timeout);
+
+        zero(its);
+
+        if (dbus_timeout_get_enabled(e->object)) {
+                timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC);
+                its.it_interval = its.it_value;
+        }
+
+        if (timerfd_settime(e->fd, 0, &its, NULL) < 0)
+                return -errno;
+
+        return 0;
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
+        EpollData *e;
+        struct epoll_event ev;
+
+        assert(timeout);
+
+        e = new0(EpollData, 1);
+        if (!e)
+                return FALSE;
+
+        e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+        if (e->fd < 0)
+                goto fail;
+
+        e->object = timeout;
+        e->is_timeout = true;
+
+        if (timeout_arm(e) < 0)
+                goto fail;
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.ptr = e;
+
+        if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0)
+                goto fail;
+
+        dbus_timeout_set_data(timeout, e, NULL);
+
+        return TRUE;
+
+fail:
+        if (e->fd >= 0)
+                close_nointr_nofail(e->fd);
+
+        free(e);
+        return FALSE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data) {
+        EpollData *e;
+
+        assert(timeout);
+
+        e = dbus_timeout_get_data(timeout);
+        if (!e)
+                return;
+
+        assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0);
+        close_nointr_nofail(e->fd);
+        free(e);
+}
+
+static void toggle_timeout(DBusTimeout *timeout, void *data) {
+        EpollData *e;
+        int r;
+
+        assert(timeout);
+
+        e = dbus_timeout_get_data(timeout);
+        if (!e)
+                return;
+
+        r = timeout_arm(e);
+        if (r < 0)
+                log_error("Failed to rearm timer: %s", strerror(-r));
+}
+
+int bus_loop_open(DBusConnection *c) {
+        int fd;
+
+        assert(c);
+
+        fd = epoll_create1(EPOLL_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (!dbus_connection_set_watch_functions(c, add_watch, remove_watch, toggle_watch, INT_TO_PTR(fd), NULL) ||
+            !dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, toggle_timeout, INT_TO_PTR(fd), NULL)) {
+                close_nointr_nofail(fd);
+                return -ENOMEM;
+        }
+
+        return fd;
+}
+
+int bus_loop_dispatch(int fd) {
+        int n;
+        struct epoll_event event;
+        EpollData *d;
+
+        assert(fd >= 0);
+
+        zero(event);
+
+        n = epoll_wait(fd, &event, 1, 0);
+        if (n < 0)
+                return errno == EAGAIN || errno == EINTR ? 0 : -errno;
+
+        assert_se(d = event.data.ptr);
+
+        if (d->is_timeout) {
+                DBusTimeout *t = d->object;
+
+                if (dbus_timeout_get_enabled(t))
+                        dbus_timeout_handle(t);
+        } else {
+                DBusWatch *w = d->object;
+
+                if (dbus_watch_get_enabled(w))
+                        dbus_watch_handle(w, bus_events_to_flags(event.events));
+        }
+
+        return 0;
+}
diff --git a/src/dbus-loop.h b/src/dbus-loop.h
new file mode 100644
index 0000000..0bbdfe5
--- /dev/null
+++ b/src/dbus-loop.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusloophfoo
+#define foodbusloophfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+int bus_loop_open(DBusConnection *c);
+int bus_loop_dispatch(int fd);
+
+#endif
diff --git a/src/logind-device.c b/src/logind-device.c
new file mode 100644
index 0000000..9084b5f
--- /dev/null
+++ b/src/logind-device.c
@@ -0,0 +1,85 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+
+#include "logind.h"
+#include "util.h"
+
+Device* device_new(Manager *m, const char *sysfs) {
+        Device *d;
+
+        assert(m);
+        assert(sysfs);
+
+        d = new0(Device, 1);
+        if (!d)
+                return NULL;
+
+        d->sysfs = strdup(sysfs);
+        if (!d->sysfs) {
+                free(d);
+                return NULL;
+        }
+
+        if (hashmap_put(m->devices, d->sysfs, d) < 0) {
+                free(d->sysfs);
+                free(d);
+                return NULL;
+        }
+
+        d->manager = m;
+        dual_timestamp_get(&d->timestamp);
+
+        return d;
+}
+
+void device_free(Device *d) {
+        assert(d);
+
+        device_detach(d);
+
+        hashmap_remove(d->manager->devices, d->sysfs);
+
+        free(d->sysfs);
+        free(d);
+}
+
+void device_detach(Device *d) {
+        assert(d);
+
+        if (d->seat)
+                LIST_REMOVE(Device, devices, d->seat->devices, d);
+
+        d->seat = NULL;
+}
+
+void device_attach(Device *d, Seat *s) {
+        assert(d);
+        assert(s);
+
+        if (d->seat)
+                device_detach(d);
+
+        LIST_PREPEND(Device, devices, d->seat->devices, d);
+        d->seat = s;
+}
diff --git a/src/logind-seat.c b/src/logind-seat.c
new file mode 100644
index 0000000..dcf1c71
--- /dev/null
+++ b/src/logind-seat.c
@@ -0,0 +1,245 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+
+#include "logind.h"
+#include "util.h"
+
+Seat *seat_new(Manager *m, const char *id) {
+        Seat *s;
+
+        assert(m);
+        assert(id);
+
+        s = new0(Seat, 1);
+        if (!s)
+                return NULL;
+
+        s->state_file = strappend("/run/systemd/seat/", id);
+        if (!s->state_file) {
+                free(s);
+                return NULL;
+        }
+
+        s->id = file_name_from_path(s->state_file);
+
+        if (hashmap_put(m->seats, s->id, s) < 0) {
+                free(s->id);
+                free(s);
+                return NULL;
+        }
+
+        s->manager = m;
+
+        return s;
+}
+
+void seat_free(Seat *s) {
+        assert(s);
+
+        while (s->sessions)
+                session_free(s->sessions);
+
+        assert(!s->active);
+
+        while (s->devices)
+                device_free(s->devices);
+
+        hashmap_remove(s->manager->seats, s->id);
+
+        free(s->state_file);
+        free(s);
+}
+
+int seat_save(Seat *s) {
+        FILE *f;
+        int r;
+
+        assert(s);
+
+        r = safe_mkdir("/run/systemd/seat", 0755, 0, 0);
+        if (r < 0)
+                return r;
+
+        f = fopen(s->state_file, "we");
+        if (!f)
+                return -errno;
+
+        fprintf(f,
+                "IS_VTCONSOLE=%i\n",
+                s->manager->vtconsole == s);
+
+        if (s->active) {
+                assert(s->active->user);
+
+                fprintf(f,
+                        "ACTIVE=%s\n"
+                        "ACTIVE_UID=%lu\n",
+                        s->active->id,
+                        (unsigned long) s->active->user->uid);
+        }
+
+        if (s->sessions) {
+                Session *i;
+                fputs("OTHER_UIDS=", f);
+
+                LIST_FOREACH(sessions_by_seat, i, s->sessions) {
+                        assert(i->user);
+
+                        if (i == s->active)
+                                continue;
+
+                        fprintf(f,
+                                "%s%lu",
+                                i == s->sessions ? "" : " ",
+                                (unsigned long) i->user->uid);
+                }
+        }
+
+        fflush(f);
+        if (ferror(f)) {
+                r = -errno;
+                unlink(s->state_file);
+        }
+
+        fclose(f);
+        return r;
+}
+
+int seat_load(Seat *s) {
+        assert(s);
+
+        return 0;
+}
+
+static int vt_allocate(int vtnr) {
+        int fd, r;
+        char *p;
+
+        assert(vtnr >= 1);
+
+        if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
+                return -ENOMEM;
+
+        fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        free(p);
+
+        r = fd < 0 ? -errno : 0;
+
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return r;
+}
+
+int seat_preallocate_vts(Seat *s) {
+        int i, r = 0;
+
+        assert(s);
+        assert(s->manager);
+
+        if (s->manager->n_autovts <= 0)
+                return 0;
+
+        if (s->manager->vtconsole != s)
+                return 0;
+
+        for (i = 1; i < s->manager->n_autovts; i++) {
+                int q;
+
+                q = vt_allocate(i);
+                if (r >= 0 && q < 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+int seat_apply_acls(Seat *s) {
+        assert(s);
+
+
+        return 0;
+}
+
+static int vt_is_busy(int vtnr) {
+        struct vt_stat vt_stat;
+        int r = 0, fd;
+
+        assert(vtnr >= 1);
+
+        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
+                r = -errno;
+        else
+                r = !!(vt_stat.v_state & (1 << vtnr));
+
+        close_nointr_nofail(fd);
+
+        return r;
+}
+
+void seat_active_vt_changed(Seat *s, int vtnr) {
+        Session *i;
+
+        assert(s);
+        assert(vtnr >= 1);
+        assert(s->manager->vtconsole == s);
+
+        s->active = NULL;
+
+        LIST_FOREACH(sessions_by_seat, i, s->sessions)
+                if (i->vtnr == vtnr) {
+                        s->active = i;
+                        break;
+                }
+
+        seat_apply_acls(s);
+
+        if (vt_is_busy(vtnr) == 0)
+                manager_spawn_autovt(s->manager, vtnr);
+}
+
+int seat_stop(Seat *s) {
+        Session *session;
+        int r = 0;
+
+        assert(s);
+
+        LIST_FOREACH(sessions_by_seat, session, s->sessions) {
+                int k;
+
+                k = session_stop(session);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
+}
diff --git a/src/logind-session.c b/src/logind-session.c
new file mode 100644
index 0000000..9af99d0
--- /dev/null
+++ b/src/logind-session.c
@@ -0,0 +1,472 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "logind.h"
+#include "strv.h"
+#include "util.h"
+#include "cgroup-util.h"
+
+Session* session_new(Manager *m, User *u, const char *id) {
+        Session *s;
+
+        assert(m);
+        assert(id);
+
+        s = new(Session, 1);
+        if (!s)
+                return NULL;
+
+        s->state_file = strappend("/run/systemd/session/", id);
+        if (!s->state_file) {
+                free(s);
+                return NULL;
+        }
+
+        s->id = file_name_from_path(s->state_file);
+
+        if (hashmap_put(m->sessions, s->id, s) < 0) {
+                free(s->id);
+                free(s);
+                return NULL;
+        }
+
+        s->manager = m;
+        s->pipe_fd = -1;
+        s->user = u;
+
+        dual_timestamp_get(&s->timestamp);
+
+        return s;
+}
+
+void session_free(Session *s) {
+        assert(s);
+
+        if (s->user) {
+                LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
+
+                if (s->user->display == s)
+                        s->user->display = NULL;
+        }
+
+        if (s->seat)
+                LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
+
+        free(s->cgroup_path);
+        strv_free(s->controllers);
+
+        free(s->tty);
+        free(s->display);
+        free(s->remote_host);
+
+        hashmap_remove(s->manager->sessions, s->id);
+
+        free(s->state_file);
+        free(s);
+}
+
+int session_save(Session *s) {
+        FILE *f;
+        int r = 0;
+
+        assert(s);
+
+        r = safe_mkdir("/run/systemd/session", 0755, 0, 0);
+        if (r < 0)
+                return r;
+
+        f = fopen(s->state_file, "we");
+        if (!f)
+                return -errno;
+
+        assert(s->user);
+
+        fprintf(f,
+                "# This is private data. Do not parse.\n"
+                "UID=%lu\n"
+                "USER=%s\n"
+                "ACTIVE=%i\n"
+                "REMOTE=%i\n"
+                "KILL_PROCESSES=%i\n",
+                (unsigned long) s->user->uid,
+                s->user->name,
+                session_is_active(s),
+                s->remote,
+                s->kill_processes);
+
+        if (s->cgroup_path)
+                fprintf(f,
+                        "CGROUP=%s\n",
+                        s->cgroup_path);
+
+        if (s->seat)
+                fprintf(f,
+                        "SEAT=%s\n",
+                        s->seat->id);
+
+        if (s->tty)
+                fprintf(f,
+                        "TTY=%s\n",
+                        s->tty);
+
+        if (s->display)
+                fprintf(f,
+                        "DISPLAY=%s\n",
+                        s->display);
+
+        if (s->remote_host)
+                fprintf(f,
+                        "REMOTE_HOST=%s\n",
+                        s->remote_host);
+
+        if (s->seat && s->seat->manager->vtconsole == s->seat)
+                fprintf(f,
+                        "VTNR=%i\n",
+                        s->vtnr);
+
+        if (s->leader > 0)
+                fprintf(f,
+                        "LEADER=%lu\n",
+                        (unsigned long) s->leader);
+
+        if (s->audit_id > 0)
+                fprintf(f,
+                        "AUDIT=%llu\n",
+                        (unsigned long long) s->audit_id);
+
+        fflush(f);
+        if (ferror(f)) {
+                r = -errno;
+                unlink(s->state_file);
+        }
+
+        fclose(f);
+        return r;
+}
+
+int session_load(Session *s) {
+        assert(s);
+
+        return 0;
+}
+
+int session_activate(Session *s) {
+        int r;
+
+        assert(s);
+
+        if (s->vtnr < 0)
+                return -ENOTSUP;
+
+        if (!s->seat)
+                return -ENOTSUP;
+
+        if (s->seat->active == s)
+                return 0;
+
+        assert(s->manager->vtconsole == s->seat);
+
+        r = chvt(s->vtnr);
+        if (r < 0)
+                return r;
+
+        s->seat->active = s;
+
+        return seat_apply_acls(s->seat);
+}
+
+bool x11_display_is_local(const char *display) {
+        assert(display);
+
+        return
+                display[0] == ':' &&
+                display[1] >= '0' &&
+                display[1] <= '9';
+}
+
+static int session_link_x11_socket(Session *s) {
+        char *t, *f, *c;
+        size_t k;
+
+        assert(s);
+        assert(s->user);
+        assert(s->user->runtime_path);
+
+        if (s->user->display)
+                return 0;
+
+        if (!s->display || !x11_display_is_local(s->display))
+                return 0;
+
+        k = strspn(s->display+1, "0123456789");
+        f = new(char, sizeof("/tmp/.X11-unix/X") + k);
+        if (!f) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        c = stpcpy(f, "/tmp/.X11-unix/X");
+        memcpy(c, s->display+1, k);
+        c[k] = 0;
+
+        if (access(f, F_OK) < 0) {
+                log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
+                free(f);
+                return -ENOENT;
+        }
+
+        t = strappend(s->user->runtime_path, "/display");
+        if (!t) {
+                log_error("Out of memory");
+                free(f);
+                return -ENOMEM;
+        }
+
+        if (link(f, t) < 0) {
+                if (errno == EEXIST) {
+                        unlink(t);
+
+                        if (link(f, t) >= 0)
+                                goto done;
+                }
+
+                if (symlink(f, t) < 0) {
+
+                        if (errno == EEXIST) {
+                                unlink(t);
+
+                                if (symlink(f, t) >= 0)
+                                        goto done;
+                        }
+
+                        log_error("Failed to link %s to %s: %m", f, t);
+                        free(f);
+                        free(t);
+                        return -errno;
+                }
+        }
+
+done:
+        log_info("Linked %s to %s.", f, t);
+        free(f);
+        free(t);
+
+        s->user->display = s;
+
+        return 0;
+}
+
+static int session_create_cgroup(Session *s) {
+        char **k;
+        char *p;
+        int r;
+
+        assert(s);
+        assert(s->user);
+        assert(s->user->cgroup_path);
+
+        if (!s->cgroup_path) {
+                if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+        } else
+                p = s->cgroup_path;
+
+        if (s->leader > 0)
+                r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, p, s->leader);
+        else
+                r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
+
+        if (r < 0) {
+                free(p);
+                s->cgroup_path = NULL;
+                log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
+                return r;
+        }
+
+        s->cgroup_path = p;
+
+        STRV_FOREACH(k, s->manager->controllers) {
+                if (s->leader > 0)
+                        r = cg_create_and_attach(*k, p, s->leader);
+                else
+                        r = cg_create(*k, p);
+
+                if (r < 0)
+                        log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
+        }
+
+        return 0;
+}
+
+int session_start(Session *s) {
+        int r;
+
+        assert(s);
+        assert(s->user);
+
+        /* Create user first */
+        r = user_start(s->user);
+        if (r < 0)
+                return r;
+
+        /* Create cgroup */
+        r = session_create_cgroup(s);
+        if (r < 0)
+                return r;
+
+        /* Create X11 symlink */
+        session_link_x11_socket(s);
+        return 0;
+}
+
+static bool session_shall_kill(Session *s) {
+        assert(s);
+
+        return s->kill_processes;
+}
+
+static int session_kill_cgroup(Session *s) {
+        int r;
+        char **k;
+
+        assert(s);
+
+        if (!s->cgroup_path)
+                return 0;
+
+        cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
+
+        if (session_shall_kill(s)) {
+
+                r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
+                if (r < 0)
+                        log_error("Failed to kill session cgroup: %s", strerror(-r));
+
+        } else {
+                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
+                if (r < 0)
+                        log_error("Failed to check session cgroup: %s", strerror(-r));
+                else if (r > 0) {
+                        r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
+                        if (r < 0)
+                                log_error("Failed to delete session cgroup: %s", strerror(-r));
+                } else
+                        r = -EBUSY;
+        }
+
+        STRV_FOREACH(k, s->user->manager->controllers)
+                cg_trim(*k, s->cgroup_path, true);
+
+        free(s->cgroup_path);
+        s->cgroup_path = NULL;
+
+        return r;
+}
+
+static int session_unlink_x11_socket(Session *s) {
+        char *t;
+        int r;
+
+        assert(s);
+        assert(s->user);
+
+        if (s->user->display != s)
+                return 0;
+
+        s->user->display = NULL;
+
+        t = strappend(s->user->runtime_path, "/display");
+        if (!t) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        r = unlink(t);
+        free(t);
+
+        return r < 0 ? -errno : 0;
+}
+
+int session_stop(Session *s) {
+        int r = 0, k;
+
+        assert(s);
+
+        /* Kill cgroup */
+        k = session_kill_cgroup(s);
+        if (k < 0)
+                r = k;
+
+        /* Remove X11 symlink */
+        session_unlink_x11_socket(s);
+
+        return r;
+}
+
+bool session_is_active(Session *s) {
+        assert(s);
+
+        if (!s->seat)
+                return true;
+
+        return s->seat->active == s;
+}
+
+int session_check_gc(Session *s) {
+        int r;
+
+        assert(s);
+
+        if (s->pipe_fd >= 0) {
+
+                r = pipe_eof(s->pipe_fd);
+                if (r < 0)
+                        return r;
+
+                if (r <= 0)
+                        return 1;
+        }
+
+        if (s->cgroup_path) {
+
+                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
+                if (r < 0)
+                        return r;
+
+                if (r <= 0)
+                        return 1;
+        }
+
+        return 0;
+}
+
+static const char* const session_type_table[_SESSION_TYPE_MAX] = {
+        [SESSION_TERMINAL] = "terminal",
+        [SESSION_X11] = "x11"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
diff --git a/src/logind-user.c b/src/logind-user.c
new file mode 100644
index 0000000..1292eed
--- /dev/null
+++ b/src/logind-user.c
@@ -0,0 +1,420 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "logind.h"
+#include "util.h"
+#include "cgroup-util.h"
+#include "hashmap.h"
+#include "strv.h"
+
+User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
+        User *u;
+
+        assert(m);
+        assert(name);
+
+        u = new(User, 1);
+        if (!u)
+                return NULL;
+
+        u->name = strdup(name);
+        if (!u->name) {
+                free(u);
+                return NULL;
+        }
+
+        if (asprintf(&u->state_file, "/run/systemd/user/%lu", (unsigned long) uid) < 0) {
+                free(u->name);
+                free(u);
+                return NULL;
+        }
+
+        if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
+                free(u->state_file);
+                free(u->name);
+                free(u);
+                return NULL;
+        }
+
+        u->manager = m;
+        u->uid = uid;
+        u->gid = gid;
+
+        return u;
+}
+
+void user_free(User *u) {
+        assert(u);
+
+        while (u->sessions)
+                session_free(u->sessions);
+
+        free(u->cgroup_path);
+
+        free(u->service);
+        free(u->runtime_path);
+
+        hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
+
+        free(u->name);
+        free(u->state_file);
+        free(u);
+}
+
+int user_save(User *u) {
+        FILE *f;
+        int r;
+
+        assert(u);
+        assert(u->state_file);
+
+        r = safe_mkdir("/run/systemd/user", 0755, 0, 0);
+        if (r < 0)
+                return r;
+
+        f = fopen(u->state_file, "we");
+        if (!f)
+                return -errno;
+
+        fprintf(f,
+                "NAME=%s\n"
+                "STATE=%s\n",
+                u->name,
+                user_state_to_string(user_get_state(u)));
+
+        if (u->cgroup_path)
+                fprintf(f,
+                        "CGROUP=%s\n",
+                        u->cgroup_path);
+
+        if (u->runtime_path)
+                fprintf(f,
+                        "RUNTIME=%s\n",
+                        u->runtime_path);
+
+        if (u->service)
+                fprintf(f,
+                        "SERVICE=%s\n",
+                        u->service);
+
+        if (u->display)
+                fprintf(f,
+                        "DISPLAY=%s\n",
+                        u->display->id);
+
+        fflush(f);
+        if (ferror(f)) {
+                r = -errno;
+                unlink(u->state_file);
+        }
+
+        fclose(f);
+        return r;
+}
+
+int user_load(User *u) {
+        int r;
+        char *display = NULL;
+        Session *s;
+
+        assert(u);
+
+        r = parse_env_file(u->state_file, "r",
+                           "CGROUP", &u->cgroup_path,
+                           "RUNTIME", &u->runtime_path,
+                           "SERVICE", &u->service,
+                           "DISPLAY", &display,
+                           NULL);
+        if (r < 0) {
+                free(display);
+
+                if (r == -ENOENT)
+                        return 0;
+
+                log_error("Failed to read %s: %s", u->state_file, strerror(-r));
+                return r;
+        }
+
+        s = hashmap_get(u->manager->sessions, display);
+        free(display);
+
+        if (s && s->display && x11_display_is_local(s->display))
+                u->display = s;
+
+        return r;
+}
+
+static int user_mkdir_runtime_path(User *u) {
+        char *p;
+        int r;
+
+        assert(u);
+
+        r = safe_mkdir("/run/user", 0755, 0, 0);
+        if (r < 0) {
+                log_error("Failed to create /run/user: %s", strerror(-r));
+                return r;
+        }
+
+        if (!u->runtime_path) {
+                p = strappend("/run/user/", u->name);
+
+                if (!p) {
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+        } else
+                p = u->runtime_path;
+
+        r = safe_mkdir(p, 0700, u->uid, u->gid);
+        if (r < 0) {
+                log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
+                free(p);
+                u->runtime_path = NULL;
+                return r;
+        }
+
+        u->runtime_path = p;
+        return 0;
+}
+
+static int user_create_cgroup(User *u) {
+        char **k;
+        char *p;
+        int r;
+
+        assert(u);
+
+        if (!u->cgroup_path) {
+                if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+        } else
+                p = u->cgroup_path;
+
+        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
+        if (r < 0) {
+                free(p);
+                u->cgroup_path = NULL;
+                log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
+                return r;
+        }
+
+        u->cgroup_path = p;
+
+        STRV_FOREACH(k, u->manager->controllers) {
+                r = cg_create(*k, p);
+                if (r < 0)
+                        log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
+        }
+
+        return 0;
+}
+
+static int user_start_service(User *u) {
+        assert(u);
+
+        return 0;
+}
+
+int user_start(User *u) {
+        int r;
+
+        assert(u);
+
+        /* Make XDG_RUNTIME_DIR */
+        r = user_mkdir_runtime_path(u);
+        if (r < 0)
+                return r;
+
+        /* Create cgroup */
+        r = user_create_cgroup(u);
+        if (r < 0)
+                return r;
+
+        /* Spawn user systemd */
+        r = user_start_service(u);
+        if (r < 0)
+                return r;
+
+        dual_timestamp_get(&u->timestamp);
+
+        return 0;
+}
+
+static int user_stop_service(User *u) {
+        assert(u);
+
+        if (!u->service)
+                return 0;
+
+        return 0;
+}
+
+static int user_shall_kill(User *u) {
+        assert(u);
+
+        return u->manager->kill_user_processes;
+}
+
+static int user_kill_cgroup(User *u) {
+        int r;
+        char **k;
+
+        assert(u);
+
+        if (!u->cgroup_path)
+                return 0;
+
+        cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
+
+        if (user_shall_kill(u)) {
+
+                r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+                if (r < 0)
+                        log_error("Failed to kill user cgroup: %s", strerror(-r));
+        } else {
+
+                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+                if (r < 0)
+                        log_error("Failed to check user cgroup: %s", strerror(-r));
+                else if (r > 0) {
+                        r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
+                        if (r < 0)
+                                log_error("Failed to delete user cgroup: %s", strerror(-r));
+                } else
+                        r = -EBUSY;
+        }
+
+        STRV_FOREACH(k, u->manager->controllers)
+                cg_trim(*k, u->cgroup_path, true);
+
+        free(u->cgroup_path);
+        u->cgroup_path = NULL;
+
+        return r;
+}
+
+static int user_remove_runtime_path(User *u) {
+        int r;
+
+        assert(u);
+
+        if (!u->runtime_path)
+                return 0;
+
+        r = rm_rf(u->runtime_path, false, true);
+        if (r < 0)
+                log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
+
+        free(u->runtime_path);
+        u->runtime_path = NULL;
+
+        return r;
+}
+
+int user_stop(User *u) {
+        Session *s;
+        int r = 0, k;
+        assert(u);
+
+        LIST_FOREACH(sessions_by_user, s, u->sessions) {
+                k = session_stop(s);
+                if (k < 0)
+                        r = k;
+        }
+
+        /* Kill systemd */
+        k = user_stop_service(u);
+        if (k < 0)
+                r = k;
+
+        /* Kill cgroup */
+        k = user_kill_cgroup(u);
+        if (k < 0)
+                r = k;
+
+        /* Kill XDG_RUNTIME_DIR */
+        k = user_remove_runtime_path(u);
+        if (k < 0)
+                r = k;
+
+        return r;
+}
+
+int user_check_gc(User *u) {
+        int r;
+        char *p;
+
+        assert(u);
+
+        if (u->sessions)
+                return 1;
+
+        if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK) >= 0;
+        free(p);
+
+        if (r > 0)
+                return 1;
+
+        if (u->cgroup_path) {
+                r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
+                if (r < 0)
+                        return r;
+
+                if (r <= 0)
+                        return 1;
+        }
+
+        return 0;
+}
+
+UserState user_get_state(User *u) {
+        Session *i;
+
+        assert(u);
+
+        if (!u->sessions)
+                return USER_LINGERING;
+
+        LIST_FOREACH(sessions_by_user, i, u->sessions)
+                if (session_is_active(i))
+                        return USER_ACTIVE;
+
+        return USER_ONLINE;
+}
+
+static const char* const user_state_table[_USER_STATE_MAX] = {
+        [USER_OFFLINE] = "offline",
+        [USER_LINGERING] = "lingering",
+        [USER_ONLINE] = "online",
+        [USER_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
diff --git a/src/logind.c b/src/logind.c
new file mode 100644
index 0000000..0c26aad
--- /dev/null
+++ b/src/logind.c
@@ -0,0 +1,886 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <pwd.h>
+#include <libudev.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+
+#include "logind.h"
+#include "dbus-common.h"
+#include "dbus-loop.h"
+
+enum {
+        FD_UDEV,
+        FD_CONSOLE,
+        FD_BUS
+};
+
+Manager *manager_new(void) {
+        Manager *m;
+
+        m = new0(Manager, 1);
+        if (!m)
+                return NULL;
+
+        m->console_active_fd = -1;
+        m->bus_fd = -1;
+        m->udev_fd = -1;
+        m->epoll_fd = -1;
+        m->n_autovts = 6;
+
+        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);
+        m->users = hashmap_new(trivial_hash_func, trivial_compare_func);
+
+        if (!m->devices || !m->seats || !m->sessions || !m->users) {
+                manager_free(m);
+                return NULL;
+        }
+
+        m->udev = udev_new();
+        if (!m->udev) {
+                manager_free(m);
+                return NULL;
+        }
+
+        if (cg_get_user_path(&m->cgroup_path) < 0) {
+                manager_free(m);
+                return NULL;
+        }
+
+        return m;
+}
+
+void manager_free(Manager *m) {
+        Session *session;
+        User *u;
+        Device *d;
+        Seat *s;
+
+        assert(m);
+
+        while ((session = hashmap_first(m->sessions)))
+                session_free(session);
+
+        while ((u = hashmap_first(m->users)))
+                user_free(u);
+
+        while ((d = hashmap_first(m->devices)))
+                device_free(d);
+
+        while ((s = hashmap_first(m->seats)))
+                seat_free(s);
+
+        hashmap_free(m->sessions);
+        hashmap_free(m->users);
+        hashmap_free(m->devices);
+        hashmap_free(m->seats);
+
+        if (m->console_active_fd >= 0)
+                close_nointr_nofail(m->console_active_fd);
+
+        if (m->udev_monitor)
+                udev_monitor_unref(m->udev_monitor);
+
+        if (m->udev)
+                udev_unref(m->udev);
+
+        if (m->bus) {
+                dbus_connection_flush(m->bus);
+                dbus_connection_close(m->bus);
+                dbus_connection_unref(m->bus);
+        }
+
+        if (m->bus_fd >= 0)
+                close_nointr_nofail(m->bus_fd);
+
+        if (m->epoll_fd >= 0)
+                close_nointr_nofail(m->epoll_fd);
+
+        free(m->cgroup_path);
+        free(m);
+}
+
+int manager_add_device(Manager *m, const char *sysfs, Device **_device) {
+        Device *d;
+
+        assert(m);
+        assert(sysfs);
+
+        d = hashmap_get(m->devices, sysfs);
+        if (d) {
+                if (_device)
+                        *_device = d;
+
+                return 0;
+        }
+
+        d = device_new(m, sysfs);
+        if (!d)
+                return -ENOMEM;
+
+        if (_device)
+                *_device = d;
+
+        return 0;
+}
+
+int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
+        Seat *s;
+
+        assert(m);
+        assert(id);
+
+        s = hashmap_get(m->seats, id);
+        if (s) {
+                if (_seat)
+                        *_seat = s;
+
+                return 0;
+        }
+
+        s = seat_new(m, id);
+        if (!s)
+                return -ENOMEM;
+
+        if (_seat)
+                *_seat = s;
+
+        return 0;
+}
+
+int manager_add_session(Manager *m, User *u, const char *id, Session **_session) {
+        Session *s;
+
+        assert(m);
+        assert(id);
+
+        s = hashmap_get(m->sessions, id);
+        if (s) {
+                if (_session)
+                        *_session = s;
+
+                return 0;
+        }
+
+        s = session_new(m, u, id);
+        if (!s)
+                return -ENOMEM;
+
+        if (_session)
+                *_session = s;
+
+        return 0;
+}
+
+int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
+        User *u;
+        int r;
+
+        assert(m);
+        assert(name);
+
+        u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
+        if (u) {
+                if (_user)
+                        *_user = u;
+
+                return 0;
+        }
+
+        u = user_new(m, uid, gid, name);
+        if (!u)
+                return -ENOMEM;
+
+        r = user_start(u);
+        if (r < 0) {
+                user_stop(u);
+                user_free(u);
+        } else {
+                if (_user)
+                        *_user = u;
+        }
+
+        return r;
+}
+
+int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
+        struct passwd *p;
+
+        assert(m);
+        assert(name);
+
+        errno = 0;
+        p = getpwnam(name);
+        if (!p)
+                return errno ? -errno : -ENOENT;
+
+        return manager_add_user(m, p->pw_uid, p->pw_gid, name, _user);
+}
+
+int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
+        struct passwd *p;
+
+        assert(m);
+
+        errno = 0;
+        p = getpwuid(uid);
+        if (!p)
+                return errno ? -errno : -ENOENT;
+
+        return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
+}
+
+int manager_process_device(Manager *m, struct udev_device *d) {
+        const char *sn;
+        Seat *seat;
+        Device *device;
+        int r;
+
+        assert(m);
+
+        sn = udev_device_get_property_value(d, "SEAT");
+        if (!sn)
+                sn = "seat0";
+
+        r = manager_add_device(m, udev_device_get_syspath(d), &device);
+        if (r < 0)
+                return r;
+
+        r = manager_add_seat(m, sn, &seat);
+        if (r < 0)
+                return r;
+
+        device_attach(device, seat);
+        return 0;
+}
+
+int manager_enumerate_devices(Manager *m) {
+        struct udev_list_entry *item = NULL, *first = NULL;
+        struct udev_enumerate *e;
+        int r = -ENOMEM;
+
+        assert(m);
+
+        /* Loads devices from udev and creates seats for them as
+         * necessary */
+
+        e = udev_enumerate_new(m->udev);
+        if (!e)
+                return -ENOMEM;
+
+        if (udev_enumerate_add_match_subsystem(e, "graphics") < 0)
+                goto finish;
+
+        if (udev_enumerate_add_match_tag(e, "seat") < 0)
+                goto finish;
+
+        if (udev_enumerate_scan_devices(e) < 0)
+                goto finish;
+
+        first = udev_enumerate_get_list_entry(e);
+        udev_list_entry_foreach(item, first) {
+                struct udev_device *d;
+                int k;
+
+                d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
+                if (!d)
+                        goto finish;
+
+                k = manager_process_device(m, d);
+                udev_device_unref(d);
+
+                if (k < 0)
+                        r = k;
+        }
+
+finish:
+        if (e)
+                udev_enumerate_unref(e);
+
+        return r;
+}
+
+int manager_enumerate_seats(Manager *m) {
+        DIR *d;
+        struct dirent *de;
+        int r = 0;
+
+        assert(m);
+
+        /* This loads data about seats stored on disk, but does not
+         * actually create any seats. Removes data of seats that no
+         * longer exist. */
+
+        d = opendir("/run/systemd/seats");
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open /run/systemd/seats: %m");
+                return -errno;
+        }
+
+        while ((de = readdir(d))) {
+                Seat *s;
+                int k;
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                s = hashmap_get(m->seats, de->d_name);
+                if (!s) {
+                        unlinkat(dirfd(d), de->d_name, 0);
+                        continue;
+                }
+
+                k = seat_load(s);
+                if (k < 0)
+                        r = k;
+        }
+
+        closedir(d);
+
+        return r;
+}
+
+static int manager_enumerate_users_from_cgroup(Manager *m) {
+        int r = 0;
+        char *name;
+        DIR *d;
+
+        r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d);
+        if (r < 0) {
+                if (r == -ENOENT)
+                        return 0;
+
+                log_error("Failed to open %s: %s", m->cgroup_path, strerror(-r));
+                return r;
+        }
+
+        while ((r = cg_read_subgroup(d, &name)) > 0) {
+                User *user;
+
+                r = manager_add_user_by_name(m, name, &user);
+                free(name);
+
+                if (r < 0)
+                        break;
+
+                if (user->cgroup_path)
+                        continue;
+
+                if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) {
+                        r = -ENOMEM;
+                        break;
+                }
+        }
+
+        if (d)
+                closedir(d);
+
+        return r;
+}
+
+int manager_enumerate_users(Manager *m) {
+        DIR *d;
+        struct dirent *de;
+        int r;
+
+        assert(m);
+
+        r = manager_enumerate_users_from_cgroup(m);
+
+        d = opendir("/run/systemd/users");
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open /run/systemd/users: %m");
+                return -errno;
+        }
+
+        while ((de = readdir(d))) {
+                unsigned long ul;
+                User *u;
+                int k;
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                k = safe_atolu(de->d_name, &ul);
+                if (k < 0) {
+                        log_error("Failed to parse file name %s: %s", de->d_name, strerror(-k));
+                        continue;
+                }
+
+                u = hashmap_get(m->users, ULONG_TO_PTR(ul));
+                if (!u) {
+                        unlinkat(dirfd(d), de->d_name, 0);
+                        continue;
+                }
+
+                k = user_load(u);
+                if (k < 0)
+                        r = k;
+        }
+
+        closedir(d);
+
+        return r;
+}
+
+static int manager_enumerate_sessions_from_cgroup(Manager *m) {
+        User *u;
+        Iterator i;
+        int r = 0;
+
+        HASHMAP_FOREACH(u, m->users, i) {
+                DIR *d;
+                char *name;
+                int k;
+
+                k = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &d);
+                if (k < 0) {
+                        if (k == -ENOENT)
+                                continue;
+
+                        log_error("Failed to open %s: %s", u->cgroup_path, strerror(-k));
+                        r = k;
+                        continue;
+                }
+
+                while ((k = cg_read_subgroup(d, &name)) > 0) {
+                        Session *session;
+
+                        k = manager_add_session(m, u, name, &session);
+                        free(name);
+
+                        if (k < 0)
+                                break;
+
+                        if (session->cgroup_path)
+                                continue;
+
+                        if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) {
+                                k = -ENOMEM;
+                                break;
+                        }
+                }
+
+                closedir(d);
+
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+int manager_enumerate_sessions(Manager *m) {
+        DIR *d;
+        struct dirent *de;
+        int r = 0;
+
+        assert(m);
+
+        r = manager_enumerate_sessions_from_cgroup(m);
+
+        d = opendir("/run/systemd/sessions");
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open /run/systemd/sessions: %m");
+                return -errno;
+        }
+
+        while ((de = readdir(d))) {
+                struct Session *s;
+                int k;
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                s = hashmap_get(m->sessions, de->d_name);
+                if (!s) {
+                        unlinkat(dirfd(d), de->d_name, 0);
+                        continue;
+                }
+
+                k = session_load(s);
+                if (k < 0)
+                        r = k;
+        }
+
+        closedir(d);
+
+        return r;
+}
+
+int manager_start_linger_users(Manager *m) {
+        DIR *d;
+        struct dirent *de;
+        int r = 0;
+
+        d = opendir("/var/lib/systemd/linger");
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open /var/lib/systemd/linger/: %m");
+                return -errno;
+        }
+
+        while ((de = readdir(d))) {
+                int k;
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                k = manager_add_user_by_name(m, de->d_name, NULL);
+                if (k < 0) {
+                        log_notice("Couldn't add lingering user %s: %s", de->d_name, strerror(-k));
+                        r = k;
+                }
+        }
+
+        closedir(d);
+
+        return r;
+}
+
+int manager_dispatch_udev(Manager *m) {
+        struct udev_device *d;
+        int r;
+
+        assert(m);
+
+        d = udev_monitor_receive_device(m->udev_monitor);
+        if (!d)
+                return -ENOMEM;
+
+        r = manager_process_device(m, d);
+        udev_device_unref(d);
+
+        return r;
+}
+
+int manager_dispatch_console(Manager *m) {
+        char t[64];
+        ssize_t k;
+        int r, vtnr;
+
+        assert(m);
+
+        lseek(m->console_active_fd, SEEK_SET, 0);
+
+        k = read(m->console_active_fd, t, sizeof(t)-1);
+        if (k <= 0) {
+                log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
+                return k < 0 ? -errno : -EIO;
+        }
+
+        t[k] = 0;
+        if (!startswith(t, "tty")) {
+                log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
+                return -EIO;
+        }
+
+        r = safe_atoi(t+3, &vtnr);
+        if (r < 0) {
+                log_error("Failed to parse VT number %s", t+3);
+                return r;
+        }
+
+        if (vtnr <= 0) {
+                log_error("VT number invalid: %s", t+3);
+                return -EIO;
+        }
+
+        if (m->vtconsole)
+                seat_active_vt_changed(m->vtconsole, vtnr);
+
+        return 0;
+}
+
+int manager_spawn_autovt(Manager *m, int vtnr) {
+        assert(m);
+
+        return 0;
+}
+
+static DBusHandlerResult login_message_handler(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult login_message_filter(
+                DBusConnection *connection,
+                DBusMessage *message,
+                void *userdata) {
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static int manager_connect_bus(Manager *m) {
+        const DBusObjectPathVTable login_vtable = {
+                .message_function = login_message_handler
+        };
+
+        DBusError error;
+        int r;
+        struct epoll_event ev;
+
+        assert(m);
+        assert(!m->bus);
+        assert(m->bus_fd < 0);
+
+        dbus_error_init(&error);
+
+        m->bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+        if (!m->bus) {
+                log_error("Failed to get system D-Bus connection: %s", error.message);
+                r = -ECONNREFUSED;
+                goto fail;
+        }
+
+        if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &login_vtable, NULL)) {
+                log_error("Not enough memory");
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (!dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) {
+                log_error("Not enough memory");
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        dbus_bus_add_match(m->bus,
+                           "type='signal',"
+                           "interface='org.freedesktop.systemd1.Agent',"
+                           "member='Released',"
+                           "path='/org/freedesktop/systemd1/agent'",
+                           &error);
+
+        if (dbus_error_is_set(&error)) {
+                log_error("Failed to register match: %s", error.message);
+                r = -EIO;
+                goto fail;
+        }
+
+        if (dbus_bus_request_name(m->bus, "org.freedesktop.login1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
+                log_error("Failed to register name on bus: %s", error.message);
+                r = -EEXIST;
+                goto fail;
+        }
+
+        m->bus_fd = bus_loop_open(m->bus);
+        if (m->bus_fd < 0) {
+                r = m->bus_fd;
+                goto fail;
+        }
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.u32 = FD_BUS;
+
+        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int manager_connect_console(Manager *m) {
+        struct epoll_event ev;
+
+        assert(m);
+        assert(m->console_active_fd < 0);
+
+        m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC);
+        if (m->console_active_fd < 0) {
+                log_error("Failed to open /sys/class/tty/tty0/active: %m");
+                return -errno;
+        }
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.u32 = FD_CONSOLE;
+
+        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->console_active_fd, &ev) < 0)
+                return -errno;
+
+        return 0;
+}
+
+static int manager_connect_udev(Manager *m) {
+        struct epoll_event ev;
+
+        assert(m);
+        assert(!m->udev_monitor);
+
+        m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
+        if (!m->udev_monitor)
+                return -ENOMEM;
+
+        udev_monitor_filter_add_match_tag(m->udev_monitor, "seat");
+
+        if (udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "graphics", NULL) < 0)
+                return -ENOMEM;
+
+        if (udev_monitor_enable_receiving(m->udev_monitor) < 0)
+                return -ENOMEM;
+
+        m->udev_fd = udev_monitor_get_fd(m->udev_monitor);
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.u32 = FD_UDEV;
+
+        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_fd, &ev) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int manager_startup(Manager *m) {
+        int r;
+
+        assert(m);
+        assert(m->epoll_fd <= 0);
+
+        m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+        if (m->epoll_fd < 0)
+                return -errno;
+
+        /* Connect to udev */
+        r = manager_connect_udev(m);
+        if (r < 0)
+                return r;
+
+        /* Connect to console */
+        r = manager_connect_console(m);
+        if (r < 0)
+                return r;
+
+        /* Connect to the bus */
+        r = manager_connect_bus(m);
+        if (r < 0)
+                return r;
+
+        /* Deserialize state */
+        manager_enumerate_devices(m);
+        manager_enumerate_seats(m);
+        manager_enumerate_users(m);
+        manager_enumerate_sessions(m);
+
+        /* Spawn lingering users */
+        manager_start_linger_users(m);
+
+        return 0;
+}
+
+int manager_run(Manager *m) {
+        assert(m);
+
+        for (;;) {
+                struct epoll_event event;
+                int n;
+
+                if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE)
+                        continue;
+
+                n = epoll_wait(m->epoll_fd, &event, 1, -1);
+                if (n < 0) {
+                        log_error("epoll() failed: %m");
+                        return -errno;
+                }
+
+                switch (event.data.u32) {
+
+                case FD_UDEV:
+                        manager_dispatch_udev(m);
+                        break;
+
+                case FD_CONSOLE:
+                        manager_dispatch_console(m);
+                        break;
+
+                case FD_BUS:
+                        bus_loop_dispatch(m->bus_fd);
+                        break;
+                }
+        }
+
+        return 0;
+}
+
+int main(int argc, char *argv[]) {
+        Manager *m = NULL;
+        int r = 0;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc != 1) {
+                log_error("This program takes no arguments.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        umask(0022);
+
+        m = manager_new();
+        if (!m) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        r = manager_startup(m);
+        if (r < 0) {
+                log_error("Failed to fully start up daemon: %s", strerror(-r));
+                goto finish;
+        }
+
+        r = manager_run(m);
+
+finish:
+        if (m)
+                manager_free(m);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/logind.h b/src/logind.h
new file mode 100644
index 0000000..5e69a71
--- /dev/null
+++ b/src/logind.h
@@ -0,0 +1,235 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologindhfoo
+#define foologindhfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <inttypes.h>
+#include <dbus/dbus.h>
+#include <libudev.h>
+
+#include "util.h"
+#include "list.h"
+#include "hashmap.h"
+#include "cgroup-util.h"
+
+/* TODO:
+ *
+ * recreate VTs when disallocated
+ * udev rules
+ * spawn user systemd
+ * non-local X11 server
+ * udev-acl
+ * reboot/shutdown halt management
+ * PAM rewrite
+ */
+
+typedef struct Manager Manager;
+typedef struct Device Device;
+typedef struct Seat Seat;
+typedef struct Session Session;
+typedef struct User User;
+
+struct Device {
+        Manager *manager;
+
+        char *sysfs;
+        Seat *seat;
+
+        dual_timestamp timestamp;
+
+        LIST_FIELDS(struct Device, devices);
+};
+
+struct Seat {
+        Manager *manager;
+        char *id;
+
+        char *state_file;
+
+        LIST_HEAD(Device, devices);
+
+        Session *active;
+        LIST_HEAD(Session, sessions);
+};
+
+typedef enum SessionType {
+        SESSION_TERMINAL,
+        SESSION_X11,
+        _SESSION_TYPE_MAX,
+        _SESSION_TYPE_INVALID = -1
+} SessionType;
+
+struct Session {
+        Manager *manager;
+
+        char *id;
+        SessionType type;
+
+        char *state_file;
+
+        User *user;
+
+        dual_timestamp timestamp;
+
+        char *tty;
+        char *display;
+
+        bool remote;
+        char *remote_host;
+
+        int vtnr;
+        Seat *seat;
+
+        pid_t leader;
+        uint64_t audit_id;
+
+        int pipe_fd;
+
+        char *cgroup_path;
+        char **controllers, **reset_controllers;
+
+        bool kill_processes;
+
+        LIST_FIELDS(Session, sessions_by_user);
+        LIST_FIELDS(Session, sessions_by_seat);
+};
+
+typedef enum UserState {
+        USER_OFFLINE,
+        USER_LINGERING,
+        USER_ONLINE,
+        USER_ACTIVE,
+        _USER_STATE_MAX,
+        _USER_STATE_INVALID = -1
+} UserState;
+
+struct User {
+        Manager *manager;
+
+        uid_t uid;
+        gid_t gid;
+        char *name;
+
+        char *state_file;
+        char *runtime_path;
+        char *service;
+        char *cgroup_path;
+
+        Session *display;
+
+        dual_timestamp timestamp;
+
+        LIST_HEAD(Session, sessions);
+};
+
+struct Manager {
+        DBusConnection *bus;
+
+        Hashmap *devices;
+        Hashmap *seats;
+        Hashmap *sessions;
+        Hashmap *users;
+
+        struct udev *udev;
+        struct udev_monitor *udev_monitor;
+
+        int udev_fd;
+        int console_active_fd;
+        int bus_fd;
+        int epoll_fd;
+
+        int n_autovts;
+
+        Seat *vtconsole;
+
+        char *cgroup_path;
+        char **controllers, **reset_controllers;
+
+        char **kill_only_users, **kill_exlude_users;
+
+        bool kill_user_processes;
+};
+
+Device* device_new(Manager *m, const char *sysfs);
+void device_free(Device *d);
+void device_attach(Device *d, Seat *s);
+void device_detach(Device *d);
+
+Seat *seat_new(Manager *m, const char *id);
+void seat_free(Seat *s);
+int seat_preallocate_vts(Seat *s);
+void seat_active_vt_changed(Seat *s, int vtnr);
+int seat_apply_acls(Seat *s);
+int seat_stop(Seat *s);
+int seat_save(Seat *s);
+int seat_load(Seat *s);
+
+Session *session_new(Manager *m, User *u, const char *id);
+void session_free(Session *s);
+int session_activate(Session *s);
+bool session_is_active(Session *s);
+int session_check_gc(Session *s);
+int session_start(Session *s);
+int session_stop(Session *s);
+int session_save(Session *s);
+int session_load(Session *s);
+
+User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name);
+void user_free(User *u);
+int user_start(User *u);
+int user_stop(User *u);
+int user_check_gc(User *u);
+UserState user_get_state(User *u);
+int user_save(User *u);
+int user_load(User *u);
+
+Manager *manager_new(void);
+void manager_free(Manager *m);
+int manager_add_device(Manager *m, const char *sysfs, Device **_device);
+int manager_add_seat(Manager *m, const char *id, Seat **_seat);
+int manager_add_session(Manager *m, User *u, const char *id, Session **_session);
+int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
+int manager_add_user_by_name(Manager *m, const char *name, User **_user);
+int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
+int manager_process_device(Manager *m, struct udev_device *d);
+int manager_dispatch_udev(Manager *m);
+int manager_dispatch_console(Manager *m);
+int manager_enumerate_devices(Manager *m);
+int manager_enumerate_seats(Manager *m);
+int manager_enumerate_sessions(Manager *m);
+int manager_enumerate_users(Manager *m);
+int manager_start_one_linger_user(Manager *m, const char *user);
+int manager_start_linger_users(Manager *m);
+int manager_startup(Manager *m);
+int manager_run(Manager *m);
+int manager_spawn_autovt(Manager *m, int vtnr);
+
+const char* session_type_to_string(SessionType t);
+SessionType session_type_from_string(const char *s);
+
+const char* user_state_to_string(UserState s);
+UserState user_state_from_string(const char *s);
+
+bool x11_display_is_local(const char *display);
+
+#endif

commit f41607a6e10202003efe307eab975e8aca37d3de
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 23:54:22 2011 +0200

    util: move string enum mapping to the end

diff --git a/src/util.c b/src/util.c
index 4a735db..ad1ca06 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4537,141 +4537,6 @@ int vt_disallocate(const char *name) {
         return 0;
 }
 
-static const char *const ioprio_class_table[] = {
-        [IOPRIO_CLASS_NONE] = "none",
-        [IOPRIO_CLASS_RT] = "realtime",
-        [IOPRIO_CLASS_BE] = "best-effort",
-        [IOPRIO_CLASS_IDLE] = "idle"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
-
-static const char *const sigchld_code_table[] = {
-        [CLD_EXITED] = "exited",
-        [CLD_KILLED] = "killed",
-        [CLD_DUMPED] = "dumped",
-        [CLD_TRAPPED] = "trapped",
-        [CLD_STOPPED] = "stopped",
-        [CLD_CONTINUED] = "continued",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
-
-static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
-        [LOG_FAC(LOG_KERN)] = "kern",
-        [LOG_FAC(LOG_USER)] = "user",
-        [LOG_FAC(LOG_MAIL)] = "mail",
-        [LOG_FAC(LOG_DAEMON)] = "daemon",
-        [LOG_FAC(LOG_AUTH)] = "auth",
-        [LOG_FAC(LOG_SYSLOG)] = "syslog",
-        [LOG_FAC(LOG_LPR)] = "lpr",
-        [LOG_FAC(LOG_NEWS)] = "news",
-        [LOG_FAC(LOG_UUCP)] = "uucp",
-        [LOG_FAC(LOG_CRON)] = "cron",
-        [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
-        [LOG_FAC(LOG_FTP)] = "ftp",
-        [LOG_FAC(LOG_LOCAL0)] = "local0",
-        [LOG_FAC(LOG_LOCAL1)] = "local1",
-        [LOG_FAC(LOG_LOCAL2)] = "local2",
-        [LOG_FAC(LOG_LOCAL3)] = "local3",
-        [LOG_FAC(LOG_LOCAL4)] = "local4",
-        [LOG_FAC(LOG_LOCAL5)] = "local5",
-        [LOG_FAC(LOG_LOCAL6)] = "local6",
-        [LOG_FAC(LOG_LOCAL7)] = "local7"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
-
-static const char *const log_level_table[] = {
-        [LOG_EMERG] = "emerg",
-        [LOG_ALERT] = "alert",
-        [LOG_CRIT] = "crit",
-        [LOG_ERR] = "err",
-        [LOG_WARNING] = "warning",
-        [LOG_NOTICE] = "notice",
-        [LOG_INFO] = "info",
-        [LOG_DEBUG] = "debug"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_level, int);
-
-static const char* const sched_policy_table[] = {
-        [SCHED_OTHER] = "other",
-        [SCHED_BATCH] = "batch",
-        [SCHED_IDLE] = "idle",
-        [SCHED_FIFO] = "fifo",
-        [SCHED_RR] = "rr"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
-
-static const char* const rlimit_table[] = {
-        [RLIMIT_CPU] = "LimitCPU",
-        [RLIMIT_FSIZE] = "LimitFSIZE",
-        [RLIMIT_DATA] = "LimitDATA",
-        [RLIMIT_STACK] = "LimitSTACK",
-        [RLIMIT_CORE] = "LimitCORE",
-        [RLIMIT_RSS] = "LimitRSS",
-        [RLIMIT_NOFILE] = "LimitNOFILE",
-        [RLIMIT_AS] = "LimitAS",
-        [RLIMIT_NPROC] = "LimitNPROC",
-        [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
-        [RLIMIT_LOCKS] = "LimitLOCKS",
-        [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
-        [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
-        [RLIMIT_NICE] = "LimitNICE",
-        [RLIMIT_RTPRIO] = "LimitRTPRIO",
-        [RLIMIT_RTTIME] = "LimitRTTIME"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
-
-static const char* const ip_tos_table[] = {
-        [IPTOS_LOWDELAY] = "low-delay",
-        [IPTOS_THROUGHPUT] = "throughput",
-        [IPTOS_RELIABILITY] = "reliability",
-        [IPTOS_LOWCOST] = "low-cost",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
-
-static const char *const signal_table[] = {
-        [SIGHUP] = "HUP",
-        [SIGINT] = "INT",
-        [SIGQUIT] = "QUIT",
-        [SIGILL] = "ILL",
-        [SIGTRAP] = "TRAP",
-        [SIGABRT] = "ABRT",
-        [SIGBUS] = "BUS",
-        [SIGFPE] = "FPE",
-        [SIGKILL] = "KILL",
-        [SIGUSR1] = "USR1",
-        [SIGSEGV] = "SEGV",
-        [SIGUSR2] = "USR2",
-        [SIGPIPE] = "PIPE",
-        [SIGALRM] = "ALRM",
-        [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
-        [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
-        [SIGCHLD] = "CHLD",
-        [SIGCONT] = "CONT",
-        [SIGSTOP] = "STOP",
-        [SIGTSTP] = "TSTP",
-        [SIGTTIN] = "TTIN",
-        [SIGTTOU] = "TTOU",
-        [SIGURG] = "URG",
-        [SIGXCPU] = "XCPU",
-        [SIGXFSZ] = "XFSZ",
-        [SIGVTALRM] = "VTALRM",
-        [SIGPROF] = "PROF",
-        [SIGWINCH] = "WINCH",
-        [SIGIO] = "IO",
-        [SIGPWR] = "PWR",
-        [SIGSYS] = "SYS"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(signal, int);
 
 static int file_is_conf(const struct dirent *d, const char *suffix) {
         assert(d);
@@ -4868,3 +4733,139 @@ int hwclock_set_time(const struct tm *tm) {
 
         return err;
 }
+
+static const char *const ioprio_class_table[] = {
+        [IOPRIO_CLASS_NONE] = "none",
+        [IOPRIO_CLASS_RT] = "realtime",
+        [IOPRIO_CLASS_BE] = "best-effort",
+        [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
+
+static const char *const sigchld_code_table[] = {
+        [CLD_EXITED] = "exited",
+        [CLD_KILLED] = "killed",
+        [CLD_DUMPED] = "dumped",
+        [CLD_TRAPPED] = "trapped",
+        [CLD_STOPPED] = "stopped",
+        [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
+        [LOG_FAC(LOG_KERN)] = "kern",
+        [LOG_FAC(LOG_USER)] = "user",
+        [LOG_FAC(LOG_MAIL)] = "mail",
+        [LOG_FAC(LOG_DAEMON)] = "daemon",
+        [LOG_FAC(LOG_AUTH)] = "auth",
+        [LOG_FAC(LOG_SYSLOG)] = "syslog",
+        [LOG_FAC(LOG_LPR)] = "lpr",
+        [LOG_FAC(LOG_NEWS)] = "news",
+        [LOG_FAC(LOG_UUCP)] = "uucp",
+        [LOG_FAC(LOG_CRON)] = "cron",
+        [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+        [LOG_FAC(LOG_FTP)] = "ftp",
+        [LOG_FAC(LOG_LOCAL0)] = "local0",
+        [LOG_FAC(LOG_LOCAL1)] = "local1",
+        [LOG_FAC(LOG_LOCAL2)] = "local2",
+        [LOG_FAC(LOG_LOCAL3)] = "local3",
+        [LOG_FAC(LOG_LOCAL4)] = "local4",
+        [LOG_FAC(LOG_LOCAL5)] = "local5",
+        [LOG_FAC(LOG_LOCAL6)] = "local6",
+        [LOG_FAC(LOG_LOCAL7)] = "local7"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
+
+static const char *const log_level_table[] = {
+        [LOG_EMERG] = "emerg",
+        [LOG_ALERT] = "alert",
+        [LOG_CRIT] = "crit",
+        [LOG_ERR] = "err",
+        [LOG_WARNING] = "warning",
+        [LOG_NOTICE] = "notice",
+        [LOG_INFO] = "info",
+        [LOG_DEBUG] = "debug"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_level, int);
+
+static const char* const sched_policy_table[] = {
+        [SCHED_OTHER] = "other",
+        [SCHED_BATCH] = "batch",
+        [SCHED_IDLE] = "idle",
+        [SCHED_FIFO] = "fifo",
+        [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
+
+static const char* const rlimit_table[] = {
+        [RLIMIT_CPU] = "LimitCPU",
+        [RLIMIT_FSIZE] = "LimitFSIZE",
+        [RLIMIT_DATA] = "LimitDATA",
+        [RLIMIT_STACK] = "LimitSTACK",
+        [RLIMIT_CORE] = "LimitCORE",
+        [RLIMIT_RSS] = "LimitRSS",
+        [RLIMIT_NOFILE] = "LimitNOFILE",
+        [RLIMIT_AS] = "LimitAS",
+        [RLIMIT_NPROC] = "LimitNPROC",
+        [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
+        [RLIMIT_LOCKS] = "LimitLOCKS",
+        [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
+        [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
+        [RLIMIT_NICE] = "LimitNICE",
+        [RLIMIT_RTPRIO] = "LimitRTPRIO",
+        [RLIMIT_RTTIME] = "LimitRTTIME"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
+
+static const char* const ip_tos_table[] = {
+        [IPTOS_LOWDELAY] = "low-delay",
+        [IPTOS_THROUGHPUT] = "throughput",
+        [IPTOS_RELIABILITY] = "reliability",
+        [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
+
+static const char *const signal_table[] = {
+        [SIGHUP] = "HUP",
+        [SIGINT] = "INT",
+        [SIGQUIT] = "QUIT",
+        [SIGILL] = "ILL",
+        [SIGTRAP] = "TRAP",
+        [SIGABRT] = "ABRT",
+        [SIGBUS] = "BUS",
+        [SIGFPE] = "FPE",
+        [SIGKILL] = "KILL",
+        [SIGUSR1] = "USR1",
+        [SIGSEGV] = "SEGV",
+        [SIGUSR2] = "USR2",
+        [SIGPIPE] = "PIPE",
+        [SIGALRM] = "ALRM",
+        [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+        [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+        [SIGCHLD] = "CHLD",
+        [SIGCONT] = "CONT",
+        [SIGSTOP] = "STOP",
+        [SIGTSTP] = "TSTP",
+        [SIGTTIN] = "TTIN",
+        [SIGTTOU] = "TTOU",
+        [SIGURG] = "URG",
+        [SIGXCPU] = "XCPU",
+        [SIGXFSZ] = "XFSZ",
+        [SIGVTALRM] = "VTALRM",
+        [SIGPROF] = "PROF",
+        [SIGWINCH] = "WINCH",
+        [SIGIO] = "IO",
+        [SIGPWR] = "PWR",
+        [SIGSYS] = "SYS"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(signal, int);

commit 1325aa4202cbd393c9577794e2b4995cc8743892
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 23:54:00 2011 +0200

    util: add pipe_eof()

diff --git a/src/util.c b/src/util.c
index 7f59021..4a735db 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4437,6 +4437,24 @@ char* hostname_cleanup(char *s) {
         return s;
 }
 
+int pipe_eof(int fd) {
+        struct pollfd pollfd;
+        int r;
+
+        zero(pollfd);
+        pollfd.fd = fd;
+        pollfd.events = POLLIN|POLLHUP;
+
+        r = poll(&pollfd, 1, 0);
+        if (r < 0)
+                return -errno;
+
+        if (r == 0)
+                return 0;
+
+        return pollfd.revents & POLLHUP;
+}
+
 int terminal_vhangup_fd(int fd) {
         if (ioctl(fd, TIOCVHANGUP) < 0)
                 return -errno;
diff --git a/src/util.h b/src/util.h
index f2156af..6076e69 100644
--- a/src/util.h
+++ b/src/util.h
@@ -357,6 +357,8 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
 
 int rm_rf(const char *path, bool only_dirs, bool delete_root);
 
+int pipe_eof(int fd);
+
 cpu_set_t* cpu_set_malloc(unsigned *ncpus);
 
 void status_vprintf(const char *format, va_list ap);

commit ab5c3e3ff172e7dc295d3022170ee6a3be062a3f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 23:53:43 2011 +0200

    english: s/_per_/_by_/

diff --git a/src/automount.c b/src/automount.c
index 33c962e..51fa003 100644
--- a/src/automount.c
+++ b/src/automount.c
@@ -142,7 +142,7 @@ static int automount_add_mount_links(Automount *a) {
 
         assert(a);
 
-        LIST_FOREACH(units_per_type, other, a->meta.manager->units_per_type[UNIT_MOUNT])
+        LIST_FOREACH(units_by_type, other, a->meta.manager->units_by_type[UNIT_MOUNT])
                 if ((r = automount_add_one_mount_link(a, (Mount*) other)) < 0)
                         return r;
 
diff --git a/src/manager.h b/src/manager.h
index 07b92c8..4557d5f 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -102,7 +102,7 @@ struct Manager {
 
         /* To make it easy to iterate through the units of a specific
          * type we maintain a per type linked list */
-        LIST_HEAD(Meta, units_per_type[_UNIT_TYPE_MAX]);
+        LIST_HEAD(Meta, units_by_type[_UNIT_TYPE_MAX]);
 
         /* Units that need to be loaded */
         LIST_HEAD(Meta, load_queue); /* this is actually more a stack than a queue, but uh. */
diff --git a/src/mount.c b/src/mount.c
index 423cf43..660af13 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -114,7 +114,7 @@ static void mount_done(Unit *u) {
         m->where = NULL;
 
         /* Try to detach us from the automount unit if there is any */
-        LIST_FOREACH(units_per_type, other, m->meta.manager->units_per_type[UNIT_AUTOMOUNT]) {
+        LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_AUTOMOUNT]) {
                 Automount *a = (Automount*) other;
 
                 if (a->mount == m)
@@ -166,7 +166,7 @@ static int mount_add_mount_links(Mount *m) {
         /* Adds in links to other mount points that might lie below or
          * above us in the hierarchy */
 
-        LIST_FOREACH(units_per_type, other, m->meta.manager->units_per_type[UNIT_MOUNT]) {
+        LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_MOUNT]) {
                 Mount *n = (Mount*) other;
                 MountParameters *pn;
 
@@ -223,7 +223,7 @@ static int mount_add_swap_links(Mount *m) {
 
         assert(m);
 
-        LIST_FOREACH(units_per_type, other, m->meta.manager->units_per_type[UNIT_SWAP])
+        LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_SWAP])
                 if ((r = swap_add_one_mount_link((Swap*) other, m)) < 0)
                         return r;
 
@@ -236,7 +236,7 @@ static int mount_add_path_links(Mount *m) {
 
         assert(m);
 
-        LIST_FOREACH(units_per_type, other, m->meta.manager->units_per_type[UNIT_PATH])
+        LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_PATH])
                 if ((r = path_add_one_mount_link((Path*) other, m)) < 0)
                         return r;
 
@@ -249,7 +249,7 @@ static int mount_add_automount_links(Mount *m) {
 
         assert(m);
 
-        LIST_FOREACH(units_per_type, other, m->meta.manager->units_per_type[UNIT_AUTOMOUNT])
+        LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_AUTOMOUNT])
                 if ((r = automount_add_one_mount_link((Automount*) other, m)) < 0)
                         return r;
 
@@ -262,7 +262,7 @@ static int mount_add_socket_links(Mount *m) {
 
         assert(m);
 
-        LIST_FOREACH(units_per_type, other, m->meta.manager->units_per_type[UNIT_SOCKET])
+        LIST_FOREACH(units_by_type, other, m->meta.manager->units_by_type[UNIT_SOCKET])
                 if ((r = socket_add_one_mount_link((Socket*) other, m)) < 0)
                         return r;
 
@@ -1687,7 +1687,7 @@ void mount_fd_event(Manager *m, int events) {
                 log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r));
 
                 /* Reset flags, just in case, for later calls */
-                LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_MOUNT]) {
+                LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_MOUNT]) {
                         Mount *mount = (Mount*) meta;
 
                         mount->is_mounted = mount->just_mounted = mount->just_changed = false;
@@ -1698,7 +1698,7 @@ void mount_fd_event(Manager *m, int events) {
 
         manager_dispatch_load_queue(m);
 
-        LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_MOUNT]) {
+        LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_MOUNT]) {
                 Mount *mount = (Mount*) meta;
 
                 if (!mount->is_mounted) {
diff --git a/src/path.c b/src/path.c
index c722a45..1c20dcf 100644
--- a/src/path.c
+++ b/src/path.c
@@ -102,7 +102,7 @@ static int path_add_mount_links(Path *p) {
 
         assert(p);
 
-        LIST_FOREACH(units_per_type, other, p->meta.manager->units_per_type[UNIT_MOUNT])
+        LIST_FOREACH(units_by_type, other, p->meta.manager->units_by_type[UNIT_MOUNT])
                 if ((r = path_add_one_mount_link(p, (Mount*) other)) < 0)
                         return r;
 
diff --git a/src/service.c b/src/service.c
index d59c4cb..1f74868 100644
--- a/src/service.c
+++ b/src/service.c
@@ -372,7 +372,7 @@ static int sysv_fix_order(Service *s) {
         /* For each pair of services where at least one lacks a LSB
          * header, we use the start priority value to order things. */
 
-        LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_SERVICE]) {
+        LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) {
                 Service *t;
                 UnitDependency d;
                 bool special_s, special_t;
@@ -1006,7 +1006,7 @@ static int fsck_fix_order(Service *s) {
         /* For each pair of services where both have an fsck priority
          * we order things based on it. */
 
-        LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_SERVICE]) {
+        LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) {
                 Service *t;
                 UnitDependency d;
 
diff --git a/src/socket.c b/src/socket.c
index c013958..8b78a3f 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -131,7 +131,7 @@ static void socket_done(Unit *u) {
         unit_unwatch_timer(u, &s->timer_watch);
 
         /* Make sure no service instance refers to us anymore. */
-        LIST_FOREACH(units_per_type, i, u->meta.manager->units_per_type[UNIT_SERVICE]) {
+        LIST_FOREACH(units_by_type, i, u->meta.manager->units_by_type[UNIT_SERVICE]) {
                 Service *service = (Service *) i;
 
                 if (service->accept_socket == s)
@@ -283,7 +283,7 @@ static int socket_add_mount_links(Socket *s) {
 
         assert(s);
 
-        LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_MOUNT])
+        LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_MOUNT])
                 if ((r = socket_add_one_mount_link(s, (Mount*) other)) < 0)
                         return r;
 
@@ -1361,7 +1361,7 @@ static void socket_enter_running(Socket *s, int cfd) {
 
                 /* If there's already a start pending don't bother to
                  * do anything */
-                LIST_FOREACH(units_per_type, i, s->meta.manager->units_per_type[UNIT_SERVICE]) {
+                LIST_FOREACH(units_by_type, i, s->meta.manager->units_by_type[UNIT_SERVICE]) {
                         Service *service = (Service *) i;
 
                         if (!set_get(service->configured_sockets, s))
diff --git a/src/swap.c b/src/swap.c
index 04df585..14719ad 100644
--- a/src/swap.c
+++ b/src/swap.c
@@ -155,7 +155,7 @@ static int swap_add_mount_links(Swap *s) {
 
         assert(s);
 
-        LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_MOUNT])
+        LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_MOUNT])
                 if ((r = swap_add_one_mount_link(s, (Mount*) other)) < 0)
                         return r;
 
@@ -1100,7 +1100,7 @@ int swap_fd_event(Manager *m, int events) {
                 log_error("Failed to reread /proc/swaps: %s", strerror(-r));
 
                 /* Reset flags, just in case, for late calls */
-                LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_SWAP]) {
+                LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_SWAP]) {
                         Swap *swap = (Swap*) meta;
 
                         swap->is_active = swap->just_activated = false;
@@ -1111,7 +1111,7 @@ int swap_fd_event(Manager *m, int events) {
 
         manager_dispatch_load_queue(m);
 
-        LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_SWAP]) {
+        LIST_FOREACH(units_by_type, meta, m->units_by_type[UNIT_SWAP]) {
                 Swap *swap = (Swap*) meta;
 
                 if (!swap->is_active) {
diff --git a/src/unit.c b/src/unit.c
index 9bb4e56..057431c 100644
--- a/src/unit.c
+++ b/src/unit.c
@@ -159,7 +159,7 @@ int unit_add_name(Unit *u, const char *text) {
                 u->meta.id = s;
                 u->meta.instance = i;
 
-                LIST_PREPEND(Meta, units_per_type, u->meta.manager->units_per_type[t], &u->meta);
+                LIST_PREPEND(Meta, units_by_type, u->meta.manager->units_by_type[t], &u->meta);
 
                 if (UNIT_VTABLE(u)->init)
                         UNIT_VTABLE(u)->init(u);
@@ -354,7 +354,7 @@ void unit_free(Unit *u) {
                 bidi_set_free(u, u->meta.dependencies[d]);
 
         if (u->meta.type != _UNIT_TYPE_INVALID)
-                LIST_REMOVE(Meta, units_per_type, u->meta.manager->units_per_type[u->meta.type], &u->meta);
+                LIST_REMOVE(Meta, units_by_type, u->meta.manager->units_by_type[u->meta.type], &u->meta);
 
         if (u->meta.in_load_queue)
                 LIST_REMOVE(Meta, load_queue, u->meta.manager->load_queue, &u->meta);
diff --git a/src/unit.h b/src/unit.h
index 717c928..1c8cf63 100644
--- a/src/unit.h
+++ b/src/unit.h
@@ -168,7 +168,7 @@ struct Meta {
         CGroupBonding *cgroup_bondings;
 
         /* Per type list */
-        LIST_FIELDS(Meta, units_per_type);
+        LIST_FIELDS(Meta, units_by_type);
 
         /* Load queue */
         LIST_FIELDS(Meta, load_queue);

commit fb19a739d528651e6c78e198269ae856192ffc68
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 21:39:15 2011 +0200

    util: introduce dirent_is_file()

diff --git a/src/util.c b/src/util.c
index 4046938..7f59021 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4181,6 +4181,20 @@ finish:
         return r;
 }
 
+bool dirent_is_file(struct dirent *de) {
+        assert(de);
+
+        if (ignore_file(de->d_name))
+                return false;
+
+        if (de->d_type != DT_REG &&
+            de->d_type != DT_LNK &&
+            de->d_type != DT_UNKNOWN)
+                return false;
+
+        return true;
+}
+
 void execute_directory(const char *directory, DIR *d, char *argv[]) {
         DIR *_d = NULL;
         struct dirent *de;
@@ -4214,12 +4228,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
                 pid_t pid;
                 int k;
 
-                if (ignore_file(de->d_name))
-                        continue;
-
-                if (de->d_type != DT_REG &&
-                    de->d_type != DT_LNK &&
-                    de->d_type != DT_UNKNOWN)
+                if (!dirent_is_file(de))
                         continue;
 
                 if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
diff --git a/src/util.h b/src/util.h
index 79d634b..f2156af 100644
--- a/src/util.h
+++ b/src/util.h
@@ -268,6 +268,7 @@ bool path_equal(const char *a, const char *b);
 
 char *ascii_strlower(char *path);
 
+bool dirent_is_file(struct dirent *de);
 bool ignore_file(const char *filename);
 
 bool chars_intersect(const char *a, const char *b);

commit d0baa06fda52905c24d93df27a9f3e879fab4d0d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 21:38:20 2011 +0200

    hostnamed: split dbus initialization into a separate function

diff --git a/src/hostnamed.c b/src/hostnamed.c
index ce69045..cf2172f 100644
--- a/src/hostnamed.c
+++ b/src/hostnamed.c
@@ -655,17 +655,55 @@ oom:
         return DBUS_HANDLER_RESULT_NEED_MEMORY;
 }
 
-int main(int argc, char *argv[]) {
-        const DBusObjectPathVTable hostname_vtable = {
+static int connect_bus(DBusConnection **_bus) {
+        static const DBusObjectPathVTable hostname_vtable = {
                 .message_function = hostname_message_handler
         };
-
-        DBusConnection *bus = NULL;
         DBusError error;
+        DBusConnection *bus = NULL;
         int r;
 
+        assert(_bus);
+
         dbus_error_init(&error);
 
+        bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
+        if (!bus) {
+                log_error("Failed to get system D-Bus connection: %s", error.message);
+                r = -ECONNREFUSED;
+                goto fail;
+        }
+
+        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
+                log_error("Not enough memory");
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
+                log_error("Failed to register name on bus: %s", error.message);
+                r = -EEXIST;
+                goto fail;
+        }
+
+        if (_bus)
+                *_bus = bus;
+
+        return 0;
+
+fail:
+        dbus_connection_close(bus);
+        dbus_connection_unref(bus);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        DBusConnection *bus = NULL;
+
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
         log_open();
@@ -687,24 +725,9 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
-        if (!bus) {
-                log_error("Failed to get system D-Bus connection: %s", error.message);
-                r = -ECONNREFUSED;
-                goto finish;
-        }
-
-        if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) {
-                log_error("Not enough memory");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) {
-                log_error("Failed to register name on bus: %s", error.message);
-                r = -EEXIST;
+        r = connect_bus(&bus);
+        if (r < 0)
                 goto finish;
-        }
 
         while (dbus_connection_read_write_dispatch(bus, -1))
                 ;
@@ -720,7 +743,5 @@ finish:
                 dbus_connection_unref(bus);
         }
 
-        dbus_error_free(&error);
-
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }

commit 3df5bf612325b0f6a19acb67befe28d6b07d3804
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon May 23 21:36:52 2011 +0200

    dbus: make bus_flags_to_events() and friends generally useful

diff --git a/src/dbus-common.c b/src/dbus-common.c
index 5db077b..c2650fd 100644
--- a/src/dbus-common.c
+++ b/src/dbus-common.c
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <dbus/dbus.h>
 #include <string.h>
+#include <sys/epoll.h>
 
 #include "log.h"
 #include "dbus-common.h"
@@ -698,3 +699,38 @@ oom:
 
         return NULL;
 }
+
+uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
+        unsigned flags;
+        uint32_t events = 0;
+
+        assert(bus_watch);
+
+        /* no watch flags for disabled watches */
+        if (!dbus_watch_get_enabled(bus_watch))
+                return 0;
+
+        flags = dbus_watch_get_flags(bus_watch);
+
+        if (flags & DBUS_WATCH_READABLE)
+                events |= EPOLLIN;
+        if (flags & DBUS_WATCH_WRITABLE)
+                events |= EPOLLOUT;
+
+        return events | EPOLLHUP | EPOLLERR;
+}
+
+unsigned bus_events_to_flags(uint32_t events) {
+        unsigned flags = 0;
+
+        if (events & EPOLLIN)
+                flags |= DBUS_WATCH_READABLE;
+        if (events & EPOLLOUT)
+                flags |= DBUS_WATCH_WRITABLE;
+        if (events & EPOLLHUP)
+                flags |= DBUS_WATCH_HANGUP;
+        if (events & EPOLLERR)
+                flags |= DBUS_WATCH_ERROR;
+
+        return flags;
+}
diff --git a/src/dbus-common.h b/src/dbus-common.h
index a88cb13..1e5545f 100644
--- a/src/dbus-common.h
+++ b/src/dbus-common.h
@@ -153,4 +153,7 @@ const char *bus_errno_to_dbus(int error);
 
 DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties);
 
+uint32_t bus_flags_to_events(DBusWatch *bus_watch);
+unsigned bus_events_to_flags(uint32_t events);
+
 #endif
diff --git a/src/dbus.c b/src/dbus.c
index 187ed30..e153c35 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -91,41 +91,6 @@ static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status,
                 set_move_one(m->bus_connections_for_dispatch, m->bus_connections, bus);
 }
 
-static uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
-        unsigned flags;
-        uint32_t events = 0;
-
-        assert(bus_watch);
-
-        /* no watch flags for disabled watches */
-        if (!dbus_watch_get_enabled(bus_watch))
-                return 0;
-
-        flags = dbus_watch_get_flags(bus_watch);
-
-        if (flags & DBUS_WATCH_READABLE)
-                events |= EPOLLIN;
-        if (flags & DBUS_WATCH_WRITABLE)
-                events |= EPOLLOUT;
-
-        return events | EPOLLHUP | EPOLLERR;
-}
-
-static unsigned events_to_bus_flags(uint32_t events) {
-        unsigned flags = 0;
-
-        if (events & EPOLLIN)
-                flags |= DBUS_WATCH_READABLE;
-        if (events & EPOLLOUT)
-                flags |= DBUS_WATCH_WRITABLE;
-        if (events & EPOLLHUP)
-                flags |= DBUS_WATCH_HANGUP;
-        if (events & EPOLLERR)
-                flags |= DBUS_WATCH_ERROR;
-
-        return flags;
-}
-
 void bus_watch_event(Manager *m, Watch *w, int events) {
         assert(m);
         assert(w);
@@ -136,7 +101,7 @@ void bus_watch_event(Manager *m, Watch *w, int events) {
         if (!dbus_watch_get_enabled(w->data.bus_watch))
                 return;
 
-        dbus_watch_handle(w->data.bus_watch, events_to_bus_flags(events));
+        dbus_watch_handle(w->data.bus_watch, bus_events_to_flags(events));
 }
 
 static dbus_bool_t bus_add_watch(DBusWatch *bus_watch, void *data) {
@@ -196,7 +161,8 @@ static void bus_remove_watch(DBusWatch *bus_watch, void *data) {
         assert(bus_watch);
         assert(m);
 
-        if (!(w = dbus_watch_get_data(bus_watch)))
+        w = dbus_watch_get_data(bus_watch);
+        if (!w)
                 return;
 
         assert(w->type == WATCH_DBUS_WATCH);
@@ -216,7 +182,10 @@ static void bus_toggle_watch(DBusWatch *bus_watch, void *data) {
         assert(bus_watch);
         assert(m);
 
-        assert_se(w = dbus_watch_get_data(bus_watch));
+        w = dbus_watch_get_data(bus_watch);
+        if (!w)
+                return;
+
         assert(w->type == WATCH_DBUS_WATCH);
 
         zero(ev);
@@ -304,10 +273,12 @@ static void bus_remove_timeout(DBusTimeout *timeout, void *data) {
         assert(timeout);
         assert(m);
 
-        if (!(w = dbus_timeout_get_data(timeout)))
+        w = dbus_timeout_get_data(timeout);
+        if (!w)
                 return;
 
         assert(w->type == WATCH_DBUS_TIMEOUT);
+
         assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
         close_nointr_nofail(w->fd);
         free(w);
@@ -321,7 +292,10 @@ static void bus_toggle_timeout(DBusTimeout *timeout, void *data) {
         assert(timeout);
         assert(m);
 
-        assert_se(w = dbus_timeout_get_data(timeout));
+        w = dbus_timeout_get_data(timeout);
+        if (!w)
+                return;
+
         assert(w->type == WATCH_DBUS_TIMEOUT);
 
         if ((r = bus_timeout_arm(m, w)) < 0)
@@ -819,7 +793,7 @@ static int bus_init_system(Manager *m) {
 
         if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) {
                 log_error("Not enough memory");
-                r = -EIO;
+                r = -ENOMEM;
                 goto fail;
         }
 
diff --git a/src/manager.c b/src/manager.c
index 62451a0..19172a2 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -2260,7 +2260,7 @@ static int process_event(Manager *m, struct epoll_event *ev) {
         assert(m);
         assert(ev);
 
-        assert(w = ev->data.ptr);
+        assert_se(w = ev->data.ptr);
 
         if (w->type == WATCH_INVALID)
                 return 0;



More information about the systemd-commits mailing list