Add optional systemd support
authorMatthias Clasen <mclasen@redhat.com>
Tue, 20 Dec 2011 03:37:05 +0000 (22:37 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Tue, 3 Jan 2012 15:03:47 +0000 (10:03 -0500)
When configured with --enable-systemd, this patch makes
polkit use systemd for session tracking instead of ConsoleKit.

Signed-off-by: David Zeuthen <davidz@redhat.com>
configure.ac
src/polkit/Makefile.am
src/polkit/polkitunixsession-systemd.c [new file with mode: 0644]
src/polkitbackend/Makefile.am
src/polkitbackend/polkitbackendsessionmonitor-systemd.c [new file with mode: 0644]

index 2ed8401..51d6890 100644 (file)
@@ -148,6 +148,26 @@ if test "x$GCC" = "xyes"; then
 fi
 
 dnl ---------------------------------------------------------------------------
+dnl - Select wether to use systemd or ConsoleKit for session tracking
+dnl ---------------------------------------------------------------------------
+
+AC_ARG_ENABLE([systemd],
+              AS_HELP_STRING([--enable-systemd], [Use systemd]),
+              [with_systemd=$enableval],
+              [with_systemd=no])
+if test "$with_systemd" = "yes" ; then
+  PKG_CHECK_MODULES(SYSTEMD, [libsystemd-login])
+  SESSION_TRACKING=systemd
+else
+  SESSION_TRACKING=ConsoleKit
+fi
+
+AC_SUBST(SYSTEMD_CFLAGS)
+AC_SUBST(SYSTEMD_LIBS)
+
+AM_CONDITIONAL(HAVE_SYSTEMD, [test "$with_systemd" = "yes"], [Using systemd])
+
+dnl ---------------------------------------------------------------------------
 dnl - Select which authentication framework to use
 dnl ---------------------------------------------------------------------------
 
@@ -449,7 +469,8 @@ echo "
        introspection:              ${found_introspection}
 
         Distribution/OS:            ${with_os_type}
-        authentication framework:   ${POLKIT_AUTHFW}
+        Authentication framework:   ${POLKIT_AUTHFW}
+        Session tracking:           ${SESSION_TRACKING}
         PAM support:                ${have_pam}"
 
 if test "$have_pam" = yes ; then
@@ -459,7 +480,6 @@ echo "
         PAM file password:          ${PAM_FILE_INCLUDE_PASSWORD}
         PAM file session:           ${PAM_FILE_INCLUDE_SESSION}"
 fi
-
 echo "
         Maintainer mode:            ${USE_MAINTAINER_MODE}
         Building verbose mode:      ${enable_verbose_mode}
index 6c5a586..0c37151 100644 (file)
@@ -69,7 +69,6 @@ libpolkit_gobject_1_la_SOURCES =                                                      \
        polkiterror.c                           polkiterror.h                           \
        polkitsubject.c                         polkitsubject.h                         \
        polkitunixprocess.c                     polkitunixprocess.h                     \
-       polkitunixsession.c                     polkitunixsession.h                     \
        polkitsystembusname.c                   polkitsystembusname.h                   \
        polkitidentity.c                        polkitidentity.h                        \
        polkitunixuser.c                        polkitunixuser.h                        \
@@ -82,13 +81,23 @@ libpolkit_gobject_1_la_SOURCES =                                                    \
        polkitpermission.c                      polkitpermission.h                      \
         $(NULL)
 
+if HAVE_SYSTEMD
+libpolkit_gobject_1_la_SOURCES += \
+       polkitunixsession-systemd.c             polkitunixsession.h
+else
+libpolkit_gobject_1_la_SOURCES += \
+       polkitunixsession.c                     polkitunixsession.h
+endif
+
 libpolkit_gobject_1_la_CFLAGS =                                                \
         -D_POLKIT_COMPILATION                                                  \
         $(GLIB_CFLAGS)                                                 \
+       $(SYSTEMD_CFLAGS)                                               \
         $(NULL)
 
 libpolkit_gobject_1_la_LIBADD =                                        \
         $(GLIB_LIBS)                                                   \
+       $(SYSTEMD_LIBS)                                                 \
         $(NULL)
 
 libpolkit_gobject_1_la_LDFLAGS = -export-symbols-regex '(^polkit_.*)'
diff --git a/src/polkit/polkitunixsession-systemd.c b/src/polkit/polkitunixsession-systemd.c
new file mode 100644 (file)
index 0000000..e7e913f
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Matthias Clasen
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <string.h>
+#include "polkitunixsession.h"
+#include "polkitsubject.h"
+#include "polkiterror.h"
+#include "polkitprivate.h"
+
+#include <systemd/sd-login.h>
+
+/**
+ * SECTION:polkitunixsession
+ * @title: PolkitUnixSession
+ * @short_description: Unix sessions
+ *
+ * An object that represents an user session.
+ *
+ * The session id is an opaque string obtained from ConsoleKit.
+ */
+
+/**
+ * PolkitUnixSession:
+ *
+ * The #PolkitUnixSession struct should not be accessed directly.
+ */
+struct _PolkitUnixSession
+{
+  GObject parent_instance;
+
+  gchar *session_id;
+
+  gint pid;
+};
+
+struct _PolkitUnixSessionClass
+{
+  GObjectClass parent_class;
+};
+
+enum
+{
+  PROP_0,
+  PROP_SESSION_ID,
+  PROP_PID,
+};
+
+static void subject_iface_init        (PolkitSubjectIface *subject_iface);
+static void initable_iface_init       (GInitableIface *initable_iface);
+static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);
+
+G_DEFINE_TYPE_WITH_CODE (PolkitUnixSession, polkit_unix_session, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (POLKIT_TYPE_SUBJECT, subject_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
+                         );
+
+static void
+polkit_unix_session_init (PolkitUnixSession *session)
+{
+}
+
+static void
+polkit_unix_session_finalize (GObject *object)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (object);
+
+  g_free (session->session_id);
+
+  if (G_OBJECT_CLASS (polkit_unix_session_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (polkit_unix_session_parent_class)->finalize (object);
+}
+
+static void
+polkit_unix_session_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (object);
+
+  switch (prop_id)
+    {
+    case PROP_SESSION_ID:
+      g_value_set_string (value, session->session_id);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+polkit_unix_session_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (object);
+
+  switch (prop_id)
+    {
+    case PROP_SESSION_ID:
+      polkit_unix_session_set_session_id (session, g_value_get_string (value));
+      break;
+
+    case PROP_PID:
+      session->pid = g_value_get_int (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+polkit_unix_session_class_init (PolkitUnixSessionClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = polkit_unix_session_finalize;
+  gobject_class->get_property = polkit_unix_session_get_property;
+  gobject_class->set_property = polkit_unix_session_set_property;
+
+  /**
+   * PolkitUnixSession:session-id:
+   *
+   * The UNIX session id.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_SESSION_ID,
+                                   g_param_spec_string ("session-id",
+                                                        "Session ID",
+                                                        "The UNIX session ID",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+
+  /**
+   * PolkitUnixSession:pid:
+   *
+   * The UNIX process id to look up the session.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PID,
+                                   g_param_spec_int ("pid",
+                                                     "Process ID",
+                                                     "Process ID to use for looking up the session",
+                                                     0,
+                                                     G_MAXINT,
+                                                     0,
+                                                     G_PARAM_CONSTRUCT_ONLY |
+                                                     G_PARAM_WRITABLE |
+                                                     G_PARAM_STATIC_NAME |
+                                                     G_PARAM_STATIC_BLURB |
+                                                     G_PARAM_STATIC_NICK));
+
+}
+
+/**
+ * polkit_unix_session_get_session_id:
+ * @session: A #PolkitUnixSession.
+ *
+ * Gets the session id for @session.
+ *
+ * Returns: The session id for @session. Do not free this string, it
+ * is owned by @session.
+ **/
+const gchar *
+polkit_unix_session_get_session_id (PolkitUnixSession *session)
+{
+  g_return_val_if_fail (POLKIT_IS_UNIX_SESSION (session), NULL);
+  return session->session_id;
+}
+
+/**
+ * polkit_unix_session_set_session_id:
+ * @session: A #PolkitUnixSession.
+ * @session_id: The session id.
+ *
+ * Sets the session id for @session to @session_id.
+ **/
+void
+polkit_unix_session_set_session_id (PolkitUnixSession *session,
+                                    const gchar       *session_id)
+{
+  g_return_if_fail (POLKIT_IS_UNIX_SESSION (session));
+  /*g_return_if_fail (session_id != NULL);*/
+  g_free (session->session_id);
+  session->session_id = g_strdup (session_id);
+}
+
+/**
+ * polkit_unix_session_new:
+ * @session_id: The session id.
+ *
+ * Creates a new #PolkitUnixSession for @session_id.
+ *
+ * Returns: (transfer full): A #PolkitUnixSession. Free with g_object_unref().
+ **/
+PolkitSubject *
+polkit_unix_session_new (const gchar *session_id)
+{
+  return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_SESSION,
+                                       "session-id", session_id,
+                                       NULL));
+}
+
+/**
+ * polkit_unix_session_new_for_process:
+ * @pid: The process id of the process to get the session for.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: The data to pass to @callback.
+ *
+ * Asynchronously creates a new #PolkitUnixSession object for the
+ * process with process id @pid.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default
+ * main loop</link> of the thread you are calling this method
+ * from. You can then call
+ * polkit_unix_session_new_for_process_finish() to get the result of
+ * the operation.
+ *
+ * This method constructs the object asynchronously, for the synchronous and blocking version
+ * use polkit_unix_session_new_for_process_sync().
+ **/
+void
+polkit_unix_session_new_for_process (gint                pid,
+                                     GCancellable       *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer            user_data)
+{
+  g_async_initable_new_async (POLKIT_TYPE_UNIX_SESSION,
+                              G_PRIORITY_DEFAULT,
+                              cancellable,
+                              callback,
+                              user_data,
+                              "pid", pid,
+                              NULL);
+}
+
+/**
+ * polkit_unix_session_new_for_process_finish:
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to polkit_unix_session_new_for_process().
+ * @error: (allow-none): Return location for error.
+ *
+ * Finishes constructing a #PolkitSubject for a process id.
+ *
+ * Returns: (transfer full) (allow-none): A #PolkitUnixSession for the @pid passed to
+ *     polkit_unix_session_new_for_process() or %NULL if @error is
+ *     set. Free with g_object_unref().
+ **/
+PolkitSubject *
+polkit_unix_session_new_for_process_finish (GAsyncResult   *res,
+                                            GError        **error)
+{
+  GObject *object;
+  GObject *source_object;
+
+  source_object = g_async_result_get_source_object (res);
+  g_assert (source_object != NULL);
+
+  object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+                                        res,
+                                        error);
+  g_object_unref (source_object);
+
+  if (object != NULL)
+    return POLKIT_SUBJECT (object);
+  else
+    return NULL;
+}
+
+
+/**
+ * polkit_unix_session_new_for_process_sync:
+ * @pid: The process id of the process to get the session for.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: (allow-none): Return location for error.
+ *
+ * Creates a new #PolkitUnixSession for the process with process id @pid.
+ *
+ * This is a synchronous call - the calling thread is blocked until a
+ * reply is received. For the asynchronous version, see
+ * polkit_unix_session_new_for_process().
+ *
+ * Returns: (allow-none) (transfer full): A #PolkitUnixSession for
+ * @pid or %NULL if @error is set. Free with g_object_unref().
+ **/
+PolkitSubject *
+polkit_unix_session_new_for_process_sync (gint           pid,
+                                          GCancellable  *cancellable,
+                                          GError       **error)
+{
+  return POLKIT_SUBJECT (g_initable_new (POLKIT_TYPE_UNIX_SESSION,
+                                         cancellable,
+                                         error,
+                                         "pid", pid,
+                                         NULL));
+}
+
+static guint
+polkit_unix_session_hash (PolkitSubject *subject)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject);
+
+  return g_str_hash (session->session_id);
+}
+
+static gboolean
+polkit_unix_session_equal (PolkitSubject *a,
+                           PolkitSubject *b)
+{
+  PolkitUnixSession *session_a;
+  PolkitUnixSession *session_b;
+
+  session_a = POLKIT_UNIX_SESSION (a);
+  session_b = POLKIT_UNIX_SESSION (b);
+
+  return g_strcmp0 (session_a->session_id, session_b->session_id) == 0;
+}
+
+static gchar *
+polkit_unix_session_to_string (PolkitSubject *subject)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject);
+
+  return g_strdup_printf ("unix-session:%s", session->session_id);
+}
+
+static gboolean
+polkit_unix_session_exists_sync (PolkitSubject   *subject,
+                                    GCancellable    *cancellable,
+                                    GError         **error)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (subject);
+  gboolean ret;
+  uid_t uid;
+
+  ret = FALSE;
+
+  if (!sd_session_get_uid (session->session_id, &uid))
+    ret = FALSE;
+
+  return ret;
+}
+
+static void
+exists_in_thread_func (GSimpleAsyncResult *res,
+                       GObject            *object,
+                       GCancellable       *cancellable)
+{
+  GError *error;
+  error = NULL;
+  if (!polkit_unix_session_exists_sync (POLKIT_SUBJECT (object),
+                                        cancellable,
+                                        &error))
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+static void
+polkit_unix_session_exists (PolkitSubject       *subject,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (POLKIT_IS_UNIX_SESSION (subject));
+
+  simple = g_simple_async_result_new (G_OBJECT (subject),
+                                      callback,
+                                      user_data,
+                                      polkit_unix_session_exists);
+  g_simple_async_result_run_in_thread (simple,
+                                       exists_in_thread_func,
+                                       G_PRIORITY_DEFAULT,
+                                       cancellable);
+  g_object_unref (simple);
+}
+
+static gboolean
+polkit_unix_session_exists_finish (PolkitSubject  *subject,
+                                      GAsyncResult   *res,
+                                      GError        **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gboolean ret;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == polkit_unix_session_exists);
+
+  ret = FALSE;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    goto out;
+
+  ret = g_simple_async_result_get_op_res_gboolean (simple);
+
+ out:
+  return ret;
+}
+
+static void
+subject_iface_init (PolkitSubjectIface *subject_iface)
+{
+  subject_iface->hash          = polkit_unix_session_hash;
+  subject_iface->equal         = polkit_unix_session_equal;
+  subject_iface->to_string     = polkit_unix_session_to_string;
+  subject_iface->exists        = polkit_unix_session_exists;
+  subject_iface->exists_finish = polkit_unix_session_exists_finish;
+  subject_iface->exists_sync   = polkit_unix_session_exists_sync;
+}
+
+static gboolean
+polkit_unix_session_initable_init (GInitable     *initable,
+                                   GCancellable  *cancellable,
+                                   GError       **error)
+{
+  PolkitUnixSession *session = POLKIT_UNIX_SESSION (initable);
+  gboolean ret;
+
+  ret = FALSE;
+
+  if (session->session_id != NULL)
+    {
+      /* already set, nothing to do */
+      ret = TRUE;
+      goto out;
+    }
+
+  if (!sd_pid_get_session (session->pid, &session->session_id))
+    ret = TRUE;
+
+out:
+  return ret;
+}
+
+static void
+initable_iface_init (GInitableIface *initable_iface)
+{
+  initable_iface->init = polkit_unix_session_initable_init;
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *async_initable_iface)
+{
+  /* use default implementation to run GInitable code in a thread */
+}
index 168ea63..b91cafa 100644 (file)
@@ -38,20 +38,29 @@ libpolkit_backend_1_la_SOURCES =                                                    \
        polkitbackendinteractiveauthority.h     polkitbackendinteractiveauthority.c     \
        polkitbackendlocalauthority.h           polkitbackendlocalauthority.c           \
        polkitbackendactionpool.h               polkitbackendactionpool.c               \
-       polkitbackendsessionmonitor.h           polkitbackendsessionmonitor.c           \
        polkitbackendconfigsource.h             polkitbackendconfigsource.c             \
        polkitbackendactionlookup.h             polkitbackendactionlookup.c             \
        polkitbackendlocalauthorizationstore.h  polkitbackendlocalauthorizationstore.c  \
         $(NULL)
 
+if HAVE_SYSTEMD
+libpolkit_backend_1_la_SOURCES += \
+       polkitbackendsessionmonitor.h           polkitbackendsessionmonitor-systemd.c
+else
+libpolkit_backend_1_la_SOURCES += \
+       polkitbackendsessionmonitor.h           polkitbackendsessionmonitor.c
+endif
+
 libpolkit_backend_1_la_CFLAGS =                                                \
         -D_POLKIT_COMPILATION                                                  \
         -D_POLKIT_BACKEND_COMPILATION                                          \
         $(GLIB_CFLAGS)                                                 \
+       $(SYSTEMD_CFLAGS)                                               \
         $(NULL)
 
 libpolkit_backend_1_la_LIBADD =                                        \
         $(GLIB_LIBS)                                                   \
+       $(SYSTEMD_LIBS)                                                 \
        $(top_builddir)/src/polkit/libpolkit-gobject-1.la               \
        $(EXPAT_LIBS)                                                   \
         $(NULL)
diff --git a/src/polkitbackend/polkitbackendsessionmonitor-systemd.c b/src/polkitbackend/polkitbackendsessionmonitor-systemd.c
new file mode 100644 (file)
index 0000000..58593c3
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <glib/gstdio.h>
+#include <systemd/sd-login.h>
+#include <stdlib.h>
+
+#include <polkit/polkit.h>
+#include "polkitbackendsessionmonitor.h"
+
+/* <internal>
+ * SECTION:polkitbackendsessionmonitor
+ * @title: PolkitBackendSessionMonitor
+ * @short_description: Monitor sessions
+ *
+ * The #PolkitBackendSessionMonitor class is a utility class to track and monitor sessions.
+ */
+
+typedef struct
+{
+  GSource source;
+  GPollFD pollfd;
+  sd_login_monitor *monitor;
+} SdSource;
+
+static gboolean
+sd_source_prepare (GSource *source,
+                   gint    *timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean
+sd_source_check (GSource *source)
+{
+  SdSource *sd_source = (SdSource *)source;
+
+  return sd_source->pollfd.revents != 0;
+}
+
+static gboolean
+sd_source_dispatch (GSource     *source,
+                    GSourceFunc  callback,
+                    gpointer     user_data)
+
+{
+  SdSource *sd_source = (SdSource *)source;
+  gboolean ret;
+
+  g_warn_if_fail (callback != NULL);
+
+  ret = (*callback) (user_data);
+
+  sd_login_monitor_flush (sd_source->monitor);
+
+  return ret;
+}
+
+static void
+sd_source_finalize (GSource *source)
+{
+  SdSource *sd_source = (SdSource*)source;
+
+  sd_login_monitor_unref (sd_source->monitor);
+}
+
+static GSourceFuncs sd_source_funcs = {
+  sd_source_prepare,
+  sd_source_check,
+  sd_source_dispatch,
+  sd_source_finalize
+};
+
+static GSource *
+sd_source_new (void)
+{
+  GSource *source;
+  SdSource *sd_source;
+  int ret;
+
+  source = g_source_new (&sd_source_funcs, sizeof (SdSource));
+  sd_source = (SdSource *)source;
+
+  if ((ret = sd_login_monitor_new (NULL, &sd_source->monitor)) < 0)
+    {
+      g_printerr ("Error getting login monitor: %d", ret);
+    }
+  else
+    {
+      sd_source->pollfd.fd = sd_login_monitor_get_fd (sd_source->monitor);
+      sd_source->pollfd.events = G_IO_IN;
+      g_source_add_poll (source, &sd_source->pollfd);
+    }
+
+  return source;
+}
+
+struct _PolkitBackendSessionMonitor
+{
+  GObject parent_instance;
+
+  GDBusConnection *system_bus;
+
+  GSource *sd_source;
+};
+
+struct _PolkitBackendSessionMonitorClass
+{
+  GObjectClass parent_class;
+
+  void (*changed) (PolkitBackendSessionMonitor *monitor);
+};
+
+
+enum
+{
+  CHANGED_SIGNAL,
+  LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE (PolkitBackendSessionMonitor, polkit_backend_session_monitor, G_TYPE_OBJECT);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+sessions_changed (gpointer user_data)
+{
+  PolkitBackendSessionMonitor *monitor = POLKIT_BACKEND_SESSION_MONITOR (user_data);
+
+  g_signal_emit (monitor, signals[CHANGED_SIGNAL], 0);
+
+  return TRUE;
+}
+
+
+static void
+polkit_backend_session_monitor_init (PolkitBackendSessionMonitor *monitor)
+{
+  GError *error;
+
+  error = NULL;
+  monitor->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+  if (monitor->system_bus == NULL)
+    {
+      g_printerr ("Error getting system bus: %s", error->message);
+      g_error_free (error);
+    }
+
+  monitor->sd_source = sd_source_new ();
+  g_source_set_callback (monitor->sd_source, sessions_changed, monitor, NULL);
+  g_source_attach (monitor->sd_source, NULL);
+}
+
+static void
+polkit_backend_session_monitor_finalize (GObject *object)
+{
+  PolkitBackendSessionMonitor *monitor = POLKIT_BACKEND_SESSION_MONITOR (object);
+
+  if (monitor->system_bus != NULL)
+    g_object_unref (monitor->system_bus);
+
+  if (monitor->sd_source != NULL)
+    {
+      g_source_destroy (monitor->sd_source);
+      g_source_unref (monitor->sd_source);
+    }
+
+  if (G_OBJECT_CLASS (polkit_backend_session_monitor_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (polkit_backend_session_monitor_parent_class)->finalize (object);
+}
+
+static void
+polkit_backend_session_monitor_class_init (PolkitBackendSessionMonitorClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = polkit_backend_session_monitor_finalize;
+
+  /**
+   * PolkitBackendSessionMonitor::changed:
+   * @monitor: A #PolkitBackendSessionMonitor
+   *
+   * Emitted when something changes.
+   */
+  signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                          POLKIT_BACKEND_TYPE_SESSION_MONITOR,
+                                          G_SIGNAL_RUN_LAST,
+                                          G_STRUCT_OFFSET (PolkitBackendSessionMonitorClass, changed),
+                                          NULL,                   /* accumulator      */
+                                          NULL,                   /* accumulator data */
+                                          g_cclosure_marshal_VOID__VOID,
+                                          G_TYPE_NONE,
+                                          0);
+}
+
+PolkitBackendSessionMonitor *
+polkit_backend_session_monitor_new (void)
+{
+  PolkitBackendSessionMonitor *monitor;
+
+  monitor = POLKIT_BACKEND_SESSION_MONITOR (g_object_new (POLKIT_BACKEND_TYPE_SESSION_MONITOR, NULL));
+
+  return monitor;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GList *
+polkit_backend_session_monitor_get_sessions (PolkitBackendSessionMonitor *monitor)
+{
+  /* TODO */
+  return NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * polkit_backend_session_monitor_get_user:
+ * @monitor: A #PolkitBackendSessionMonitor.
+ * @subject: A #PolkitSubject.
+ * @error: Return location for error.
+ *
+ * Gets the user corresponding to @subject or %NULL if no user exists.
+ *
+ * Returns: %NULL if @error is set otherwise a #PolkitUnixUser that should be freed with g_object_unref().
+ */
+PolkitIdentity *
+polkit_backend_session_monitor_get_user_for_subject (PolkitBackendSessionMonitor  *monitor,
+                                                     PolkitSubject                *subject,
+                                                     GError                      **error)
+{
+  PolkitIdentity *ret;
+  guint32 uid;
+
+  ret = NULL;
+
+  if (POLKIT_IS_UNIX_PROCESS (subject))
+    {
+      uid = polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject));
+      if ((gint) uid == -1)
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "Unix process subject does not have uid set");
+          goto out;
+        }
+      ret = polkit_unix_user_new (uid);
+    }
+  else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
+    {
+      GVariant *result;
+
+      result = g_dbus_connection_call_sync (monitor->system_bus,
+                                            "org.freedesktop.DBus",
+                                            "/org/freedesktop/DBus",
+                                            "org.freedesktop.DBus",
+                                            "GetConnectionUnixUser",
+                                            g_variant_new ("(s)", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject))),
+                                            G_VARIANT_TYPE ("(u)"),
+                                            G_DBUS_CALL_FLAGS_NONE,
+                                            -1, /* timeout_msec */
+                                            NULL, /* GCancellable */
+                                            error);
+      if (result == NULL)
+        goto out;
+      g_variant_get (result, "(u)", &uid);
+      g_variant_unref (result);
+
+      ret = polkit_unix_user_new (uid);
+    }
+  else if (POLKIT_IS_UNIX_SESSION (subject))
+    {
+
+      if (sd_session_get_uid (polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (subject)), &uid) < 0)
+        {
+          g_set_error (error,
+                       POLKIT_ERROR,
+                       POLKIT_ERROR_FAILED,
+                       "Error getting uid for session");
+          goto out;
+        }
+
+      ret = polkit_unix_user_new (uid);
+    }
+
+ out:
+  return ret;
+}
+
+/**
+ * polkit_backend_session_monitor_get_session_for_subject:
+ * @monitor: A #PolkitBackendSessionMonitor.
+ * @subject: A #PolkitSubject.
+ * @error: Return location for error.
+ *
+ * Gets the session corresponding to @subject or %NULL if no session exists.
+ *
+ * Returns: %NULL if @error is set otherwise a #PolkitUnixSession that should be freed with g_object_unref().
+ */
+PolkitSubject *
+polkit_backend_session_monitor_get_session_for_subject (PolkitBackendSessionMonitor *monitor,
+                                                        PolkitSubject               *subject,
+                                                        GError                     **error)
+{
+  PolkitSubject *session;
+
+  session = NULL;
+
+  if (POLKIT_IS_UNIX_PROCESS (subject))
+    {
+      gchar *session_id;
+      pid_t pid;
+
+      pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject));
+      if (sd_pid_get_session (pid, &session_id) < 0)
+        goto out;
+
+      session = polkit_unix_session_new (session_id);
+      free (session_id);
+    }
+  else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
+    {
+      guint32 pid;
+      gchar *session_id;
+      GVariant *result;
+
+      result = g_dbus_connection_call_sync (monitor->system_bus,
+                                            "org.freedesktop.DBus",
+                                            "/org/freedesktop/DBus",
+                                            "org.freedesktop.DBus",
+                                            "GetConnectionUnixProcessID",
+                                            g_variant_new ("(s)", polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject))),
+                                            G_VARIANT_TYPE ("(u)"),
+                                            G_DBUS_CALL_FLAGS_NONE,
+                                            -1, /* timeout_msec */
+                                            NULL, /* GCancellable */
+                                            error);
+      if (result == NULL)
+        goto out;
+      g_variant_get (result, "(u)", &pid);
+      g_variant_unref (result);
+
+      if (sd_pid_get_session (pid, &session_id) < 0)
+        goto out;
+
+      session = polkit_unix_session_new (session_id);
+      free (session_id);
+    }
+  else
+    {
+      g_set_error (error,
+                   POLKIT_ERROR,
+                   POLKIT_ERROR_NOT_SUPPORTED,
+                   "Cannot get user for subject of type %s",
+                   g_type_name (G_TYPE_FROM_INSTANCE (subject)));
+    }
+
+ out:
+
+  return session;
+}
+
+gboolean
+polkit_backend_session_monitor_is_session_local (PolkitBackendSessionMonitor *monitor,
+                                                 PolkitSubject               *session)
+{
+  char *seat;
+
+  if (!sd_session_get_seat (polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session)), &seat))
+    {
+      free (seat);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+gboolean
+polkit_backend_session_monitor_is_session_active (PolkitBackendSessionMonitor *monitor,
+                                                  PolkitSubject               *session)
+{
+  return sd_session_is_active (polkit_unix_session_get_session_id (POLKIT_UNIX_SESSION (session)));
+}
+