diff --git a/data/gnome-session-failed.service.in b/data/gnome-session-failed.service.in
new file mode 100644
index 00000000..4e22ab76
--- /dev/null
+++ b/data/gnome-session-failed.service.in
@@ -0,0 +1,19 @@
+[Unit]
+Description=GNOME Session Failed lockdown screen (user)
+OnFailure=gnome-session-shutdown.target
+OnFailureJobMode=replace-irreversibly
+CollectMode=inactive-or-failed
+
+PartOf=gnome-session-failed.target
+
+# We could do this, but it requires an intermediate target for OnFailure
+# handling, so gnome-session-failed checks RUNNING_UNDER_GDM itself
+#Conflicts=gnome-session@gnome-login.target
+# or in the case of GDM and then not passing --allow-logout
+#Requisite=gnome-session@gnome-login.target
+
+[Service]
+Type=simple
+ExecStart=@libexecdir@/gnome-session-failed --allow-logout
+# The fail whale doesn't trigger a shutdown itself, so do it here
+ExecStopPost=-@libexecdir@/gnome-session-ctl --shutdown
diff --git a/data/gnome-session-failed.target b/data/gnome-session-failed.target
new file mode 100644
index 00000000..3ad2eb19
--- /dev/null
+++ b/data/gnome-session-failed.target
@@ -0,0 +1,12 @@
+[Unit]
+Description=GNOME Session Failed
+OnFailure=gnome-session-shutdown.target
+OnFailureJobMode=replace-irreversibly
+
+# We need an initialized session
+Requisite=gnome-session-initialized.target
+BindsTo=gnome-session-initialized.target
+After=gnome-session-initialized.target
+
+BindsTo=gnome-session-failed.service
+After=gnome-session-failed.service
diff --git a/data/gnome-session-manager@.service.in b/data/gnome-session-manager@.service.in
index 19012cf2..b06dbeb3 100644
--- a/data/gnome-session-manager@.service.in
+++ b/data/gnome-session-manager@.service.in
@@ -15,5 +15,5 @@ Before=gnome-session-manager.target
 
 [Service]
 Type=notify
-ExecStart=@libexecdir@/gnome-session-service --session=%i
+ExecStart=@libexecdir@/gnome-session-binary --systemd-service --session=%i
 ExecStopPost=-@libexecdir@/gnome-session-ctl --shutdown
diff --git a/data/gnome-session-shutdown.target b/data/gnome-session-shutdown.target
index 6240926d..2c3d1963 100644
--- a/data/gnome-session-shutdown.target
+++ b/data/gnome-session-shutdown.target
@@ -14,8 +14,8 @@ After=graphical-session.target graphical-session-pre.target
 Conflicts=gnome-session.target gnome-session-manager.target
 After=gnome-session.target gnome-session-manager.target
 
-Conflicts=gnome-session-pre.target gnome-session-initialized.target
-After=gnome-session-pre.target gnome-session-initialized.target
+Conflicts=gnome-session-pre.target gnome-session-initialized.target gnome-session-failed.target
+After=gnome-session-pre.target gnome-session-initialized.target gnome-session-failed.target
 
 # We need to make sure this unit is stopped; primarily so that the tree of
 # units that we created is completely cleaned.
diff --git a/data/gnome-session-wayland.target b/data/gnome-session-wayland.target
index 9a0a72a5..6552b4d4 100644
--- a/data/gnome-session-wayland.target
+++ b/data/gnome-session-wayland.target
@@ -1,5 +1,6 @@
 [Unit]
 Description=GNOME Wayland Session
+# On wayland all is lost, do a shutdown
 OnFailure=gnome-session-shutdown.target
 OnFailureJobMode=replace-irreversibly
 # Avoid default After/Before rules
diff --git a/data/gnome-session-x11.target b/data/gnome-session-x11.target
index a91d0877..e13cc9ee 100644
--- a/data/gnome-session-x11.target
+++ b/data/gnome-session-x11.target
@@ -1,7 +1,8 @@
 [Unit]
 Description=GNOME X11 Session
-OnFailure=gnome-session-shutdown.target
-OnFailureJobMode=replace-irreversibly
+# On X11, try to show the fail screen
+OnFailure=gnome-session-failed.target
+OnFailureJobMode=replace
 # Avoid default After/Before rules
 DefaultDependencies=no
 
diff --git a/data/gnome-session-x11@.target b/data/gnome-session-x11@.target
index b371759a..ce5879b2 100644
--- a/data/gnome-session-x11@.target
+++ b/data/gnome-session-x11@.target
@@ -1,7 +1,7 @@
 [Unit]
 Description=GNOME X11 Session (session: %i)
-OnFailure=gnome-session-shutdown.target
-OnFailureJobMode=replace-irreversibly
+OnFailure=gnome-session-failed.target
+OnFailureJobMode=replace
 DefaultDependencies=no
 # Start happens explicitly
 RefuseManualStart=no
diff --git a/data/gnome-session.target b/data/gnome-session.target
index bf4615b6..ea305558 100644
--- a/data/gnome-session.target
+++ b/data/gnome-session.target
@@ -1,7 +1,7 @@
 [Unit]
 Description=GNOME Session
-OnFailure=gnome-session-shutdown.target
-OnFailureJobMode=replace-irreversibly
+OnFailure=gnome-session-failed.target
+OnFailureJobMode=replace
 DefaultDependencies=no
 RefuseManualStart=yes
 RefuseManualStop=yes
diff --git a/data/gnome-session@.target b/data/gnome-session@.target
index d5ca3a11..536d9c0f 100644
--- a/data/gnome-session@.target
+++ b/data/gnome-session@.target
@@ -1,7 +1,7 @@
 [Unit]
 Description=GNOME Session (session: %i)
-OnFailure=gnome-session-shutdown.target
-OnFailureJobMode=replace-irreversibly
+OnFailure=gnome-session-failed.target
+OnFailureJobMode=replace
 DefaultDependencies=no
 RefuseManualStart=yes
 RefuseManualStop=yes
diff --git a/data/gnome.session.conf.in b/data/gnome.session.conf.in
new file mode 100644
index 00000000..73da0850
--- /dev/null
+++ b/data/gnome.session.conf.in
@@ -0,0 +1,6 @@
+[Unit]
+@wants_components@
+
+Wants=localsearch-3.service
+
+@requires_components@
diff --git a/data/gnome.session.desktop b/data/gnome.session.desktop
deleted file mode 100644
index 1a084cf3..00000000
--- a/data/gnome.session.desktop
+++ /dev/null
@@ -1,2 +0,0 @@
-[GNOME Session]
-Name=GNOME
diff --git a/data/gnome.session.desktop.in.in b/data/gnome.session.desktop.in.in
new file mode 100644
index 00000000..b3ea9fcf
--- /dev/null
+++ b/data/gnome.session.desktop.in.in
@@ -0,0 +1,4 @@
+[GNOME Session]
+Name=GNOME
+# Must be in sync with gnome-session@gnome.target.d/gnome.session.conf drop-in
+RequiredComponents=@required_components@;
diff --git a/data/meson.build b/data/meson.build
index 76b15e92..e4256fcd 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -60,18 +60,87 @@ if have_x11
   endforeach
 endif
 
-# Next, let's install the necessary systemd scaffolding
+# Next, let's configure the required components for a session.
 
-install_data(
-    'gnome.session.conf',
-    install_dir: systemd_userunitdir / 'gnome-session@gnome.target.d',
+required_components = [
+  'org.gnome.Shell',
+]
+
+wanted_components = [
+  'org.gnome.SettingsDaemon.A11ySettings',
+  'org.gnome.SettingsDaemon.Color',
+  'org.gnome.SettingsDaemon.Datetime',
+  'org.gnome.SettingsDaemon.Housekeeping',
+  'org.gnome.SettingsDaemon.Keyboard',
+  'org.gnome.SettingsDaemon.MediaKeys',
+  'org.gnome.SettingsDaemon.Power',
+  'org.gnome.SettingsDaemon.PrintNotifications',
+  'org.gnome.SettingsDaemon.Rfkill',
+  'org.gnome.SettingsDaemon.ScreensaverProxy',
+  'org.gnome.SettingsDaemon.Sharing',
+  'org.gnome.SettingsDaemon.Smartcard',
+  'org.gnome.SettingsDaemon.Sound',
+  'org.gnome.SettingsDaemon.UsbProtection',
+  'org.gnome.SettingsDaemon.Wwan',
+]
+
+# Generate the legacy "builtin" session configuration. In this mode, gnome-session
+# parses <session>.session files that list other .desktop files to start and
+# manage. gnome-session then does this management on its own.
+
+builtin_required = required_components + wanted_components
+desktop = 'gnome.session.desktop'
+desktop_in = configure_file(
+  input: desktop + '.in.in',
+  output: desktop + '.in',
+  configuration: {
+    'libexecdir': session_libexecdir,
+    'required_components': ';'.join(builtin_required),
+  }
 )
 
+i18n.merge_file(
+  type: 'desktop',
+  input: desktop_in,
+  output: 'gnome.session',
+  po_dir: po_dir,
+  install: true,
+  install_dir: join_paths(session_pkgdatadir, 'sessions')
+)
+
+# Generate the systemd session configuration. In this mode, gnome-session lets
+# systemd start and manage the necessary session components. These are defined
+# via systemd unit files. The list of required components to start comes via a
+# drop-in file for gnome-session@<session>.target
+
+wanted_targets = []
+foreach component: wanted_components
+  wanted_targets += 'Wants=@0@.target'.format(component)
+endforeach
+
+required_targets = []
+foreach component: required_components
+  wanted_targets += 'Requires=@0@.target'.format(component)
+endforeach
+
+configure_file(
+  input: 'gnome.session.conf.in',
+  output: 'gnome.session.conf',
+  configuration: {
+    'wants_components': '\n'.join(wanted_targets),
+    'requires_components': '\n'.join(required_targets),
+  },
+  install_dir: systemd_userunitdir / 'gnome-session@gnome.target.d',
+)
+
+# Install the other necessary systemd scaffolding
+
 systemd_service = [
   'gnome-session-manager@.service',
   'gnome-session-signal-init.service',
   'gnome-session-restart-dbus.service',
   'gnome-session-monitor.service',
+  'gnome-session-failed.service',
 ]
 
 foreach service: systemd_service
@@ -91,11 +160,11 @@ systemd_target = [
   'gnome-session-wayland.target',
   'gnome-session@.target',
   'gnome-session.target',
-  'gnome-session-basic-services.target',
   'gnome-session-pre.target',
   'gnome-session-manager.target',
   'gnome-session-initialized.target',
   'gnome-session-shutdown.target',
+  'gnome-session-failed.target',
   'gnome-session-x11-services.target',
   'gnome-session-x11-services-ready.target',
 ]
@@ -128,15 +197,6 @@ install_data(
 
 # Install some other misc. configuration files
 
-i18n.merge_file(
-  type: 'desktop',
-  input: 'gnome.session.desktop',
-  output: 'gnome.session',
-  po_dir: po_dir,
-  install: true,
-  install_dir: join_paths(session_pkgdatadir, 'sessions')
-)
-
 install_data(
   'org.gnome.SessionManager.gschema.xml',
   install_dir: join_paths(session_datadir, 'glib-2.0', 'schemas'),
diff --git a/doc/gnome-session.xml b/doc/gnome-session.xml
index 1085490f..64da44ed 100644
--- a/doc/gnome-session.xml
+++ b/doc/gnome-session.xml
@@ -37,7 +37,9 @@
     </partintro>
 
     <xi:include href="ref-org.gnome.SessionManager.xml"/>
+    <xi:include href="ref-org.gnome.SessionManager.Client.xml"/>
     <xi:include href="ref-org.gnome.SessionManager.ClientPrivate.xml"/>
+    <xi:include href="ref-org.gnome.SessionManager.App.xml"/>
     <xi:include href="ref-org.gnome.SessionManager.Inhibitor.xml"/>
     <xi:include href="ref-org.gnome.SessionManager.Presence.xml"/>
   </part>
diff --git a/gnome-session/gdm-log.c b/gnome-session/gdm-log.c
new file mode 100644
index 00000000..c0dca411
--- /dev/null
+++ b/gnome-session/gdm-log.c
@@ -0,0 +1,205 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: William Jon McCann <mccann@jhu.edu>
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <syslog.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gdm-log.h"
+
+static gboolean initialized = FALSE;
+static int      syslog_levels = (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+
+static void
+log_level_to_priority_and_prefix (GLogLevelFlags log_level,
+                                  int           *priorityp,
+                                  const char   **prefixp)
+{
+        int         priority;
+        const char *prefix;
+
+        /* Process the message prefix and priority */
+        switch (log_level & G_LOG_LEVEL_MASK) {
+        case G_LOG_FLAG_FATAL:
+                priority = LOG_EMERG;
+                prefix = "FATAL";
+                break;
+        case G_LOG_LEVEL_ERROR:
+                priority = LOG_ERR;
+                prefix = "ERROR";
+                break;
+        case G_LOG_LEVEL_CRITICAL:
+                priority = LOG_CRIT;
+                prefix = "CRITICAL";
+                break;
+        case G_LOG_LEVEL_WARNING:
+                priority = LOG_WARNING;
+                prefix = "WARNING";
+                break;
+        case G_LOG_LEVEL_MESSAGE:
+                priority = LOG_NOTICE;
+                prefix = "MESSAGE";
+                break;
+        case G_LOG_LEVEL_INFO:
+                priority = LOG_INFO;
+                prefix = "INFO";
+                break;
+        case G_LOG_LEVEL_DEBUG:
+                /* if debug was requested then bump this up to ERROR
+                 * to ensure it is seen in a log */
+                if (syslog_levels & G_LOG_LEVEL_DEBUG) {
+                        priority = LOG_WARNING;
+                        prefix = "DEBUG(+)";
+                } else {
+                        priority = LOG_DEBUG;
+                        prefix = "DEBUG";
+                }
+                break;
+        default:
+                priority = LOG_DEBUG;
+                prefix = "UNKNOWN";
+                break;
+        }
+
+        if (priorityp != NULL) {
+                *priorityp = priority;
+        }
+        if (prefixp != NULL) {
+                *prefixp = prefix;
+        }
+}
+
+void
+gdm_log_default_handler (const gchar   *log_domain,
+                         GLogLevelFlags log_level,
+                         const gchar   *message,
+                         gpointer       unused_data)
+{
+        GString     *gstring;
+        int          priority;
+        const char  *level_prefix;
+        char        *string;
+        gboolean     do_log;
+        gboolean     is_fatal;
+
+        is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
+
+        do_log = (log_level & syslog_levels);
+        if (! do_log) {
+                return;
+        }
+
+        if (! initialized) {
+                gdm_log_init ();
+        }
+
+        log_level_to_priority_and_prefix (log_level,
+                                          &priority,
+                                          &level_prefix);
+
+        gstring = g_string_new (NULL);
+
+        if (log_domain != NULL) {
+                g_string_append (gstring, log_domain);
+                g_string_append_c (gstring, '-');
+        }
+        g_string_append (gstring, level_prefix);
+
+        g_string_append (gstring, ": ");
+        if (message == NULL) {
+                g_string_append (gstring, "(NULL) message");
+        } else {
+                g_string_append (gstring, message);
+        }
+        if (is_fatal) {
+                g_string_append (gstring, "\naborting...\n");
+        } else {
+                g_string_append (gstring, "\n");
+        }
+
+        string = g_string_free (gstring, FALSE);
+
+        syslog (priority, "%s", string);
+
+        g_free (string);
+}
+
+void
+gdm_log_toggle_debug (void)
+{
+        if (syslog_levels & G_LOG_LEVEL_DEBUG) {
+                g_debug ("Debugging disabled");
+                syslog_levels &= ~G_LOG_LEVEL_DEBUG;
+        } else {
+                syslog_levels |= G_LOG_LEVEL_DEBUG;
+                g_debug ("Debugging enabled");
+        }
+}
+
+void
+gdm_log_set_debug (gboolean debug)
+{
+        if (debug) {
+                syslog_levels |= G_LOG_LEVEL_DEBUG;
+                g_debug ("Enabling debugging");
+        } else {
+                g_debug ("Disabling debugging");
+                syslog_levels &= ~G_LOG_LEVEL_DEBUG;
+        }
+}
+
+void
+gdm_log_init (void)
+{
+        const char *prg_name;
+        int         options;
+
+        g_log_set_default_handler (gdm_log_default_handler, NULL);
+
+        prg_name = g_get_prgname ();
+
+        options = LOG_PID;
+#ifdef LOG_PERROR
+        options |= LOG_PERROR;
+#endif
+
+        openlog (prg_name, options, LOG_DAEMON);
+
+        initialized = TRUE;
+}
+
+void
+gdm_log_shutdown (void)
+{
+        closelog ();
+        initialized = FALSE;
+}
+
diff --git a/gnome-session/gdm-log.h b/gnome-session/gdm-log.h
new file mode 100644
index 00000000..33daf40e
--- /dev/null
+++ b/gnome-session/gdm-log.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: William Jon McCann <mccann@jhu.edu>
+ *
+ */
+
+#ifndef __GDM_LOG_H
+#define __GDM_LOG_H
+
+#include <stdarg.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void      gdm_log_default_handler (const gchar   *log_domain,
+                                   GLogLevelFlags log_level,
+                                   const gchar   *message,
+                                   gpointer      unused_data);
+void      gdm_log_set_debug       (gboolean       debug);
+void      gdm_log_toggle_debug    (void);
+void      gdm_log_init            (void);
+void      gdm_log_shutdown        (void);
+
+/* compatibility */
+#define   gdm_fail               g_critical
+#define   gdm_error              g_warning
+#define   gdm_info               g_message
+#define   gdm_debug              g_debug
+
+#define   gdm_assert             g_assert
+#define   gdm_assert_not_reached g_assert_not_reached
+
+G_END_DECLS
+
+#endif /* __GDM_LOG_H */
diff --git a/gnome-session/gnome-session.in b/gnome-session/gnome-session.in
new file mode 100755
index 00000000..b3b93a1c
--- /dev/null
+++ b/gnome-session/gnome-session.in
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+if [ "x$XDG_SESSION_TYPE" = "xwayland" ] &&
+   [ "x$XDG_SESSION_CLASS" != "xgreeter" ] &&
+   [  -n "$SHELL" ] &&
+   grep -q "$SHELL" /etc/shells &&
+   ! (echo "$SHELL" | grep -q "false") &&
+   ! (echo "$SHELL" | grep -q "nologin"); then
+  if [ "$1" != '-l' ]; then
+    exec bash -c "exec -l '$SHELL' -c 'exec $0 -l $*'"
+  else
+    shift
+  fi
+fi
+
+SETTING=$(G_MESSAGES_DEBUG='' gsettings get org.gnome.system.locale region)
+REGION=${SETTING#\'}
+REGION=${REGION%\'}
+
+if [ -n "$REGION" ]; then
+  unset LC_NUMERIC LC_TIME LC_MONETARY LC_PAPER LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
+
+  if [ "$LANG" != "$REGION" ] ; then
+    # LC_CTYPE
+    export LC_NUMERIC=$REGION
+    export LC_TIME=$REGION
+    # LC_COLLATE
+    export LC_MONETARY=$REGION
+    # LC_MESSAGES
+    export LC_PAPER=$REGION
+    # LC_NAME
+    export LC_ADDRESS=$REGION
+    export LC_TELEPHONE=$REGION
+    export LC_MEASUREMENT=$REGION
+    # LC_IDENTIFICATION
+  fi
+fi
+
+exec @libexecdir@/gnome-session-binary "$@"
diff --git a/gnome-session/gsm-app.c b/gnome-session/gsm-app.c
index d38138be..c05ae06a 100644
--- a/gnome-session/gsm-app.c
+++ b/gnome-session/gsm-app.c
@@ -21,49 +21,174 @@
 #include "config.h"
 #endif
 
+#include <glib.h>
 #include <string.h>
 
-#include <glib.h>
-#include <gio/gio.h>
-#include <gio/gdesktopappinfo.h>
+#include "gsm-app.h"
+#include "org.gnome.SessionManager.App.h"
 
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include <libgnome-desktop/gnome-systemd.h>
+/* If a component crashes twice within a minute, we count that as a fatal error */
+#define _GSM_APP_RESPAWN_RATELIMIT_USEC (60 * G_USEC_PER_SEC)
 
-#include "gsm-app.h"
-#include "gsm-util.h"
+typedef struct
+{
+        char            *id;
+        char            *app_id;
+        int              phase;
+        char            *startup_id;
+        gboolean         registered;
+        gint64           last_restart_time;
+        GDBusConnection *connection;
+        GsmExportedApp  *skeleton;
+} GsmAppPrivate;
 
-#define GSM_APP_SYSTEMD_SKIP_KEY   "X-systemd-skip"
-#define GSM_APP_SYSTEMD_HIDDEN_KEY "X-GNOME-HiddenUnderSystemd"
-#define GSM_APP_ENABLED_KEY        "X-GNOME-Autostart-enabled"
-#define GSM_APP_PHASE_KEY          "X-GNOME-Autostart-Phase"
 
-/* This comment is a record of keys that were previously used but are not used
- * anymore. We keep this so that we don't accidentally redefine these keys in
- * the future, to be used in some incompatible way.
- *
- * X-GNOME-AutoRestart
- * X-GNOME-Autostart-discard-exec
- * AutostartCondition
- * X-GNOME-DBus-Name
- * X-GNOME-DBus-Path
- * X-GNOME-DBus-Start-Arguments
- * X-GNOME-Autostart-startup-id
- * */
-
-struct _GsmApp
-{
-        GObject          parent;
-        GDesktopAppInfo *inner;
+enum {
+        EXITED,
+        DIED,
+        LAST_SIGNAL
 };
 
+static guint32 app_serial = 1;
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 enum {
         PROP_0,
-        PROP_INNER,
+        PROP_ID,
+        PROP_STARTUP_ID,
+        PROP_PHASE,
+        PROP_REGISTERED,
         LAST_PROP
 };
 
-G_DEFINE_TYPE (GsmApp, gsm_app, G_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_PRIVATE (GsmApp, gsm_app, G_TYPE_OBJECT)
+
+GQuark
+gsm_app_error_quark (void)
+{
+        static GQuark ret = 0;
+        if (ret == 0) {
+                ret = g_quark_from_static_string ("gsm_app_error");
+        }
+
+        return ret;
+
+}
+
+static gboolean
+gsm_app_get_app_id (GsmExportedApp        *skeleton,
+                    GDBusMethodInvocation *invocation,
+                    GsmApp                *app)
+{
+        const gchar *id;
+
+        id = GSM_APP_GET_CLASS (app)->impl_get_app_id (app);
+        gsm_exported_app_complete_get_app_id (skeleton, invocation, id);
+
+        return TRUE;
+}
+
+static gboolean
+gsm_app_get_startup_id (GsmExportedApp        *skeleton,
+                        GDBusMethodInvocation *invocation,
+                        GsmApp                *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+        const gchar *id;
+
+        id = g_strdup (priv->startup_id);
+        gsm_exported_app_complete_get_startup_id (skeleton, invocation, id);
+
+        return TRUE;
+}
+
+static gboolean
+gsm_app_get_phase (GsmExportedApp        *skeleton,
+                   GDBusMethodInvocation *invocation,
+                   GsmApp                *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        gsm_exported_app_complete_get_phase (skeleton, invocation, priv->phase);
+        return TRUE;
+}
+
+static guint32
+get_next_app_serial (void)
+{
+        guint32 serial;
+
+        serial = app_serial++;
+
+        if ((gint32)app_serial < 0) {
+                app_serial = 1;
+        }
+
+        return serial;
+}
+
+static gboolean
+register_app (GsmApp *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+        GError *error;
+        GsmExportedApp *skeleton;
+
+        error = NULL;
+        priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+        if (error != NULL) {
+                g_critical ("error getting session bus: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        skeleton = gsm_exported_app_skeleton_new ();
+        priv->skeleton = skeleton;
+        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
+                                          priv->connection, priv->id,
+                                          &error);
+
+        if (error != NULL) {
+                g_critical ("error registering app on session bus: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        g_signal_connect (skeleton, "handle-get-app-id",
+                          G_CALLBACK (gsm_app_get_app_id), app);
+        g_signal_connect (skeleton, "handle-get-phase",
+                          G_CALLBACK (gsm_app_get_phase), app);
+        g_signal_connect (skeleton, "handle-get-startup-id",
+                          G_CALLBACK (gsm_app_get_startup_id), app);
+
+        return TRUE;
+}
+
+static GObject *
+gsm_app_constructor (GType                  type,
+                     guint                  n_construct_properties,
+                     GObjectConstructParam *construct_properties)
+{
+        GsmApp    *app;
+        GsmAppPrivate *priv;
+        gboolean   res;
+
+        app = GSM_APP (G_OBJECT_CLASS (gsm_app_parent_class)->constructor (type,
+                                                                           n_construct_properties,
+                                                                           construct_properties));
+        priv = gsm_app_get_instance_private (app);
+
+        g_free (priv->id);
+        priv->id = g_strdup_printf ("/org/gnome/SessionManager/App%u", get_next_app_serial ());
+
+        res = register_app (app);
+        if (! res) {
+                g_warning ("Unable to register app with session bus");
+        }
+
+        return G_OBJECT (app);
+}
 
 static void
 gsm_app_init (GsmApp *app)
@@ -71,13 +196,43 @@ gsm_app_init (GsmApp *app)
 }
 
 static void
-gsm_app_set_inner (GsmApp          *app,
-                   GDesktopAppInfo *app_info)
+gsm_app_set_phase (GsmApp *app,
+                   int     phase)
 {
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
         g_return_if_fail (GSM_IS_APP (app));
-        g_return_if_fail (app_info == NULL || G_IS_DESKTOP_APP_INFO (app_info));
 
-        g_set_object (&app->inner, app_info);
+        priv->phase = phase;
+}
+
+static void
+gsm_app_set_id (GsmApp     *app,
+                const char *id)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        g_return_if_fail (GSM_IS_APP (app));
+
+        g_free (priv->id);
+
+        priv->id = g_strdup (id);
+        g_object_notify (G_OBJECT (app), "id");
+
+}
+static void
+gsm_app_set_startup_id (GsmApp     *app,
+                        const char *startup_id)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        g_return_if_fail (GSM_IS_APP (app));
+
+        g_free (priv->startup_id);
+
+        priv->startup_id = g_strdup (startup_id);
+        g_object_notify (G_OBJECT (app), "startup-id");
+
 }
 
 static void
@@ -89,8 +244,17 @@ gsm_app_set_property (GObject      *object,
         GsmApp *app = GSM_APP (object);
 
         switch (prop_id) {
-        case PROP_INNER:
-                gsm_app_set_inner (app, g_value_get_object (value));
+        case PROP_STARTUP_ID:
+                gsm_app_set_startup_id (app, g_value_get_string (value));
+                break;
+        case PROP_ID:
+                gsm_app_set_id (app, g_value_get_string (value));
+                break;
+        case PROP_PHASE:
+                gsm_app_set_phase (app, g_value_get_int (value));
+                break;
+        case PROP_REGISTERED:
+                gsm_app_set_registered (app, g_value_get_boolean (value));
                 break;
         default:
                 break;
@@ -104,10 +268,20 @@ gsm_app_get_property (GObject    *object,
                       GParamSpec *pspec)
 {
         GsmApp *app = GSM_APP (object);
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
 
         switch (prop_id) {
-        case PROP_INNER:
-                g_value_set_object (value, app->inner);
+        case PROP_STARTUP_ID:
+                g_value_set_string (value, priv->startup_id);
+                break;
+        case PROP_ID:
+                g_value_set_string (value, priv->id);
+                break;
+        case PROP_PHASE:
+                g_value_set_int (value, priv->phase);
+                break;
+        case PROP_REGISTERED:
+                g_value_set_boolean (value, priv->registered);
                 break;
         default:
                 break;
@@ -118,8 +292,21 @@ static void
 gsm_app_dispose (GObject *object)
 {
         GsmApp *app = GSM_APP (object);
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        g_free (priv->startup_id);
+        priv->startup_id = NULL;
 
-        g_clear_object (&app->inner);
+        g_free (priv->id);
+        priv->id = NULL;
+
+        if (priv->skeleton != NULL) {
+                g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (priv->skeleton),
+                                                                    priv->connection);
+                g_clear_object (&priv->skeleton);
+        }
+
+        g_clear_object (&priv->connection);
 
         G_OBJECT_CLASS (gsm_app_parent_class)->dispose (object);
 }
@@ -132,22 +319,103 @@ gsm_app_class_init (GsmAppClass *klass)
         object_class->set_property = gsm_app_set_property;
         object_class->get_property = gsm_app_get_property;
         object_class->dispose = gsm_app_dispose;
+        object_class->constructor = gsm_app_constructor;
+
+        klass->impl_start = NULL;
+        klass->impl_get_app_id = NULL;
+        klass->impl_get_autorestart = NULL;
+        klass->impl_provides = NULL;
+        klass->impl_get_provides = NULL;
+        klass->impl_is_running = NULL;
+
+        g_object_class_install_property (object_class,
+                                         PROP_PHASE,
+                                         g_param_spec_int ("phase",
+                                                           "Phase",
+                                                           "Phase",
+                                                           -1,
+                                                           G_MAXINT,
+                                                           -1,
+                                                           G_PARAM_READWRITE));
+        g_object_class_install_property (object_class,
+                                         PROP_ID,
+                                         g_param_spec_string ("id",
+                                                              "ID",
+                                                              "ID",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        g_object_class_install_property (object_class,
+                                         PROP_STARTUP_ID,
+                                         g_param_spec_string ("startup-id",
+                                                              "startup ID",
+                                                              "Session management startup ID",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
         g_object_class_install_property (object_class,
-                                         PROP_INNER,
-                                         g_param_spec_object ("inner",
-                                                              "Inner",
-                                                              "Inner",
-                                                              G_TYPE_DESKTOP_APP_INFO,
-                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                         PROP_REGISTERED,
+                                         g_param_spec_boolean ("registered",
+                                                               "Registered",
+                                                               "Registered",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE));
+
+        signals[EXITED] =
+                g_signal_new ("exited",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmAppClass, exited),
+                              NULL, NULL, NULL,
+                              G_TYPE_NONE,
+                              1, G_TYPE_UCHAR);
+        signals[DIED] =
+                g_signal_new ("died",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmAppClass, died),
+                              NULL, NULL, NULL,
+                              G_TYPE_NONE,
+                              1, G_TYPE_INT);
+}
+
+const char *
+gsm_app_peek_id (GsmApp *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        return priv->id;
 }
 
 const char *
 gsm_app_peek_app_id (GsmApp *app)
 {
-        g_return_val_if_fail (GSM_IS_APP (app), NULL);
+        return GSM_APP_GET_CLASS (app)->impl_get_app_id (app);
+}
+
+const char *
+gsm_app_peek_startup_id (GsmApp *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
 
-        return g_app_info_get_id (G_APP_INFO (app->inner));
+        return priv->startup_id;
+}
+
+/**
+ * gsm_app_peek_phase:
+ * @app: a %GsmApp
+ *
+ * Returns @app's startup phase.
+ *
+ * Return value: @app's startup phase
+ **/
+GsmManagerPhase
+gsm_app_peek_phase (GsmApp *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        g_return_val_if_fail (GSM_IS_APP (app), GSM_MANAGER_PHASE_APPLICATION);
+
+        return priv->phase;
 }
 
 gboolean
@@ -155,146 +423,171 @@ gsm_app_peek_is_disabled (GsmApp *app)
 {
         g_return_val_if_fail (GSM_IS_APP (app), FALSE);
 
-        /* GSM_AUTOSTART_APP_ENABLED_KEY key, used by old gnome-session */
-        if (g_desktop_app_info_has_key (app->inner, GSM_APP_ENABLED_KEY) &&
-            !g_desktop_app_info_get_boolean (app->inner, GSM_APP_ENABLED_KEY)) {
-                g_debug ("App %s is disabled by " GSM_APP_ENABLED_KEY,
-                         gsm_app_peek_app_id (app));
-                return TRUE;
+        if (GSM_APP_GET_CLASS (app)->impl_is_disabled) {
+                return GSM_APP_GET_CLASS (app)->impl_is_disabled (app);
+        } else {
+                return FALSE;
         }
+}
 
-        /* Hidden key, used by fdo Desktop Entry spec */
-        if (g_desktop_app_info_get_is_hidden (app->inner)) {
-                g_debug ("App %s is disabled by Hidden",
-                         gsm_app_peek_app_id (app));
-                return TRUE;
-        }
+gboolean
+gsm_app_peek_is_conditionally_disabled (GsmApp *app)
+{
+        g_return_val_if_fail (GSM_IS_APP (app), FALSE);
 
-        /* Check OnlyShowIn/NotShowIn/TryExec */
-        if (!g_desktop_app_info_get_show_in (app->inner, NULL)) {
-                g_debug ("App %s is not for the current desktop",
-                         gsm_app_peek_app_id (app));
-                return TRUE;
+        if (GSM_APP_GET_CLASS (app)->impl_is_conditionally_disabled) {
+                return GSM_APP_GET_CLASS (app)->impl_is_conditionally_disabled (app);
+        } else {
+                return FALSE;
         }
+}
 
-        /* Check if app is systemd enabled */
-        if (g_desktop_app_info_get_boolean (app->inner, GSM_APP_SYSTEMD_HIDDEN_KEY)) {
-                g_debug ("App %s is disabled by " GSM_APP_SYSTEMD_HIDDEN_KEY,
-                         gsm_app_peek_app_id (app));
-                return TRUE;
-        }
-        if (g_desktop_app_info_get_boolean (app->inner, GSM_APP_SYSTEMD_SKIP_KEY)) {
-                g_debug ("App %s is disabled by " GSM_APP_SYSTEMD_SKIP_KEY,
-                         gsm_app_peek_app_id (app));
-                return TRUE;
+gboolean
+gsm_app_is_running (GsmApp *app)
+{
+        g_return_val_if_fail (GSM_IS_APP (app), FALSE);
+
+        if (GSM_APP_GET_CLASS (app)->impl_is_running) {
+                return GSM_APP_GET_CLASS (app)->impl_is_running (app);
+        } else {
+                return FALSE;
         }
+}
+
+gboolean
+gsm_app_peek_autorestart (GsmApp *app)
+{
+        g_return_val_if_fail (GSM_IS_APP (app), FALSE);
 
-        return FALSE;
+        if (GSM_APP_GET_CLASS (app)->impl_get_autorestart) {
+                return GSM_APP_GET_CLASS (app)->impl_get_autorestart (app);
+        } else {
+                return FALSE;
+        }
 }
 
-static void
-app_launched (GAppLaunchContext *ctx,
-              GAppInfo          *appinfo,
-              GVariant          *platform_data,
-              gpointer           data)
+gboolean
+gsm_app_provides (GsmApp *app, const char *service)
 {
-        GsmApp *app = data;
+        if (GSM_APP_GET_CLASS (app)->impl_provides) {
+                return GSM_APP_GET_CLASS (app)->impl_provides (app, service);
+        } else {
+                return FALSE;
+        }
+}
 
-        gint pid = 0;
-        g_variant_lookup (platform_data, "pid", "i", &pid);
+char **
+gsm_app_get_provides (GsmApp *app)
+{
+        if (GSM_APP_GET_CLASS (app)->impl_get_provides) {
+                return GSM_APP_GET_CLASS (app)->impl_get_provides (app);
+        } else {
+                return NULL;
+        }
+}
 
-        /* If pid == 0 the application was launched through D-Bus
-         * activation, therefore it's already in its own unit */
-        if (pid == 0)
-                return;
+gboolean
+gsm_app_has_autostart_condition (GsmApp     *app,
+                                 const char *condition)
+{
 
-        /* We are not interested in the result. */
-        gnome_start_systemd_scope (gsm_app_peek_app_id (app),
-                                   pid,
-                                   NULL,
-                                   NULL,
-                                   NULL, NULL, NULL);
+        if (GSM_APP_GET_CLASS (app)->impl_has_autostart_condition) {
+                return GSM_APP_GET_CLASS (app)->impl_has_autostart_condition (app, condition);
+        } else {
+                return FALSE;
+        }
 }
 
 gboolean
 gsm_app_start (GsmApp  *app,
                GError **error)
 {
-        g_autoptr (GAppLaunchContext) ctx = NULL;
-        GError *local_error = NULL;
-        const char * const *variable_denylist;
-        const char * const *child_environment;
-        guint handler;
-        gboolean success;
-
-        g_debug ("GsmApp: starting %s (command=%s)",
-                 gsm_app_peek_app_id (app),
-                 g_app_info_get_commandline (G_APP_INFO (app->inner)));
-
-        ctx = g_app_launch_context_new ();
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
 
-        variable_denylist = gsm_util_get_variable_blacklist ();
-        for (size_t i = 0; variable_denylist[i] != NULL; i++)
-                g_app_launch_context_unsetenv (ctx, variable_denylist[i]);
+        g_debug ("Starting app: %s", priv->id);
+        return GSM_APP_GET_CLASS (app)->impl_start (app, error);
+}
 
-        child_environment = gsm_util_listenv ();
-        for (size_t i = 0; child_environment[i] != NULL; i++) {
-                g_auto (GStrv) split = g_strsplit (child_environment[i], "=", 2);
-                if (split[1] != NULL)
-                        g_app_launch_context_setenv (ctx, split[0], split[1]);
+gboolean
+gsm_app_restart (GsmApp  *app,
+                 GError **error)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+        gint64 current_time;
+        g_debug ("Re-starting app: %s", priv->id);
+
+        current_time = g_get_monotonic_time ();
+        if (priv->last_restart_time > 0
+            && (current_time - priv->last_restart_time) < _GSM_APP_RESPAWN_RATELIMIT_USEC) {
+                g_warning ("App '%s' respawning too quickly", gsm_app_peek_app_id (app));
+                g_set_error (error,
+                             GSM_APP_ERROR,
+                             GSM_APP_ERROR_RESTART_LIMIT,
+                             "Component '%s' crashing too quickly",
+                             gsm_app_peek_app_id (app));
+                return FALSE;
         }
+        priv->last_restart_time = current_time;
 
-        handler = g_signal_connect (ctx, "launched", G_CALLBACK (app_launched), app);
-        success = g_desktop_app_info_launch_uris_as_manager (app->inner,
-                                                             NULL,
-                                                             ctx,
-                                                             G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
-                                                             NULL, NULL, NULL, NULL,
-                                                             &local_error);
-        if (!success)
-                g_propagate_prefixed_error (error, local_error,
-                                            "Unable to start app (%s): ",
-                                            gsm_app_peek_app_id (app));
+        return GSM_APP_GET_CLASS (app)->impl_restart (app, error);
+}
 
-        g_signal_handler_disconnect (ctx, handler);
+gboolean
+gsm_app_stop (GsmApp  *app,
+              GError **error)
+{
+        return GSM_APP_GET_CLASS (app)->impl_stop (app, error);
+}
+
+void
+gsm_app_exited (GsmApp *app,
+                guchar  exit_code)
+{
+        g_return_if_fail (GSM_IS_APP (app));
 
-        return success;
+        g_signal_emit (app, signals[EXITED], 0, exit_code);
 }
 
-GsmApp *
-gsm_app_new (GDesktopAppInfo  *info,
-             GError          **error)
+void
+gsm_app_died (GsmApp *app,
+              int     signal)
 {
-        g_autofree char *app_phase = NULL;
+        g_return_if_fail (GSM_IS_APP (app));
 
-        g_return_val_if_fail (info != NULL, NULL);
+        g_signal_emit (app, signals[DIED], 0, signal);
+}
 
-        app_phase = g_desktop_app_info_get_string (info, GSM_APP_PHASE_KEY);
-        if (app_phase && strcmp (app_phase, "Application") != 0) {
-                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                             "App %s sets " GSM_APP_PHASE_KEY ", but gnome-session no longer manages session services",
-                             g_app_info_get_id (G_APP_INFO(info)));
-                return NULL;
-        }
+gboolean
+gsm_app_get_registered (GsmApp *app)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
+
+        g_return_val_if_fail (GSM_IS_APP (app), FALSE);
 
-        return g_object_new (GSM_TYPE_APP, "inner", info, NULL);
+        return priv->registered;
 }
 
-GsmApp *
-gsm_app_new_for_path (const char  *path,
-                      GError     **error)
+void
+gsm_app_set_registered (GsmApp   *app,
+                        gboolean  registered)
 {
-        g_autoptr (GDesktopAppInfo) info = NULL;
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
 
-        g_return_val_if_fail (path != NULL, NULL);
+        g_return_if_fail (GSM_IS_APP (app));
 
-        info = g_desktop_app_info_new_from_filename (path);
-        if (info == NULL) {
-                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                             "Desktop file %s couldn't be parsed, or references a missing TryExec binary",
-                             path);
-                return NULL;
+        if (priv->registered != registered) {
+                priv->registered = registered;
+                g_object_notify (G_OBJECT (app), "registered");
         }
+}
+
+gboolean
+gsm_app_save_to_keyfile (GsmApp    *app,
+                         GKeyFile  *keyfile,
+                         GError   **error)
+{
+        GsmAppPrivate *priv = gsm_app_get_instance_private (app);
 
-        return gsm_app_new (info, error);
+        g_debug ("Saving app: %s", priv->id);
+        return GSM_APP_GET_CLASS (app)->impl_save_to_keyfile (app, keyfile, error);
 }
diff --git a/gnome-session/gsm-app.h b/gnome-session/gsm-app.h
index a3099709..765a70da 100644
--- a/gnome-session/gsm-app.h
+++ b/gnome-session/gsm-app.h
@@ -21,27 +21,98 @@
 #define __GSM_APP_H__
 
 #include <glib-object.h>
-#include <gio/gdesktopappinfo.h>
 #include <sys/types.h>
 
+
 #include "gsm-manager.h"
 #include "gsm-client.h"
 
 G_BEGIN_DECLS
 
-#define GSM_TYPE_APP (gsm_app_get_type ())
-G_DECLARE_FINAL_TYPE (GsmApp, gsm_app, GSM, APP, GObject)
+#define GSM_TYPE_APP            (gsm_app_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GsmApp, gsm_app, GSM, APP, GObject)
+
+struct _GsmAppClass
+{
+        GObjectClass parent_class;
+
+        /* signals */
+        void        (*exited)       (GsmApp *app,
+                                     guchar  exit_code);
+        void        (*died)         (GsmApp *app,
+                                     int     signal);
+
+        /* virtual methods */
+        gboolean    (*impl_start)                     (GsmApp     *app,
+                                                       GError    **error);
+        gboolean    (*impl_restart)                   (GsmApp     *app,
+                                                       GError    **error);
+        gboolean    (*impl_stop)                      (GsmApp     *app,
+                                                       GError    **error);
+        gboolean    (*impl_provides)                  (GsmApp     *app,
+                                                       const char *service);
+        char **     (*impl_get_provides)              (GsmApp     *app);
+        gboolean    (*impl_has_autostart_condition)   (GsmApp     *app,
+                                                       const char *service);
+        gboolean    (*impl_is_running)                (GsmApp     *app);
+
+        gboolean    (*impl_get_autorestart)           (GsmApp     *app);
+        const char *(*impl_get_app_id)                (GsmApp     *app);
+        gboolean    (*impl_is_disabled)               (GsmApp     *app);
+        gboolean    (*impl_is_conditionally_disabled) (GsmApp     *app);
+
+        gboolean    (*impl_save_to_keyfile)           (GsmApp     *app,
+                                                       GKeyFile   *keyfile,
+                                                       GError    **error);
+};
+
+typedef enum
+{
+        GSM_APP_ERROR_GENERAL = 0,
+        GSM_APP_ERROR_RESTART_LIMIT,
+        GSM_APP_ERROR_START,
+        GSM_APP_ERROR_STOP,
+        GSM_APP_NUM_ERRORS
+} GsmAppError;
+
+#define GSM_APP_ERROR gsm_app_error_quark ()
 
-GsmApp          *gsm_app_new                            (GDesktopAppInfo  *info,
-                                                         GError          **error);
-GsmApp          *gsm_app_new_for_path                   (const char  *path,
-                                                         GError     **error);
+GQuark           gsm_app_error_quark                    (void);
+
+gboolean         gsm_app_peek_autorestart               (GsmApp     *app);
+
+const char      *gsm_app_peek_id                        (GsmApp     *app);
 const char      *gsm_app_peek_app_id                    (GsmApp     *app);
+const char      *gsm_app_peek_startup_id                (GsmApp     *app);
 GsmManagerPhase  gsm_app_peek_phase                     (GsmApp     *app);
 gboolean         gsm_app_peek_is_disabled               (GsmApp     *app);
+gboolean         gsm_app_peek_is_conditionally_disabled (GsmApp     *app);
 
 gboolean         gsm_app_start                          (GsmApp     *app,
                                                          GError    **error);
+gboolean         gsm_app_restart                        (GsmApp     *app,
+                                                         GError    **error);
+gboolean         gsm_app_stop                           (GsmApp     *app,
+                                                         GError    **error);
+gboolean         gsm_app_is_running                     (GsmApp     *app);
+
+void             gsm_app_exited                         (GsmApp     *app,
+                                                         guchar      exit_code);
+void             gsm_app_died                           (GsmApp     *app,
+                                                         int         signal);
+
+gboolean         gsm_app_provides                       (GsmApp     *app,
+                                                         const char *service);
+char           **gsm_app_get_provides                   (GsmApp     *app);
+gboolean         gsm_app_has_autostart_condition        (GsmApp     *app,
+                                                         const char *condition);
+gboolean         gsm_app_get_registered                 (GsmApp     *app);
+void             gsm_app_set_registered                 (GsmApp     *app,
+                                                         gboolean  registered);
+
+gboolean         gsm_app_save_to_keyfile                (GsmApp    *app,
+                                                         GKeyFile  *keyfile,
+                                                         GError   **error);
 
 G_END_DECLS
 
diff --git a/gnome-session/gsm-autostart-app.c b/gnome-session/gsm-autostart-app.c
new file mode 100644
index 00000000..fbac8e71
--- /dev/null
+++ b/gnome-session/gsm-autostart-app.c
@@ -0,0 +1,1505 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-systemd.h>
+
+#include <systemd/sd-journal.h>
+#include <systemd/sd-daemon.h>
+
+#include "gsm-autostart-app.h"
+#include "gsm-util.h"
+
+enum {
+        AUTOSTART_LAUNCH_SPAWN = 0,
+        AUTOSTART_LAUNCH_ACTIVATE
+};
+
+enum {
+        GSM_CONDITION_NONE           = 0,
+        GSM_CONDITION_IF_EXISTS      = 1,
+        GSM_CONDITION_UNLESS_EXISTS  = 2,
+        GSM_CONDITION_GSETTINGS      = 3,
+        GSM_CONDITION_IF_SESSION     = 4,
+        GSM_CONDITION_UNLESS_SESSION = 5,
+        GSM_CONDITION_UNKNOWN        = 6
+};
+
+#define GSM_SESSION_CLIENT_DBUS_INTERFACE "org.gnome.SessionClient"
+
+typedef struct
+{
+        gboolean              mask_systemd;
+        char                 *desktop_filename;
+        char                 *desktop_id;
+        char                 *startup_id;
+
+        GDesktopAppInfo      *app_info;
+        /* provides defined in session definition */
+        GSList               *session_provides;
+
+        /* desktop file state */
+        char                 *condition_string;
+        gboolean              condition;
+        gboolean              autorestart;
+
+        GFileMonitor         *condition_monitor;
+        guint                 condition_notify_id;
+        GSettings            *condition_settings;
+
+        int                   launch_type;
+        GPid                  pid;
+        guint                 child_watch_id;
+} GsmAutostartAppPrivate;
+
+enum {
+        CONDITION_CHANGED,
+        LAST_SIGNAL
+};
+
+typedef enum {
+        PROP_DESKTOP_FILENAME = 1,
+        PROP_MASK_SYSTEMD,
+} GsmAutostartAppProperty;
+
+static GParamSpec *props[PROP_MASK_SYSTEMD + 1] = { NULL, };
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void gsm_autostart_app_initable_iface_init (GInitableIface  *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GsmAutostartApp, gsm_autostart_app, GSM_TYPE_APP,
+                         G_ADD_PRIVATE (GsmAutostartApp)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gsm_autostart_app_initable_iface_init))
+
+static void
+gsm_autostart_app_init (GsmAutostartApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+
+        priv->pid = -1;
+        priv->condition_monitor = NULL;
+        priv->condition = FALSE;
+}
+
+static gboolean
+is_disabled (GsmApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+
+        /* GSM_AUTOSTART_APP_ENABLED_KEY key, used by old gnome-session */
+        if (g_desktop_app_info_has_key (priv->app_info,
+                                        GSM_AUTOSTART_APP_ENABLED_KEY) &&
+            !g_desktop_app_info_get_boolean (priv->app_info,
+                                             GSM_AUTOSTART_APP_ENABLED_KEY)) {
+                g_debug ("app %s is disabled by " GSM_AUTOSTART_APP_ENABLED_KEY,
+                         gsm_app_peek_id (app));
+                return TRUE;
+        }
+
+        /* Hidden key, used by autostart spec */
+        if (g_desktop_app_info_get_is_hidden (priv->app_info)) {
+                g_debug ("app %s is disabled by Hidden",
+                         gsm_app_peek_id (app));
+                return TRUE;
+        }
+
+        /* Check OnlyShowIn/NotShowIn/TryExec */
+        if (!g_desktop_app_info_get_show_in (priv->app_info, NULL)) {
+                g_debug ("app %s is not for the current desktop",
+                         gsm_app_peek_id (app));
+                return TRUE;
+        }
+
+        /* Check if app is systemd enabled and mask-systemd is set. */
+        if (priv->mask_systemd &&
+            g_desktop_app_info_has_key (priv->app_info,
+                                        GSM_AUTOSTART_APP_SYSTEMD_KEY) &&
+            g_desktop_app_info_get_boolean (priv->app_info,
+                                            GSM_AUTOSTART_APP_SYSTEMD_KEY)) {
+                g_debug ("app %s is disabled by " GSM_AUTOSTART_APP_SYSTEMD_KEY,
+                         gsm_app_peek_id (app));
+                return TRUE;
+        }
+
+        /* Do not check AutostartCondition - this method is only to determine
+         if the app is unconditionally disabled */
+
+        return FALSE;
+}
+
+static gboolean
+parse_condition_string (const char *condition_string,
+                        guint      *condition_kindp,
+                        char      **keyp)
+{
+        const char *space;
+        const char *key;
+        int         len;
+        guint       kind;
+
+        space = condition_string + strcspn (condition_string, " ");
+        len = space - condition_string;
+        key = space;
+        while (isspace ((unsigned char)*key)) {
+                key++;
+        }
+
+        kind = GSM_CONDITION_UNKNOWN;
+
+        if (!g_ascii_strncasecmp (condition_string, "if-exists", len) && key) {
+                kind = GSM_CONDITION_IF_EXISTS;
+        } else if (!g_ascii_strncasecmp (condition_string, "unless-exists", len) && key) {
+                kind = GSM_CONDITION_UNLESS_EXISTS;
+        } else if (!g_ascii_strncasecmp (condition_string, "GSettings", len)) {
+                kind = GSM_CONDITION_GSETTINGS;
+        } else if (!g_ascii_strncasecmp (condition_string, "GNOME3", len)) {
+                condition_string = key;
+                space = condition_string + strcspn (condition_string, " ");
+                len = space - condition_string;
+                key = space;
+                while (isspace ((unsigned char)*key)) {
+                        key++;
+                }
+                if (!g_ascii_strncasecmp (condition_string, "if-session", len) && key) {
+                        kind = GSM_CONDITION_IF_SESSION;
+                } else if (!g_ascii_strncasecmp (condition_string, "unless-session", len) && key) {
+                        kind = GSM_CONDITION_UNLESS_SESSION;
+                }
+        }
+
+        if (kind == GSM_CONDITION_UNKNOWN) {
+                key = NULL;
+        }
+
+        if (keyp != NULL) {
+                *keyp = g_strdup (key);
+        }
+
+        if (condition_kindp != NULL) {
+                *condition_kindp = kind;
+        }
+
+        return (kind != GSM_CONDITION_UNKNOWN);
+}
+
+static void
+if_exists_condition_cb (GFileMonitor     *monitor,
+                        GFile            *file,
+                        GFile            *other_file,
+                        GFileMonitorEvent event,
+                        GsmApp           *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        gboolean                condition = FALSE;
+
+        switch (event) {
+        case G_FILE_MONITOR_EVENT_CREATED:
+                condition = TRUE;
+                break;
+        case G_FILE_MONITOR_EVENT_DELETED:
+                condition = FALSE;
+                break;
+        default:
+                /* Ignore any other monitor event */
+                return;
+        }
+
+        /* Emit only if the condition actually changed */
+        if (condition != priv->condition) {
+                priv->condition = condition;
+                g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition);
+        }
+}
+
+static void
+unless_exists_condition_cb (GFileMonitor     *monitor,
+                            GFile            *file,
+                            GFile            *other_file,
+                            GFileMonitorEvent event,
+                            GsmApp           *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        gboolean                condition = FALSE;
+
+        switch (event) {
+        case G_FILE_MONITOR_EVENT_CREATED:
+                condition = FALSE;
+                break;
+        case G_FILE_MONITOR_EVENT_DELETED:
+                condition = TRUE;
+                break;
+        default:
+                /* Ignore any other monitor event */
+                return;
+        }
+
+        /* Emit only if the condition actually changed */
+        if (condition != priv->condition) {
+                priv->condition = condition;
+                g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition);
+        }
+}
+
+static void
+gsettings_condition_cb (GSettings  *settings,
+                        const char *key,
+                        gpointer    user_data)
+{
+        GsmApp *app = GSM_APP (user_data);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        gboolean                condition;
+
+        g_return_if_fail (GSM_IS_APP (user_data));
+
+        condition = g_settings_get_boolean (settings, key);
+
+        g_debug ("GsmAutostartApp: app:%s condition changed condition:%d",
+                 gsm_app_peek_id (app),
+                 condition);
+
+        /* Emit only if the condition actually changed */
+        if (condition != priv->condition) {
+                priv->condition = condition;
+                g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition);
+        }
+}
+
+static gboolean
+setup_gsettings_condition_monitor (GsmAutostartApp *app,
+                                   const char      *key)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        GSettingsSchemaSource *source;
+        GSettingsSchema *schema;
+        GSettings *settings;
+        GSettingsSchemaKey *schema_key;
+        const GVariantType *key_type;
+        char **elems;
+        gboolean retval = FALSE;
+        char *signal;
+
+        retval = FALSE;
+
+        schema = NULL;
+
+        elems = g_strsplit (key, " ", 2);
+
+        if (elems == NULL)
+                goto out;
+
+        if (elems[0] == NULL || elems[1] == NULL)
+                goto out;
+
+        source = g_settings_schema_source_get_default ();
+
+        schema = g_settings_schema_source_lookup (source, elems[0], TRUE);
+
+        if (schema == NULL)
+                goto out;
+
+        if (!g_settings_schema_has_key (schema, elems[1]))
+                goto out;
+
+        schema_key = g_settings_schema_get_key (schema, elems[1]);
+
+        g_assert (schema_key != NULL);
+
+        key_type = g_settings_schema_key_get_value_type (schema_key);
+
+        g_settings_schema_key_unref (schema_key);
+
+        g_assert (key_type != NULL);
+
+        if (!g_variant_type_equal (key_type, G_VARIANT_TYPE_BOOLEAN))
+                goto out;
+
+        settings = g_settings_new_full (schema, NULL, NULL);
+        retval = g_settings_get_boolean (settings, elems[1]);
+
+        signal = g_strdup_printf ("changed::%s", elems[1]);
+        g_signal_connect (G_OBJECT (settings), signal,
+                          G_CALLBACK (gsettings_condition_cb), app);
+        g_free (signal);
+
+        priv->condition_settings = settings;
+
+out:
+        if (schema)
+                g_settings_schema_unref (schema);
+        g_strfreev (elems);
+
+        return retval;
+}
+
+static void
+if_session_condition_cb (GObject    *object,
+                         GParamSpec *pspec,
+                         gpointer    user_data)
+{
+        GsmApp *app = GSM_APP (user_data);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        char                   *session_name;
+        char                   *key;
+        gboolean                condition;
+
+        g_return_if_fail (GSM_IS_APP (user_data));
+
+        parse_condition_string (priv->condition_string, NULL, &key);
+
+        g_object_get (object, "session-name", &session_name, NULL);
+        condition = strcmp (session_name, key) == 0;
+        g_free (session_name);
+
+        g_free (key);
+
+        g_debug ("GsmAutostartApp: app:%s condition changed condition:%d",
+                 gsm_app_peek_id (app),
+                 condition);
+
+        /* Emit only if the condition actually changed */
+        if (condition != priv->condition) {
+                priv->condition = condition;
+                g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition);
+        }
+}
+
+static void
+unless_session_condition_cb (GObject    *object,
+                             GParamSpec *pspec,
+                             gpointer    user_data)
+{
+        GsmApp *app = GSM_APP (user_data);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        char                   *session_name;
+        char                   *key;
+        gboolean                condition;
+
+        g_return_if_fail (GSM_IS_APP (user_data));
+
+        parse_condition_string (priv->condition_string, NULL, &key);
+
+        g_object_get (object, "session-name", &session_name, NULL);
+        condition = strcmp (session_name, key) != 0;
+        g_free (session_name);
+
+        g_free (key);
+
+        g_debug ("GsmAutostartApp: app:%s condition changed condition:%d",
+                 gsm_app_peek_id (app),
+                 condition);
+
+        /* Emit only if the condition actually changed */
+        if (condition != priv->condition) {
+                priv->condition = condition;
+                g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition);
+        }
+}
+
+static char *
+resolve_conditional_file_path (const char *file)
+{
+        if (g_path_is_absolute (file))
+                return g_strdup (file);
+        return g_build_filename (g_get_user_config_dir (), file, NULL);
+}
+
+static void
+setup_condition_monitor (GsmAutostartApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        guint    kind;
+        char    *key;
+        gboolean res;
+        gboolean disabled;
+
+        if (priv->condition_monitor != NULL) {
+                g_file_monitor_cancel (priv->condition_monitor);
+        }
+
+        if (priv->condition_string == NULL) {
+                return;
+        }
+
+        /* if it is disabled outright there is no point in monitoring */
+        if (is_disabled (GSM_APP (app))) {
+                return;
+        }
+
+        key = NULL;
+        res = parse_condition_string (priv->condition_string, &kind, &key);
+        if (! res) {
+                g_free (key);
+                return;
+        }
+
+        if (key == NULL) {
+                return;
+        }
+
+        if (kind == GSM_CONDITION_IF_EXISTS) {
+                char  *file_path;
+                GFile *file;
+
+                file_path = resolve_conditional_file_path (key);
+                disabled = !g_file_test (file_path, G_FILE_TEST_EXISTS);
+
+                file = g_file_new_for_path (file_path);
+                priv->condition_monitor = g_file_monitor_file (file, 0, NULL, NULL);
+
+                g_signal_connect (priv->condition_monitor, "changed",
+                                  G_CALLBACK (if_exists_condition_cb),
+                                  app);
+
+                g_object_unref (file);
+                g_free (file_path);
+        } else if (kind == GSM_CONDITION_UNLESS_EXISTS) {
+                char  *file_path;
+                GFile *file;
+
+                file_path = resolve_conditional_file_path (key);
+                disabled = g_file_test (file_path, G_FILE_TEST_EXISTS);
+
+                file = g_file_new_for_path (file_path);
+                priv->condition_monitor = g_file_monitor_file (file, 0, NULL, NULL);
+
+                g_signal_connect (priv->condition_monitor, "changed",
+                                  G_CALLBACK (unless_exists_condition_cb),
+                                  app);
+
+                g_object_unref (file);
+                g_free (file_path);
+        } else if (kind == GSM_CONDITION_GSETTINGS) {
+                disabled = !setup_gsettings_condition_monitor (app, key);
+        } else if (kind == GSM_CONDITION_IF_SESSION) {
+                GsmManager *manager;
+                char *session_name;
+
+                /* get the singleton */
+                manager = gsm_manager_get ();
+
+                g_object_get (manager, "session-name", &session_name, NULL);
+                disabled = strcmp (session_name, key) != 0;
+
+                g_signal_connect (manager, "notify::session-name",
+                                  G_CALLBACK (if_session_condition_cb), app);
+                g_free (session_name);
+        } else if (kind == GSM_CONDITION_UNLESS_SESSION) {
+                GsmManager *manager;
+                char *session_name;
+
+                /* get the singleton */
+                manager = gsm_manager_get ();
+
+                g_object_get (manager, "session-name", &session_name, NULL);
+                disabled = strcmp (session_name, key) == 0;
+
+                g_signal_connect (manager, "notify::session-name",
+                                  G_CALLBACK (unless_session_condition_cb), app);
+                g_free (session_name);
+        } else {
+                disabled = TRUE;
+        }
+
+        g_free (key);
+
+        if (disabled) {
+                /* FIXME: cache the disabled value? */
+        }
+}
+
+static void
+load_desktop_file (GsmAutostartApp  *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        char    *dbus_name;
+        char    *startup_id;
+        char    *phase_str;
+        int      phase;
+        gboolean res;
+
+        g_assert (priv->app_info != NULL);
+
+        phase_str = g_desktop_app_info_get_string (priv->app_info,
+                                                   GSM_AUTOSTART_APP_PHASE_KEY);
+        if (phase_str != NULL) {
+                if (strcmp (phase_str, "EarlyInitialization") == 0) {
+                        phase = GSM_MANAGER_PHASE_EARLY_INITIALIZATION;
+                } else if (strcmp (phase_str, "PreDisplayServer") == 0) {
+                        phase = GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER;
+                } else if (strcmp (phase_str, "DisplayServer") == 0) {
+                        phase = GSM_MANAGER_PHASE_DISPLAY_SERVER;
+                } else if (strcmp (phase_str, "Initialization") == 0) {
+                        phase = GSM_MANAGER_PHASE_INITIALIZATION;
+                } else if (strcmp (phase_str, "WindowManager") == 0) {
+                        phase = GSM_MANAGER_PHASE_WINDOW_MANAGER;
+                } else if (strcmp (phase_str, "Panel") == 0) {
+                        phase = GSM_MANAGER_PHASE_PANEL;
+                } else if (strcmp (phase_str, "Desktop") == 0) {
+                        phase = GSM_MANAGER_PHASE_DESKTOP;
+                } else {
+                        phase = GSM_MANAGER_PHASE_APPLICATION;
+                }
+
+                g_free (phase_str);
+        } else {
+                phase = GSM_MANAGER_PHASE_APPLICATION;
+        }
+
+        dbus_name = g_desktop_app_info_get_string (priv->app_info,
+                                                   GSM_AUTOSTART_APP_DBUS_NAME_KEY);
+        if (dbus_name != NULL) {
+                priv->launch_type = AUTOSTART_LAUNCH_ACTIVATE;
+        } else {
+                priv->launch_type = AUTOSTART_LAUNCH_SPAWN;
+        }
+
+        /* this must only be done on first load */
+        switch (priv->launch_type) {
+        case AUTOSTART_LAUNCH_SPAWN:
+                startup_id =
+                        g_desktop_app_info_get_string (priv->app_info,
+                                                       GSM_AUTOSTART_APP_STARTUP_ID_KEY);
+
+                if (startup_id == NULL) {
+                        startup_id = gsm_util_generate_startup_id ();
+                }
+                break;
+        case AUTOSTART_LAUNCH_ACTIVATE:
+                startup_id = g_strdup (dbus_name);
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+
+        res = g_desktop_app_info_has_key (priv->app_info,
+                                          GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        if (res) {
+                priv->autorestart = g_desktop_app_info_get_boolean (priv->app_info,
+                                                                         GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        } else {
+                priv->autorestart = FALSE;
+        }
+
+        g_free (priv->condition_string);
+        priv->condition_string = g_desktop_app_info_get_string (priv->app_info,
+                                                                   "AutostartCondition");
+        setup_condition_monitor (app);
+
+        g_object_set (app,
+                      "phase", phase,
+                      "startup-id", startup_id,
+                      NULL);
+
+        g_free (startup_id);
+        g_free (dbus_name);
+}
+
+static void
+gsm_autostart_app_set_desktop_filename (GsmAutostartApp *app,
+                                        const char      *desktop_filename)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+
+        if (g_strcmp0 (priv->desktop_filename, desktop_filename) == 0)
+                return;
+
+        if (priv->app_info != NULL) {
+                g_clear_object (&priv->app_info);
+                g_clear_pointer (&priv->desktop_filename, g_free);
+                g_clear_pointer (&priv->desktop_id, g_free);
+        }
+
+        if (desktop_filename != NULL) {
+                priv->desktop_filename = g_strdup (desktop_filename);
+                priv->desktop_id = g_path_get_basename (desktop_filename);
+        }
+
+        g_object_notify_by_pspec (G_OBJECT (app), props[PROP_DESKTOP_FILENAME]);
+}
+
+static void
+gsm_autostart_app_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+        GsmAutostartApp *self = GSM_AUTOSTART_APP (object);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+
+        switch ((GsmAutostartAppProperty) prop_id) {
+        case PROP_DESKTOP_FILENAME:
+                gsm_autostart_app_set_desktop_filename (self, g_value_get_string (value));
+                break;
+        case PROP_MASK_SYSTEMD:
+                if (priv->mask_systemd != g_value_get_boolean (value)) {
+                        priv->mask_systemd = g_value_get_boolean (value);
+                        g_object_notify_by_pspec (object, props[PROP_MASK_SYSTEMD]);
+                }
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsm_autostart_app_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+        GsmAutostartApp *self = GSM_AUTOSTART_APP (object);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+
+        switch ((GsmAutostartAppProperty) prop_id) {
+        case PROP_DESKTOP_FILENAME:
+                if (priv->app_info != NULL) {
+                        g_value_set_string (value, g_desktop_app_info_get_filename (priv->app_info));
+                } else {
+                        g_value_set_string (value, NULL);
+                }
+                break;
+        case PROP_MASK_SYSTEMD:
+                g_value_set_boolean (value, priv->mask_systemd);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsm_autostart_app_dispose (GObject *object)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (object));
+
+        g_clear_pointer (&priv->startup_id, g_free);
+
+        if (priv->session_provides) {
+                g_slist_free_full (priv->session_provides, g_free);
+                priv->session_provides = NULL;
+        }
+
+        g_clear_pointer (&priv->condition_string, g_free);
+        g_clear_object (&priv->condition_settings);
+        g_clear_object (&priv->app_info);
+        g_clear_pointer (&priv->desktop_filename, g_free);
+        g_clear_pointer (&priv->desktop_id, g_free);
+
+        if (priv->child_watch_id > 0) {
+                g_source_remove (priv->child_watch_id);
+                priv->child_watch_id = 0;
+        }
+
+        if (priv->condition_monitor) {
+                g_file_monitor_cancel (priv->condition_monitor);
+        }
+
+        G_OBJECT_CLASS (gsm_autostart_app_parent_class)->dispose (object);
+}
+
+static gboolean
+is_running (GsmApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        gboolean                is;
+
+        /* is running if pid is still valid or
+         * or a client is connected
+         */
+        /* FIXME: check client */
+        is = (priv->pid != -1);
+
+        return is;
+}
+
+static gboolean
+is_conditionally_disabled (GsmApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        gboolean                res;
+        gboolean                disabled;
+        char                   *key;
+        guint                   kind;
+
+        /* Check AutostartCondition */
+        if (priv->condition_string == NULL) {
+                return FALSE;
+        }
+
+        key = NULL;
+        res = parse_condition_string (priv->condition_string, &kind, &key);
+        if (! res) {
+                g_free (key);
+                return TRUE;
+        }
+
+        if (key == NULL) {
+                return TRUE;
+        }
+
+        if (kind == GSM_CONDITION_IF_EXISTS) {
+                char *file_path;
+
+                file_path = resolve_conditional_file_path (key);
+                disabled = !g_file_test (file_path, G_FILE_TEST_EXISTS);
+                g_free (file_path);
+        } else if (kind == GSM_CONDITION_UNLESS_EXISTS) {
+                char *file_path;
+
+                file_path = resolve_conditional_file_path (key);
+                disabled = g_file_test (file_path, G_FILE_TEST_EXISTS);
+                g_free (file_path);
+        } else if (kind == GSM_CONDITION_GSETTINGS &&
+                   priv->condition_settings != NULL) {
+                char **elems;
+                elems = g_strsplit (key, " ", 2);
+                disabled = !g_settings_get_boolean (priv->condition_settings, elems[1]);
+                g_strfreev (elems);
+        } else if (kind == GSM_CONDITION_IF_SESSION) {
+                GsmManager *manager;
+                char *session_name;
+
+                /* get the singleton */
+                manager = gsm_manager_get ();
+
+                g_object_get (manager, "session-name", &session_name, NULL);
+                disabled = strcmp (session_name, key) != 0;
+                g_free (session_name);
+        } else if (kind == GSM_CONDITION_UNLESS_SESSION) {
+                GsmManager *manager;
+                char *session_name;
+
+                /* get the singleton */
+                manager = gsm_manager_get ();
+
+                g_object_get (manager, "session-name", &session_name, NULL);
+                disabled = strcmp (session_name, key) == 0;
+                g_free (session_name);
+        } else {
+                disabled = TRUE;
+        }
+
+        /* Set initial condition */
+        priv->condition = !disabled;
+
+        g_free (key);
+
+        return disabled;
+}
+
+static void
+app_exited (GPid             pid,
+            int              status,
+            GsmAutostartApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+
+        g_debug ("GsmAutostartApp: (pid:%d) done (%s:%d)",
+                 (int) pid,
+                 WIFEXITED (status) ? "status"
+                 : WIFSIGNALED (status) ? "signal"
+                 : "unknown",
+                 WIFEXITED (status) ? WEXITSTATUS (status)
+                 : WIFSIGNALED (status) ? WTERMSIG (status)
+                 : -1);
+
+        g_spawn_close_pid (priv->pid);
+        priv->pid = -1;
+        priv->child_watch_id = 0;
+
+        if (WIFEXITED (status)) {
+                gsm_app_exited (GSM_APP (app), WEXITSTATUS (status));
+        } else if (WIFSIGNALED (status)) {
+                gsm_app_died (GSM_APP (app), WTERMSIG (status));
+        }
+}
+
+static int
+_signal_pid (int pid,
+             int signal)
+{
+        int status;
+
+        /* perhaps block sigchld */
+        g_debug ("GsmAutostartApp: sending signal %d to process %d", signal, pid);
+        errno = 0;
+        status = kill (pid, signal);
+
+        if (status < 0) {
+                if (errno == ESRCH) {
+                        g_warning ("Child process %d was already dead.",
+                                   (int)pid);
+                } else {
+                        g_warning ("Couldn't kill child process %d: %s",
+                                   pid,
+                                   g_strerror (errno));
+                }
+        }
+
+        /* perhaps unblock sigchld */
+
+        return status;
+}
+
+static gboolean
+autostart_app_stop_spawn (GsmAutostartApp *app,
+                          GError         **error)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        int res;
+
+        if (priv->pid < 1) {
+                g_set_error (error,
+                             GSM_APP_ERROR,
+                             GSM_APP_ERROR_STOP,
+                             "Not running");
+                return FALSE;
+        }
+
+        res = _signal_pid (priv->pid, SIGTERM);
+        if (res != 0) {
+                g_set_error (error,
+                             GSM_APP_ERROR,
+                             GSM_APP_ERROR_STOP,
+                             "Unable to stop: %s",
+                             g_strerror (errno));
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+autostart_app_stop_activate (GsmAutostartApp *app,
+                             GError         **error)
+{
+        return TRUE;
+}
+
+static gboolean
+gsm_autostart_app_stop (GsmApp  *app,
+                        GError **error)
+{
+        GsmAutostartApp *self = GSM_AUTOSTART_APP (app);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+        gboolean         ret;
+
+        g_return_val_if_fail (priv->app_info != NULL, FALSE);
+
+        switch (priv->launch_type) {
+        case AUTOSTART_LAUNCH_SPAWN:
+                ret = autostart_app_stop_spawn (self, error);
+                break;
+        case AUTOSTART_LAUNCH_ACTIVATE:
+                ret = autostart_app_stop_activate (self, error);
+                break;
+        default:
+                g_assert_not_reached ();
+                break;
+        }
+
+        return ret;
+}
+
+static void
+app_launched (GAppLaunchContext *ctx,
+              GAppInfo    *appinfo,
+              GVariant    *platform_data,
+              gpointer     data)
+{
+        GsmAutostartApp *app = data;
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        gint pid;
+        gchar *sn_id;
+
+        pid = 0;
+        sn_id = NULL;
+
+        g_variant_lookup (platform_data, "pid", "i", &pid);
+        g_variant_lookup (platform_data, "startup-notification-id", "s", &sn_id);
+        priv->pid = pid;
+        priv->startup_id = sn_id;
+
+        /* We are not interested in the result. */
+        gnome_start_systemd_scope (priv->desktop_id,
+                                   pid,
+                                   NULL,
+                                   NULL,
+                                   NULL, NULL, NULL);
+}
+
+static void
+on_child_setup (GsmAutostartApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        int standard_output, standard_error;
+
+        /* The FALSE means programs aren't expected to prefix each
+         * line with <n> prefix to specify priority.
+         */
+        standard_output = sd_journal_stream_fd (priv->desktop_id,
+                                                LOG_INFO,
+                                                FALSE);
+        standard_error = sd_journal_stream_fd (priv->desktop_id,
+                                               LOG_WARNING,
+                                               FALSE);
+
+        if (standard_output >= 0) {
+                dup2 (standard_output, STDOUT_FILENO);
+                close (standard_output);
+        }
+
+        if (standard_error >= 0) {
+                dup2 (standard_error, STDERR_FILENO);
+                close (standard_error);
+        }
+}
+
+static gboolean
+autostart_app_start_spawn (GsmAutostartApp *app,
+                           GError         **error)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        gboolean         success;
+        GError          *local_error;
+        const char      *startup_id;
+        const char * const *variable_blacklist;
+        const char * const *child_environment;
+        int i;
+        GAppLaunchContext *ctx;
+        GSpawnChildSetupFunc child_setup_func = NULL;
+        gpointer             child_setup_data = NULL;
+        guint handler;
+
+        startup_id = gsm_app_peek_startup_id (GSM_APP (app));
+        g_assert (startup_id != NULL);
+
+        g_debug ("GsmAutostartApp: starting %s: command=%s startup-id=%s", priv->desktop_id, g_app_info_get_commandline (G_APP_INFO (priv->app_info)), startup_id);
+
+        g_free (priv->startup_id);
+        local_error = NULL;
+        ctx = g_app_launch_context_new ();
+
+        variable_blacklist = gsm_util_get_variable_blacklist ();
+        for (i = 0; variable_blacklist[i] != NULL; i++)
+                g_app_launch_context_unsetenv (ctx, variable_blacklist[i]);
+
+        child_environment = gsm_util_listenv ();
+        for (i = 0; child_environment[i] != NULL; i++) {
+                char **environment_tuple;
+                const char *key;
+                const char *value;
+
+                environment_tuple = g_strsplit (child_environment[i], "=", 2);
+                key = environment_tuple[0];
+                value = environment_tuple[1];
+
+                if (value != NULL)
+                        g_app_launch_context_setenv (ctx, key, value);
+
+                g_strfreev (environment_tuple);
+        }
+
+        if (startup_id != NULL) {
+                g_app_launch_context_setenv (ctx, "DESKTOP_AUTOSTART_ID", startup_id);
+        }
+
+        if (sd_booted () > 0) {
+                child_setup_func = (GSpawnChildSetupFunc) on_child_setup;
+                child_setup_data = app;
+        }
+
+        handler = g_signal_connect (ctx, "launched", G_CALLBACK (app_launched), app);
+        success = g_desktop_app_info_launch_uris_as_manager (priv->app_info,
+                                                             NULL,
+                                                             ctx,
+                                                             G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+                                                             child_setup_func, child_setup_data,
+                                                             NULL, NULL,
+                                                             &local_error);
+        g_signal_handler_disconnect (ctx, handler);
+
+        if (success) {
+                if (priv->pid > 0) {
+                        g_debug ("GsmAutostartApp: started pid:%d", priv->pid);
+                        priv->child_watch_id = g_child_watch_add (priv->pid,
+                                                                       (GChildWatchFunc)app_exited,
+                                                                       app);
+                }
+        } else {
+                g_set_error (error,
+                             GSM_APP_ERROR,
+                             GSM_APP_ERROR_START,
+                             "Unable to start application: %s", local_error->message);
+                g_error_free (local_error);
+        }
+
+        return success;
+}
+
+static void
+start_notify (GObject      *source,
+              GAsyncResult *result,
+              gpointer      user_data)
+{
+        GError          *error;
+        GsmAutostartApp *app = GSM_AUTOSTART_APP (user_data);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+
+        error = NULL;
+
+        g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+
+        if (error != NULL) {
+                g_warning ("GsmAutostartApp: Error starting application: %s", error->message);
+                g_error_free (error);
+        } else {
+                g_debug ("GsmAutostartApp: Started application %s", priv->desktop_id);
+        }
+}
+
+static gboolean
+autostart_app_start_activate (GsmAutostartApp  *app,
+                              GError          **error)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        const char      *name;
+        char            *path;
+        char            *arguments;
+        GDBusConnection *bus;
+        GError          *local_error;
+
+        local_error = NULL;
+        bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+        if (local_error != NULL) {
+                g_warning ("error getting session bus: %s", local_error->message);
+                g_propagate_error (error, local_error);
+                return FALSE;
+        }
+
+        name = gsm_app_peek_startup_id (GSM_APP (app));
+        g_assert (name != NULL);
+
+        path = g_desktop_app_info_get_string (priv->app_info,
+                                              GSM_AUTOSTART_APP_DBUS_PATH_KEY);
+        if (path == NULL) {
+                /* just pick one? */
+                path = g_strdup ("/");
+        }
+
+        arguments = g_desktop_app_info_get_string (priv->app_info,
+                                                   GSM_AUTOSTART_APP_DBUS_ARGS_KEY);
+
+        g_dbus_connection_call (bus,
+                                name,
+                                path,
+                                GSM_SESSION_CLIENT_DBUS_INTERFACE,
+                                "Start",
+                                g_variant_new ("(s)", arguments),
+                                NULL,
+                                G_DBUS_CALL_FLAGS_NONE,
+                                -1, NULL,
+                                start_notify, app);
+        g_object_unref (bus);
+
+        return TRUE;
+}
+
+static gboolean
+gsm_autostart_app_start (GsmApp  *app,
+                         GError **error)
+{
+        GsmAutostartApp *self = GSM_AUTOSTART_APP (app);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+        gboolean         ret;
+
+        g_return_val_if_fail (priv->app_info != NULL, FALSE);
+
+        switch (priv->launch_type) {
+        case AUTOSTART_LAUNCH_SPAWN:
+                ret = autostart_app_start_spawn (self, error);
+                break;
+        case AUTOSTART_LAUNCH_ACTIVATE:
+                ret = autostart_app_start_activate (self, error);
+                break;
+        default:
+                g_assert_not_reached ();
+                break;
+        }
+
+        return ret;
+}
+
+static gboolean
+gsm_autostart_app_restart (GsmApp  *app,
+                           GError **error)
+{
+        GError  *local_error;
+        gboolean res;
+
+        /* ignore stop errors - it is fine if it is already stopped */
+        local_error = NULL;
+        res = gsm_app_stop (app, &local_error);
+        if (! res) {
+                g_debug ("GsmAutostartApp: Couldn't stop app: %s", local_error->message);
+                g_error_free (local_error);
+        }
+
+        res = gsm_app_start (app, &local_error);
+        if (! res) {
+                g_propagate_error (error, local_error);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+gsm_autostart_app_provides (GsmApp     *app,
+                            const char *service)
+{
+        gchar           *provides_str;
+        char           **provides;
+        gsize            i;
+        GSList          *l;
+        GsmAutostartApp *self = GSM_AUTOSTART_APP (app);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+
+        g_return_val_if_fail (GSM_IS_APP (app), FALSE);
+
+        if (priv->app_info == NULL) {
+                return FALSE;
+        }
+
+        for (l = priv->session_provides; l != NULL; l = l->next) {
+                if (!strcmp (l->data, service))
+                        return TRUE;
+        }
+
+        provides_str = g_desktop_app_info_get_string (priv->app_info,
+                                                      GSM_AUTOSTART_APP_PROVIDES_KEY);
+        if (!provides_str) {
+                return FALSE;
+        }
+        provides = g_strsplit (provides_str, ";", -1);
+        g_free (provides_str);
+
+        for (i = 0; provides[i]; i++) {
+                if (!strcmp (provides[i], service)) {
+                        g_strfreev (provides);
+                        return TRUE;
+                }
+        }
+
+        g_strfreev (provides);
+
+        return FALSE;
+}
+
+static char **
+gsm_autostart_app_get_provides (GsmApp *app)
+{
+        GsmAutostartApp  *self = GSM_AUTOSTART_APP (app);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+        gchar            *provides_str;
+        char            **provides;
+        gsize             provides_len;
+        char            **result;
+        gsize             result_len;
+        int               i;
+        GSList           *l;
+
+        g_return_val_if_fail (GSM_IS_APP (app), NULL);
+
+        if (priv->app_info == NULL) {
+                return NULL;
+        }
+
+        provides_str = g_desktop_app_info_get_string (priv->app_info,
+                                                      GSM_AUTOSTART_APP_PROVIDES_KEY);
+
+        if (provides_str == NULL) {
+                return NULL;
+        }
+
+        provides = g_strsplit (provides_str, ";", -1);
+        provides_len = g_strv_length (provides);
+        g_free (provides_str);
+
+        if (!priv->session_provides) {
+                return provides;
+        }
+
+        result_len = provides_len + g_slist_length (priv->session_provides);
+        result = g_new (char *, result_len + 1); /* including last NULL */
+
+        for (i = 0; provides[i] != NULL; i++)
+                result[i] = provides[i];
+        g_free (provides);
+
+        for (l = priv->session_provides; l != NULL; l = l->next, i++)
+                result[i] = g_strdup (l->data);
+
+        result[i] = NULL;
+
+        g_assert (i == result_len);
+
+        return result;
+}
+
+void
+gsm_autostart_app_add_provides (GsmAutostartApp *aapp,
+                                const char      *provides)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (aapp);
+
+        g_return_if_fail (GSM_IS_AUTOSTART_APP (aapp));
+
+        priv->session_provides = g_slist_prepend (priv->session_provides,
+                                                  g_strdup (provides));
+}
+
+static gboolean
+gsm_autostart_app_has_autostart_condition (GsmApp     *app,
+                                           const char *condition)
+{
+        GsmAutostartApp *self = GSM_AUTOSTART_APP (app);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (self);
+
+        g_return_val_if_fail (GSM_IS_APP (app), FALSE);
+        g_return_val_if_fail (condition != NULL, FALSE);
+
+        if (priv->condition_string == NULL) {
+                return FALSE;
+        }
+
+        if (strcmp (priv->condition_string, condition) == 0) {
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static gboolean
+gsm_autostart_app_get_autorestart (GsmApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+        gboolean res;
+        gboolean autorestart;
+
+        if (priv->app_info == NULL) {
+                return FALSE;
+        }
+
+        autorestart = FALSE;
+
+        res = g_desktop_app_info_has_key (priv->app_info,
+                                          GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        if (res) {
+                autorestart = g_desktop_app_info_get_boolean (priv->app_info,
+                                                              GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        }
+
+        return autorestart;
+}
+
+static const char *
+gsm_autostart_app_get_app_id (GsmApp *app)
+{
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (GSM_AUTOSTART_APP (app));
+
+        if (priv->app_info == NULL) {
+                return NULL;
+        }
+
+        return g_app_info_get_id (G_APP_INFO (priv->app_info));
+}
+
+static gboolean
+gsm_autostart_app_initable_init (GInitable *initable,
+                                 GCancellable *cancellable,
+                                 GError  **error)
+{
+        GsmAutostartApp *app = GSM_AUTOSTART_APP (initable);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+
+        g_assert (priv->desktop_filename != NULL);
+        priv->app_info = g_desktop_app_info_new_from_filename (priv->desktop_filename);
+        if (priv->app_info == NULL) {
+                g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                             "Desktop file %s for application %s could not be parsed or references a missing TryExec binary",
+                             priv->desktop_filename,
+                             priv->desktop_id);
+                return FALSE;
+        }
+
+        load_desktop_file (app);
+
+        return TRUE;
+}
+
+static gboolean
+gsm_autostart_app_save_to_keyfile (GsmApp    *base_app,
+                                   GKeyFile  *keyfile,
+                                   GError   **error)
+{
+        GsmAutostartApp *app = GSM_AUTOSTART_APP (base_app);
+        GsmAutostartAppPrivate *priv = gsm_autostart_app_get_instance_private (app);
+        char   **provides = NULL;
+        char    *dbus_name;
+        char    *phase;
+        gboolean res;
+
+        provides = gsm_app_get_provides (base_app);
+        if (provides != NULL) {
+                g_key_file_set_string_list (keyfile,
+                                            G_KEY_FILE_DESKTOP_GROUP,
+                                            GSM_AUTOSTART_APP_PROVIDES_KEY,
+                                            (const char * const *)
+                                            provides,
+                                            g_strv_length (provides));
+                g_strfreev (provides);
+        }
+
+        phase = g_desktop_app_info_get_string (priv->app_info,
+                                                   GSM_AUTOSTART_APP_PHASE_KEY);
+        if (phase != NULL) {
+                g_key_file_set_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_GROUP,
+                                       GSM_AUTOSTART_APP_PHASE_KEY,
+                                       phase);
+                g_free (phase);
+        }
+
+        dbus_name = g_desktop_app_info_get_string (priv->app_info,
+                                                   GSM_AUTOSTART_APP_DBUS_NAME_KEY);
+        if (dbus_name != NULL) {
+                g_key_file_set_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_GROUP,
+                                       GSM_AUTOSTART_APP_DBUS_NAME_KEY,
+                                       dbus_name);
+                g_free (dbus_name);
+        }
+
+        res = g_desktop_app_info_has_key (priv->app_info,
+                                          GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        if (res) {
+                g_key_file_set_boolean (keyfile,
+                                        G_KEY_FILE_DESKTOP_GROUP,
+                                        GSM_AUTOSTART_APP_AUTORESTART_KEY,
+                                        g_desktop_app_info_get_boolean (priv->app_info,
+                                                                        GSM_AUTOSTART_APP_AUTORESTART_KEY));
+        }
+
+        res = g_desktop_app_info_has_key (priv->app_info,
+                                          GSM_AUTOSTART_APP_AUTORESTART_KEY);
+        if (res) {
+                char *autostart_condition;
+
+                autostart_condition = g_desktop_app_info_get_string (priv->app_info, "AutostartCondition");
+
+                g_key_file_set_string (keyfile,
+                                       G_KEY_FILE_DESKTOP_GROUP,
+                                       "AutostartCondition",
+                                       autostart_condition);
+                g_free (autostart_condition);
+        }
+
+        return TRUE;
+}
+
+static void
+gsm_autostart_app_initable_iface_init (GInitableIface  *iface)
+{
+        iface->init = gsm_autostart_app_initable_init;
+}
+
+static void
+gsm_autostart_app_class_init (GsmAutostartAppClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+        GsmAppClass  *app_class = GSM_APP_CLASS (klass);
+
+        object_class->set_property = gsm_autostart_app_set_property;
+        object_class->get_property = gsm_autostart_app_get_property;
+        object_class->dispose = gsm_autostart_app_dispose;
+
+        app_class->impl_is_disabled = is_disabled;
+        app_class->impl_is_conditionally_disabled = is_conditionally_disabled;
+        app_class->impl_is_running = is_running;
+        app_class->impl_start = gsm_autostart_app_start;
+        app_class->impl_restart = gsm_autostart_app_restart;
+        app_class->impl_stop = gsm_autostart_app_stop;
+        app_class->impl_provides = gsm_autostart_app_provides;
+        app_class->impl_get_provides = gsm_autostart_app_get_provides;
+        app_class->impl_has_autostart_condition = gsm_autostart_app_has_autostart_condition;
+        app_class->impl_get_app_id = gsm_autostart_app_get_app_id;
+        app_class->impl_get_autorestart = gsm_autostart_app_get_autorestart;
+        app_class->impl_save_to_keyfile = gsm_autostart_app_save_to_keyfile;
+
+        props[PROP_DESKTOP_FILENAME] =
+                g_param_spec_string ("desktop-filename",
+                                     "Desktop filename",
+                                     "Freedesktop .desktop file",
+                                     NULL,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+        props[PROP_MASK_SYSTEMD] =
+                g_param_spec_boolean ("mask-systemd",
+                                      "Mask if systemd started",
+                                      "Mask if GNOME systemd flag is set in desktop file",
+                                      FALSE,
+                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+        g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
+
+        signals[CONDITION_CHANGED] =
+                g_signal_new ("condition-changed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmAutostartAppClass, condition_changed),
+                              NULL, NULL, NULL,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_BOOLEAN);
+}
+
+GsmApp *
+gsm_autostart_app_new (const char *desktop_file,
+                       gboolean    mask_systemd,
+                       GError    **error)
+{
+        return (GsmApp*) g_initable_new (GSM_TYPE_AUTOSTART_APP, NULL, error,
+                                         "desktop-filename", desktop_file,
+                                         "mask-systemd", mask_systemd,
+                                         NULL);
+}
diff --git a/gnome-session/gsm-autostart-app.h b/gnome-session/gsm-autostart-app.h
new file mode 100644
index 00000000..91a74a67
--- /dev/null
+++ b/gnome-session/gsm-autostart-app.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSM_AUTOSTART_APP_H__
+#define __GSM_AUTOSTART_APP_H__
+
+#include "gsm-app.h"
+
+G_BEGIN_DECLS
+
+#define GSM_TYPE_AUTOSTART_APP            (gsm_autostart_app_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GsmAutostartApp, gsm_autostart_app, GSM, AUTOSTART_APP, GsmApp)
+
+struct _GsmAutostartAppClass
+{
+        GsmAppClass parent_class;
+
+        /* signals */
+        void     (*condition_changed)  (GsmApp  *app,
+                                        gboolean condition);
+};
+
+GsmApp *gsm_autostart_app_new                (const char *desktop_file,
+                                              gboolean    mask_systemd,
+                                              GError    **error);
+
+void    gsm_autostart_app_add_provides       (GsmAutostartApp *aapp,
+                                              const char      *provides);
+
+#define GSM_AUTOSTART_APP_SYSTEMD_KEY     "X-GNOME-HiddenUnderSystemd"
+#define GSM_AUTOSTART_APP_ENABLED_KEY     "X-GNOME-Autostart-enabled"
+#define GSM_AUTOSTART_APP_PHASE_KEY       "X-GNOME-Autostart-Phase"
+#define GSM_AUTOSTART_APP_PROVIDES_KEY    "X-GNOME-Provides"
+#define GSM_AUTOSTART_APP_STARTUP_ID_KEY  "X-GNOME-Autostart-startup-id"
+#define GSM_AUTOSTART_APP_AUTORESTART_KEY "X-GNOME-AutoRestart"
+#define GSM_AUTOSTART_APP_DBUS_NAME_KEY   "X-GNOME-DBus-Name"
+#define GSM_AUTOSTART_APP_DBUS_PATH_KEY   "X-GNOME-DBus-Path"
+#define GSM_AUTOSTART_APP_DBUS_ARGS_KEY   "X-GNOME-DBus-Start-Arguments"
+#define GSM_AUTOSTART_APP_DISCARD_KEY     "X-GNOME-Autostart-discard-exec"
+
+G_END_DECLS
+
+#endif /* __GSM_AUTOSTART_APP_H__ */
diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c
index 7e0e3f20..c8123eab 100644
--- a/gnome-session/gsm-client.c
+++ b/gnome-session/gsm-client.c
@@ -19,32 +19,29 @@
 
 #include "config.h"
 
-#include <gio/gio.h>
-
 #include "gsm-client.h"
-#include "org.gnome.SessionManager.ClientPrivate.h"
-
-struct _GsmClient
-{
-        GObject                   parent_instance;
+#include "org.gnome.SessionManager.Client.h"
 
-        char                     *id;
-        char                     *app_id;
-        char                     *bus_name;
+static guint32 client_serial = 1;
 
-        GPid                      caller_pid;
-
-        GDBusConnection          *connection;
-        GsmExportedClientPrivate *skeleton;
-        guint                     watch_id;
-};
+typedef struct
+{
+        char            *id;
+        char            *startup_id;
+        char            *app_id;
+        guint            status;
+        GsmExportedClient *skeleton;
+        GDBusConnection *connection;
+} GsmClientPrivate;
 
 typedef enum {
-        PROP_0,
+        PROP_STARTUP_ID = 1,
         PROP_APP_ID,
-        PROP_BUS_NAME,
+        PROP_STATUS,
 } GsmClientProperty;
 
+static GParamSpec *props[PROP_STATUS + 1] = { NULL, };
+
 enum {
         DISCONNECTED,
         END_SESSION_RESPONSE,
@@ -53,81 +50,156 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-G_DEFINE_TYPE (GsmClient, gsm_client, G_TYPE_OBJECT);
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GsmClient, gsm_client, G_TYPE_OBJECT)
+
+#define GSM_CLIENT_DBUS_IFACE "org.gnome.SessionManager.Client"
+
+static const GDBusErrorEntry gsm_client_error_entries[] = {
+        { GSM_CLIENT_ERROR_GENERAL, GSM_CLIENT_DBUS_IFACE ".GeneralError" },
+        { GSM_CLIENT_ERROR_NOT_REGISTERED, GSM_CLIENT_DBUS_IFACE ".NotRegistered" }
+};
+
+GQuark
+gsm_client_error_quark (void)
+{
+        static volatile gsize quark_volatile = 0;
+
+        g_dbus_error_register_error_domain ("gsm_client_error",
+                                            &quark_volatile,
+                                            gsm_client_error_entries,
+                                            G_N_ELEMENTS (gsm_client_error_entries));
+        return quark_volatile;
+}
 
 static guint32
 get_next_client_serial (void)
 {
-        static guint32 next_serial = 1;
         guint32 serial;
 
-        serial = next_serial++;
+        serial = client_serial++;
 
-        if ((gint32)next_serial < 0)
-                next_serial = 1;
+        if ((gint32)client_serial < 0) {
+                client_serial = 1;
+        }
 
         return serial;
 }
 
 static gboolean
-handle_end_session_response (GsmExportedClientPrivate *skeleton,
-                             GDBusMethodInvocation    *invocation,
-                             gboolean                  is_ok,
-                             const char               *reason,
-                             GsmClient                *client)
+gsm_client_get_startup_id (GsmExportedClient     *skeleton,
+                           GDBusMethodInvocation *invocation,
+                           GsmClient             *client)
 {
-        g_debug ("GsmClient(%s): got EndSessionResponse is-ok:%d reason=%s",
-                 client->app_id, is_ok, reason);
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
 
-        g_signal_emit (client, signals[END_SESSION_RESPONSE], 0, is_ok, reason);
+        gsm_exported_client_complete_get_startup_id (skeleton, invocation, priv->startup_id);
+        return TRUE;
+}
+
+static gboolean
+gsm_client_get_app_id (GsmExportedClient     *skeleton,
+                       GDBusMethodInvocation *invocation,
+                       GsmClient             *client)
+{
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
 
-        gsm_exported_client_private_complete_end_session_response (skeleton, invocation);
+        gsm_exported_client_complete_get_app_id (skeleton, invocation, priv->app_id);
         return TRUE;
 }
 
 static gboolean
-register_client (GsmClient *client)
+gsm_client_get_restart_style_hint (GsmExportedClient     *skeleton,
+                                   GDBusMethodInvocation *invocation,
+                                   GsmClient             *client)
 {
-        g_autoptr (GError) error = NULL;
+        guint hint;
 
-        client->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
-        if (error != NULL) {
-                g_critical ("GsmClient: Couldn't connect to session bus: %s",
-                            error->message);
-                return FALSE;
-        }
+        hint = GSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client);
+        gsm_exported_client_complete_get_restart_style_hint (skeleton, invocation, hint);
+        return TRUE;
+}
+
+static gboolean
+gsm_client_get_status (GsmExportedClient     *skeleton,
+                       GDBusMethodInvocation *invocation,
+                       GsmClient             *client)
+{
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+
+        gsm_exported_client_complete_get_status (skeleton, invocation, priv->status);
+        return TRUE;
+}
+
+static gboolean
+gsm_client_get_unix_process_id (GsmExportedClient     *skeleton,
+                                GDBusMethodInvocation *invocation,
+                                GsmClient             *client)
+{
+        guint pid;
+
+        pid = GSM_CLIENT_GET_CLASS (client)->impl_get_unix_process_id (client);
+        gsm_exported_client_complete_get_unix_process_id (skeleton, invocation, pid);
+        return TRUE;
+}
+
+static gboolean
+gsm_client_stop_dbus (GsmExportedClient     *skeleton,
+                      GDBusMethodInvocation *invocation,
+                      GsmClient             *client)
+{
+        GError *error = NULL;
+        gsm_client_stop (client, &error);
 
-        client->skeleton = gsm_exported_client_private_skeleton_new ();
-        g_debug ("exporting client to object path: %s", client->id);
-        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (client->skeleton),
-                                          client->connection, client->id, &error);
         if (error != NULL) {
-                g_critical ("GsmClient: error exporting on session bus: %s",
-                            error->message);
-                return FALSE;
+                g_dbus_method_invocation_take_error (invocation, error);
+        } else {
+                gsm_exported_client_complete_stop (skeleton, invocation);
         }
 
-        g_signal_connect (client->skeleton,
-                          "handle-end-session-response",
-                          G_CALLBACK (handle_end_session_response),
-                          client);
-
         return TRUE;
 }
 
-static void
-on_client_vanished (GDBusConnection *connection,
-                    const char      *name,
-                    gpointer         user_data)
+static gboolean
+register_client (GsmClient *client)
 {
-        GsmClient *client = user_data;
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+        GError *error = NULL;
+        GsmExportedClient *skeleton;
+
+        priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+        if (priv->connection == NULL) {
+                g_critical ("error getting session bus: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        skeleton = gsm_exported_client_skeleton_new ();
+        priv->skeleton = skeleton;
+        g_debug ("exporting client to object path: %s", priv->id);
+        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
+                                          priv->connection,
+                                          priv->id, &error);
 
-        g_bus_unwatch_name (client->watch_id);
-        client->watch_id = 0;
+        if (error != NULL) {
+                g_critical ("error exporting client on session bus: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
 
-        client->caller_pid = 0;
+        g_signal_connect (skeleton, "handle-get-app-id",
+                          G_CALLBACK (gsm_client_get_app_id), client);
+        g_signal_connect (skeleton, "handle-get-restart-style-hint",
+                          G_CALLBACK (gsm_client_get_restart_style_hint), client);
+        g_signal_connect (skeleton, "handle-get-startup-id",
+                          G_CALLBACK (gsm_client_get_startup_id), client);
+        g_signal_connect (skeleton, "handle-get-status",
+                          G_CALLBACK (gsm_client_get_status), client);
+        g_signal_connect (skeleton, "handle-get-unix-process-id",
+                          G_CALLBACK (gsm_client_get_unix_process_id), client);
+        g_signal_connect (skeleton, "handle-stop",
+                          G_CALLBACK (gsm_client_stop_dbus), client);
 
-        g_signal_emit (client, signals[DISCONNECTED], 0);
+        return TRUE;
 }
 
 static GObject *
@@ -135,27 +207,23 @@ gsm_client_constructor (GType                  type,
                         guint                  n_construct_properties,
                         GObjectConstructParam *construct_properties)
 {
+        GsmClientPrivate *priv;
         GsmClient *client;
+        gboolean   res;
 
         client = GSM_CLIENT (G_OBJECT_CLASS (gsm_client_parent_class)->constructor (type,
                                                                                     n_construct_properties,
                                                                                     construct_properties));
+        priv = gsm_client_get_instance_private (client);
 
-        g_free (client->id);
-        client->id = g_strdup_printf ("/org/gnome/SessionManager/Client%u", get_next_client_serial ());
+        g_free (priv->id);
+        priv->id = g_strdup_printf ("/org/gnome/SessionManager/Client%u", get_next_client_serial ());
 
-        if (!register_client (client)) {
+        res = register_client (client);
+        if (! res) {
                 g_warning ("Unable to register client with session bus");
         }
 
-        client->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
-                                             client->bus_name,
-                                             G_BUS_NAME_WATCHER_FLAGS_NONE,
-                                             NULL,
-                                             on_client_vanished,
-                                             client,
-                                             NULL);
-
         return G_OBJECT (client);
 }
 
@@ -164,35 +232,82 @@ gsm_client_init (GsmClient *client)
 {
 }
 
-void
-gsm_client_set_app_id (GsmClient  *client,
-                       const char *app_id)
+static void
+gsm_client_finalize (GObject *object)
 {
-        g_return_if_fail (GSM_IS_CLIENT (client));
+        GsmClient *client;
+        GsmClientPrivate *priv;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GSM_IS_CLIENT (object));
 
-        if (g_strcmp0 (client->app_id, app_id) == 0)
-                return;
+        client = GSM_CLIENT (object);
+        priv = gsm_client_get_instance_private (client);
+
+        g_return_if_fail (priv != NULL);
+
+        g_free (priv->id);
+        g_free (priv->startup_id);
+        g_free (priv->app_id);
+
+        if (priv->skeleton != NULL) {
+                g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (priv->skeleton),
+                                                                    priv->connection);
+                g_clear_object (&priv->skeleton);
+        }
 
-        g_free (client->app_id);
-        client->app_id = g_strdup (app_id ?: "");
+        g_clear_object (&priv->connection);
 
-        g_object_notify (G_OBJECT (client), "app-id");
+        G_OBJECT_CLASS (gsm_client_parent_class)->finalize (object);
+}
+
+void
+gsm_client_set_status (GsmClient *client,
+                       guint      status)
+{
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+
+        g_return_if_fail (GSM_IS_CLIENT (client));
+        if (priv->status != status) {
+                priv->status = status;
+                g_object_notify_by_pspec (G_OBJECT (client), props[PROP_STATUS]);
+        }
 }
 
 static void
-gsm_client_set_bus_name (GsmClient  *client,
-                         const char *bus_name)
+gsm_client_set_startup_id (GsmClient  *client,
+                           const char *startup_id)
 {
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+
         g_return_if_fail (GSM_IS_CLIENT (client));
-        g_return_if_fail (bus_name != NULL);
 
-        if (g_strcmp0 (client->bus_name, bus_name) == 0)
-                return;
+        g_free (priv->startup_id);
+
+        if (startup_id != NULL) {
+                priv->startup_id = g_strdup (startup_id);
+        } else {
+                priv->startup_id = g_strdup ("");
+        }
+        g_object_notify_by_pspec (G_OBJECT (client), props[PROP_STARTUP_ID]);
+}
+
+void
+gsm_client_set_app_id (GsmClient  *client,
+                       const char *app_id)
+{
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
 
-        g_free (client->bus_name);
-        client->bus_name = g_strdup (bus_name);
+        g_return_if_fail (GSM_IS_CLIENT (client));
 
-        g_object_notify (G_OBJECT (client), "bus-name");
+        g_free (priv->app_id);
+
+        if (app_id != NULL) {
+                priv->app_id = g_strdup (app_id);
+        } else {
+                priv->app_id = g_strdup ("");
+        }
+        g_object_notify_by_pspec (G_OBJECT (client), props[PROP_APP_ID]);
 }
 
 static void
@@ -201,14 +316,19 @@ gsm_client_set_property (GObject       *object,
                          const GValue  *value,
                          GParamSpec    *pspec)
 {
-        GsmClient *self = GSM_CLIENT (object);
+        GsmClient *self;
+
+        self = GSM_CLIENT (object);
 
         switch ((GsmClientProperty) prop_id) {
+        case PROP_STARTUP_ID:
+                gsm_client_set_startup_id (self, g_value_get_string (value));
+                break;
         case PROP_APP_ID:
                 gsm_client_set_app_id (self, g_value_get_string (value));
                 break;
-        case PROP_BUS_NAME:
-                gsm_client_set_bus_name (self, g_value_get_string (value));
+        case PROP_STATUS:
+                gsm_client_set_status (self, g_value_get_uint (value));
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -223,13 +343,17 @@ gsm_client_get_property (GObject    *object,
                          GParamSpec *pspec)
 {
         GsmClient *self = GSM_CLIENT (object);
+        GsmClientPrivate *priv = gsm_client_get_instance_private (self);
 
         switch ((GsmClientProperty) prop_id) {
+        case PROP_STARTUP_ID:
+                g_value_set_string (value, priv->startup_id);
+                break;
         case PROP_APP_ID:
-                g_value_set_string (value, self->app_id);
+                g_value_set_string (value, priv->app_id);
                 break;
-        case PROP_BUS_NAME:
-                g_value_set_string (value, self->bus_name);
+        case PROP_STATUS:
+                g_value_set_uint (value, priv->status);
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -237,29 +361,30 @@ gsm_client_get_property (GObject    *object,
         }
 }
 
+static gboolean
+default_stop (GsmClient *client,
+              GError   **error)
+{
+        g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
+
+        g_warning ("Stop not implemented");
+
+        return TRUE;
+}
+
 static void
 gsm_client_dispose (GObject *object)
 {
         GsmClient *client;
+        GsmClientPrivate *priv;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GSM_IS_CLIENT (object));
-        client = GSM_CLIENT (object);
-
-        g_free (client->id);
-        g_free (client->app_id);
-        g_free (client->bus_name);
-
-        if (client->skeleton != NULL) {
-                g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->skeleton),
-                                                                    client->connection);
-                g_clear_object (&client->skeleton);
-        }
 
-        g_clear_object (&client->connection);
+        client = GSM_CLIENT (object);
+        priv = gsm_client_get_instance_private (client);
 
-        if (client->watch_id != 0)
-                g_bus_unwatch_name (client->watch_id);
+        g_debug ("GsmClient: disposing %s", priv->id);
 
         G_OBJECT_CLASS (gsm_client_parent_class)->dispose (object);
 }
@@ -272,51 +397,60 @@ gsm_client_class_init (GsmClientClass *klass)
         object_class->get_property = gsm_client_get_property;
         object_class->set_property = gsm_client_set_property;
         object_class->constructor = gsm_client_constructor;
+        object_class->finalize = gsm_client_finalize;
         object_class->dispose = gsm_client_dispose;
 
+        klass->impl_stop = default_stop;
+
         signals[DISCONNECTED] =
                 g_signal_new ("disconnected",
                               G_OBJECT_CLASS_TYPE (object_class),
                               G_SIGNAL_RUN_LAST,
-                              0, NULL, NULL, NULL,
+                              G_STRUCT_OFFSET (GsmClientClass, disconnected),
+                              NULL, NULL, NULL,
                               G_TYPE_NONE,
                               0);
         signals[END_SESSION_RESPONSE] =
                 g_signal_new ("end-session-response",
                               G_OBJECT_CLASS_TYPE (object_class),
                               G_SIGNAL_RUN_LAST,
-                              0, NULL, NULL, NULL,
+                              G_STRUCT_OFFSET (GsmClientClass, end_session_response),
+                              NULL, NULL, NULL,
                               G_TYPE_NONE,
-                              2, G_TYPE_BOOLEAN, G_TYPE_STRING);
-
-        g_object_class_install_property (object_class,
-                                         PROP_APP_ID,
-                                         g_param_spec_string ("app-id",
-                                                              "app-id",
-                                                              "app-id",
-                                                              "",
-                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
-        g_object_class_install_property (object_class,
-                                         PROP_BUS_NAME,
-                                         g_param_spec_string ("bus-name",
-                                                              "bus-name",
-                                                              "bus-name",
-                                                              NULL,
-                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
+                              4, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING);
+
+        props[PROP_STARTUP_ID] =
+                g_param_spec_string ("startup-id",
+                                     "startup-id",
+                                     "startup-id",
+                                     "",
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+        props[PROP_APP_ID] =
+                g_param_spec_string ("app-id",
+                                     "app-id",
+                                     "app-id",
+                                     "",
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+        props[PROP_STATUS] =
+                g_param_spec_uint ("status",
+                                   "status",
+                                   "status",
+                                   0,
+                                   G_MAXINT,
+                                   GSM_CLIENT_UNREGISTERED,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+        g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
 }
 
 const char *
 gsm_client_peek_id (GsmClient *client)
 {
-        g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
-        return client->id;
-}
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
 
-const char *
-gsm_client_peek_bus_name (GsmClient *client)
-{
         g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
-        return client->bus_name;
+
+        return priv->id;
 }
 
 /**
@@ -332,8 +466,54 @@ gsm_client_peek_bus_name (GsmClient *client)
 const char *
 gsm_client_peek_app_id (GsmClient *client)
 {
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+
+        g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
+
+        return priv->app_id;
+}
+
+const char *
+gsm_client_peek_startup_id (GsmClient *client)
+{
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+
         g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
-        return client->app_id;
+
+        return priv->startup_id;
+}
+
+guint
+gsm_client_peek_status (GsmClient *client)
+{
+        GsmClientPrivate *priv = gsm_client_get_instance_private (client);
+
+        g_return_val_if_fail (GSM_IS_CLIENT (client), GSM_CLIENT_UNREGISTERED);
+
+        return priv->status;
+}
+
+guint
+gsm_client_peek_restart_style_hint (GsmClient *client)
+{
+        g_return_val_if_fail (GSM_IS_CLIENT (client), GSM_CLIENT_RESTART_NEVER);
+
+        return GSM_CLIENT_GET_CLASS (client)->impl_get_restart_style_hint (client);
+}
+
+/**
+ * gsm_client_get_app_name:
+ * @client: a #GsmClient.
+ *
+ * Returns: a copy of the application name of the client, or %NULL if no such
+ * name is known.
+ **/
+char *
+gsm_client_get_app_name (GsmClient *client)
+{
+        g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
+
+        return GSM_CLIENT_GET_CLASS (client)->impl_get_app_name (client);
 }
 
 gboolean
@@ -342,10 +522,7 @@ gsm_client_cancel_end_session (GsmClient *client,
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
-        g_debug ("GsmClient: sending CancelEndSession signal to %s", client->bus_name);
-
-        gsm_exported_client_private_emit_cancel_end_session (client->skeleton);
-        return TRUE;
+        return GSM_CLIENT_GET_CLASS (client)->impl_cancel_end_session (client, error);
 }
 
 
@@ -356,10 +533,7 @@ gsm_client_query_end_session (GsmClient                *client,
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
-        g_debug ("GsmClient: sending QueryEndSession signal to %s", client->bus_name);
-
-        gsm_exported_client_private_emit_query_end_session (client->skeleton, flags);
-        return TRUE;
+        return GSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error);
 }
 
 gboolean
@@ -369,10 +543,7 @@ gsm_client_end_session (GsmClient                *client,
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
-        g_debug ("GsmClient: sending EndSession signal to %s", client->bus_name);
-
-        gsm_exported_client_private_emit_end_session (client->skeleton, flags);
-        return TRUE;
+        return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error);
 }
 
 gboolean
@@ -381,18 +552,32 @@ gsm_client_stop (GsmClient *client,
 {
         g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE);
 
-        g_debug ("GsmClient: sending Stop signal to %s", client->bus_name);
+        return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error);
+}
 
-        gsm_exported_client_private_emit_stop (client->skeleton);
-        return TRUE;
+void
+gsm_client_disconnected (GsmClient *client)
+{
+        g_signal_emit (client, signals[DISCONNECTED], 0);
 }
 
-GsmClient *
-gsm_client_new (const char *app_id,
-                const char *bus_name)
+GKeyFile *
+gsm_client_save (GsmClient *client,
+                 GsmApp    *app,
+                 GError   **error)
+{
+        g_return_val_if_fail (GSM_IS_CLIENT (client), NULL);
+
+        return GSM_CLIENT_GET_CLASS (client)->impl_save (client, app, error);
+}
+
+void
+gsm_client_end_session_response (GsmClient  *client,
+                                 gboolean    is_ok,
+                                 gboolean    do_last,
+                                 gboolean    cancel,
+                                 const char *reason)
 {
-        return g_object_new (GSM_TYPE_CLIENT,
-                             "app-id", app_id,
-                             "bus-name", bus_name,
-                             NULL);
+        g_signal_emit (client, signals[END_SESSION_RESPONSE], 0,
+                       is_ok, do_last, cancel, reason);
 }
diff --git a/gnome-session/gsm-client.h b/gnome-session/gsm-client.h
index 120dbda7..dbd9da41 100644
--- a/gnome-session/gsm-client.h
+++ b/gnome-session/gsm-client.h
@@ -26,24 +26,88 @@
 
 G_BEGIN_DECLS
 
+#define GSM_TYPE_CLIENT            (gsm_client_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GsmClient, gsm_client, GSM, CLIENT, GObject)
+
+typedef struct _GsmApp           GsmApp;
+typedef struct _GsmClient        GsmClient;
+typedef struct _GsmClientClass   GsmClientClass;
+
+typedef enum {
+        GSM_CLIENT_UNREGISTERED = 0,
+        GSM_CLIENT_REGISTERED,
+        GSM_CLIENT_FINISHED,
+        GSM_CLIENT_FAILED
+} GsmClientStatus;
+
+typedef enum {
+        GSM_CLIENT_RESTART_NEVER = 0,
+        GSM_CLIENT_RESTART_IF_RUNNING,
+        GSM_CLIENT_RESTART_ANYWAY,
+        GSM_CLIENT_RESTART_IMMEDIATELY
+} GsmClientRestartStyle;
+
 typedef enum {
-        GSM_CLIENT_END_SESSION_FLAG_NONE = 0,
         GSM_CLIENT_END_SESSION_FLAG_FORCEFUL = 1 << 0,
+        GSM_CLIENT_END_SESSION_FLAG_SAVE     = 1 << 1,
+        GSM_CLIENT_END_SESSION_FLAG_LAST     = 1 << 2
 } GsmClientEndSessionFlag;
 
-#define GSM_TYPE_CLIENT            (gsm_client_get_type ())
-G_DECLARE_FINAL_TYPE (GsmClient, gsm_client, GSM, CLIENT, GObject)
+struct _GsmClientClass
+{
+        GObjectClass parent_class;
+
+        /* signals */
+        void         (*disconnected)               (GsmClient  *client);
+        void         (*end_session_response)       (GsmClient  *client,
+                                                    gboolean    ok,
+                                                    gboolean    do_last,
+                                                    gboolean    cancel,
+                                                    const char *reason);
+
+        /* virtual methods */
+        char *                (*impl_get_app_name)           (GsmClient *client);
+        GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client);
+        guint                 (*impl_get_unix_process_id)    (GsmClient *client);
+        gboolean              (*impl_query_end_session)      (GsmClient *client,
+                                                              GsmClientEndSessionFlag flags,
+                                                              GError   **error);
+        gboolean              (*impl_end_session)            (GsmClient *client,
+                                                              GsmClientEndSessionFlag flags,
+                                                              GError   **error);
+        gboolean              (*impl_cancel_end_session)     (GsmClient *client,
+                                                              GError   **error);
+        gboolean              (*impl_stop)                   (GsmClient *client,
+                                                              GError   **error);
+        GKeyFile *            (*impl_save)                   (GsmClient *client,
+                                                              GsmApp    *app,
+                                                              GError   **error);
+};
 
-GsmClient *           gsm_client_new                        (const char *app_id,
-                                                             const char *bus_name);
+typedef enum
+{
+        GSM_CLIENT_ERROR_GENERAL = 0,
+        GSM_CLIENT_ERROR_NOT_REGISTERED,
+        GSM_CLIENT_NUM_ERRORS
+} GsmClientError;
+
+#define GSM_CLIENT_ERROR gsm_client_error_quark ()
+GQuark                gsm_client_error_quark                (void);
 
 const char           *gsm_client_peek_id                    (GsmClient  *client);
-const char *          gsm_client_peek_bus_name              (GsmClient  *client);
 
+
+const char *          gsm_client_peek_startup_id            (GsmClient  *client);
 const char *          gsm_client_peek_app_id                (GsmClient  *client);
+guint                 gsm_client_peek_restart_style_hint    (GsmClient  *client);
+guint                 gsm_client_peek_status                (GsmClient  *client);
+
 
+char                 *gsm_client_get_app_name               (GsmClient  *client);
 void                  gsm_client_set_app_id                 (GsmClient  *client,
                                                              const char *app_id);
+void                  gsm_client_set_status                 (GsmClient  *client,
+                                                             guint       status);
 
 gboolean              gsm_client_end_session                (GsmClient                *client,
                                                              GsmClientEndSessionFlag   flags,
@@ -54,8 +118,23 @@ gboolean              gsm_client_query_end_session          (GsmClient
 gboolean              gsm_client_cancel_end_session         (GsmClient  *client,
                                                              GError    **error);
 
+void                  gsm_client_disconnected               (GsmClient  *client);
+
+GKeyFile             *gsm_client_save                       (GsmClient  *client,
+                                                             GsmApp     *app,
+                                                             GError    **error);
+
 gboolean              gsm_client_stop                       (GsmClient  *client,
                                                              GError    **error);
+
+/* private */
+
+void                  gsm_client_end_session_response       (GsmClient  *client,
+                                                             gboolean    is_ok,
+                                                             gboolean    do_last,
+                                                             gboolean    cancel,
+                                                             const char *reason);
+
 G_END_DECLS
 
 #endif /* __GSM_CLIENT_H__ */
diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c
new file mode 100644
index 00000000..746b1e02
--- /dev/null
+++ b/gnome-session/gsm-dbus-client.c
@@ -0,0 +1,439 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <gio/gio.h>
+
+#include "org.gnome.SessionManager.ClientPrivate.h"
+#include "gsm-dbus-client.h"
+
+#include "gsm-manager.h"
+#include "gsm-util.h"
+
+#define SM_DBUS_NAME                     "org.gnome.SessionManager"
+#define SM_DBUS_CLIENT_PRIVATE_INTERFACE "org.gnome.SessionManager.ClientPrivate"
+
+struct _GsmDBusClient
+{
+        GObject               parent_instance;
+
+        char                 *bus_name;
+        GPid                  caller_pid;
+        GsmClientRestartStyle restart_style_hint;
+
+        GDBusConnection      *connection;
+        GsmExportedClientPrivate *skeleton;
+        guint                 watch_id;
+};
+
+typedef enum {
+        PROP_BUS_NAME = 1,
+} GsmDBusClientProperty;
+
+static GParamSpec *props[PROP_BUS_NAME + 1] = { NULL, };
+
+G_DEFINE_TYPE (GsmDBusClient, gsm_dbus_client, GSM_TYPE_CLIENT)
+
+static gboolean
+setup_connection (GsmDBusClient *client)
+{
+        GError *error = NULL;
+
+        if (client->connection == NULL) {
+                client->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+                if (error != NULL) {
+                        g_debug ("GsmDbusClient: Couldn't connect to session bus: %s",
+                                 error->message);
+                        g_error_free (error);
+                        return FALSE;
+                }
+        }
+
+        return TRUE;
+}
+
+static gboolean
+handle_end_session_response (GsmExportedClientPrivate *skeleton,
+                             GDBusMethodInvocation    *invocation,
+                             gboolean                  is_ok,
+                             const char               *reason,
+                             GsmDBusClient            *client)
+{
+        g_debug ("GsmDBusClient: got EndSessionResponse is-ok:%d reason=%s", is_ok, reason);
+        gsm_client_end_session_response (GSM_CLIENT (client),
+                                         is_ok, FALSE, FALSE, reason);
+
+        gsm_exported_client_private_complete_end_session_response (skeleton, invocation);
+        return TRUE;
+}
+
+static GObject *
+gsm_dbus_client_constructor (GType                  type,
+                             guint                  n_construct_properties,
+                             GObjectConstructParam *construct_properties)
+{
+        GsmDBusClient *client;
+        GError *error = NULL;
+        GsmExportedClientPrivate *skeleton;
+
+        client = GSM_DBUS_CLIENT (G_OBJECT_CLASS (gsm_dbus_client_parent_class)->constructor (type,
+                                                                                              n_construct_properties,
+                                                                                              construct_properties));
+
+        if (! setup_connection (client)) {
+                g_object_unref (client);
+                return NULL;
+        }
+
+        skeleton = gsm_exported_client_private_skeleton_new ();
+        client->skeleton = skeleton;
+        g_debug ("exporting dbus client to object path: %s", gsm_client_peek_id (GSM_CLIENT (client)));
+        g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
+                                          client->connection,
+                                          gsm_client_peek_id (GSM_CLIENT (client)),
+                                          &error);
+
+        if (error != NULL) {
+                g_critical ("error exporting client private on session bus: %s", error->message);
+                g_error_free (error);
+                g_object_unref (client);
+                return NULL;
+        }
+
+        g_signal_connect (skeleton, "handle-end-session-response",
+                          G_CALLBACK (handle_end_session_response), client);
+
+        return G_OBJECT (client);
+}
+
+static void
+gsm_dbus_client_init (GsmDBusClient *client)
+{
+}
+
+/* adapted from PolicyKit */
+static gboolean
+get_caller_info (GsmDBusClient *client,
+                 const char    *sender,
+                 uid_t         *calling_uid_out,
+                 pid_t         *calling_pid_out)
+{
+        g_autoptr(GDBusConnection) connection = NULL;
+        GError          *error;
+        g_autoptr(GVariant) uid_variant = NULL;
+        g_autoptr(GVariant) pid_variant = NULL;
+        uid_t            uid;
+        pid_t            pid;
+
+        if (sender == NULL) {
+                return FALSE;
+        }
+
+        error = NULL;
+        connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+        if (error != NULL) {
+                g_warning ("error getting session bus: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        uid_variant = g_dbus_connection_call_sync (connection,
+                                                   "org.freedesktop.DBus",
+                                                   "/org/freedesktop/DBus",
+                                                   "org.freedesktop.DBus",
+                                                   "GetConnectionUnixUser",
+                                                   g_variant_new ("(s)", sender),
+                                                   G_VARIANT_TYPE ("(u)"),
+                                                   G_DBUS_CALL_FLAGS_NONE,
+                                                   -1, NULL, &error);
+
+        if (error != NULL) {
+                g_debug ("GetConnectionUnixUser() failed: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        pid_variant = g_dbus_connection_call_sync (connection,
+                                                   "org.freedesktop.DBus",
+                                                   "/org/freedesktop/DBus",
+                                                   "org.freedesktop.DBus",
+                                                   "GetConnectionUnixProcessID",
+                                                   g_variant_new ("(s)", sender),
+                                                   G_VARIANT_TYPE ("(u)"),
+                                                   G_DBUS_CALL_FLAGS_NONE,
+                                                   -1, NULL, &error);
+
+        if (error != NULL) {
+                g_debug ("GetConnectionUnixProcessID() failed: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        g_variant_get (uid_variant, "(u)", &uid);
+        g_variant_get (pid_variant, "(u)", &pid);
+
+        if (calling_uid_out != NULL) {
+                *calling_uid_out = uid;
+        }
+        if (calling_pid_out != NULL) {
+                *calling_pid_out = pid;
+        }
+
+        g_debug ("uid = %d", uid);
+        g_debug ("pid = %d", pid);
+
+        return TRUE;
+}
+
+static void
+on_client_vanished (GDBusConnection *connection,
+                    const char      *name,
+                    gpointer         user_data)
+{
+        GsmDBusClient  *client = user_data;
+
+        g_bus_unwatch_name (client->watch_id);
+        client->watch_id = 0;
+
+        gsm_client_disconnected (GSM_CLIENT (client));
+}
+
+static void
+gsm_dbus_client_set_bus_name (GsmDBusClient  *client,
+                              const char     *bus_name)
+{
+        g_return_if_fail (GSM_IS_DBUS_CLIENT (client));
+
+        g_free (client->bus_name);
+
+        client->bus_name = g_strdup (bus_name);
+        g_object_notify_by_pspec (G_OBJECT (client), props[PROP_BUS_NAME]);
+
+        if (!get_caller_info (client, bus_name, NULL, &client->caller_pid)) {
+                client->caller_pid = 0;
+        }
+
+        client->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                                   bus_name,
+                                                   G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                                   NULL,
+                                                   on_client_vanished,
+                                                   client,
+                                                   NULL);
+}
+
+const char *
+gsm_dbus_client_get_bus_name (GsmDBusClient  *client)
+{
+        g_return_val_if_fail (GSM_IS_DBUS_CLIENT (client), NULL);
+
+        return client->bus_name;
+}
+
+static void
+gsm_dbus_client_set_property (GObject       *object,
+                              guint          prop_id,
+                              const GValue  *value,
+                              GParamSpec    *pspec)
+{
+        GsmDBusClient *self = GSM_DBUS_CLIENT (object);
+
+        switch ((GsmDBusClientProperty) prop_id) {
+        case PROP_BUS_NAME:
+                gsm_dbus_client_set_bus_name (self, g_value_get_string (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsm_dbus_client_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+        GsmDBusClient *self = GSM_DBUS_CLIENT (object);
+
+        switch ((GsmDBusClientProperty) prop_id) {
+        case PROP_BUS_NAME:
+                g_value_set_string (value, self->bus_name);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gsm_dbus_client_finalize (GObject *object)
+{
+        GsmDBusClient *client = (GsmDBusClient *) object;
+
+        g_free (client->bus_name);
+
+        if (client->skeleton != NULL) {
+                g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (client->skeleton),
+                                                                    client->connection);
+                g_clear_object (&client->skeleton);
+        }
+
+        g_clear_object (&client->connection);
+
+        if (client->watch_id != 0)
+                g_bus_unwatch_name (client->watch_id);
+
+        G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object);
+}
+
+static GKeyFile *
+dbus_client_save (GsmClient *client,
+                  GsmApp    *app,
+                  GError   **error)
+{
+        g_debug ("GsmDBusClient: saving client with id %s",
+                 gsm_client_peek_id (client));
+
+        /* FIXME: We still don't support client saving for D-Bus
+         * session clients */
+
+        return NULL;
+}
+
+static gboolean
+dbus_client_stop (GsmClient *client,
+                  GError   **error)
+{
+        GsmDBusClient  *dbus_client = (GsmDBusClient *) client;
+        gsm_exported_client_private_emit_stop (dbus_client->skeleton);
+        return TRUE;
+}
+
+static char *
+dbus_client_get_app_name (GsmClient *client)
+{
+        /* Always use app-id instead */
+        return NULL;
+}
+
+static GsmClientRestartStyle
+dbus_client_get_restart_style_hint (GsmClient *client)
+{
+        return (GSM_DBUS_CLIENT (client)->restart_style_hint);
+}
+
+static guint
+dbus_client_get_unix_process_id (GsmClient *client)
+{
+        return (GSM_DBUS_CLIENT (client)->caller_pid);
+}
+
+static gboolean
+dbus_client_query_end_session (GsmClient                *client,
+                               GsmClientEndSessionFlag   flags,
+                               GError                  **error)
+{
+        GsmDBusClient  *dbus_client = (GsmDBusClient *) client;
+
+        if (dbus_client->bus_name == NULL) {
+                g_set_error (error,
+                             GSM_CLIENT_ERROR,
+                             GSM_CLIENT_ERROR_NOT_REGISTERED,
+                             "Client is not registered");
+                return FALSE;
+        }
+
+        g_debug ("GsmDBusClient: sending QueryEndSession signal to %s", dbus_client->bus_name);
+
+        gsm_exported_client_private_emit_query_end_session (dbus_client->skeleton, flags);
+        return TRUE;
+}
+
+static gboolean
+dbus_client_end_session (GsmClient                *client,
+                         GsmClientEndSessionFlag   flags,
+                         GError                  **error)
+{
+        GsmDBusClient  *dbus_client = (GsmDBusClient *) client;
+
+        gsm_exported_client_private_emit_end_session (dbus_client->skeleton, flags);
+        return TRUE;
+}
+
+static gboolean
+dbus_client_cancel_end_session (GsmClient *client,
+                                GError   **error)
+{
+        GsmDBusClient  *dbus_client = (GsmDBusClient *) client;
+        gsm_exported_client_private_emit_cancel_end_session (dbus_client->skeleton);
+        return TRUE;
+}
+
+static void
+gsm_dbus_client_class_init (GsmDBusClientClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+        GsmClientClass *client_class = GSM_CLIENT_CLASS (klass);
+
+        object_class->finalize             = gsm_dbus_client_finalize;
+        object_class->constructor          = gsm_dbus_client_constructor;
+        object_class->get_property         = gsm_dbus_client_get_property;
+        object_class->set_property         = gsm_dbus_client_set_property;
+
+        client_class->impl_save                   = dbus_client_save;
+        client_class->impl_stop                   = dbus_client_stop;
+        client_class->impl_query_end_session      = dbus_client_query_end_session;
+        client_class->impl_end_session            = dbus_client_end_session;
+        client_class->impl_cancel_end_session     = dbus_client_cancel_end_session;
+        client_class->impl_get_app_name           = dbus_client_get_app_name;
+        client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint;
+        client_class->impl_get_unix_process_id    = dbus_client_get_unix_process_id;
+
+        props[PROP_BUS_NAME] =
+                g_param_spec_string ("bus-name",
+                                     "bus-name",
+                                     "bus-name",
+                                     NULL,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+        g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
+}
+
+GsmClient *
+gsm_dbus_client_new (const char *startup_id,
+                     const char *bus_name)
+{
+        GsmDBusClient *client;
+
+        client = g_object_new (GSM_TYPE_DBUS_CLIENT,
+                               "startup-id", startup_id,
+                               "bus-name", bus_name,
+                               NULL);
+
+        return GSM_CLIENT (client);
+}
diff --git a/gnome-session/gsm-dbus-client.h b/gnome-session/gsm-dbus-client.h
new file mode 100644
index 00000000..459d4963
--- /dev/null
+++ b/gnome-session/gsm-dbus-client.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSM_DBUS_CLIENT_H__
+#define __GSM_DBUS_CLIENT_H__
+
+#include "gsm-client.h"
+
+G_BEGIN_DECLS
+
+#define GSM_TYPE_DBUS_CLIENT            (gsm_dbus_client_get_type ())
+G_DECLARE_FINAL_TYPE (GsmDBusClient, gsm_dbus_client, GSM, DBUS_CLIENT, GsmClient)
+
+GsmClient *    gsm_dbus_client_new                (const char     *startup_id,
+                                                   const char     *bus_name);
+const char *   gsm_dbus_client_get_bus_name       (GsmDBusClient  *client);
+
+G_END_DECLS
+
+#endif /* __GSM_DBUS_CLIENT_H__ */
diff --git a/gnome-session/gsm-fail-whale-dialog.c b/gnome-session/gsm-fail-whale-dialog.c
new file mode 100644
index 00000000..9b2bca4f
--- /dev/null
+++ b/gnome-session/gsm-fail-whale-dialog.c
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011,2025 Red Hat, Inc.
+ * Copyright (C) 2019 Canonical Ltd.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *        Colin Walters <walters@verbum.org>
+ *        Marco Trevisan <marco@ubuntu.com>
+ *        Adrian Vovk <adrianvovk@gmail.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gsm-icon-names.h"
+
+typedef struct GsmFailContext {
+        GMainLoop *loop;
+        GList *monitors;
+
+        gboolean debug_mode;
+        gboolean allow_logout;
+        gboolean extensions;
+} GsmFailContext;
+
+static gboolean
+on_close_req (void)
+{
+        /* Suppress any further handling, including destruction of the window. */
+        return TRUE;
+}
+
+static void
+on_logout_clicked (GtkWidget      *button,
+                   GsmFailContext *ctx)
+{
+        if (!ctx->debug_mode) {
+                g_spawn_command_line_async ("gnome-session-quit --force", NULL);
+        }
+        g_main_loop_quit (ctx->loop);
+}
+
+static void
+create_fail_dialog (GdkMonitor     *monitor,
+                    GsmFailContext *ctx)
+{
+        GtkWindow *dialog;
+        GtkBox *box;
+        GtkImage *icon;
+        gchar *markup;
+        GtkLabel *title;
+        gchar *message_text;
+        GtkLabel *message;
+
+        dialog = GTK_WINDOW (gtk_window_new ());
+
+        gtk_window_set_title (dialog, "");
+        gtk_window_set_icon_name (dialog, GSM_ICON_COMPUTER_FAIL);
+
+        /* we only allow the window to be closed via on_monitors_changed */
+        gtk_window_set_deletable (dialog, FALSE);
+        g_signal_connect(dialog, "close-request", G_CALLBACK (on_close_req), NULL);
+
+        box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 12));
+        gtk_widget_set_valign (GTK_WIDGET (box), GTK_ALIGN_CENTER);
+        gtk_window_set_child (dialog, GTK_WIDGET (box));
+
+        icon = GTK_IMAGE (gtk_image_new_from_icon_name (GSM_ICON_COMPUTER_FAIL));
+        gtk_image_set_pixel_size (icon, 128);
+        gtk_box_append (box, GTK_WIDGET (icon));
+
+
+        markup = g_strdup_printf ("<b><big>%s</big></b>", _("Oh no! Something has gone wrong."));
+        title = GTK_LABEL (gtk_label_new (markup));
+        gtk_label_set_use_markup (title, TRUE);
+        gtk_box_append (box, GTK_WIDGET (title));
+        g_free (markup);
+
+        if (!ctx->allow_logout)
+                message_text = _("A problem has occurred and the system can’t recover. Please contact a system administrator");
+        else if (ctx->extensions)
+                message_text = _("A problem has occurred and the system can’t recover. All extensions have been disabled as a precaution.");
+        else
+                message_text = _("A problem has occurred and the system can’t recover.\nPlease log out and try again.");
+        message = GTK_LABEL (gtk_label_new (message_text));
+        gtk_label_set_justify (message, GTK_JUSTIFY_CENTER);
+        gtk_label_set_wrap (message, TRUE);
+        gtk_box_append (box, GTK_WIDGET (message));
+
+        if (ctx->allow_logout) {
+                GtkButton *btn;
+                btn = GTK_BUTTON (gtk_button_new_with_mnemonic (_("_Log Out")));
+                gtk_widget_set_halign (GTK_WIDGET (btn), GTK_ALIGN_CENTER);
+                g_signal_connect (btn, "clicked",
+                                  G_CALLBACK (on_logout_clicked), ctx);
+                gtk_box_append (box, GTK_WIDGET (btn));
+        }
+
+        gtk_window_present (dialog);
+        gtk_window_fullscreen_on_monitor (dialog, monitor);
+
+        g_object_set_data_full (G_OBJECT (monitor), "gsm_fail_whale",
+                                dialog, (GDestroyNotify) gtk_window_destroy);
+}
+
+static void
+on_monitors_changed (GListModel     *monitors,
+                     guint           position,
+                     guint           removed,
+                     guint           added,
+                     GsmFailContext *ctx)
+{
+        GList *cursor;
+        guint i;
+
+        cursor = g_list_nth (ctx->monitors, position);
+
+        for (i = position; i < position + added; i++) {
+                GdkMonitor *monitor = GDK_MONITOR (g_list_model_get_item (monitors, i));
+                create_fail_dialog(monitor, ctx);
+                ctx->monitors = g_list_insert_before (ctx->monitors, cursor, monitor);
+        }
+
+        for (i = 0; i < removed; i++) {
+                GList *next = cursor->next;
+                g_object_unref (G_OBJECT (cursor->data));
+                ctx->monitors = g_list_delete_link (ctx->monitors, cursor);
+                cursor = next;
+        }
+}
+
+int main (int argc, char *argv[])
+{
+        GsmFailContext ctx = {};
+        GOptionEntry entries[] = {
+                { "debug", 0, 0, G_OPTION_ARG_NONE, &ctx.debug_mode, N_("Enable debugging code"), NULL },
+                { "allow-logout", 0, 0, G_OPTION_ARG_NONE, &ctx.allow_logout, N_("Allow logout"), NULL },
+                { "extensions", 0, 0, G_OPTION_ARG_NONE, &ctx.extensions, N_("Show extension warning"), NULL },
+                { NULL, 0, 0, 0, NULL, NULL, NULL }
+        };
+
+        GError *error = NULL;
+        GOptionContext *opts;
+        GListModel *monitors;
+
+        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+        textdomain (GETTEXT_PACKAGE);
+
+        opts = g_option_context_new (" - fail whale");
+        g_option_context_add_main_entries (opts, entries, GETTEXT_PACKAGE);
+        if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+                g_warning ("%s", error->message);
+                exit (1);
+        }
+        g_option_context_free (opts);
+
+        /* Force-off allow_logout when running inside GDM, this is needed
+         * because the systemd service always passes --allow-logout
+         */
+        if (g_strcmp0 (g_getenv ("RUNNING_UNDER_GDM"), "true") == 0)
+                ctx.allow_logout = FALSE;
+
+        gtk_init ();
+        ctx.loop = g_main_loop_new (NULL, TRUE);
+
+        monitors = gdk_display_get_monitors (gdk_display_get_default ());
+        on_monitors_changed(monitors, 0, 0, g_list_model_get_n_items (monitors), &ctx);
+        g_signal_connect (monitors, "items-changed", G_CALLBACK (on_monitors_changed), &ctx);
+
+        g_main_loop_run (ctx.loop);
+
+        g_main_loop_unref (ctx.loop);
+        g_list_free_full (ctx.monitors, g_object_unref);
+
+        return 0;
+}
+
diff --git a/gnome-session/gsm-fail-whale.c b/gnome-session/gsm-fail-whale.c
new file mode 100644
index 00000000..a0304f28
--- /dev/null
+++ b/gnome-session/gsm-fail-whale.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ * gsm-fail-whale.c
+ * Copyright (C) 2012 Red Hat, Inc
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <signal.h>
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "gsm-fail-whale.h"
+#include "gsm-util.h"
+
+static void
+on_fail_whale_failed (void)
+{
+        raise (SIGTERM);
+}
+
+void
+gsm_fail_whale_dialog_we_failed  (gboolean            debug_mode,
+                                  gboolean            allow_logout,
+                                  GsmShellExtensions *extensions)
+{
+        gint i;
+        gchar *argv[5];
+        static GPid pid = 0;
+
+        if (pid != 0) {
+                return;
+        }
+
+        i = 0;
+        argv[i++] = LIBEXECDIR "/gnome-session-failed";
+        if (debug_mode)
+                argv[i++] = "--debug";
+        if (allow_logout)
+                argv[i++] = "--allow-logout";
+        if (extensions != NULL && gsm_shell_extensions_n_extensions (extensions) > 0)
+                argv[i++] = "--extensions";
+        argv[i++] = NULL;
+
+        if (!g_spawn_async (NULL, argv, (char **) gsm_util_listenv (), G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL)) {
+                exit (1);
+        }
+
+        g_child_watch_add (pid,
+                           (GChildWatchFunc)on_fail_whale_failed,
+                           NULL);
+}
diff --git a/gnome-session/gsm-fail-whale.h b/gnome-session/gsm-fail-whale.h
new file mode 100644
index 00000000..5b93dcab
--- /dev/null
+++ b/gnome-session/gsm-fail-whale.h
@@ -0,0 +1,34 @@
+/* gsm-fail-whale.h
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSM_FAIL_WHALE_H__
+#define __GSM_FAIL_WHALE_H__
+
+#include <glib.h>
+
+#include "gsm-shell-extensions.h"
+
+G_BEGIN_DECLS
+
+void        gsm_fail_whale_dialog_we_failed  (gboolean            debug_mode,
+                                              gboolean            allow_logout,
+                                              GsmShellExtensions *extensions);
+
+G_END_DECLS
+
+#endif /* __GSM_FAIL_WHALE_H__ */
+
diff --git a/gnome-session/gsm-icon-names.h b/gnome-session/gsm-icon-names.h
new file mode 100644
index 00000000..82f28345
--- /dev/null
+++ b/gnome-session/gsm-icon-names.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Novell, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSM_ICON_NAMES_H__
+#define __GSM_ICON_NAMES_H__
+
+#define GSM_ICON_COMPUTER_FAIL       "computer-fail-symbolic"
+#define GSM_ICON_INHIBITOR_DEFAULT   "gnome-windows"
+#define GSM_ICON_LOGOUT              "system-log-out"
+#define GSM_ICON_SHUTDOWN            "system-shutdown"
+#define GSM_ICON_XSMP_DEFAULT        "system-run"
+
+#endif /*__GSM_ICON_NAMES_H__ */
diff --git a/gnome-session/gsm-inhibitor.c b/gnome-session/gsm-inhibitor.c
index f1768578..13440beb 100644
--- a/gnome-session/gsm-inhibitor.c
+++ b/gnome-session/gsm-inhibitor.c
@@ -39,6 +39,7 @@ struct GsmInhibitorPrivate
         char *client_id;
         char *reason;
         guint flags;
+        guint toplevel_xid;
         guint cookie;
         GDBusConnection *connection;
         GsmExportedInhibitor *skeleton;
@@ -53,6 +54,7 @@ enum {
         PROP_APP_ID,
         PROP_CLIENT_ID,
         PROP_FLAGS,
+        PROP_TOPLEVEL_XID,
         PROP_COOKIE
 };
 
@@ -148,6 +150,15 @@ gsm_inhibitor_get_flags (GsmExportedInhibitor  *skeleton,
         return TRUE;
 }
 
+static gboolean
+gsm_inhibitor_get_toplevel_xid (GsmExportedInhibitor  *skeleton,
+                                GDBusMethodInvocation *invocation,
+                                GsmInhibitor          *inhibitor)
+{
+        gsm_exported_inhibitor_complete_get_toplevel_xid (skeleton, invocation, inhibitor->priv->toplevel_xid);
+        return TRUE;
+}
+
 static guint32
 get_next_inhibitor_serial (void)
 {
@@ -197,6 +208,8 @@ register_inhibitor (GsmInhibitor *inhibitor)
                           G_CALLBACK (gsm_inhibitor_get_flags), inhibitor);
         g_signal_connect (skeleton, "handle-get-reason",
                           G_CALLBACK (gsm_inhibitor_get_reason), inhibitor);
+        g_signal_connect (skeleton, "handle-get-toplevel-xid",
+                          G_CALLBACK (gsm_inhibitor_get_toplevel_xid), inhibitor);
 
         return TRUE;
 }
@@ -336,6 +349,18 @@ gsm_inhibitor_set_flags (GsmInhibitor  *inhibitor,
         }
 }
 
+static void
+gsm_inhibitor_set_toplevel_xid (GsmInhibitor  *inhibitor,
+                                guint          xid)
+{
+        g_return_if_fail (GSM_IS_INHIBITOR (inhibitor));
+
+        if (inhibitor->priv->toplevel_xid != xid) {
+                inhibitor->priv->toplevel_xid = xid;
+                g_object_notify (G_OBJECT (inhibitor), "toplevel-xid");
+        }
+}
+
 const char *
 gsm_inhibitor_peek_bus_name (GsmInhibitor  *inhibitor)
 {
@@ -384,6 +409,14 @@ gsm_inhibitor_peek_flags (GsmInhibitor  *inhibitor)
         return inhibitor->priv->flags;
 }
 
+guint
+gsm_inhibitor_peek_toplevel_xid (GsmInhibitor  *inhibitor)
+{
+        g_return_val_if_fail (GSM_IS_INHIBITOR (inhibitor), 0);
+
+        return inhibitor->priv->toplevel_xid;
+}
+
 guint
 gsm_inhibitor_peek_cookie (GsmInhibitor  *inhibitor)
 {
@@ -421,6 +454,9 @@ gsm_inhibitor_set_property (GObject       *object,
         case PROP_COOKIE:
                 gsm_inhibitor_set_cookie (self, g_value_get_uint (value));
                 break;
+        case PROP_TOPLEVEL_XID:
+                gsm_inhibitor_set_toplevel_xid (self, g_value_get_uint (value));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -456,6 +492,9 @@ gsm_inhibitor_get_property (GObject    *object,
         case PROP_COOKIE:
                 g_value_set_uint (value, self->priv->cookie);
                 break;
+        case PROP_TOPLEVEL_XID:
+                g_value_set_uint (value, self->priv->toplevel_xid);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
@@ -542,6 +581,15 @@ gsm_inhibitor_class_init (GsmInhibitorClass *klass)
                                                             G_MAXINT,
                                                             0,
                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        g_object_class_install_property (object_class,
+                                         PROP_TOPLEVEL_XID,
+                                         g_param_spec_uint ("toplevel-xid",
+                                                            "toplevel-xid",
+                                                            "toplevel-xid",
+                                                            0,
+                                                            G_MAXINT,
+                                                            0,
+                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
         g_object_class_install_property (object_class,
                                          PROP_COOKIE,
                                          g_param_spec_uint ("cookie",
@@ -555,6 +603,7 @@ gsm_inhibitor_class_init (GsmInhibitorClass *klass)
 
 GsmInhibitor *
 gsm_inhibitor_new (const char    *app_id,
+                   guint          toplevel_xid,
                    guint          flags,
                    const char    *reason,
                    const char    *bus_name,
@@ -567,6 +616,7 @@ gsm_inhibitor_new (const char    *app_id,
                                   "reason", reason,
                                   "bus-name", bus_name,
                                   "flags", flags,
+                                  "toplevel-xid", toplevel_xid,
                                   "cookie", cookie,
                                   NULL);
 
diff --git a/gnome-session/gsm-inhibitor.h b/gnome-session/gsm-inhibitor.h
index 7b50271d..519b70cf 100644
--- a/gnome-session/gsm-inhibitor.h
+++ b/gnome-session/gsm-inhibitor.h
@@ -62,6 +62,7 @@ GQuark         gsm_inhibitor_error_quark          (void);
 GType          gsm_inhibitor_get_type             (void) G_GNUC_CONST;
 
 GsmInhibitor * gsm_inhibitor_new                  (const char    *app_id,
+                                                   guint          toplevel_xid,
                                                    guint          flags,
                                                    const char    *reason,
                                                    const char    *bus_name,
@@ -80,6 +81,7 @@ const char *   gsm_inhibitor_peek_reason          (GsmInhibitor  *inhibitor);
 const char *   gsm_inhibitor_peek_bus_name        (GsmInhibitor  *inhibitor);
 guint          gsm_inhibitor_peek_cookie          (GsmInhibitor  *inhibitor);
 guint          gsm_inhibitor_peek_flags           (GsmInhibitor  *inhibitor);
+guint          gsm_inhibitor_peek_toplevel_xid    (GsmInhibitor  *inhibitor);
 
 G_END_DECLS
 
diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index b7f1788e..743a372e 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -44,62 +44,108 @@
 
 #include <systemd/sd-daemon.h>
 
-#include "gsm-app.h"
-#include "gsm-client.h"
+#include "gsm-store.h"
 #include "gsm-inhibitor.h"
 #include "gsm-presence.h"
 #include "gsm-shell.h"
-#include "gsm-store.h"
-#include "gsm-system.h"
+
+#include "gsm-dbus-client.h"
+
+#include "gsm-autostart-app.h"
+
 #include "gsm-util.h"
+#include "gsm-icon-names.h"
+#include "gsm-system.h"
+#include "gsm-shell-extensions.h"
+#include "gsm-fail-whale.h"
+
+/* UUIDs for log messages */
+#define GSM_MANAGER_STARTUP_SUCCEEDED_MSGID     "0ce153587afa4095832d233c17a88001"
+#define GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID "10dd2dc188b54a5e98970f56499d1f73"
 
 #define GSM_MANAGER_DBUS_PATH "/org/gnome/SessionManager"
 #define GSM_MANAGER_DBUS_NAME "org.gnome.SessionManager"
 #define GSM_MANAGER_DBUS_IFACE "org.gnome.SessionManager"
 
+/* Probably about the longest amount of time someone could reasonably
+ * want to wait, at least for something happening more than once.
+ * We can get deployed on very slow media though like CDROM devices,
+ * often with complex stacking/compressing filesystems on top, which
+ * is not a recipie for speed.   Particularly now that we throw up
+ * a fail whale if required components don't show up quickly enough,
+ * let's make this fairly long.
+ */
+#define GSM_MANAGER_PHASE_TIMEOUT 90 /* seconds */
+
+#define GDM_FLEXISERVER_COMMAND "gdmflexiserver"
+#define GDM_FLEXISERVER_ARGS    "--startnew Standard"
+
 #define SESSION_SCHEMA            "org.gnome.desktop.session"
 #define KEY_IDLE_DELAY            "idle-delay"
+#define KEY_SESSION_NAME          "session-name"
 
 #define GSM_MANAGER_SCHEMA        "org.gnome.SessionManager"
 #define KEY_LOGOUT_PROMPT         "logout-prompt"
+#define KEY_SHOW_FALLBACK_WARNING "show-fallback-warning"
+
+#define SCREENSAVER_SCHEMA        "org.gnome.desktop.screensaver"
+#define KEY_SLEEP_LOCK            "lock-enabled"
 
 #define LOCKDOWN_SCHEMA           "org.gnome.desktop.lockdown"
 #define KEY_DISABLE_LOG_OUT       "disable-log-out"
 #define KEY_DISABLE_USER_SWITCHING "disable-user-switching"
 
+static void app_registered (GsmApp     *app, GParamSpec *spec, GsmManager *manager);
+
 typedef enum
 {
         GSM_MANAGER_LOGOUT_NONE,
         GSM_MANAGER_LOGOUT_LOGOUT,
         GSM_MANAGER_LOGOUT_REBOOT,
+        GSM_MANAGER_LOGOUT_REBOOT_INTERACT,
         GSM_MANAGER_LOGOUT_SHUTDOWN,
+        GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT,
 } GsmManagerLogoutType;
 
-struct _GsmManager
+typedef struct
 {
-        GObject                 parent;
-
+        gboolean                failsafe;
+        gboolean                systemd_managed;
         gboolean                systemd_initialized;
+        gboolean                manager_initialized;
         GsmStore               *clients;
         GsmStore               *inhibitors;
         GsmInhibitorFlag        inhibited_actions;
         GsmStore               *apps;
         GsmPresence            *presence;
         char                   *session_name;
+        gboolean                is_fallback_session : 1;
 
         /* Current status */
         GsmManagerPhase         phase;
         guint                   phase_timeout_id;
+        GSList                 *required_apps;
+        GSList                 *pending_apps;
         GsmManagerLogoutMode    logout_mode;
         GSList                 *query_clients;
+        guint                   query_timeout_id;
+        /* This is used for GSM_MANAGER_PHASE_END_SESSION only at the moment,
+         * since it uses a sublist of all running client that replied in a
+         * specific way */
+        GSList                 *next_query_clients;
         /* This is the action that will be done just before we exit */
         GsmManagerLogoutType    logout_type;
 
+        /* List of clients which were disconnected due to disabled condition
+         * and shouldn't be automatically restarted */
+        GSList                 *condition_clients;
+
         GSList                 *pending_end_session_tasks;
         GCancellable           *end_session_cancellable;
 
         GSettings              *settings;
         GSettings              *session_settings;
+        GSettings              *screensaver_settings;
         GSettings              *lockdown_settings;
 
         GsmSystem              *system;
@@ -108,33 +154,45 @@ struct _GsmManager
         gboolean                dbus_disconnected : 1;
 
         GsmShell               *shell;
-        gulong                  shell_end_session_dialog_canceled_id;
-        gulong                  shell_end_session_dialog_open_failed_id;
-        gulong                  shell_end_session_dialog_confirmed_logout_id;
-        gulong                  shell_end_session_dialog_confirmed_shutdown_id;
-        gulong                  shell_end_session_dialog_confirmed_reboot_id;
-};
+        guint                   shell_end_session_dialog_canceled_id;
+        guint                   shell_end_session_dialog_open_failed_id;
+        guint                   shell_end_session_dialog_confirmed_logout_id;
+        guint                   shell_end_session_dialog_confirmed_shutdown_id;
+        guint                   shell_end_session_dialog_confirmed_reboot_id;
+} GsmManagerPrivate;
 
 enum {
         PROP_0,
+        PROP_CLIENT_STORE,
         PROP_SESSION_NAME,
+        PROP_FALLBACK,
+        PROP_FAILSAFE,
+        PROP_SYSTEMD_MANAGED
 };
 
+enum {
+        PHASE_CHANGED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
 static void     gsm_manager_class_init  (GsmManagerClass *klass);
 static void     gsm_manager_init        (GsmManager      *manager);
 
 static gboolean _log_out_is_locked_down     (GsmManager *manager);
 
-static void     on_client_end_session_response (GsmClient  *client,
-                                                gboolean    is_ok,
-                                                const char *reason,
-                                                GsmManager *manager);
-
+static void     _handle_client_end_session_response (GsmManager *manager,
+                                                     GsmClient  *client,
+                                                     gboolean    is_ok,
+                                                     gboolean    do_last,
+                                                     gboolean    cancel,
+                                                     const char *reason);
 static void     show_shell_end_session_dialog (GsmManager                   *manager,
                                                GsmShellEndSessionDialogType  type);
 static gpointer manager_object = NULL;
 
-G_DEFINE_TYPE (GsmManager, gsm_manager, G_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_PRIVATE (GsmManager, gsm_manager, G_TYPE_OBJECT)
 
 static const GDBusErrorEntry gsm_manager_error_entries[] = {
         { GSM_MANAGER_ERROR_GENERAL, GSM_MANAGER_DBUS_IFACE ".GeneralError" },
@@ -165,7 +223,7 @@ start_app_or_warn (GsmManager *manager,
         gboolean res;
         GError *error = NULL;
 
-        g_debug ("GsmManager: starting app '%s'", gsm_app_peek_app_id (app));
+        g_debug ("GsmManager: starting app '%s'", gsm_app_peek_id (app));
 
         res = gsm_app_start (app, &error);
         if (error != NULL) {
@@ -175,6 +233,75 @@ start_app_or_warn (GsmManager *manager,
         return res;
 }
 
+static gboolean
+is_app_required (GsmManager *manager,
+                 GsmApp     *app)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        return g_slist_find (priv->required_apps, app) != NULL;
+}
+
+static void
+on_required_app_failure (GsmManager  *manager,
+                         GsmApp      *app)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        const gchar *app_id;
+        gboolean allow_logout;
+        GsmShellExtensions *extensions;
+
+        app_id = gsm_app_peek_app_id (app);
+
+        if (g_str_equal (app_id, "org.gnome.Shell.desktop")) {
+                extensions = g_object_new (GSM_TYPE_SHELL_EXTENSIONS, NULL);
+                gsm_shell_extensions_disable_all (extensions);
+        } else {
+                extensions = NULL;
+        }
+
+        if (gsm_system_is_login_session (priv->system)) {
+                allow_logout = FALSE;
+        } else {
+                allow_logout = !_log_out_is_locked_down (manager);
+        }
+
+        sd_journal_send ("MESSAGE_ID=%s", GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID,
+                         "PRIORITY=%d", 3,
+                         "MESSAGE=Unrecoverable failure in required component %s", app_id,
+                         NULL);
+
+        gsm_fail_whale_dialog_we_failed (FALSE,
+                                         allow_logout,
+                                         extensions);
+}
+
+static void
+on_display_server_failure (GsmManager *manager,
+                           GsmApp     *app)
+{
+        const gchar *app_id;
+        GsmShellExtensions *extensions;
+
+        app_id = gsm_app_peek_app_id (app);
+
+        if (g_str_equal (app_id, "org.gnome.Shell.desktop")) {
+                extensions = g_object_new (GSM_TYPE_SHELL_EXTENSIONS, NULL);
+                gsm_shell_extensions_disable_all (extensions);
+
+                g_object_unref (extensions);
+        } else {
+                extensions = NULL;
+        }
+
+        sd_journal_send ("MESSAGE_ID=%s", GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID,
+                         "PRIORITY=%d", 3,
+                         "MESSAGE=Unrecoverable failure in required component %s", app_id,
+                         NULL);
+
+        gsm_quit ();
+}
+
 static gboolean
 _debug_client (const char *id,
                GsmClient  *client,
@@ -187,7 +314,9 @@ _debug_client (const char *id,
 static void
 debug_clients (GsmManager *manager)
 {
-        gsm_store_foreach (manager->clients,
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        gsm_store_foreach (priv->clients,
                            (GsmStoreFunc)_debug_client,
                            manager);
 }
@@ -204,25 +333,129 @@ _find_by_cookie (const char   *id,
         return (*cookie_ap == cookie_b);
 }
 
+static gboolean
+_client_has_startup_id (const char *id,
+                        GsmClient  *client,
+                        const char *startup_id_a)
+{
+        const char *startup_id_b;
+
+        startup_id_b = gsm_client_peek_startup_id (client);
+        if (IS_STRING_EMPTY (startup_id_b)) {
+                return FALSE;
+        }
+
+        return (strcmp (startup_id_a, startup_id_b) == 0);
+}
+
+static void
+app_condition_changed (GsmApp     *app,
+                       gboolean    condition,
+                       GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GsmClient *client;
+
+        g_debug ("GsmManager: app:%s condition changed condition:%d",
+                 gsm_app_peek_id (app),
+                 condition);
+
+        client = (GsmClient *)gsm_store_find (priv->clients,
+                                              (GsmStoreFunc)_client_has_startup_id,
+                                              (char *)gsm_app_peek_startup_id (app));
+
+        if (condition) {
+                if (!gsm_app_is_running (app) && client == NULL) {
+                        start_app_or_warn (manager, app);
+                } else {
+                        g_debug ("GsmManager: not starting - app still running '%s'", gsm_app_peek_id (app));
+                }
+        } else {
+                GError  *error;
+                gboolean res;
+
+                if (client != NULL) {
+                        /* Kill client in case condition if false and make sure it won't
+                         * be automatically restarted by adding the client to
+                         * condition_clients */
+                        priv->condition_clients =
+                                g_slist_prepend (priv->condition_clients, client);
+
+                        g_debug ("GsmManager: stopping client %s for app", gsm_client_peek_id (client));
+
+                        error = NULL;
+                        res = gsm_client_stop (client, &error);
+                        if (! res) {
+                                g_warning ("Not able to stop app client from its condition: %s",
+                                           error->message);
+                                g_error_free (error);
+                        }
+                } else {
+                        g_debug ("GsmManager: stopping app %s", gsm_app_peek_id (app));
+
+                        /* If we don't have a client then we should try to kill the app */
+                        error = NULL;
+                        res = gsm_app_stop (app, &error);
+                        if (! res) {
+                                g_warning ("Not able to stop app from its condition: %s",
+                                           error->message);
+                                g_error_free (error);
+                        }
+                }
+        }
+}
+
 static const char *
 phase_num_to_name (guint phase)
 {
+        const char *name;
+
         switch (phase) {
+        case GSM_MANAGER_PHASE_STARTUP:
+                name = "STARTUP";
+                break;
+        case GSM_MANAGER_PHASE_EARLY_INITIALIZATION:
+                name = "EARLY_INITIALIZATION";
+                break;
+        case GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER:
+                name = "PRE_DISPLAY_SERVER";
+                break;
+        case GSM_MANAGER_PHASE_DISPLAY_SERVER:
+                name = "DISPLAY_SERVER";
+                break;
         case GSM_MANAGER_PHASE_INITIALIZATION:
-                return "INITIALIZATION";
+                name = "INITIALIZATION";
+                break;
+        case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+                name = "WINDOW_MANAGER";
+                break;
+        case GSM_MANAGER_PHASE_PANEL:
+                name = "PANEL";
+                break;
+        case GSM_MANAGER_PHASE_DESKTOP:
+                name = "DESKTOP";
+                break;
         case GSM_MANAGER_PHASE_APPLICATION:
-                return "APPLICATION";
+                name = "APPLICATION";
+                break;
         case GSM_MANAGER_PHASE_RUNNING:
-                return "RUNNING";
+                name = "RUNNING";
+                break;
         case GSM_MANAGER_PHASE_QUERY_END_SESSION:
-                return "QUERY_END_SESSION";
+                name = "QUERY_END_SESSION";
+                break;
         case GSM_MANAGER_PHASE_END_SESSION:
-                return "END_SESSION";
+                name = "END_SESSION";
+                break;
         case GSM_MANAGER_PHASE_EXIT:
-                return "EXIT";
+                name = "EXIT";
+                break;
         default:
                 g_assert_not_reached ();
+                break;
         }
+
+        return name;
 }
 
 static void start_phase (GsmManager *manager);
@@ -230,17 +463,24 @@ static void start_phase (GsmManager *manager);
 static void
 gsm_manager_quit (GsmManager *manager)
 {
-        switch (manager->logout_type) {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        /* See the comment in request_reboot() for some more details about how
+         * this works. */
+
+        switch (priv->logout_type) {
         case GSM_MANAGER_LOGOUT_LOGOUT:
         case GSM_MANAGER_LOGOUT_NONE:
                 gsm_quit ();
                 break;
         case GSM_MANAGER_LOGOUT_REBOOT:
-                gsm_system_complete_shutdown (manager->system);
+        case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
+                gsm_system_complete_shutdown (priv->system);
                 gsm_quit ();
                 break;
         case GSM_MANAGER_LOGOUT_SHUTDOWN:
-                gsm_system_complete_shutdown (manager->system);
+        case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
+                gsm_system_complete_shutdown (priv->system);
                 gsm_quit ();
                 break;
         default:
@@ -254,18 +494,47 @@ static gboolean do_query_end_session_exit (GsmManager *manager);
 static void
 end_phase (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         gboolean start_next_phase = TRUE;
 
         g_debug ("GsmManager: ending phase %s",
-                 phase_num_to_name (manager->phase));
+                 phase_num_to_name (priv->phase));
 
-        g_slist_free (manager->query_clients);
-        manager->query_clients = NULL;
+        g_slist_free (priv->pending_apps);
+        priv->pending_apps = NULL;
 
-        g_clear_handle_id (&manager->phase_timeout_id, g_source_remove);
+        g_slist_free (priv->query_clients);
+        priv->query_clients = NULL;
 
-        switch (manager->phase) {
+        g_slist_free (priv->next_query_clients);
+        priv->next_query_clients = NULL;
+
+        if (priv->query_timeout_id > 0) {
+                g_source_remove (priv->query_timeout_id);
+                priv->query_timeout_id = 0;
+        }
+        if (priv->phase_timeout_id > 0) {
+                g_source_remove (priv->phase_timeout_id);
+                priv->phase_timeout_id = 0;
+        }
+
+        switch (priv->phase) {
+        case GSM_MANAGER_PHASE_STARTUP:
+        case GSM_MANAGER_PHASE_EARLY_INITIALIZATION:
+        case GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER:
+        case GSM_MANAGER_PHASE_DISPLAY_SERVER:
+                break;
         case GSM_MANAGER_PHASE_INITIALIZATION:
+                priv->manager_initialized = TRUE;
+                /* Wait for systemd if it isn't initialized yet*/
+                if (priv->systemd_managed && !priv->systemd_initialized) {
+                        sd_notify (0, "STATUS=GNOME Session Manager waiting for gnome-session-initialized.target (via signal)");
+                        start_next_phase = FALSE;
+                }
+                break;
+        case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+        case GSM_MANAGER_PHASE_PANEL:
+        case GSM_MANAGER_PHASE_DESKTOP:
         case GSM_MANAGER_PHASE_APPLICATION:
                 break;
         case GSM_MANAGER_PHASE_RUNNING:
@@ -290,17 +559,199 @@ end_phase (GsmManager *manager)
         }
 
         if (start_next_phase) {
-                manager->phase++;
+                priv->phase++;
                 start_phase (manager);
         }
 }
 
+static void
+app_event_during_startup (GsmManager *manager,
+                          GsmApp     *app)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (!(priv->phase < GSM_MANAGER_PHASE_APPLICATION))
+                return;
+
+        priv->pending_apps = g_slist_remove (priv->pending_apps, app);
+
+        if (priv->pending_apps == NULL) {
+                if (priv->phase_timeout_id > 0) {
+                        g_source_remove (priv->phase_timeout_id);
+                        priv->phase_timeout_id = 0;
+                }
+
+                end_phase (manager);
+        }
+}
+
+static gboolean
+is_app_display_server (GsmManager *manager,
+                       GsmApp     *app)
+{
+        GsmManagerPhase phase;
+
+        /* Apps can only really act as a display server if
+         * we're a wayland session.
+         */
+        if (g_strcmp0 (g_getenv ("XDG_SESSION_TYPE"), "wayland") != 0)
+                return FALSE;
+
+        phase = gsm_app_peek_phase (app);
+
+        return (phase == GSM_MANAGER_PHASE_DISPLAY_SERVER &&
+                is_app_required (manager, app));
+}
+
+static void
+_restart_app (GsmManager *manager,
+              GsmApp     *app)
+{
+        GError *error = NULL;
+
+        if (is_app_display_server (manager, app)) {
+                on_display_server_failure (manager, app);
+                return;
+        }
+
+        if (!gsm_app_restart (app, &error)) {
+                if (is_app_required (manager, app)) {
+                        on_required_app_failure (manager, app);
+                } else {
+                        g_warning ("Error on restarting session managed app: %s", error->message);
+                }
+                g_clear_error (&error);
+
+                app_event_during_startup (manager, app);
+        }
+}
+
+static void
+app_died (GsmApp     *app,
+          int         signal,
+          GsmManager *manager)
+{
+        g_warning ("Application '%s' killed by signal %d", gsm_app_peek_app_id (app), signal);
+
+        if (gsm_app_get_registered (app) && gsm_app_peek_autorestart (app)) {
+                g_debug ("Component '%s' is autorestart, ignoring died signal",
+                         gsm_app_peek_app_id (app));
+                return;
+        }
+
+        _restart_app (manager, app);
+
+        /* For now, we don't do anything with crashes from
+         * non-required apps after they hit the restart limit.
+         *
+         * Note that both required and not-required apps will be
+         * caught by ABRT/apport type infrastructure, and it'd be
+         * better to pick up the crash from there and do something
+         * un-intrusive about it generically.
+         */
+}
+
+static void
+app_exited (GsmApp     *app,
+            guchar      exit_code,
+            GsmManager *manager)
+{
+        if (exit_code != 0)
+                g_warning ("App '%s' exited with code %d", gsm_app_peek_app_id (app), exit_code);
+        else
+                g_debug ("App %s exited successfully", gsm_app_peek_app_id (app));
+
+        /* Consider that non-success exit status means "crash" for required components */
+        if (exit_code != 0 && is_app_required (manager, app)) {
+                if (gsm_app_get_registered (app) && gsm_app_peek_autorestart (app)) {
+                        g_debug ("Component '%s' is autorestart, ignoring non-successful exit",
+                                 gsm_app_peek_app_id (app));
+                        return;
+                }
+
+                _restart_app (manager, app);
+        } else {
+                app_event_during_startup (manager, app);
+        }
+}
+
+static void
+app_registered (GsmApp     *app,
+                GParamSpec *spec,
+                GsmManager *manager)
+{
+        if (!gsm_app_get_registered (app)) {
+                return;
+        }
+
+        g_debug ("App %s registered", gsm_app_peek_app_id (app));
+
+        app_event_during_startup (manager, app);
+}
+
+static gboolean
+on_phase_timeout (GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GSList *a;
+
+        priv->phase_timeout_id = 0;
+
+        switch (priv->phase) {
+        case GSM_MANAGER_PHASE_STARTUP:
+        case GSM_MANAGER_PHASE_EARLY_INITIALIZATION:
+        case GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER:
+        case GSM_MANAGER_PHASE_DISPLAY_SERVER:
+        case GSM_MANAGER_PHASE_INITIALIZATION:
+        case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+        case GSM_MANAGER_PHASE_PANEL:
+        case GSM_MANAGER_PHASE_DESKTOP:
+        case GSM_MANAGER_PHASE_APPLICATION:
+                for (a = priv->pending_apps; a; a = a->next) {
+                        GsmApp *app = a->data;
+                        g_warning ("Application '%s' failed to register before timeout",
+                                   gsm_app_peek_app_id (app));
+                        if (is_app_required (manager, app))
+                                on_required_app_failure (manager, app);
+                }
+                break;
+        case GSM_MANAGER_PHASE_RUNNING:
+                break;
+        case GSM_MANAGER_PHASE_QUERY_END_SESSION:
+        case GSM_MANAGER_PHASE_END_SESSION:
+                break;
+        case GSM_MANAGER_PHASE_EXIT:
+                break;
+        default:
+                g_assert_not_reached ();
+                break;
+        }
+
+        end_phase (manager);
+
+        return FALSE;
+}
+
 static gboolean
 _start_app (const char *id,
             GsmApp     *app,
             GsmManager *manager)
 {
-        if (gsm_app_peek_is_disabled (app)) {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (gsm_app_peek_phase (app) != priv->phase) {
+                goto out;
+        }
+
+        /* Keep track of app autostart condition in order to react
+         * accordingly in the future. */
+        g_signal_connect (app,
+                          "condition-changed",
+                          G_CALLBACK (app_condition_changed),
+                          manager);
+
+        if (gsm_app_peek_is_disabled (app)
+            || gsm_app_peek_is_conditionally_disabled (app)) {
                 g_debug ("GsmManager: Skipping disabled app: %s", id);
                 goto out;
         }
@@ -308,6 +759,30 @@ _start_app (const char *id,
         if (!start_app_or_warn (manager, app))
                 goto out;
 
+        if (priv->phase < GSM_MANAGER_PHASE_APPLICATION) {
+                /* Historical note - apparently,
+                 * e.g. gnome-settings-daemon used to "daemonize", and
+                 * so gnome-session assumes process exit means "ok
+                 * we're done".  Of course this is broken, we don't
+                 * even distinguish between exit code 0 versus not-0,
+                 * nor do we have any metadata which tells us a
+                 * process is going to "daemonize" or not (and
+                 * basically nothing should be anyways).
+                 */
+                g_signal_connect (app,
+                                  "exited",
+                                  G_CALLBACK (app_exited),
+                                  manager);
+                g_signal_connect (app,
+                                  "notify::registered",
+                                  G_CALLBACK (app_registered),
+                                  manager);
+                g_signal_connect (app,
+                                  "died",
+                                  G_CALLBACK (app_died),
+                                  manager);
+                priv->pending_apps = g_slist_prepend (priv->pending_apps, app);
+        }
  out:
         return FALSE;
 }
@@ -315,11 +790,21 @@ _start_app (const char *id,
 static void
 do_phase_startup (GsmManager *manager)
 {
-        gsm_store_foreach (manager->apps,
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        gsm_store_foreach (priv->apps,
                            (GsmStoreFunc)_start_app,
                            manager);
 
-        end_phase (manager);
+        if (priv->pending_apps != NULL) {
+                if (priv->phase < GSM_MANAGER_PHASE_APPLICATION) {
+                        priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT,
+                                                                                 (GSourceFunc)on_phase_timeout,
+                                                                                 manager);
+                }
+        } else {
+                end_phase (manager);
+        }
 }
 
 typedef struct {
@@ -329,10 +814,10 @@ typedef struct {
 
 
 static gboolean
-_client_end_session (const char           *id,
-                     GsmClient            *client,
+_client_end_session (GsmClient            *client,
                      ClientEndSessionData *data)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (data->manager);
         gboolean ret;
         GError  *error;
 
@@ -348,18 +833,27 @@ _client_end_session (const char           *id,
                 /* FIXME: what should we do if we can't communicate with client? */
         } else {
                 g_debug ("GsmManager: adding client to end-session clients: %s", gsm_client_peek_id (client));
-                data->manager->query_clients = g_slist_prepend (data->manager->query_clients, client);
+                priv->query_clients = g_slist_prepend (priv->query_clients, client);
         }
 
         return FALSE;
 }
 
+static gboolean
+_client_end_session_helper (const char           *id,
+                            GsmClient            *client,
+                            ClientEndSessionData *data)
+{
+        return _client_end_session (client, data);
+}
+
 static void
 complete_end_session_tasks (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GSList *l;
 
-        for (l = manager->pending_end_session_tasks;
+        for (l = priv->pending_end_session_tasks;
              l != NULL;
              l = l->next) {
                 GTask *task = G_TASK (l->data);
@@ -367,49 +861,68 @@ complete_end_session_tasks (GsmManager *manager)
                     g_task_return_boolean (task, TRUE);
         }
 
-        g_slist_free_full (manager->pending_end_session_tasks,
+        g_slist_free_full (priv->pending_end_session_tasks,
                            (GDestroyNotify) g_object_unref);
-        manager->pending_end_session_tasks = NULL;
+        priv->pending_end_session_tasks = NULL;
 }
 
-static gboolean
-on_end_session_timeout (GsmManager *manager)
+static void
+do_phase_end_session (GsmManager *manager)
 {
-        manager->phase_timeout_id = 0;
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        ClientEndSessionData data;
 
-        for (GSList *l = manager->query_clients; l != NULL; l = l->next) {
-                g_warning ("Client '%s' failed to reply before timeout",
-                           gsm_client_peek_id (l->data));
+        complete_end_session_tasks (manager);
+
+        data.manager = manager;
+        data.flags = 0;
+
+        if (priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
+                data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
         }
 
-        end_phase (manager);
-        return FALSE;
+        if (priv->phase_timeout_id > 0) {
+                g_source_remove (priv->phase_timeout_id);
+                priv->phase_timeout_id = 0;
+        }
+
+        if (gsm_store_size (priv->clients) > 0) {
+                priv->phase_timeout_id = g_timeout_add_seconds (GSM_MANAGER_PHASE_TIMEOUT,
+                                                                         (GSourceFunc)on_phase_timeout,
+                                                                         manager);
+
+                gsm_store_foreach (priv->clients,
+                                   (GsmStoreFunc)_client_end_session_helper,
+                                   &data);
+        } else {
+                end_phase (manager);
+        }
 }
 
 static void
-do_phase_end_session (GsmManager *manager)
+do_phase_end_session_part_2 (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         ClientEndSessionData data;
 
-        complete_end_session_tasks (manager);
-
         data.manager = manager;
-        data.flags = GSM_CLIENT_END_SESSION_FLAG_NONE;
+        data.flags = 0;
 
-        if (manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
+        if (priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
                 data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
         }
+        data.flags |= GSM_CLIENT_END_SESSION_FLAG_LAST;
 
-        g_clear_handle_id (&manager->phase_timeout_id, g_source_remove);
+        /* keep the timeout that was started at the beginning of the
+         * GSM_MANAGER_PHASE_END_SESSION phase */
 
-        if (gsm_store_size (manager->clients) > 0) {
-                manager->phase_timeout_id = g_timeout_add_seconds (10,
-                                                                   (GSourceFunc)on_end_session_timeout,
-                                                                   manager);
+        if (g_slist_length (priv->next_query_clients) > 0) {
+                g_slist_foreach (priv->next_query_clients,
+                                 (GFunc)_client_end_session,
+                                 &data);
 
-                gsm_store_foreach (manager->clients,
-                                   (GsmStoreFunc)_client_end_session,
-                                   &data);
+                g_slist_free (priv->next_query_clients);
+                priv->next_query_clients = NULL;
         } else {
                 end_phase (manager);
         }
@@ -436,15 +949,53 @@ _client_stop (const char *id,
         return FALSE;
 }
 
+static void
+maybe_restart_user_bus (GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GsmSystem *system;
+        g_autoptr(GVariant) reply = NULL;
+        g_autoptr(GError) error = NULL;
+
+        if (priv->dbus_disconnected)
+                return;
+
+        system = gsm_get_system ();
+
+        if (!gsm_system_is_last_session_for_user (system))
+                return;
+
+        reply = g_dbus_connection_call_sync (priv->connection,
+                                             "org.freedesktop.systemd1",
+                                             "/org/freedesktop/systemd1",
+                                             "org.freedesktop.systemd1.Manager",
+                                             "StopUnit",
+                                             g_variant_new ("(ss)", "dbus.service", "fail"),
+                                             NULL,
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             NULL,
+                                             &error);
+
+        if (error != NULL) {
+                g_debug ("GsmManager: reloading user bus failed: %s", error->message);
+        }
+}
+
 static void
 do_phase_exit (GsmManager *manager)
 {
-        if (gsm_store_size (manager->clients) > 0) {
-                gsm_store_foreach (manager->clients,
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (gsm_store_size (priv->clients) > 0) {
+                gsm_store_foreach (priv->clients,
                                    (GsmStoreFunc)_client_stop,
                                    NULL);
         }
 
+        if (!priv->systemd_managed)
+                maybe_restart_user_bus (manager);
+
         end_phase (manager);
 }
 
@@ -453,6 +1004,7 @@ _client_query_end_session (const char           *id,
                            GsmClient            *client,
                            ClientEndSessionData *data)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (data->manager);
         gboolean ret;
         GError  *error;
 
@@ -468,7 +1020,7 @@ _client_query_end_session (const char           *id,
                 /* FIXME: what should we do if we can't communicate with client? */
         } else {
                 g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client));
-                data->manager->query_clients = g_slist_prepend (data->manager->query_clients, client);
+                priv->query_clients = g_slist_prepend (priv->query_clients, client);
         }
 
         return FALSE;
@@ -492,17 +1044,18 @@ inhibitor_has_flag (gpointer      key,
 static gboolean
 gsm_manager_is_logout_inhibited (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitor *inhibitor;
 
-        if (manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
+        if (priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
                 return FALSE;
         }
 
-        if (manager->inhibitors == NULL) {
+        if (priv->inhibitors == NULL) {
                 return FALSE;
         }
 
-        inhibitor = (GsmInhibitor *)gsm_store_find (manager->inhibitors,
+        inhibitor = (GsmInhibitor *)gsm_store_find (priv->inhibitors,
                                                     (GsmStoreFunc)inhibitor_has_flag,
                                                     GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_LOGOUT));
         if (inhibitor == NULL) {
@@ -514,13 +1067,14 @@ gsm_manager_is_logout_inhibited (GsmManager *manager)
 static gboolean
 gsm_manager_is_idle_inhibited (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitor *inhibitor;
 
-        if (manager->inhibitors == NULL) {
+        if (priv->inhibitors == NULL) {
                 return FALSE;
         }
 
-        inhibitor = (GsmInhibitor *)gsm_store_find (manager->inhibitors,
+        inhibitor = (GsmInhibitor *)gsm_store_find (priv->inhibitors,
                                                     (GsmStoreFunc)inhibitor_has_flag,
                                                     GUINT_TO_POINTER (GSM_INHIBITOR_FLAG_IDLE));
         if (inhibitor == NULL) {
@@ -565,27 +1119,29 @@ inhibitor_is_jit (gpointer      key,
 static void
 cancel_end_session (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         /* just ignore if received outside of shutdown */
-        if (manager->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+        if (priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
                 return;
         }
 
         /* switch back to running phase */
         g_debug ("GsmManager: Cancelling the end of session");
 
-        g_cancellable_cancel (manager->end_session_cancellable);
+        g_cancellable_cancel (priv->end_session_cancellable);
 
         gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_RUNNING);
-        manager->logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL;
+        priv->logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL;
 
-        manager->logout_type = GSM_MANAGER_LOGOUT_NONE;
+        priv->logout_type = GSM_MANAGER_LOGOUT_NONE;
 
         /* clear all JIT inhibitors */
-        gsm_store_foreach_remove (manager->inhibitors,
+        gsm_store_foreach_remove (priv->inhibitors,
                                   (GsmStoreFunc)inhibitor_is_jit,
                                   (gpointer)manager);
 
-        gsm_store_foreach (manager->clients,
+        gsm_store_foreach (priv->clients,
                            (GsmStoreFunc)_client_cancel_end_session,
                            NULL);
 
@@ -595,32 +1151,35 @@ cancel_end_session (GsmManager *manager)
 static void
 end_session_or_show_shell_dialog (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         gboolean logout_prompt;
         GsmShellEndSessionDialogType type;
         gboolean logout_inhibited;
 
-        switch (manager->logout_type) {
+        switch (priv->logout_type) {
         case GSM_MANAGER_LOGOUT_LOGOUT:
                 type = GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT;
                 break;
         case GSM_MANAGER_LOGOUT_REBOOT:
+        case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
                 type = GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART;
                 break;
         case GSM_MANAGER_LOGOUT_SHUTDOWN:
+        case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
                 type = GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN;
                 break;
         default:
                 g_warning ("Unexpected logout type %d when creating end session dialog",
-                           manager->logout_type);
+                           priv->logout_type);
                 type = GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT;
                 break;
         }
 
         logout_inhibited = gsm_manager_is_logout_inhibited (manager);
-        logout_prompt = g_settings_get_boolean (manager->settings,
+        logout_prompt = g_settings_get_boolean (priv->settings,
                                                 KEY_LOGOUT_PROMPT);
 
-        switch (manager->logout_mode) {
+        switch (priv->logout_mode) {
         case GSM_MANAGER_LOGOUT_MODE_NORMAL:
                 if (logout_inhibited || logout_prompt) {
                         show_shell_end_session_dialog (manager, type);
@@ -650,11 +1209,16 @@ end_session_or_show_shell_dialog (GsmManager *manager)
 static void
 query_end_session_complete (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: query end session complete");
 
         /* Remove the timeout since this can be called from outside the timer
          * and we don't want to have it called twice */
-        g_clear_handle_id (&manager->phase_timeout_id, g_source_remove);
+        if (priv->query_timeout_id > 0) {
+                g_source_remove (priv->query_timeout_id);
+                priv->query_timeout_id = 0;
+        }
 
         end_session_or_show_shell_dialog (manager);
 }
@@ -672,11 +1236,12 @@ generate_cookie (void)
 static guint32
 _generate_unique_cookie (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         guint32 cookie;
 
         do {
                 cookie = generate_cookie ();
-        } while (gsm_store_find (manager->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL);
+        } while (gsm_store_find (priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL);
 
         return cookie;
 }
@@ -684,37 +1249,56 @@ _generate_unique_cookie (GsmManager *manager)
 static gboolean
 _on_query_end_session_timeout (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GSList *l;
 
-        manager->phase_timeout_id = 0;
+        priv->query_timeout_id = 0;
 
         g_debug ("GsmManager: query end session timed out");
 
-        for (l = manager->query_clients; l != NULL; l = l->next) {
+        for (l = priv->query_clients; l != NULL; l = l->next) {
+                guint         cookie;
                 GsmInhibitor *inhibitor;
+                const char   *bus_name;
+                char         *app_id;
 
                 g_warning ("Client '%s' failed to reply before timeout",
                            gsm_client_peek_id (l->data));
 
                 /* Don't add "not responding" inhibitors if logout is forced
                  */
-                if (manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
+                if (priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
                         continue;
                 }
 
                 /* Add JIT inhibit for unresponsive client */
+                if (GSM_IS_DBUS_CLIENT (l->data)) {
+                        bus_name = gsm_dbus_client_get_bus_name (l->data);
+                } else {
+                        bus_name = NULL;
+                }
+
+                app_id = g_strdup (gsm_client_peek_app_id (l->data));
+                if (IS_STRING_EMPTY (app_id)) {
+                        /* XSMP clients don't give us an app id unless we start them */
+                        g_free (app_id);
+                        app_id = gsm_client_get_app_name (l->data);
+                }
+
+                cookie = _generate_unique_cookie (manager);
                 inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (l->data),
-                                                          gsm_client_peek_app_id (l->data),
+                                                          app_id,
                                                           GSM_INHIBITOR_FLAG_LOGOUT,
                                                           _("Not responding"),
-                                                          gsm_client_peek_bus_name (l->data),
-                                                          _generate_unique_cookie (manager));
-                gsm_store_add (manager->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
+                                                          bus_name,
+                                                          cookie);
+                g_free (app_id);
+                gsm_store_add (priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
                 g_object_unref (inhibitor);
         }
 
-        g_slist_free (manager->query_clients);
-        manager->query_clients = NULL;
+        g_slist_free (priv->query_clients);
+        priv->query_clients = NULL;
 
         query_end_session_complete (manager);
 
@@ -724,81 +1308,112 @@ _on_query_end_session_timeout (GsmManager *manager)
 static void
 do_phase_query_end_session (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         ClientEndSessionData data;
 
         data.manager = manager;
-        data.flags = GSM_CLIENT_END_SESSION_FLAG_NONE;
+        data.flags = 0;
 
-        if (manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
+        if (priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
                 data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL;
         }
+        /* We only query if an app is ready to log out, so we don't use
+         * GSM_CLIENT_END_SESSION_FLAG_SAVE here.
+         */
 
         debug_clients (manager);
         g_debug ("GsmManager: sending query-end-session to clients (logout mode: %s)",
-                 manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_NORMAL? "normal" :
-                 manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE? "forceful":
+                 priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_NORMAL? "normal" :
+                 priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE? "forceful":
                  "no confirmation");
-        gsm_store_foreach (manager->clients,
+        gsm_store_foreach (priv->clients,
                            (GsmStoreFunc)_client_query_end_session,
                            &data);
 
-        manager->phase_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager);
+        /* This phase doesn't time out unless logout is forced. Typically, this
+         * separate timer is only used to show UI. */
+        priv->query_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager);
 }
 
 static void
 update_idle (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         if (gsm_manager_is_idle_inhibited (manager)) {
-                gsm_presence_set_idle_enabled (manager->presence, FALSE);
+                gsm_presence_set_idle_enabled (priv->presence, FALSE);
         } else {
-                gsm_presence_set_idle_enabled (manager->presence, TRUE);
+                gsm_presence_set_idle_enabled (priv->presence, TRUE);
         }
 }
 
 static void
 start_phase (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: starting phase %s\n",
-                 phase_num_to_name (manager->phase));
+                 phase_num_to_name (priv->phase));
 
         /* reset state */
-        g_slist_free (manager->query_clients);
-        manager->query_clients = NULL;
+        g_slist_free (priv->pending_apps);
+        priv->pending_apps = NULL;
+        g_slist_free (priv->query_clients);
+        priv->query_clients = NULL;
+        g_slist_free (priv->next_query_clients);
+        priv->next_query_clients = NULL;
+
+        if (priv->query_timeout_id > 0) {
+                g_source_remove (priv->query_timeout_id);
+                priv->query_timeout_id = 0;
+        }
+        if (priv->phase_timeout_id > 0) {
+                g_source_remove (priv->phase_timeout_id);
+                priv->phase_timeout_id = 0;
+        }
 
-        g_clear_handle_id (&manager->phase_timeout_id, g_source_remove);
+        sd_notifyf (0, "STATUS=GNOME Session Manager phase is %s", phase_num_to_name (priv->phase));
 
-        switch (manager->phase) {
-        case GSM_MANAGER_PHASE_INITIALIZATION:
-                sd_notify (0, "READY=1\nSTATUS=Waiting for session to start");
+        switch (priv->phase) {
+        case GSM_MANAGER_PHASE_STARTUP:
+        case GSM_MANAGER_PHASE_EARLY_INITIALIZATION:
+        case GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER:
+                do_phase_startup (manager);
                 break;
+        case GSM_MANAGER_PHASE_DISPLAY_SERVER:
+                sd_notify (0, "READY=1");
+                do_phase_startup (manager);
+                break;
+        case GSM_MANAGER_PHASE_INITIALIZATION:
+        case GSM_MANAGER_PHASE_WINDOW_MANAGER:
+        case GSM_MANAGER_PHASE_PANEL:
+        case GSM_MANAGER_PHASE_DESKTOP:
         case GSM_MANAGER_PHASE_APPLICATION:
-                sd_notify (0, "STATUS=Starting applications");
-                gsm_exported_manager_emit_session_running (manager->skeleton);
                 do_phase_startup (manager);
                 break;
         case GSM_MANAGER_PHASE_RUNNING:
-                sd_notify (0, "STATUS=Running");
                 sd_journal_send ("MESSAGE_ID=%s", GSM_MANAGER_STARTUP_SUCCEEDED_MSGID,
                                  "PRIORITY=%d", 5,
                                  "MESSAGE=Entering running state",
                                  NULL);
-                if (manager->pending_end_session_tasks != NULL)
+                if (priv->pending_end_session_tasks != NULL)
                         complete_end_session_tasks (manager);
-                g_object_unref (manager->end_session_cancellable);
-                manager->end_session_cancellable = g_cancellable_new ();
+                g_object_unref (priv->end_session_cancellable);
+                priv->end_session_cancellable = g_cancellable_new ();
+                gsm_exported_manager_emit_session_running (priv->skeleton);
                 update_idle (manager);
                 break;
         case GSM_MANAGER_PHASE_QUERY_END_SESSION:
-                sd_notify (0, "STATUS=Querying end of session");
                 do_phase_query_end_session (manager);
                 break;
         case GSM_MANAGER_PHASE_END_SESSION:
-                sd_notify (0, "STOPPING=1\nSTATUS=Logging out");
-                gsm_exported_manager_emit_session_over (manager->skeleton);
+                sd_notify (0, "STOPPING=1");
+
                 do_phase_end_session (manager);
                 break;
         case GSM_MANAGER_PHASE_EXIT:
-                sd_notify (0, "STOPPING=1\nSTATUS=Quitting");
+                sd_notify (0, "STOPPING=1");
+
                 do_phase_exit (manager);
                 break;
         default:
@@ -812,9 +1427,19 @@ _debug_app_for_phase (const char *id,
                       GsmApp     *app,
                       gpointer    data)
 {
-        g_debug ("GsmManager:\tapp-id:%s\tis-disabled:%d",
+        guint phase;
+
+        phase = GPOINTER_TO_UINT (data);
+
+        if (gsm_app_peek_phase (app) != phase) {
+                return FALSE;
+        }
+
+        g_debug ("GsmManager:\tID: %s\tapp-id:%s\tis-disabled:%d\tis-conditionally-disabled:%d",
+                 gsm_app_peek_id (app),
                  gsm_app_peek_app_id (app),
-                 gsm_app_peek_is_disabled (app));
+                 gsm_app_peek_is_disabled (app),
+                 gsm_app_peek_is_conditionally_disabled (app));
 
         return FALSE;
 }
@@ -822,32 +1447,80 @@ _debug_app_for_phase (const char *id,
 static void
 debug_app_summary (GsmManager *manager)
 {
-        g_debug ("GsmManager: Autostart app summary");
-        gsm_store_foreach (manager->apps,
-                           (GsmStoreFunc)_debug_app_for_phase,
-                           NULL);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        guint phase;
+
+        g_debug ("GsmManager: App startup summary");
+        for (phase = GSM_MANAGER_PHASE_EARLY_INITIALIZATION; phase < GSM_MANAGER_PHASE_RUNNING; phase++) {
+                g_debug ("GsmManager: Phase %s", phase_num_to_name (phase));
+                gsm_store_foreach (priv->apps,
+                                   (GsmStoreFunc)_debug_app_for_phase,
+                                   GUINT_TO_POINTER (phase));
+        }
 }
 
 void
 gsm_manager_start (GsmManager *manager)
 {
-        g_debug ("GsmManager: Starting");
+        g_debug ("GsmManager: GSM starting to manage");
 
         g_return_if_fail (GSM_IS_MANAGER (manager));
-        g_return_if_fail (manager->phase == GSM_MANAGER_PHASE_INITIALIZATION);
 
+        gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_EARLY_INITIALIZATION);
         debug_app_summary (manager);
         start_phase (manager);
 }
 
+char *
+_gsm_manager_get_default_session (GsmManager     *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        g_autoptr(GSettings) session_settings = NULL;
+
+        if (manager)
+                session_settings = g_object_ref (priv->session_settings);
+        else
+                session_settings  = g_settings_new (SESSION_SCHEMA);
+        return g_settings_get_string (session_settings,
+                                      KEY_SESSION_NAME);
+}
+
 void
 _gsm_manager_set_active_session (GsmManager     *manager,
-                                 const char     *session_name)
+                                 const char     *session_name,
+                                 gboolean        is_fallback)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_free (priv->session_name);
+        priv->session_name = g_strdup (session_name);
+        priv->is_fallback_session = is_fallback;
+
+        gsm_exported_manager_set_session_name (priv->skeleton, session_name);
+}
+
+static gboolean
+_app_has_app_id (const char   *id,
+                 GsmApp       *app,
+                 const char   *app_id_a)
+{
+        const char *app_id_b;
+
+        app_id_b = gsm_app_peek_app_id (app);
+        return (app_id_b != NULL && strcmp (app_id_a, app_id_b) == 0);
+}
+
+static GsmApp *
+find_app_for_app_id (GsmManager *manager,
+                     const char *app_id)
 {
-        g_free (manager->session_name);
-        manager->session_name = g_strdup (session_name);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GsmApp *app;
 
-        gsm_exported_manager_set_session_name (manager->skeleton, session_name);
+        app = (GsmApp *)gsm_store_find (priv->apps,
+                                        (GsmStoreFunc)_app_has_app_id,
+                                        (char *)app_id);
+        return app;
 }
 
 static gboolean
@@ -873,23 +1546,109 @@ inhibitor_has_client_id (gpointer      key,
         return matches;
 }
 
+static gboolean
+_app_has_startup_id (const char *id,
+                     GsmApp     *app,
+                     const char *startup_id_a)
+{
+        const char *startup_id_b;
+
+        startup_id_b = gsm_app_peek_startup_id (app);
+
+        if (IS_STRING_EMPTY (startup_id_b)) {
+                return FALSE;
+        }
+
+        return (strcmp (startup_id_a, startup_id_b) == 0);
+}
+
+static GsmApp *
+find_app_for_startup_id (GsmManager *manager,
+                        const char *startup_id)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GsmApp *found_app;
+        GSList *a;
+
+        found_app = NULL;
+
+        /* If we're starting up the session, try to match the new client
+         * with one pending apps for the current phase. If not, try to match
+         * with any of the autostarted apps. */
+        if (priv->phase < GSM_MANAGER_PHASE_APPLICATION) {
+                for (a = priv->pending_apps; a != NULL; a = a->next) {
+                        GsmApp *app = GSM_APP (a->data);
+
+                        if (strcmp (startup_id, gsm_app_peek_startup_id (app)) == 0) {
+                                found_app = app;
+                                goto out;
+                        }
+                }
+        } else {
+                GsmApp *app;
+
+                app = (GsmApp *)gsm_store_find (priv->apps,
+                                                (GsmStoreFunc)_app_has_startup_id,
+                                                (char *)startup_id);
+                if (app != NULL) {
+                        found_app = app;
+                        goto out;
+                }
+        }
+ out:
+        return found_app;
+}
+
 static void
 _disconnect_client (GsmManager *manager,
                     GsmClient  *client)
 {
-        g_debug ("GsmManager: disconnect client: %s (app: %s)",
-                 gsm_client_peek_id (client),
-                 gsm_client_peek_app_id (client));
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        gboolean              is_condition_client;
+        GsmApp               *app;
+        const char           *app_id;
+        const char           *startup_id;
+        gboolean              app_restart;
+        GsmClientRestartStyle client_restart_hint;
+
+        g_debug ("GsmManager: disconnect client: %s", gsm_client_peek_id (client));
 
         /* take a ref so it doesn't get finalized */
         g_object_ref (client);
 
+        gsm_client_set_status (client, GSM_CLIENT_FINISHED);
+
+        is_condition_client = FALSE;
+        if (g_slist_find (priv->condition_clients, client)) {
+                priv->condition_clients = g_slist_remove (priv->condition_clients, client);
+
+                is_condition_client = TRUE;
+        }
+
         /* remove any inhibitors for this client */
-        gsm_store_foreach_remove (manager->inhibitors,
+        gsm_store_foreach_remove (priv->inhibitors,
                                   (GsmStoreFunc)inhibitor_has_client_id,
                                   (gpointer)gsm_client_peek_id (client));
 
-        switch (manager->phase) {
+        app = NULL;
+
+        /* first try to match on startup ID */
+        startup_id = gsm_client_peek_startup_id (client);
+        if (! IS_STRING_EMPTY (startup_id)) {
+                app = find_app_for_startup_id (manager, startup_id);
+
+        }
+
+        /* then try to find matching app-id */
+        if (app == NULL) {
+                app_id = gsm_client_peek_app_id (client);
+                if (! IS_STRING_EMPTY (app_id)) {
+                        g_debug ("GsmManager: disconnect for app '%s'", app_id);
+                        app = find_app_for_app_id (manager, app_id);
+                }
+        }
+
+        switch (priv->phase) {
         case GSM_MANAGER_PHASE_QUERY_END_SESSION:
                 /* Instead of answering our end session query, the client just exited.
                  * Treat that as an "okay, end the session" answer.
@@ -897,15 +1656,18 @@ _disconnect_client (GsmManager *manager,
                  * This call implicitly removes any inhibitors for the client, along
                  * with removing the client from the pending query list.
                  */
-                on_client_end_session_response (client,
-                                                TRUE,
-                                                "Client exited in query end "
-                                                "session phase instead of end "
-                                                "session phase",
-                                                manager);
+                _handle_client_end_session_response (manager,
+                                                     client,
+                                                     TRUE,
+                                                     FALSE,
+                                                     FALSE,
+                                                     "Client exited in "
+                                                     "query end session phase "
+                                                     "instead of end session "
+                                                     "phase");
                 break;
         case GSM_MANAGER_PHASE_END_SESSION:
-                if (! g_slist_find (manager->query_clients, client)) {
+                if (! g_slist_find (priv->query_clients, client)) {
                         /* the client sent its EndSessionResponse and we already
                          * processed it.
                          */
@@ -918,17 +1680,54 @@ _disconnect_client (GsmManager *manager,
                  * in library code after the callback. Or maybe the application
                  * crashed while handling EndSession. Or it was lazy.
                  */
-                on_client_end_session_response (client,
-                                                TRUE,
-                                                "Client exited in end session "
-                                                "phase without sending "
-                                                "EndSessionResponse",
-                                                manager);
+                _handle_client_end_session_response (manager,
+                                                     client,
+                                                     TRUE,
+                                                     FALSE,
+                                                     FALSE,
+                                                     "Client exited in "
+                                                     "end session phase without "
+                                                     "sending EndSessionResponse");
         default:
                 /* do nothing */
                 break;
         }
 
+        if (priv->dbus_disconnected && GSM_IS_DBUS_CLIENT (client)) {
+                g_debug ("GsmManager: dbus disconnected, not restarting application");
+                goto out;
+        }
+
+        if (app == NULL) {
+                g_debug ("GsmManager: unable to find application for client - not restarting");
+                goto out;
+        }
+
+        if (priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+                g_debug ("GsmManager: in shutdown, not restarting application");
+                goto out;
+        }
+
+        app_restart = gsm_app_peek_autorestart (app);
+        client_restart_hint = gsm_client_peek_restart_style_hint (client);
+
+        /* allow legacy clients to override the app info */
+        if (! app_restart
+            && client_restart_hint != GSM_CLIENT_RESTART_IMMEDIATELY) {
+                g_debug ("GsmManager: autorestart not set, not restarting application");
+                goto out;
+        }
+
+        if (is_condition_client) {
+                g_debug ("GsmManager: app conditionally disabled, not restarting application");
+                goto out;
+        }
+
+        g_debug ("GsmManager: restarting app");
+
+        _restart_app (manager, app);
+
+ out:
         g_object_unref (client);
 }
 
@@ -944,13 +1743,17 @@ _disconnect_dbus_client (const char       *id,
 {
         const char *name;
 
+        if (! GSM_IS_DBUS_CLIENT (client)) {
+                return FALSE;
+        }
+
         /* If no service name, then we simply disconnect all clients */
         if (!data->service_name) {
                 _disconnect_client (data->manager, client);
                 return TRUE;
         }
 
-        name = gsm_client_peek_bus_name (client);
+        name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client));
         if (IS_STRING_EMPTY (name)) {
                 return FALSE;
         }
@@ -976,96 +1779,195 @@ static void
 remove_clients_for_connection (GsmManager *manager,
                                const char *service_name)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         RemoveClientData data;
 
         data.service_name = service_name;
         data.manager = manager;
 
         /* disconnect dbus clients for name */
-        gsm_store_foreach_remove (manager->clients,
+        gsm_store_foreach_remove (priv->clients,
                                   (GsmStoreFunc)_disconnect_dbus_client,
                                   &data);
 
-        if (manager->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION
-            && gsm_store_size (manager->clients) == 0) {
+        if (priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION
+            && gsm_store_size (priv->clients) == 0) {
                 g_debug ("GsmManager: last client disconnected - exiting");
                 end_phase (manager);
         }
 }
 
+static void
+gsm_manager_set_failsafe (GsmManager *manager,
+                          gboolean    enabled)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_return_if_fail (GSM_IS_MANAGER (manager));
+
+        priv->failsafe = enabled;
+}
+
 gboolean
 gsm_manager_get_dbus_disconnected (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
 
-        return manager->dbus_disconnected;
+        return priv->dbus_disconnected;
+}
+
+gboolean
+gsm_manager_get_failsafe (GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+        return priv->failsafe;
+}
+
+gboolean
+gsm_manager_get_systemd_managed (GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
+
+        return priv->systemd_managed;
 }
 
 static void
 on_client_disconnected (GsmClient  *client,
                         GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: disconnect client");
         _disconnect_client (manager, client);
-        gsm_store_remove (manager->clients, gsm_client_peek_id (client));
-        if (manager->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION
-            && gsm_store_size (manager->clients) == 0) {
+        gsm_store_remove (priv->clients, gsm_client_peek_id (client));
+        if (priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION
+            && gsm_store_size (priv->clients) == 0) {
                 g_debug ("GsmManager: last client disconnected - exiting");
                 end_phase (manager);
         }
 }
 
 static void
-on_client_end_session_response (GsmClient  *client,
-                                gboolean    is_ok,
-                                const char *reason,
-                                GsmManager *manager)
+_handle_client_end_session_response (GsmManager *manager,
+                                     GsmClient  *client,
+                                     gboolean    is_ok,
+                                     gboolean    do_last,
+                                     gboolean    cancel,
+                                     const char *reason)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         /* just ignore if received outside of shutdown */
-        if (manager->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+        if (priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) {
                 return;
         }
 
-        g_debug ("GsmManager: Response from end session request: is-ok=%d reason=%s", is_ok, reason ?: "(none)");
+        g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :"");
+
+        if (cancel) {
+                cancel_end_session (manager);
+                return;
+        }
 
-        manager->query_clients = g_slist_remove (manager->query_clients, client);
+        priv->query_clients = g_slist_remove (priv->query_clients, client);
 
-        if (!is_ok && manager->logout_mode != GSM_MANAGER_LOGOUT_MODE_FORCE) {
+        if (! is_ok && priv->logout_mode != GSM_MANAGER_LOGOUT_MODE_FORCE) {
+                guint         cookie;
                 GsmInhibitor *inhibitor;
+                char         *app_id;
+                const char   *bus_name;
+
+                /* FIXME: do we support updating the reason? */
 
                 /* Create JIT inhibit */
+                if (GSM_IS_DBUS_CLIENT (client)) {
+                        bus_name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client));
+                } else {
+                        bus_name = NULL;
+                }
+
+                app_id = g_strdup (gsm_client_peek_app_id (client));
+                if (IS_STRING_EMPTY (app_id)) {
+                        /* XSMP clients don't give us an app id unless we start them */
+                        g_free (app_id);
+                        app_id = gsm_client_get_app_name (client);
+                }
+
+                cookie = _generate_unique_cookie (manager);
                 inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (client),
-                                                          gsm_client_peek_app_id (client),
+                                                          app_id,
                                                           GSM_INHIBITOR_FLAG_LOGOUT,
                                                           reason != NULL ? reason : _("Not responding"),
-                                                          gsm_client_peek_bus_name (client),
-                                                          _generate_unique_cookie (manager));
-                gsm_store_add (manager->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
+                                                          bus_name,
+                                                          cookie);
+                g_free (app_id);
+                gsm_store_add (priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
                 g_object_unref (inhibitor);
         } else {
-                gsm_store_foreach_remove (manager->inhibitors,
+                gsm_store_foreach_remove (priv->inhibitors,
                                           (GsmStoreFunc)inhibitor_has_client_id,
                                           (gpointer)gsm_client_peek_id (client));
         }
 
-        if (manager->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION) {
-                if (manager->query_clients == NULL)
+        if (priv->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+                if (priv->query_clients == NULL) {
                         query_end_session_complete (manager);
-        } else if (manager->phase == GSM_MANAGER_PHASE_END_SESSION) {
+                }
+        } else if (priv->phase == GSM_MANAGER_PHASE_END_SESSION) {
+                if (do_last) {
+                        /* This only makes sense if we're in part 1 of
+                         * GSM_MANAGER_PHASE_END_SESSION. Doing this in part 2
+                         * can only happen because of a buggy client that loops
+                         * wanting to be last again and again. The phase
+                         * timeout will take care of this issue. */
+                        priv->next_query_clients = g_slist_prepend (priv->next_query_clients,
+                                                                             client);
+                }
+
                 /* we can continue to the next step if all clients have replied
                  * and if there's no inhibitor */
-                if (manager->query_clients != NULL || gsm_manager_is_logout_inhibited (manager))
+                if (priv->query_clients != NULL
+                    || gsm_manager_is_logout_inhibited (manager)) {
                         return;
+                }
 
-                end_phase (manager);
+                if (priv->next_query_clients != NULL) {
+                        do_phase_end_session_part_2 (manager);
+                } else {
+                        end_phase (manager);
+                }
         }
 }
 
+static void
+on_client_end_session_response (GsmClient  *client,
+                                gboolean    is_ok,
+                                gboolean    do_last,
+                                gboolean    cancel,
+                                const char *reason,
+                                GsmManager *manager)
+{
+        _handle_client_end_session_response (manager,
+                                             client,
+                                             is_ok,
+                                             do_last,
+                                             cancel,
+                                             reason);
+}
+
 static void
 on_store_client_added (GsmStore   *store,
                        const char *id,
                        GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmClient *client;
 
         g_debug ("GsmManager: Client added: %s", id);
@@ -1077,7 +1979,7 @@ on_store_client_added (GsmStore   *store,
                           G_CALLBACK (on_client_end_session_response),
                           manager);
 
-        gsm_exported_manager_emit_client_added (manager->skeleton, id);
+        gsm_exported_manager_emit_client_added (priv->skeleton, id);
         /* FIXME: disconnect signal handler */
 }
 
@@ -1086,9 +1988,51 @@ on_store_client_removed (GsmStore   *store,
                          const char *id,
                          GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: Client removed: %s", id);
 
-        gsm_exported_manager_emit_client_removed (manager->skeleton, id);
+        gsm_exported_manager_emit_client_removed (priv->skeleton, id);
+}
+
+static void
+gsm_manager_set_client_store (GsmManager *manager,
+                              GsmStore   *store)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_return_if_fail (GSM_IS_MANAGER (manager));
+
+        if (store != NULL) {
+                g_object_ref (store);
+        }
+
+        if (priv->clients != NULL) {
+                g_signal_handlers_disconnect_by_func (priv->clients,
+                                                      on_store_client_added,
+                                                      manager);
+                g_signal_handlers_disconnect_by_func (priv->clients,
+                                                      on_store_client_removed,
+                                                      manager);
+
+                g_object_unref (priv->clients);
+        }
+
+
+        g_debug ("GsmManager: setting client store %p", store);
+
+        priv->clients = store;
+
+        if (priv->clients != NULL) {
+                g_signal_connect (priv->clients,
+                                  "added",
+                                  G_CALLBACK (on_store_client_added),
+                                  manager);
+                g_signal_connect (priv->clients,
+                                  "removed",
+                                  G_CALLBACK (on_store_client_removed),
+                                  manager);
+        }
 }
 
 static void
@@ -1097,7 +2041,26 @@ gsm_manager_set_property (GObject       *object,
                           const GValue  *value,
                           GParamSpec    *pspec)
 {
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        GsmManager *self = GSM_MANAGER (object);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (self);
+
+        switch (prop_id) {
+        case PROP_FAILSAFE:
+                gsm_manager_set_failsafe (self, g_value_get_boolean (value));
+                break;
+         case PROP_FALLBACK:
+                priv->is_fallback_session = g_value_get_boolean (value);
+                break;
+         case PROP_CLIENT_STORE:
+                gsm_manager_set_client_store (self, g_value_get_object (value));
+                break;
+        case PROP_SYSTEMD_MANAGED:
+                priv->systemd_managed = g_value_get_boolean (value);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
 }
 
 static void
@@ -1107,10 +2070,20 @@ gsm_manager_get_property (GObject    *object,
                           GParamSpec *pspec)
 {
         GsmManager *self = GSM_MANAGER (object);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (self);
 
         switch (prop_id) {
+        case PROP_FAILSAFE:
+                g_value_set_boolean (value, priv->failsafe);
+                break;
         case PROP_SESSION_NAME:
-                g_value_set_string (value, self->session_name);
+                g_value_set_string (value, priv->session_name);
+                break;
+        case PROP_FALLBACK:
+                g_value_set_boolean (value, priv->is_fallback_session);
+                break;
+        case PROP_CLIENT_STORE:
+                g_value_set_object (value, priv->clients);
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1118,6 +2091,14 @@ gsm_manager_get_property (GObject    *object,
         }
 }
 
+static gboolean
+_find_app_provides (const char *id,
+                    GsmApp     *app,
+                    const char *service)
+{
+        return gsm_app_provides (app, service);
+}
+
 static GObject *
 gsm_manager_constructor (GType                  type,
                          guint                  n_construct_properties,
@@ -1135,21 +2116,25 @@ static void
 update_inhibited_actions (GsmManager *manager,
                           GsmInhibitorFlag new_inhibited_actions)
 {
-        if (manager->inhibited_actions == new_inhibited_actions)
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->inhibited_actions == new_inhibited_actions)
                 return;
 
-        gsm_system_set_inhibitors (manager->system, new_inhibited_actions);
+        gsm_system_set_inhibitors (priv->system, new_inhibited_actions);
 
-        manager->inhibited_actions = new_inhibited_actions;
-        gsm_exported_manager_set_inhibited_actions (manager->skeleton,
-                                                    manager->inhibited_actions);
+        priv->inhibited_actions = new_inhibited_actions;
+        gsm_exported_manager_set_inhibited_actions (priv->skeleton,
+                                                    priv->inhibited_actions);
 }
 
 static void
 on_inhibitor_vanished (GsmInhibitor *inhibitor,
                        GsmManager   *manager)
 {
-        gsm_store_remove (manager->inhibitors, gsm_inhibitor_peek_id (inhibitor));
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        gsm_store_remove (priv->inhibitors, gsm_inhibitor_peek_id (inhibitor));
 }
 
 static void
@@ -1157,6 +2142,7 @@ on_store_inhibitor_added (GsmStore   *store,
                           const char *id,
                           GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitor *i;
         GsmInhibitorFlag new_inhibited_actions;
 
@@ -1164,12 +2150,12 @@ on_store_inhibitor_added (GsmStore   *store,
 
         i = GSM_INHIBITOR (gsm_store_lookup (store, id));
 
-        new_inhibited_actions = manager->inhibited_actions | gsm_inhibitor_peek_flags (i);
+        new_inhibited_actions = priv->inhibited_actions | gsm_inhibitor_peek_flags (i);
         update_inhibited_actions (manager, new_inhibited_actions);
 
         g_signal_connect_object (i, "vanished", G_CALLBACK (on_inhibitor_vanished), manager, 0);
 
-        gsm_exported_manager_emit_inhibitor_added (manager->skeleton, id);
+        gsm_exported_manager_emit_inhibitor_added (priv->skeleton, id);
 
         update_idle (manager);
 }
@@ -1191,21 +2177,22 @@ on_store_inhibitor_removed (GsmStore   *store,
                             const char *id,
                             GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitorFlag new_inhibited_actions;
 
         g_debug ("GsmManager: Inhibitor removed: %s", id);
 
         new_inhibited_actions = 0;
-        gsm_store_foreach (manager->inhibitors,
+        gsm_store_foreach (priv->inhibitors,
                            collect_inhibition_flags,
                            &new_inhibited_actions);
         update_inhibited_actions (manager, new_inhibited_actions);
 
-        gsm_exported_manager_emit_inhibitor_removed (manager->skeleton, id);
+        gsm_exported_manager_emit_inhibitor_removed (priv->skeleton, id);
 
         update_idle (manager);
 
-        if (manager->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+        if (priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
                 end_session_or_show_shell_dialog (manager);
         }
 }
@@ -1214,51 +2201,57 @@ static void
 gsm_manager_dispose (GObject *object)
 {
         GsmManager *manager = GSM_MANAGER (object);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
 
         g_debug ("GsmManager: disposing manager");
 
-        g_clear_object (&manager->end_session_cancellable);
-        g_clear_pointer (&manager->session_name, g_free);
+        g_clear_object (&priv->end_session_cancellable);
+        g_clear_pointer (&priv->session_name, g_free);
 
-        if (manager->clients != NULL) {
-                g_signal_handlers_disconnect_by_func (manager->clients,
+        if (priv->clients != NULL) {
+                g_signal_handlers_disconnect_by_func (priv->clients,
                                                       on_store_client_added,
                                                       manager);
-                g_signal_handlers_disconnect_by_func (manager->clients,
+                g_signal_handlers_disconnect_by_func (priv->clients,
                                                       on_store_client_removed,
                                                       manager);
-                g_object_unref (manager->clients);
-                manager->clients = NULL;
+                g_object_unref (priv->clients);
+                priv->clients = NULL;
         }
 
-        g_clear_object (&manager->apps);
+        g_clear_object (&priv->apps);
+        g_slist_free (priv->required_apps);
+        priv->required_apps = NULL;
+        g_slist_free (priv->pending_apps);
+        priv->pending_apps = NULL;
 
-        if (manager->inhibitors != NULL) {
-                g_signal_handlers_disconnect_by_func (manager->inhibitors,
+        if (priv->inhibitors != NULL) {
+                g_signal_handlers_disconnect_by_func (priv->inhibitors,
                                                       on_store_inhibitor_added,
                                                       manager);
-                g_signal_handlers_disconnect_by_func (manager->inhibitors,
+                g_signal_handlers_disconnect_by_func (priv->inhibitors,
                                                       on_store_inhibitor_removed,
                                                       manager);
 
-                g_object_unref (manager->inhibitors);
-                manager->inhibitors = NULL;
+                g_object_unref (priv->inhibitors);
+                priv->inhibitors = NULL;
         }
 
-        g_clear_object (&manager->presence);
-        g_clear_object (&manager->settings);
-        g_clear_object (&manager->session_settings);
-        g_clear_object (&manager->lockdown_settings);
-        g_clear_object (&manager->system);
-        g_clear_object (&manager->shell);
+        g_clear_object (&priv->presence);
+        g_clear_object (&priv->settings);
+        g_clear_object (&priv->session_settings);
+        g_clear_object (&priv->screensaver_settings);
+        g_clear_object (&priv->lockdown_settings);
+        g_clear_object (&priv->system);
+        g_clear_object (&priv->shell);
 
-        if (manager->skeleton != NULL) {
-                g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (manager->skeleton),
-                                                                    manager->connection);
-                g_clear_object (&manager->skeleton);
+        if (priv->skeleton != NULL) {
+                g_dbus_interface_skeleton_unexport_from_connection (G_DBUS_INTERFACE_SKELETON (priv->skeleton),
+                                                                    priv->connection);
+                g_clear_object (&priv->skeleton);
         }
 
-        g_clear_object (&manager->connection);
+        g_clear_object (&priv->connection);
 
         G_OBJECT_CLASS (gsm_manager_parent_class)->dispose (object);
 }
@@ -1273,6 +2266,29 @@ gsm_manager_class_init (GsmManagerClass *klass)
         object_class->constructor = gsm_manager_constructor;
         object_class->dispose = gsm_manager_dispose;
 
+        signals [PHASE_CHANGED] =
+                g_signal_new ("phase-changed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmManagerClass, phase_changed),
+                              NULL, NULL, NULL,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
+
+        g_object_class_install_property (object_class,
+                                         PROP_FAILSAFE,
+                                         g_param_spec_boolean ("failsafe",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        /**
+         * GsmManager::session-name
+         *
+         * Then name of the currently active session, typically "gnome" or "gnome-fallback".
+         * This may be the name of the configured default session, or the name of a fallback
+         * session in case we fell back.
+         */
         g_object_class_install_property (object_class,
                                          PROP_SESSION_NAME,
                                          g_param_spec_string ("session-name",
@@ -1280,6 +2296,36 @@ gsm_manager_class_init (GsmManagerClass *klass)
                                                               NULL,
                                                               NULL,
                                                               G_PARAM_READABLE));
+
+        /**
+         * GsmManager::fallback
+         *
+         * If %TRUE, the current session is running in the "fallback" mode;
+         * this is distinct from whether or not it was configured as default.
+         */
+        g_object_class_install_property (object_class,
+                                         PROP_FALLBACK,
+                                         g_param_spec_boolean ("fallback",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (object_class,
+                                         PROP_CLIENT_STORE,
+                                         g_param_spec_object ("client-store",
+                                                              NULL,
+                                                              NULL,
+                                                              GSM_TYPE_STORE,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (object_class,
+                                         PROP_SYSTEMD_MANAGED,
+                                         g_param_spec_boolean ("systemd-managed",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
@@ -1298,20 +2344,23 @@ on_presence_status_changed (GsmPresence  *presence,
 static void
 on_gsm_system_active_changed (GsmSystem  *system,
                               GParamSpec *pspec,
-                              GsmManager *manager)
+                              GsmManager *self)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (self);
         gboolean is_active;
 
-        is_active = gsm_system_is_active (manager->system);
+        is_active = gsm_system_is_active (priv->system);
 
         g_debug ("emitting SessionIsActive");
-        gsm_exported_manager_set_session_is_active (manager->skeleton, is_active);
+        gsm_exported_manager_set_session_is_active (priv->skeleton, is_active);
 }
 
 static gboolean
 _log_out_is_locked_down (GsmManager *manager)
 {
-        return g_settings_get_boolean (manager->lockdown_settings,
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        return g_settings_get_boolean (priv->lockdown_settings,
                                        KEY_DISABLE_LOG_OUT);
 }
 
@@ -1328,16 +2377,60 @@ complete_end_session_task (GsmManager            *manager,
                 g_dbus_method_invocation_return_value (invocation, NULL);
 }
 
+static void
+request_reboot (GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_debug ("GsmManager: requesting reboot");
+
+        /* FIXME: We need to support a more structured shutdown here,
+         * but that's blocking on an improved ConsoleKit api.
+         *
+         * See https://bugzilla.gnome.org/show_bug.cgi?id=585614
+         */
+        priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_INTERACT;
+        end_phase (manager);
+}
+
+static void
+request_shutdown (GsmManager *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_debug ("GsmManager: requesting shutdown");
+
+        /* See the comment in request_reboot() for some more details about
+         * what work needs to be done here. */
+        priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT;
+        end_phase (manager);
+}
+
+static void
+request_logout (GsmManager           *manager,
+                GsmManagerLogoutMode  mode)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        g_debug ("GsmManager: requesting logout");
+
+        priv->logout_mode = mode;
+        priv->logout_type = GSM_MANAGER_LOGOUT_LOGOUT;
+
+        end_phase (manager);
+}
+
 static gboolean
 gsm_manager_shutdown (GsmExportedManager    *skeleton,
                       GDBusMethodInvocation *invocation,
                       GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GTask *task;
 
         g_debug ("GsmManager: Shutdown called");
 
-        if (manager->phase < GSM_MANAGER_PHASE_RUNNING) {
+        if (priv->phase < GSM_MANAGER_PHASE_RUNNING) {
                 g_dbus_method_invocation_return_error (invocation,
                                                        GSM_MANAGER_ERROR,
                                                        GSM_MANAGER_ERROR_NOT_IN_RUNNING,
@@ -1353,15 +2446,12 @@ gsm_manager_shutdown (GsmExportedManager    *skeleton,
                 return TRUE;
         }
 
-        task = g_task_new (manager, manager->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, invocation);
+        task = g_task_new (manager, priv->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, invocation);
 
-        manager->pending_end_session_tasks = g_slist_prepend (manager->pending_end_session_tasks,
+        priv->pending_end_session_tasks = g_slist_prepend (priv->pending_end_session_tasks,
                                                                     task);
 
-        g_debug ("GsmManager: requesting shutdown");
-
-        manager->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN;
-        end_phase (manager);
+        request_shutdown (manager);
 
         return TRUE;
 }
@@ -1371,11 +2461,12 @@ gsm_manager_reboot (GsmExportedManager    *skeleton,
                     GDBusMethodInvocation *invocation,
                     GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GTask *task;
 
         g_debug ("GsmManager: Reboot called");
 
-        if (manager->phase < GSM_MANAGER_PHASE_RUNNING) {
+        if (priv->phase < GSM_MANAGER_PHASE_RUNNING) {
                 g_dbus_method_invocation_return_error (invocation,
                                                        GSM_MANAGER_ERROR,
                                                        GSM_MANAGER_ERROR_NOT_IN_RUNNING,
@@ -1391,15 +2482,12 @@ gsm_manager_reboot (GsmExportedManager    *skeleton,
                 return TRUE;
         }
 
-        task = g_task_new (manager, manager->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, invocation);
+        task = g_task_new (manager, priv->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, invocation);
 
-        manager->pending_end_session_tasks = g_slist_prepend (manager->pending_end_session_tasks,
+        priv->pending_end_session_tasks = g_slist_prepend (priv->pending_end_session_tasks,
                                                                     task);
 
-        g_debug ("GsmManager: requesting reboot");
-
-        manager->logout_type = GSM_MANAGER_LOGOUT_REBOOT;
-        end_phase (manager);
+        request_reboot (manager);
 
         return TRUE;
 }
@@ -1409,15 +2497,16 @@ gsm_manager_can_shutdown (GsmExportedManager    *skeleton,
                           GDBusMethodInvocation *invocation,
                           GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         gboolean shutdown_available;
 
         g_debug ("GsmManager: CanShutdown called");
 
         shutdown_available = !_log_out_is_locked_down (manager) &&
-                (gsm_system_can_stop (manager->system)
-                 || gsm_system_can_restart (manager->system)
-                 || gsm_system_can_suspend (manager->system)
-                 || gsm_system_can_hibernate (manager->system));
+                (gsm_system_can_stop (priv->system)
+                 || gsm_system_can_restart (priv->system)
+                 || gsm_system_can_suspend (priv->system)
+                 || gsm_system_can_hibernate (priv->system));
 
         gsm_exported_manager_complete_can_shutdown (skeleton, invocation, shutdown_available);
 
@@ -1429,12 +2518,13 @@ gsm_manager_can_reboot_to_firmware_setup (GsmExportedManager    *skeleton,
                                           GDBusMethodInvocation *invocation,
                                           GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         gboolean reboot_to_firmware_available;
 
         g_debug ("GsmManager: CanRebootToFirmwareSetup called");
 
         reboot_to_firmware_available = !_log_out_is_locked_down (manager) &&
-                gsm_system_can_restart_to_firmware_setup (manager->system);
+                gsm_system_can_restart_to_firmware_setup (priv->system);
 
         gsm_exported_manager_complete_can_reboot_to_firmware_setup (skeleton, invocation, reboot_to_firmware_available);
 
@@ -1447,9 +2537,11 @@ gsm_manager_set_reboot_to_firmware_setup (GsmExportedManager    *skeleton,
                                           gboolean               enable,
                                           GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: SetRebootToFirmwareSetup called");
 
-        gsm_system_set_restart_to_firmware_setup (manager->system, enable);
+        gsm_system_set_restart_to_firmware_setup (priv->system, enable);
 
         gsm_exported_manager_complete_set_reboot_to_firmware_setup (skeleton, invocation);
 
@@ -1463,11 +2555,13 @@ gsm_manager_setenv (GsmExportedManager    *skeleton,
                     const char            *value,
                     GsmManager            *manager)
 {
-        if (manager->phase > GSM_MANAGER_PHASE_INITIALIZATION) {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->phase > GSM_MANAGER_PHASE_INITIALIZATION) {
                 g_dbus_method_invocation_return_error (invocation,
                                                        GSM_MANAGER_ERROR,
                                                        GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
-                                                       "Setenv interface is only available during the Initialization phase");
+                                                       "Setenv interface is only available during the DisplayServer and Initialization phase");
         } else {
                 gsm_util_setenv (variable, value);
                 gsm_exported_manager_complete_setenv (skeleton, invocation);
@@ -1481,23 +2575,32 @@ gsm_manager_initialized (GsmExportedManager    *skeleton,
                          GDBusMethodInvocation *invocation,
                          GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         /* Signaled by helper when gnome-session-initialized.target is reached. */
-        if (manager->systemd_initialized) {
+        if (!priv->systemd_managed) {
+                g_dbus_method_invocation_return_error (invocation,
+                                                       GSM_MANAGER_ERROR,
+                                                       GSM_MANAGER_ERROR_GENERAL,
+                                                       "Initialized interface is only available when gnome-session is managed by systemd");
+        } else if (priv->systemd_initialized) {
                 g_dbus_method_invocation_return_error (invocation,
                                                        GSM_MANAGER_ERROR,
                                                        GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
                                                        "Systemd initialization was already signaled");
-        } else if (manager->phase > GSM_MANAGER_PHASE_INITIALIZATION) {
+        } else if (priv->phase > GSM_MANAGER_PHASE_INITIALIZATION) {
                 g_dbus_method_invocation_return_error (invocation,
                                                        GSM_MANAGER_ERROR,
                                                        GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
                                                        "Initialized interface is only available during startup");
         } else {
-                manager->systemd_initialized = TRUE;
+                priv->systemd_initialized = TRUE;
 
-                g_assert (manager->phase == GSM_MANAGER_PHASE_INITIALIZATION);
-                manager->phase++;
-                start_phase (manager);
+                if (priv->manager_initialized) {
+                        g_assert (priv->phase == GSM_MANAGER_PHASE_INITIALIZATION);
+                        priv->phase++;
+                        start_phase (manager);
+                }
 
                 gsm_exported_manager_complete_initialized (skeleton, invocation);
         }
@@ -1574,7 +2677,9 @@ gsm_manager_initialization_error (GsmExportedManager    *skeleton,
                                   gboolean               fatal,
                                   GsmManager            *manager)
 {
-        if (manager->phase != GSM_MANAGER_PHASE_INITIALIZATION) {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->phase != GSM_MANAGER_PHASE_INITIALIZATION) {
                 g_dbus_method_invocation_return_error (invocation,
                                                        GSM_MANAGER_ERROR,
                                                        GSM_MANAGER_ERROR_NOT_IN_INITIALIZATION,
@@ -1588,12 +2693,29 @@ gsm_manager_initialization_error (GsmExportedManager    *skeleton,
         return TRUE;
 }
 
+static void
+user_logout (GsmManager           *manager,
+             GsmManagerLogoutMode  mode)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+                priv->logout_mode = mode;
+                end_session_or_show_shell_dialog (manager);
+                return;
+        }
+
+        request_logout (manager, mode);
+}
+
 gboolean
 gsm_manager_logout (GsmManager *manager,
                     guint logout_mode,
                     GError **error)
 {
-        if (manager->phase < GSM_MANAGER_PHASE_RUNNING) {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->phase < GSM_MANAGER_PHASE_RUNNING) {
                 g_set_error (error,
                              GSM_MANAGER_ERROR,
                              GSM_MANAGER_ERROR_NOT_IN_RUNNING,
@@ -1611,13 +2733,9 @@ gsm_manager_logout (GsmManager *manager,
 
         switch (logout_mode) {
         case GSM_MANAGER_LOGOUT_MODE_NORMAL:
-                g_debug ("GsmManager: requesting logout");
-                break;
         case GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION:
-                g_debug ("GsmManager: requesting no-confirmation logout");
-                break;
         case GSM_MANAGER_LOGOUT_MODE_FORCE:
-                g_debug ("GsmManager: requesting forced logout");
+                user_logout (manager, logout_mode);
                 break;
 
         default:
@@ -1630,18 +2748,6 @@ gsm_manager_logout (GsmManager *manager,
                 return FALSE;
         }
 
-        manager->logout_mode = logout_mode;
-
-        if (manager->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
-                /* Someone can upgrade a normal logout to a forced logout
-                 * while we're busy prompting. In this case, re-evaluate */
-                end_session_or_show_shell_dialog (manager);
-                return TRUE;
-        }
-
-        manager->logout_type = GSM_MANAGER_LOGOUT_LOGOUT;
-        end_phase (manager);
-
         return TRUE;
 }
 
@@ -1668,17 +2774,21 @@ static gboolean
 gsm_manager_register_client (GsmExportedManager    *skeleton,
                              GDBusMethodInvocation *invocation,
                              const char            *app_id,
-                             const char            *ignored, /* was startup_id */
+                             const char            *startup_id,
                              GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        char       *new_startup_id;
         const char *sender;
         GsmClient  *client;
+        GsmApp     *app;
 
+        app = NULL;
         client = NULL;
 
-        g_debug ("GsmManager: RegisterClient %s", app_id);
+        g_debug ("GsmManager: RegisterClient %s", startup_id);
 
-        if (manager->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
+        if (priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
                 g_debug ("Unable to register client: shutting down");
 
                 g_dbus_method_invocation_return_error (invocation,
@@ -1688,8 +2798,39 @@ gsm_manager_register_client (GsmExportedManager    *skeleton,
                 return TRUE;
         }
 
+        if (IS_STRING_EMPTY (startup_id)) {
+                new_startup_id = gsm_util_generate_startup_id ();
+        } else {
+
+                client = (GsmClient *)gsm_store_find (priv->clients,
+                                                      (GsmStoreFunc)_client_has_startup_id,
+                                                      (char *)startup_id);
+                /* We can't have two clients with the same startup id. */
+                if (client != NULL) {
+                        g_debug ("Unable to register client: already registered");
+
+                        g_dbus_method_invocation_return_error (invocation,
+                                                               GSM_MANAGER_ERROR,
+                                                               GSM_MANAGER_ERROR_ALREADY_REGISTERED,
+                                                               "Unable to register client");
+                        return TRUE;
+                }
+
+                new_startup_id = g_strdup (startup_id);
+        }
+
+        g_debug ("GsmManager: Adding new client %s to session", new_startup_id);
+
+        if (app == NULL && !IS_STRING_EMPTY (startup_id)) {
+                app = find_app_for_startup_id (manager, startup_id);
+        }
+        if (app == NULL && !IS_STRING_EMPTY (app_id)) {
+                /* try to associate this app id with a known app */
+                app = find_app_for_app_id (manager, app_id);
+        }
+
         sender = g_dbus_method_invocation_get_sender (invocation);
-        client = gsm_client_new (app_id, sender);
+        client = gsm_dbus_client_new (new_startup_id, sender);
         if (client == NULL) {
                 g_debug ("Unable to create client");
 
@@ -1700,14 +2841,29 @@ gsm_manager_register_client (GsmExportedManager    *skeleton,
                 return TRUE;
         }
 
-        gsm_store_add (manager->clients, gsm_client_peek_id (client), G_OBJECT (client));
-        g_object_unref (client); /* the store will own the ref */
+        gsm_store_add (priv->clients, gsm_client_peek_id (client), G_OBJECT (client));
+        /* the store will own the ref */
+        g_object_unref (client);
 
         g_signal_connect (client,
                           "disconnected",
                           G_CALLBACK (on_client_disconnected),
                           manager);
 
+        if (app != NULL) {
+                gsm_client_set_app_id (client, gsm_app_peek_app_id (app));
+                gsm_app_set_registered (app, TRUE);
+        } else {
+                /* if an app id is specified store it in the client
+                   so we can save it later */
+                gsm_client_set_app_id (client, app_id);
+        }
+
+        gsm_client_set_status (client, GSM_CLIENT_REGISTERED);
+
+        g_assert (new_startup_id != NULL);
+        g_free (new_startup_id);
+
         gsm_exported_manager_complete_register_client (skeleton, invocation, gsm_client_peek_id (client));
 
         return TRUE;
@@ -1719,8 +2875,28 @@ gsm_manager_unregister_client (GsmExportedManager    *skeleton,
                                const char            *client_id,
                                GsmManager            *manager)
 {
-        g_debug ("GsmManager: UnregisterClient %s (ignoring)", client_id);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GsmClient *client;
+
+        g_debug ("GsmManager: UnregisterClient %s", client_id);
+
+        client = (GsmClient *)gsm_store_lookup (priv->clients, client_id);
+        if (client == NULL) {
+                g_debug ("Unable to unregister client: not registered");
+
+                g_dbus_method_invocation_return_error (invocation,
+                                                       GSM_MANAGER_ERROR,
+                                                       GSM_MANAGER_ERROR_NOT_REGISTERED,
+                                                       "Unable to unregister client");
+                return TRUE;
+        }
+
+        /* don't disconnect client here, only change the status.
+           Wait until it leaves the bus before disconnecting it */
+        gsm_client_set_status (client, GSM_CLIENT_UNREGISTERED);
+
         gsm_exported_manager_complete_unregister_client (skeleton, invocation);
+
         return TRUE;
 }
 
@@ -1728,41 +2904,62 @@ static gboolean
 gsm_manager_inhibit (GsmExportedManager    *skeleton,
                      GDBusMethodInvocation *invocation,
                      const char            *app_id,
-                     guint                  unused, /* Was an X window ID */
+                     guint                  toplevel_xid,
                      const char            *reason,
                      guint                  flags,
                      GsmManager            *manager)
 {
-        const char *validation_error = NULL;
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitor *inhibitor;
         guint         cookie;
 
-        g_debug ("GsmManager: Inhibit app_id=%s reason=%s flags=%u",
-                 app_id, reason, flags);
+        g_debug ("GsmManager: Inhibit xid=%u app_id=%s reason=%s flags=%u",
+                 toplevel_xid,
+                 app_id,
+                 reason,
+                 flags);
 
-        if (manager->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE)
-                validation_error = "Forced logout cannot be inhibited";
-        else if (IS_STRING_EMPTY (reason))
-                validation_error = "Reason not specified";
-        else if (flags == 0)
-                validation_error = "Invalid inhibit flags";
+        if (priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) {
+                GError *new_error;
 
-        if (validation_error != NULL) {
-                g_debug ("GsmManager: Unable to inhibit: %s", validation_error);
-                g_dbus_method_invocation_return_error (invocation,
-                                                       GSM_MANAGER_ERROR,
-                                                       GSM_MANAGER_ERROR_GENERAL,
-                                                       "%s", validation_error);
+                new_error = g_error_new (GSM_MANAGER_ERROR,
+                                         GSM_MANAGER_ERROR_GENERAL,
+                                         "Forced logout cannot be inhibited");
+                g_debug ("GsmManager: Unable to inhibit: %s", new_error->message);
+                g_dbus_method_invocation_take_error (invocation, new_error);
+                return TRUE;
+        }
+
+        if (IS_STRING_EMPTY (reason)) {
+                GError *new_error;
+
+                new_error = g_error_new (GSM_MANAGER_ERROR,
+                                         GSM_MANAGER_ERROR_GENERAL,
+                                         "Reason not specified");
+                g_debug ("GsmManager: Unable to inhibit: %s", new_error->message);
+                g_dbus_method_invocation_take_error (invocation, new_error);
+                return TRUE;
+        }
+
+        if (flags == 0) {
+                GError *new_error;
+
+                new_error = g_error_new (GSM_MANAGER_ERROR,
+                                         GSM_MANAGER_ERROR_GENERAL,
+                                         "Invalid inhibit flags");
+                g_debug ("GsmManager: Unable to inhibit: %s", new_error->message);
+                g_dbus_method_invocation_take_error (invocation, new_error);
                 return TRUE;
         }
 
         cookie = _generate_unique_cookie (manager);
         inhibitor = gsm_inhibitor_new (app_id,
+                                       toplevel_xid,
                                        flags,
                                        reason,
                                        g_dbus_method_invocation_get_sender (invocation),
                                        cookie);
-        gsm_store_add (manager->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
+        gsm_store_add (priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor));
         g_object_unref (inhibitor);
 
         gsm_exported_manager_complete_inhibit (skeleton, invocation, cookie);
@@ -1776,11 +2973,12 @@ gsm_manager_uninhibit (GsmExportedManager    *skeleton,
                        guint                  cookie,
                        GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitor *inhibitor;
 
         g_debug ("GsmManager: Uninhibit %u", cookie);
 
-        inhibitor = (GsmInhibitor *)gsm_store_find (manager->inhibitors,
+        inhibitor = (GsmInhibitor *)gsm_store_find (priv->inhibitors,
                                                     (GsmStoreFunc)_find_by_cookie,
                                                     &cookie);
         if (inhibitor == NULL) {
@@ -1794,13 +2992,14 @@ gsm_manager_uninhibit (GsmExportedManager    *skeleton,
                 return TRUE;
         }
 
-        g_debug ("GsmManager: removing inhibitor %s reason '%s' %u connection %s",
+        g_debug ("GsmManager: removing inhibitor %s %u reason '%s' %u connection %s",
                  gsm_inhibitor_peek_app_id (inhibitor),
+                 gsm_inhibitor_peek_toplevel_xid (inhibitor),
                  gsm_inhibitor_peek_reason (inhibitor),
                  gsm_inhibitor_peek_flags (inhibitor),
                  gsm_inhibitor_peek_bus_name (inhibitor));
 
-        gsm_store_remove (manager->inhibitors, gsm_inhibitor_peek_id (inhibitor));
+        gsm_store_remove (priv->inhibitors, gsm_inhibitor_peek_id (inhibitor));
 
         gsm_exported_manager_complete_uninhibit (skeleton, invocation);
 
@@ -1813,14 +3012,15 @@ gsm_manager_is_inhibited (GsmExportedManager    *skeleton,
                           guint                  flags,
                           GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmInhibitor *inhibitor;
         gboolean is_inhibited;
 
-        if (manager->inhibitors == NULL
-            || gsm_store_size (manager->inhibitors) == 0) {
+        if (priv->inhibitors == NULL
+            || gsm_store_size (priv->inhibitors) == 0) {
                 is_inhibited = FALSE;
         } else {
-                inhibitor = (GsmInhibitor *) gsm_store_find (manager->inhibitors,
+                inhibitor = (GsmInhibitor *) gsm_store_find (priv->inhibitors,
                                                              (GsmStoreFunc)inhibitor_has_flag,
                                                              GUINT_TO_POINTER (flags));
                 if (inhibitor == NULL) {
@@ -1844,15 +3044,37 @@ listify_store_ids (char       *id,
         return FALSE;
 }
 
+static gboolean
+gsm_manager_get_clients (GsmExportedManager    *skeleton,
+                         GDBusMethodInvocation *invocation,
+                         GsmManager            *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GPtrArray *clients;
+
+        clients = g_ptr_array_new_with_free_func (g_free);
+        gsm_store_foreach (priv->clients,
+                           (GsmStoreFunc) listify_store_ids,
+                           &clients);
+        g_ptr_array_add (clients, NULL);
+
+        gsm_exported_manager_complete_get_clients (skeleton, invocation,
+                                                   (const gchar * const *) clients->pdata);
+        g_ptr_array_unref (clients);
+
+        return TRUE;
+}
+
 static gboolean
 gsm_manager_get_inhibitors (GsmExportedManager    *skeleton,
                             GDBusMethodInvocation *invocation,
                             GsmManager            *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GPtrArray *inhibitors;
 
         inhibitors = g_ptr_array_new_with_free_func (g_free);
-        gsm_store_foreach (manager->inhibitors,
+        gsm_store_foreach (priv->inhibitors,
                            (GsmStoreFunc) listify_store_ids,
                            &inhibitors);
         g_ptr_array_add (inhibitors, NULL);
@@ -1864,18 +3086,54 @@ gsm_manager_get_inhibitors (GsmExportedManager    *skeleton,
         return TRUE;
 }
 
+static gboolean
+_app_has_autostart_condition (const char *id,
+                              GsmApp     *app,
+                              const char *condition)
+{
+        gboolean has;
+        gboolean disabled;
+
+        has = gsm_app_has_autostart_condition (app, condition);
+        disabled = gsm_app_peek_is_disabled (app);
+
+        return has && !disabled;
+}
+
+static gboolean
+gsm_manager_is_autostart_condition_handled (GsmExportedManager    *skeleton,
+                                            GDBusMethodInvocation *invocation,
+                                            const char            *condition,
+                                            GsmManager            *manager)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        GsmApp *app;
+        gboolean handled;
+
+        app = (GsmApp *) gsm_store_find (priv->apps,(
+                                         GsmStoreFunc) _app_has_autostart_condition,
+                                         (char *)condition);
+
+        if (app != NULL) {
+                handled = TRUE;
+        } else {
+                handled = FALSE;
+        }
+
+        gsm_exported_manager_complete_is_autostart_condition_handled (skeleton, invocation, handled);
+
+        return TRUE;
+}
+
 static gboolean
 gsm_manager_is_session_running (GsmExportedManager    *skeleton,
                                 GDBusMethodInvocation *invocation,
                                 GsmManager            *manager)
 {
-        gboolean running;
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
 
-        running = (manager->phase == GSM_MANAGER_PHASE_APPLICATION ||
-                   manager->phase == GSM_MANAGER_PHASE_RUNNING ||
-                   manager->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION);
-
-        gsm_exported_manager_complete_is_session_running (skeleton, invocation, running);
+        gsm_exported_manager_complete_is_session_running (skeleton, invocation,
+                                                          priv->phase == GSM_MANAGER_PHASE_RUNNING);
         return TRUE;
 }
 
@@ -1885,15 +3143,21 @@ on_session_connection_closed (GDBusConnection *connection,
                               GError *error,
                               gpointer user_data)
 {
-        GsmManager *manager = GSM_MANAGER (user_data);
+        GsmManager *manager;
+        GsmManagerPrivate *priv;
+
+        manager = GSM_MANAGER (user_data);
+        priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: dbus disconnected; disconnecting dbus clients...");
-        manager->dbus_disconnected = TRUE;
+        priv->dbus_disconnected = TRUE;
         remove_clients_for_connection (manager, NULL);
 }
 
 static gboolean
 register_manager (GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GDBusConnection *connection;
         GsmExportedManager *skeleton;
         GError *error = NULL;
@@ -1923,6 +3187,8 @@ register_manager (GsmManager *manager)
                           G_CALLBACK (gsm_manager_can_reboot_to_firmware_setup), manager);
         g_signal_connect (skeleton, "handle-can-shutdown",
                           G_CALLBACK (gsm_manager_can_shutdown), manager);
+        g_signal_connect (skeleton, "handle-get-clients",
+                          G_CALLBACK (gsm_manager_get_clients), manager);
         g_signal_connect (skeleton, "handle-get-inhibitors",
                           G_CALLBACK (gsm_manager_get_inhibitors), manager);
         g_signal_connect (skeleton, "handle-get-locale",
@@ -1931,6 +3197,8 @@ register_manager (GsmManager *manager)
                           G_CALLBACK (gsm_manager_inhibit), manager);
         g_signal_connect (skeleton, "handle-initialization-error",
                           G_CALLBACK (gsm_manager_initialization_error), manager);
+        g_signal_connect (skeleton, "handle-is-autostart-condition-handled",
+                          G_CALLBACK (gsm_manager_is_autostart_condition_handled), manager);
         g_signal_connect (skeleton, "handle-is-inhibited",
                           G_CALLBACK (gsm_manager_is_inhibited), manager);
         g_signal_connect (skeleton, "handle-is-session-running",
@@ -1954,18 +3222,18 @@ register_manager (GsmManager *manager)
         g_signal_connect (skeleton, "handle-unregister-client",
                           G_CALLBACK (gsm_manager_unregister_client), manager);
 
-        manager->dbus_disconnected = FALSE;
+        priv->dbus_disconnected = FALSE;
         g_signal_connect (connection, "closed",
                           G_CALLBACK (on_session_connection_closed), manager);
 
-        manager->connection = connection;
-        manager->skeleton = skeleton;
+        priv->connection = connection;
+        priv->skeleton = skeleton;
 
-        g_signal_connect (manager->system, "notify::active",
+        g_signal_connect (priv->system, "notify::active",
                           G_CALLBACK (on_gsm_system_active_changed), manager);
 
         /* cold-plug SessionIsActive */
-        on_gsm_system_active_changed (manager->system, NULL, manager);
+        on_gsm_system_active_changed (priv->system, NULL, manager);
 
         return TRUE;
 }
@@ -1986,50 +3254,43 @@ idle_timeout_get_mapping (GValue *value,
 static void
 gsm_manager_init (GsmManager *manager)
 {
-        manager->settings = g_settings_new (GSM_MANAGER_SCHEMA);
-        manager->session_settings = g_settings_new (SESSION_SCHEMA);
-        manager->lockdown_settings = g_settings_new (LOCKDOWN_SCHEMA);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
 
-        manager->clients = gsm_store_new ();
-        g_signal_connect (manager->clients,
-                          "added",
-                          G_CALLBACK (on_store_client_added),
-                          manager);
-        g_signal_connect (manager->clients,
-                          "removed",
-                          G_CALLBACK (on_store_client_removed),
-                          manager);
+        priv->settings = g_settings_new (GSM_MANAGER_SCHEMA);
+        priv->session_settings = g_settings_new (SESSION_SCHEMA);
+        priv->screensaver_settings = g_settings_new (SCREENSAVER_SCHEMA);
+        priv->lockdown_settings = g_settings_new (LOCKDOWN_SCHEMA);
 
-        manager->inhibitors = gsm_store_new ();
-        g_signal_connect (manager->inhibitors,
+        priv->inhibitors = gsm_store_new ();
+        g_signal_connect (priv->inhibitors,
                           "added",
                           G_CALLBACK (on_store_inhibitor_added),
                           manager);
-        g_signal_connect (manager->inhibitors,
+        g_signal_connect (priv->inhibitors,
                           "removed",
                           G_CALLBACK (on_store_inhibitor_removed),
                           manager);
 
-        manager->apps = gsm_store_new ();
+        priv->apps = gsm_store_new ();
 
-        manager->presence = gsm_presence_new ();
-        g_signal_connect (manager->presence,
+        priv->presence = gsm_presence_new ();
+        g_signal_connect (priv->presence,
                           "status-changed",
                           G_CALLBACK (on_presence_status_changed),
                           manager);
 
-        g_settings_bind_with_mapping (manager->session_settings,
+        g_settings_bind_with_mapping (priv->session_settings,
                                       KEY_IDLE_DELAY,
-                                      manager->presence,
+                                      priv->presence,
                                       "idle-timeout",
                                       G_SETTINGS_BIND_GET,
                                       idle_timeout_get_mapping,
                                       NULL,
                                       NULL, NULL);
 
-        manager->system = gsm_get_system ();
-        manager->shell = gsm_get_shell ();
-        manager->end_session_cancellable = g_cancellable_new ();
+        priv->system = gsm_get_system ();
+        priv->shell = gsm_get_shell ();
+        priv->end_session_cancellable = g_cancellable_new ();
 }
 
 GsmManager *
@@ -2039,18 +3300,23 @@ gsm_manager_get (void)
 }
 
 GsmManager *
-gsm_manager_new (void)
+gsm_manager_new (GsmStore *client_store,
+                 gboolean  failsafe,
+                 gboolean  systemd_managed)
 {
         if (manager_object != NULL) {
                 g_object_ref (manager_object);
         } else {
                 gboolean res;
 
-                manager_object = g_object_new (GSM_TYPE_MANAGER, NULL);
+                manager_object = g_object_new (GSM_TYPE_MANAGER,
+                                               "client-store", client_store,
+                                               "failsafe", failsafe,
+                                               "systemd-managed", systemd_managed,
+                                               NULL);
 
                 g_object_add_weak_pointer (manager_object,
                                            (gpointer *) &manager_object);
-
                 res = register_manager (manager_object);
                 if (! res) {
                         g_object_unref (manager_object);
@@ -2064,16 +3330,37 @@ gsm_manager_new (void)
 static void
 disconnect_shell_dialog_signals (GsmManager *manager)
 {
-        g_clear_signal_handler (&manager->shell_end_session_dialog_canceled_id,
-                                manager->shell);
-        g_clear_signal_handler (&manager->shell_end_session_dialog_open_failed_id,
-                                manager->shell);
-        g_clear_signal_handler (&manager->shell_end_session_dialog_confirmed_logout_id,
-                                manager->shell);
-        g_clear_signal_handler (&manager->shell_end_session_dialog_confirmed_shutdown_id,
-                                manager->shell);
-        g_clear_signal_handler (&manager->shell_end_session_dialog_confirmed_reboot_id,
-                                manager->shell);
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->shell_end_session_dialog_canceled_id != 0) {
+                g_signal_handler_disconnect (priv->shell,
+                                             priv->shell_end_session_dialog_canceled_id);
+                priv->shell_end_session_dialog_canceled_id = 0;
+        }
+
+        if (priv->shell_end_session_dialog_confirmed_logout_id != 0) {
+                g_signal_handler_disconnect (priv->shell,
+                                             priv->shell_end_session_dialog_confirmed_logout_id);
+                priv->shell_end_session_dialog_confirmed_logout_id = 0;
+        }
+
+        if (priv->shell_end_session_dialog_confirmed_shutdown_id != 0) {
+                g_signal_handler_disconnect (priv->shell,
+                                             priv->shell_end_session_dialog_confirmed_shutdown_id);
+                priv->shell_end_session_dialog_confirmed_shutdown_id = 0;
+        }
+
+        if (priv->shell_end_session_dialog_confirmed_reboot_id != 0) {
+                g_signal_handler_disconnect (priv->shell,
+                                             priv->shell_end_session_dialog_confirmed_reboot_id);
+                priv->shell_end_session_dialog_confirmed_reboot_id = 0;
+        }
+
+        if (priv->shell_end_session_dialog_open_failed_id != 0) {
+                g_signal_handler_disconnect (priv->shell,
+                                             priv->shell_end_session_dialog_open_failed_id);
+                priv->shell_end_session_dialog_open_failed_id = 0;
+        }
 }
 
 static void
@@ -2085,20 +3372,27 @@ on_shell_end_session_dialog_canceled (GsmShell   *shell,
 }
 
 static void
-handle_end_session_dialog_confirmation (GsmManager           *manager,
-                                        GsmManagerLogoutType  logout_type)
-{
-        disconnect_shell_dialog_signals (manager);
-
-        if (manager->phase >= GSM_MANAGER_PHASE_END_SESSION)
-                return; /* Already logging out, nothing to do */
-
-        if (manager->logout_type != logout_type) {
-                g_warning ("GsmManager: Shell confirmed unexpected logout type");
-                return; /* Make it obvious that something went wrong */
+_handle_end_session_dialog_response (GsmManager           *manager,
+                                     GsmManagerLogoutType  logout_type)
+{
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        /* Note we're checking for END_SESSION here and
+         * QUERY_END_SESSION in the fallback cases elsewhere.
+         *
+         * That's because they run at different times in the logout
+         * process. The shell combines the inhibit and
+         * confirmation dialogs, so it gets displayed after we've collected
+         * inhibitors. The fallback code has two distinct dialogs, once of
+         * which we can (and do show) before collecting the inhibitors.
+         */
+        if (priv->phase >= GSM_MANAGER_PHASE_END_SESSION) {
+                /* Already shutting down, nothing more to do */
+                return;
         }
 
-        manager->logout_mode = GSM_MANAGER_LOGOUT_MODE_FORCE;
+        priv->logout_mode = GSM_MANAGER_LOGOUT_MODE_FORCE;
+        priv->logout_type = logout_type;
         end_phase (manager);
 }
 
@@ -2106,55 +3400,60 @@ static void
 on_shell_end_session_dialog_confirmed_logout (GsmShell   *shell,
                                               GsmManager *manager)
 {
-        handle_end_session_dialog_confirmation (manager, GSM_MANAGER_LOGOUT_LOGOUT);
+        _handle_end_session_dialog_response (manager, GSM_MANAGER_LOGOUT_LOGOUT);
+        disconnect_shell_dialog_signals (manager);
 }
 
 static void
 on_shell_end_session_dialog_confirmed_shutdown (GsmShell   *shell,
                                                 GsmManager *manager)
 {
-        handle_end_session_dialog_confirmation (manager, GSM_MANAGER_LOGOUT_SHUTDOWN);
+        _handle_end_session_dialog_response (manager, GSM_MANAGER_LOGOUT_SHUTDOWN);
+        disconnect_shell_dialog_signals (manager);
 }
 
 static void
 on_shell_end_session_dialog_confirmed_reboot (GsmShell   *shell,
                                               GsmManager *manager)
 {
-        handle_end_session_dialog_confirmation (manager, GSM_MANAGER_LOGOUT_REBOOT);
+        _handle_end_session_dialog_response (manager, GSM_MANAGER_LOGOUT_REBOOT);
+        disconnect_shell_dialog_signals (manager);
 }
 
 static void
 connect_shell_dialog_signals (GsmManager *manager)
 {
-        if (manager->shell_end_session_dialog_canceled_id != 0)
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
+        if (priv->shell_end_session_dialog_canceled_id != 0)
                 return;
 
-        manager->shell_end_session_dialog_canceled_id =
-                g_signal_connect (manager->shell,
+        priv->shell_end_session_dialog_canceled_id =
+                g_signal_connect (priv->shell,
                                   "end-session-dialog-canceled",
                                   G_CALLBACK (on_shell_end_session_dialog_canceled),
                                   manager);
 
-        manager->shell_end_session_dialog_open_failed_id =
-                g_signal_connect (manager->shell,
+        priv->shell_end_session_dialog_open_failed_id =
+                g_signal_connect (priv->shell,
                                   "end-session-dialog-open-failed",
                                   G_CALLBACK (on_shell_end_session_dialog_canceled),
                                   manager);
 
-        manager->shell_end_session_dialog_confirmed_logout_id =
-                g_signal_connect (manager->shell,
+        priv->shell_end_session_dialog_confirmed_logout_id =
+                g_signal_connect (priv->shell,
                                   "end-session-dialog-confirmed-logout",
                                   G_CALLBACK (on_shell_end_session_dialog_confirmed_logout),
                                   manager);
 
-        manager->shell_end_session_dialog_confirmed_shutdown_id =
-                g_signal_connect (manager->shell,
+        priv->shell_end_session_dialog_confirmed_shutdown_id =
+                g_signal_connect (priv->shell,
                                   "end-session-dialog-confirmed-shutdown",
                                   G_CALLBACK (on_shell_end_session_dialog_confirmed_shutdown),
                                   manager);
 
-        manager->shell_end_session_dialog_confirmed_reboot_id =
-                g_signal_connect (manager->shell,
+        priv->shell_end_session_dialog_confirmed_reboot_id =
+                g_signal_connect (priv->shell,
                                   "end-session-dialog-confirmed-reboot",
                                   G_CALLBACK (on_shell_end_session_dialog_confirmed_reboot),
                                   manager);
@@ -2164,14 +3463,15 @@ static void
 show_shell_end_session_dialog (GsmManager                   *manager,
                                GsmShellEndSessionDialogType  type)
 {
-        if (!gsm_shell_is_running (manager->shell))
-                return;
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
 
-        connect_shell_dialog_signals (manager);
+        if (!gsm_shell_is_running (priv->shell))
+                return;
 
-        gsm_shell_open_end_session_dialog (manager->shell,
+        gsm_shell_open_end_session_dialog (priv->shell,
                                            type,
-                                           manager->inhibitors);
+                                           priv->inhibitors);
+        connect_shell_dialog_signals (manager);
 }
 
 /*
@@ -2185,57 +3485,151 @@ gboolean
 gsm_manager_set_phase (GsmManager      *manager,
                        GsmManagerPhase  phase)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
-        manager->phase = phase;
+        priv->phase = phase;
         return (TRUE);
 }
 
 static void
 append_app (GsmManager *manager,
-            GsmApp     *app)
+            GsmApp     *app,
+            gboolean    is_required)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        const char *id;
         const char *app_id;
         GsmApp     *dup;
 
+        id = gsm_app_peek_id (app);
+        if (IS_STRING_EMPTY (id)) {
+                g_debug ("GsmManager: not adding app: no id");
+                return;
+        }
+
+        dup = (GsmApp *)gsm_store_lookup (priv->apps, id);
+        if (dup != NULL) {
+                g_debug ("GsmManager: not adding app: already added");
+                return;
+        }
+
         app_id = gsm_app_peek_app_id (app);
         if (IS_STRING_EMPTY (app_id)) {
                 g_debug ("GsmManager: not adding app: no app-id");
                 return;
         }
 
-        dup = (GsmApp *) gsm_store_lookup (manager->apps, app_id);
+        dup = find_app_for_app_id (manager, app_id);
         if (dup != NULL) {
                 g_debug ("GsmManager: not adding app: app-id '%s' already exists", app_id);
+
+                if (is_required &&
+                    !g_slist_find (priv->required_apps, dup)) {
+                        g_debug ("GsmManager: making app '%s' required", gsm_app_peek_app_id (dup));
+                        priv->required_apps = g_slist_prepend (priv->required_apps, dup);
+                }
+
                 return;
         }
 
-        gsm_store_add (manager->apps, app_id, G_OBJECT (app));
+        gsm_store_add (priv->apps, id, G_OBJECT (app));
+        if (is_required) {
+                g_debug ("GsmManager: adding required app %s", gsm_app_peek_app_id (app));
+                priv->required_apps = g_slist_prepend (priv->required_apps, app);
+        }
 }
 
-gboolean
-gsm_manager_add_autostart_app (GsmManager *manager,
-                               const char *path)
+static gboolean
+add_autostart_app_internal (GsmManager *manager,
+                            const char *path,
+                            gboolean    is_required)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
         GsmApp  *app;
+        char   **internal_provides;
         GError *error = NULL;
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
         g_return_val_if_fail (path != NULL, FALSE);
 
-        app = gsm_app_new_for_path (path, &error);
+        /* Note: if we cannot add the app because its service is already
+         * provided, because its app-id is taken, or because of any other
+         * reason meaning there is already an app playing its role, then we
+         * should make sure that relevant properties (like
+         * provides/is_required) are set in the pre-existing app if needed. */
+        app = gsm_autostart_app_new (path, priv->systemd_managed, &error);
         if (app == NULL) {
                 g_warning ("%s", error->message);
                 g_clear_error (&error);
                 return FALSE;
         }
 
+        internal_provides = gsm_app_get_provides (app);
+        if (internal_provides) {
+                int i;
+                gboolean provided = FALSE;
+
+                for (i = 0; internal_provides[i] != NULL; i++) {
+                        GsmApp *dup;
+
+                        dup = (GsmApp *)gsm_store_find (priv->apps,
+                                                        (GsmStoreFunc)_find_app_provides,
+                                                        (char *)internal_provides[i]);
+                        if (dup != NULL) {
+                                g_debug ("GsmManager: service '%s' is already provided", internal_provides[i]);
+
+                                if (is_required &&
+                                    !g_slist_find (priv->required_apps, dup)) {
+                                        g_debug ("GsmManager: making app '%s' required", gsm_app_peek_app_id (dup));
+                                        priv->required_apps = g_slist_prepend (priv->required_apps, dup);
+                                }
+
+                                provided = TRUE;
+                                break;
+                        }
+                }
+
+                g_strfreev (internal_provides);
+
+                if (provided) {
+                        g_object_unref (app);
+                        return FALSE;
+                }
+        }
+
         g_debug ("GsmManager: read %s", path);
-        append_app (manager, app);
+        append_app (manager, app, is_required);
         g_object_unref (app);
 
         return TRUE;
 }
 
+gboolean
+gsm_manager_add_autostart_app (GsmManager *manager,
+                               const char *path)
+{
+        return add_autostart_app_internal (manager, path, FALSE);
+}
+
+/**
+ * gsm_manager_add_required_app:
+ * @manager: a #GsmManager
+ * @path: Path to desktop file
+ *
+ * Similar to gsm_manager_add_autostart_app(), except marks the
+ * component as being required; we then try harder to ensure
+ * it's running and inform the user if we can't.
+ *
+ */
+gboolean
+gsm_manager_add_required_app (GsmManager *manager,
+                              const char *path)
+{
+        return add_autostart_app_internal (manager, path, TRUE);
+}
+
+
 gboolean
 gsm_manager_add_autostart_apps_from_dir (GsmManager *manager,
                                          const char *path)
@@ -2275,17 +3669,19 @@ on_shutdown_prepared (GsmSystem  *system,
                       gboolean    success,
                       GsmManager *manager)
 {
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+
         g_debug ("GsmManager: on_shutdown_prepared, success: %d", success);
         g_signal_handlers_disconnect_by_func (system, on_shutdown_prepared, manager);
 
         if (success) {
                 /* move to end-session phase */
-                g_assert (manager->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION);
-                manager->phase++;
+                g_assert (priv->phase == GSM_MANAGER_PHASE_QUERY_END_SESSION);
+                priv->phase++;
                 start_phase (manager);
         } else {
                 disconnect_shell_dialog_signals (manager);
-                gsm_shell_close_end_session_dialog (manager->shell);
+                gsm_shell_close_end_session_dialog (priv->shell);
                 /* back to running phase */
                 cancel_end_session (manager);
         }
@@ -2294,14 +3690,33 @@ on_shutdown_prepared (GsmSystem  *system,
 static gboolean
 do_query_end_session_exit (GsmManager *manager)
 {
-        if (manager->logout_type != GSM_MANAGER_LOGOUT_SHUTDOWN &&
-            manager->logout_type != GSM_MANAGER_LOGOUT_REBOOT)
-                return TRUE; /* Continue to end session phase */
+        GsmManagerPrivate *priv = gsm_manager_get_instance_private (manager);
+        gboolean reboot = FALSE;
+        gboolean shutdown = FALSE;
+
+        switch (priv->logout_type) {
+        case GSM_MANAGER_LOGOUT_LOGOUT:
+                break;
+        case GSM_MANAGER_LOGOUT_REBOOT:
+        case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
+                reboot = TRUE;
+                break;
+        case GSM_MANAGER_LOGOUT_SHUTDOWN:
+        case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
+                shutdown = TRUE;
+                break;
+        default:
+                g_warning ("Unexpected logout type %d in do_query_end_session_exit()",
+                           priv->logout_type);
+                break;
+        }
 
-        g_signal_connect (manager->system, "shutdown-prepared",
-                          G_CALLBACK (on_shutdown_prepared), manager);
-        gsm_system_prepare_shutdown (manager->system,
-                                     manager->logout_type == GSM_MANAGER_LOGOUT_REBOOT);
+        if (reboot || shutdown) {
+                g_signal_connect (priv->system, "shutdown-prepared",
+                                  G_CALLBACK (on_shutdown_prepared), manager);
+                gsm_system_prepare_shutdown (priv->system, reboot);
+                return FALSE; /* don't leave query end session yet */
+        }
 
-        return FALSE; /* don't leave query end session yet */
+        return TRUE; /* go to end session phase */
 }
diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h
index b358d2b5..3cfd8608 100644
--- a/gnome-session/gsm-manager.h
+++ b/gnome-session/gsm-manager.h
@@ -28,25 +28,41 @@
 
 G_BEGIN_DECLS
 
-/* UUIDs for log messages */
-#define GSM_MANAGER_STARTUP_SUCCEEDED_MSGID     "0ce153587afa4095832d233c17a88001"
-#define GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID "10dd2dc188b54a5e98970f56499d1f73"
-
 #define GSM_TYPE_MANAGER         (gsm_manager_get_type ())
-G_DECLARE_FINAL_TYPE (GsmManager, gsm_manager, GSM, MANAGER, GObject)
+G_DECLARE_DERIVABLE_TYPE (GsmManager, gsm_manager, GSM, MANAGER, GObject)
+
+struct _GsmManagerClass
+{
+        GObjectClass   parent_class;
+
+        void          (* phase_changed)       (GsmManager      *manager,
+                                               const char      *phase);
+};
 
 typedef enum {
-        /* We're waiting for systemd to start session services, the compositor, etc */
-        GSM_MANAGER_PHASE_INITIALIZATION = 0,
-        /* The session is running, but we're still launching the user's autostart apps */
+        /* gsm's own startup/initialization phase */
+        GSM_MANAGER_PHASE_STARTUP = 0,
+        /* gnome-initial-setup */
+        GSM_MANAGER_PHASE_EARLY_INITIALIZATION,
+        /* gnome-keyring-daemon */
+        GSM_MANAGER_PHASE_PRE_DISPLAY_SERVER,
+        /* wayland compositor and XWayland */
+        GSM_MANAGER_PHASE_DISPLAY_SERVER,
+        /* xrandr setup, gnome-settings-daemon, etc */
+        GSM_MANAGER_PHASE_INITIALIZATION,
+        /* window/compositing managers */
+        GSM_MANAGER_PHASE_WINDOW_MANAGER,
+        /* apps that will create _NET_WM_WINDOW_TYPE_PANEL windows */
+        GSM_MANAGER_PHASE_PANEL,
+        /* apps that will create _NET_WM_WINDOW_TYPE_DESKTOP windows */
+        GSM_MANAGER_PHASE_DESKTOP,
+        /* everything else */
         GSM_MANAGER_PHASE_APPLICATION,
-        /* The session is running */
+        /* done launching */
         GSM_MANAGER_PHASE_RUNNING,
-        /* Someone requested for the session to end,  */
+        /* shutting down */
         GSM_MANAGER_PHASE_QUERY_END_SESSION,
-        /* The session is shutting down */
         GSM_MANAGER_PHASE_END_SESSION,
-        /* The session has shut down, and gnome-session is about to exit */
         GSM_MANAGER_PHASE_EXIT
 } GsmManagerPhase;
 
@@ -65,13 +81,20 @@ typedef enum
 #define GSM_MANAGER_ERROR gsm_manager_error_quark ()
 GQuark              gsm_manager_error_quark                    (void);
 
-GsmManager *        gsm_manager_new                            (void);
+GsmManager *        gsm_manager_new                            (GsmStore       *client_store,
+                                                                gboolean        failsafe,
+                                                                gboolean        systemd_managed);
 GsmManager *        gsm_manager_get                            (void);
 
+
+gboolean            gsm_manager_get_failsafe                   (GsmManager     *manager);
 gboolean            gsm_manager_get_dbus_disconnected          (GsmManager *manager);
+gboolean            gsm_manager_get_systemd_managed            (GsmManager     *manager);
 
 gboolean            gsm_manager_add_autostart_app              (GsmManager     *manager,
                                                                 const char     *path);
+gboolean            gsm_manager_add_required_app               (GsmManager     *manager,
+                                                                const char     *path);
 gboolean            gsm_manager_add_autostart_apps_from_dir    (GsmManager     *manager,
                                                                 const char     *path);
 gboolean            gsm_manager_add_legacy_session_apps        (GsmManager     *manager,
@@ -79,8 +102,11 @@ gboolean            gsm_manager_add_legacy_session_apps        (GsmManager     *
 
 void                gsm_manager_start                          (GsmManager     *manager);
 
+char *              _gsm_manager_get_default_session           (GsmManager     *manager);
+
 void                _gsm_manager_set_active_session            (GsmManager     *manager,
-                                                                const char     *session_name);
+                                                                const char     *session_name,
+                                                                gboolean        is_fallback);
 
 gboolean            gsm_manager_logout                         (GsmManager     *manager,
                                                                 guint           logout_mode,
@@ -89,8 +115,6 @@ gboolean            gsm_manager_logout                         (GsmManager     *
 gboolean            gsm_manager_set_phase                      (GsmManager     *manager,
                                                                 GsmManagerPhase phase);
 
-void                gsm_quit                                   (void);
-
 G_END_DECLS
 
 #endif /* __GSM_MANAGER_H */
diff --git a/gnome-session/gsm-process-helper.c b/gnome-session/gsm-process-helper.c
new file mode 100644
index 00000000..32f26c8d
--- /dev/null
+++ b/gnome-session/gsm-process-helper.c
@@ -0,0 +1,113 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Novell, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include "gsm-process-helper.h"
+
+typedef struct {
+        gboolean        done;
+        GSubprocess    *process;
+        gboolean        caught_error;
+        GError        **error;
+        GMainContext   *maincontext;
+        GSource        *timeout_source;
+} GsmProcessHelper;
+
+static void
+on_child_exited (GObject        *source,
+                 GAsyncResult   *result,
+                 gpointer        data)
+{
+        GsmProcessHelper *helper = data;
+
+        helper->done = TRUE;
+
+        if (!g_subprocess_wait_check_finish ((GSubprocess*)source, result,
+                                             helper->caught_error ? NULL : helper->error))
+                helper->caught_error = TRUE;
+
+        g_clear_pointer (&helper->timeout_source, g_source_destroy);
+
+        g_main_context_wakeup (helper->maincontext);
+}
+
+static gboolean
+on_child_timeout (gpointer data)
+{
+        GsmProcessHelper *helper = data;
+        
+        g_assert (!helper->done);
+        
+        g_subprocess_force_exit (helper->process);
+        
+        g_set_error_literal (helper->error,
+                             G_IO_CHANNEL_ERROR,
+                             G_IO_CHANNEL_ERROR_FAILED,
+                             "Timed out");
+
+        helper->timeout_source = NULL;
+        
+        return FALSE;
+}
+
+gboolean
+gsm_process_helper (const char   *command_line,
+                    unsigned int  timeout,
+                    GError      **error)
+{
+        gboolean ret = FALSE;
+        GsmProcessHelper helper = { 0, };
+        gchar **argv = NULL;
+        GMainContext *subcontext = NULL;
+
+        if (!g_shell_parse_argv (command_line, NULL, &argv, error))
+                goto out;
+
+        helper.error = error;
+
+        subcontext = g_main_context_new ();
+        g_main_context_push_thread_default (subcontext);
+
+        helper.process = g_subprocess_newv ((const char*const*)argv, 0, error);
+        if (!helper.process)
+                goto out;
+
+        g_subprocess_wait_async (helper.process, NULL, on_child_exited, &helper);
+
+        helper.timeout_source = g_timeout_source_new (timeout);
+
+        g_source_set_callback (helper.timeout_source, on_child_timeout, &helper, NULL);
+        g_source_attach (helper.timeout_source, subcontext);
+
+        while (!helper.done)
+                g_main_context_iteration (subcontext, TRUE);
+
+        ret = helper.caught_error;
+ out:
+        g_strfreev (argv);
+        if (subcontext) {
+                g_main_context_pop_thread_default (subcontext);
+                g_main_context_unref (subcontext);
+        }
+        g_clear_object (&helper.process);
+        return ret;
+}
diff --git a/gnome-session/gsm-process-helper.h b/gnome-session/gsm-process-helper.h
new file mode 100644
index 00000000..4ca55a95
--- /dev/null
+++ b/gnome-session/gsm-process-helper.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Novell, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSM_PROCESS_HELPER_H
+#define __GSM_PROCESS_HELPER_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+int  gsm_process_helper (const char    *command_line,
+                         unsigned int   timeout,
+                         GError       **error);
+
+G_END_DECLS
+
+#endif /* __GSM_PROCESS_HELPER_H */
diff --git a/gnome-session/gsm-session-fill.c b/gnome-session/gsm-session-fill.c
index b94dec25..d6aeaa50 100644
--- a/gnome-session/gsm-session-fill.c
+++ b/gnome-session/gsm-session-fill.c
@@ -23,36 +23,117 @@
 
 #include "gsm-system.h"
 #include "gsm-manager.h"
+#include "gsm-process-helper.h"
+#include "gsm-util.h"
 
 #define GSM_KEYFILE_SESSION_GROUP "GNOME Session"
+#define GSM_KEYFILE_RUNNABLE_KEY "IsRunnableHelper"
+#define GSM_KEYFILE_FALLBACK_KEY "FallbackSession"
+#define GSM_KEYFILE_REQUIRED_COMPONENTS_KEY "RequiredComponents"
+
+/* See https://bugzilla.gnome.org/show_bug.cgi?id=641992 for discussion */
+#define GSM_RUNNABLE_HELPER_TIMEOUT 3000 /* ms */
+
+typedef void (*GsmFillHandleComponent) (const char *component,
+                                        const char *app_path,
+                                        gpointer    user_data);
 
 static void
-load_standard_apps (GsmManager *manager,
-                    GKeyFile   *keyfile)
+handle_required_components (GKeyFile               *keyfile,
+                            GsmFillHandleComponent  callback,
+                            gpointer                user_data)
 {
-        char *dir;
-        const char * const *system_dirs;
+        char **required_components;
         int    i;
 
-        if (g_key_file_get_boolean (keyfile, GSM_KEYFILE_SESSION_GROUP, "Kiosk", NULL))
+        g_assert (keyfile != NULL);
+        g_assert (callback != NULL);
+
+        required_components = g_key_file_get_string_list (keyfile,
+                                                          GSM_KEYFILE_SESSION_GROUP,
+                                                          GSM_KEYFILE_REQUIRED_COMPONENTS_KEY,
+                                                          NULL, NULL);
+
+        if (!required_components)
                 return;
 
-        dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL);
-        gsm_manager_add_autostart_apps_from_dir (manager, dir);
-        g_free (dir);
+        for (i = 0; required_components[i] != NULL; i++) {
+                char *app_path;
 
-        system_dirs = g_get_system_data_dirs ();
-        for (i = 0; system_dirs[i]; i++) {
-                dir = g_build_filename (system_dirs[i], "gnome", "autostart", NULL);
-                gsm_manager_add_autostart_apps_from_dir (manager, dir);
-                g_free (dir);
+                app_path = gsm_util_find_desktop_file_for_app_name (required_components[i],
+                                                                    TRUE);
+                callback (required_components[i], app_path, user_data);
+                g_free (app_path);
         }
 
-        system_dirs = g_get_system_config_dirs ();
-        for (i = 0; system_dirs[i]; i++) {
-                dir = g_build_filename (system_dirs[i], "autostart", NULL);
-                gsm_manager_add_autostart_apps_from_dir (manager, dir);
-                g_free (dir);
+        g_strfreev (required_components);
+}
+
+static void
+check_required_components_helper (const char *component,
+                                  const char *app_path,
+                                  gpointer    user_data)
+{
+        gboolean *error = user_data;
+
+        if (app_path == NULL) {
+                g_warning ("Unable to find required component '%s'", component);
+                *error = TRUE;
+        }
+}
+
+static gboolean
+check_required (GKeyFile *keyfile)
+{
+        gboolean error = FALSE;
+
+        g_debug ("fill: *** Checking required components");
+
+        handle_required_components (keyfile,
+                                    check_required_components_helper,
+                                    &error);
+
+        g_debug ("fill: *** Done checking required components");
+
+        return !error;
+}
+
+static void
+append_required_components_helper (const char *component,
+                                   const char *app_path,
+                                   gpointer    user_data)
+{
+        GsmManager *manager = user_data;
+
+        if (app_path == NULL)
+                g_warning ("Unable to find required component '%s'", component);
+        else
+                gsm_manager_add_required_app (manager, app_path);
+}
+
+
+static void
+load_standard_apps (GsmManager *manager,
+                    GKeyFile   *keyfile)
+{
+        g_debug ("fill: *** Adding required components");
+        handle_required_components (keyfile,
+                                    append_required_components_helper,
+                                    manager);
+        g_debug ("fill: *** Done adding required components");
+
+        if (!gsm_manager_get_failsafe (manager)) {
+                char **autostart_dirs;
+                int    i;
+
+                autostart_dirs = gsm_util_get_autostart_dirs ();
+
+                for (i = 0; autostart_dirs[i]; i++) {
+                        gsm_manager_add_autostart_apps_from_dir (manager,
+                                                                 autostart_dirs[i]);
+                }
+
+                g_strfreev (autostart_dirs);
         }
 }
 
@@ -60,6 +141,8 @@ static GKeyFile *
 get_session_keyfile_if_valid (const char *path)
 {
         GKeyFile  *keyfile;
+        gsize      len;
+        char     **list;
 
         g_debug ("fill: *** Looking if %s is a valid session file", path);
 
@@ -75,6 +158,15 @@ get_session_keyfile_if_valid (const char *path)
                 goto error;
         }
 
+        list = g_key_file_get_string_list (keyfile,
+                                           GSM_KEYFILE_SESSION_GROUP,
+                                           GSM_KEYFILE_REQUIRED_COMPONENTS_KEY,
+                                           &len, NULL);
+        if (list)
+                g_strfreev (list);
+        if (len == 0)
+                g_warning ("Session '%s': no component in the session.", path);
+
         return keyfile;
 
 error:
@@ -131,11 +223,68 @@ find_valid_session_keyfile (const char *session)
 }
 
 static GKeyFile *
-get_session_keyfile (const char *session)
+get_session_keyfile (const char *session,
+                     char      **actual_session,
+                     gboolean   *is_fallback)
 {
         GKeyFile *keyfile;
+        gboolean  session_runnable;
+        char     *value;
+        GError *error = NULL;
+
+        *actual_session = NULL;
+
         g_debug ("fill: *** Getting session '%s'", session);
+
         keyfile = find_valid_session_keyfile (session);
+
+        if (!keyfile)
+                return NULL;
+
+        session_runnable = TRUE;
+
+        value = g_key_file_get_string (keyfile,
+                                       GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_RUNNABLE_KEY,
+                                       NULL);
+        if (!IS_STRING_EMPTY (value)) {
+                g_debug ("fill: *** Launching helper '%s' to know if session is runnable", value);
+                session_runnable = gsm_process_helper (value, GSM_RUNNABLE_HELPER_TIMEOUT, &error);
+                if (!session_runnable) {
+                        g_warning ("Session '%s' runnable check failed: %s", session,
+                                   error->message);
+                        g_clear_error (&error);
+                }
+        }
+        g_free (value);
+
+        if (session_runnable) {
+                session_runnable = check_required (keyfile);
+        }
+
+        if (session_runnable) {
+                *actual_session = g_strdup (session);
+                if (is_fallback)
+                        *is_fallback = FALSE;
+                return keyfile;
+        }
+
+        g_debug ("fill: *** Session is not runnable");
+
+        /* We can't run this session, so try to use the fallback */
+        value = g_key_file_get_string (keyfile,
+                                       GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_FALLBACK_KEY,
+                                       NULL);
+
+        g_key_file_free (keyfile);
+        keyfile = NULL;
+
+        if (!IS_STRING_EMPTY (value)) {
+                if (is_fallback)
+                        *is_fallback = TRUE;
+                keyfile = get_session_keyfile (value, actual_session, NULL);
+        }
+        g_free (value);
+
         return keyfile;
 }
 
@@ -144,13 +293,17 @@ gsm_session_fill (GsmManager  *manager,
                   const char  *session)
 {
         GKeyFile *keyfile;
+        gboolean is_fallback;
+        char *actual_session;
 
-        keyfile = get_session_keyfile (session);
+        keyfile = get_session_keyfile (session, &actual_session, &is_fallback);
 
         if (!keyfile)
                 return FALSE;
 
-        _gsm_manager_set_active_session (manager, session);
+        _gsm_manager_set_active_session (manager, actual_session, is_fallback);
+
+        g_free (actual_session);
 
         load_standard_apps (manager, keyfile);
 
diff --git a/gnome-session/gsm-shell-extensions.c b/gnome-session/gsm-shell-extensions.c
new file mode 100644
index 00000000..064babe4
--- /dev/null
+++ b/gnome-session/gsm-shell-extensions.c
@@ -0,0 +1,197 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc
+ *
+ * This program 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, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *      Jasper St. Pierre <jstpierre@mecheye.net>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <json-glib/json-glib.h>
+
+#include "gsm-shell-extensions.h"
+
+#define SHELL_SCHEMA "org.gnome.shell"
+#define DISABLE_EXTENSIONS_KEY "disable-user-extensions"
+
+struct _GsmShellExtensionsPrivate
+{
+  GSettings *settings;
+  guint num_extensions;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GsmShellExtensions, gsm_shell_extensions, G_TYPE_OBJECT,
+                         G_ADD_PRIVATE (GsmShellExtensions));
+
+/**
+ * gsm_shell_extensions_finalize:
+ * @object: (in): A #GsmShellExtensions.
+ *
+ * Finalizer for a #GsmShellExtensions instance.  Frees any resources held by
+ * the instance.
+ */
+static void
+gsm_shell_extensions_finalize (GObject *object)
+{
+  GsmShellExtensions *extensions = GSM_SHELL_EXTENSIONS (object);
+  GsmShellExtensionsPrivate *priv = extensions->priv;
+
+  g_clear_object (&priv->settings);
+
+  G_OBJECT_CLASS (gsm_shell_extensions_parent_class)->finalize (object);
+}
+
+/**
+ * gsm_shell_extensions_class_init:
+ * @klass: (in): A #GsmShellExtensionsClass.
+ *
+ * Initializes the #GsmShellExtensionsClass and prepares the vtable.
+ */
+static void
+gsm_shell_extensions_class_init (GsmShellExtensionsClass *klass)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = gsm_shell_extensions_finalize;
+}
+
+static void
+gsm_shell_extensions_scan_dir (GsmShellExtensions *self,
+                               GFile              *dir)
+{
+  GFileEnumerator *enumerator;
+  GFileInfo *info;
+  JsonParser *metadata_parser;
+
+  metadata_parser = json_parser_new ();
+
+  enumerator = g_file_enumerate_children (dir,
+                                          "standard::*",
+                                          G_FILE_QUERY_INFO_NONE,
+                                          NULL,
+                                          NULL);
+
+  if (enumerator == NULL)
+    return;
+
+  while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
+    {
+      gchar *metadata_filename;
+      const gchar *metadata_uuid;
+      gchar *dir_uuid;
+      JsonObject *metadata_root;
+
+      dir_uuid = (char *) g_file_info_get_name (info);
+
+      metadata_filename = g_build_filename (g_file_get_path (dir),
+                                            dir_uuid,
+                                            "metadata.json",
+                                            NULL);
+
+      if (!json_parser_load_from_file (metadata_parser, metadata_filename, NULL))
+        continue;
+
+      g_free (metadata_filename);
+
+      metadata_root = json_node_get_object (json_parser_get_root (metadata_parser));
+
+      metadata_uuid = json_object_get_string_member (metadata_root, "uuid");
+      if (!g_str_equal (metadata_uuid, dir_uuid))
+        {
+          g_warning ("Extension with dirname '%s' does not match metadata's UUID of '%s'. Skipping.",
+                     dir_uuid, metadata_uuid);
+          continue;
+        }
+
+      self->priv->num_extensions++;
+    }
+}
+
+static void
+gsm_shell_extensions_scan (GsmShellExtensions *self)
+{
+  gchar *dirname;
+  GFile *dir;
+  const gchar * const * system_data_dirs;
+  int                   i;
+
+  /* User data dir first. */
+  dirname = g_build_filename (g_get_user_data_dir (), "gnome-shell", "extensions", NULL);
+  dir = g_file_new_for_path (dirname);
+  g_free (dirname);
+
+  gsm_shell_extensions_scan_dir (self, dir);
+  g_object_unref (dir);
+
+  system_data_dirs = g_get_system_data_dirs ();
+  for (i = 0; system_data_dirs[i]; i++)
+    {
+      dirname = g_build_filename (system_data_dirs[i], "gnome-shell", "extensions", NULL);
+      dir = g_file_new_for_path (dirname);
+      g_free (dirname);
+
+      gsm_shell_extensions_scan_dir (self, dir);
+      g_object_unref (dir);
+    }
+}
+
+/**
+ * gsm_shell_extensions_init:
+ * @self: (in): A #GsmShellExtensions.
+ *
+ * Initializes the newly created #GsmShellExtensions instance.
+ */
+static void
+gsm_shell_extensions_init (GsmShellExtensions *self)
+{
+  GSettingsSchemaSource *source;
+  GSettingsSchema *schema;
+
+  self->priv = gsm_shell_extensions_get_instance_private (self);
+
+  source = g_settings_schema_source_get_default ();
+  schema = g_settings_schema_source_lookup (source, SHELL_SCHEMA, TRUE);
+
+  if (schema != NULL)
+    {
+      self->priv->settings = g_settings_new_full (schema, NULL, NULL);
+      g_settings_schema_unref (schema);
+    }
+
+  if (self->priv->settings != NULL)
+    gsm_shell_extensions_scan (self);
+}
+
+gboolean
+gsm_shell_extensions_disable_all (GsmShellExtensions *self)
+{
+  return g_settings_set_boolean (self->priv->settings,
+                                 DISABLE_EXTENSIONS_KEY,
+                                 TRUE);
+}
+
+guint
+gsm_shell_extensions_n_extensions (GsmShellExtensions *self)
+{
+  if (self->priv->settings == NULL)
+    return 0;
+
+  return self->priv->num_extensions;
+}
diff --git a/gnome-session/gsm-shell-extensions.h b/gnome-session/gsm-shell-extensions.h
new file mode 100644
index 00000000..a3b3d063
--- /dev/null
+++ b/gnome-session/gsm-shell-extensions.h
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc
+ *
+ * This program 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, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *      Jasper St. Pierre <jstpierre@mecheye.net>
+ */
+
+#ifndef __GSM_SHELL_EXTENSIONS_H
+#define __GSM_SHELL_EXTENSIONS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GSM_TYPE_SHELL_EXTENSIONS            (gsm_shell_extensions_get_type ())
+#define GSM_SHELL_EXTENSIONS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensions))
+#define GSM_SHELL_EXTENSIONS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsClass))
+#define GSM_IS_SHELL_EXTENSIONS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SHELL_EXTENSIONS))
+#define GSM_IS_SHELL_EXTENSIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GSM_TYPE_SHELL_EXTENSIONS))
+#define GSM_SHELL_EXTENSIONS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsClass))
+
+typedef struct _GsmShellExtensions        GsmShellExtensions;
+typedef struct _GsmShellExtensionsClass   GsmShellExtensionsClass;
+typedef struct _GsmShellExtensionsPrivate GsmShellExtensionsPrivate;
+
+struct _GsmShellExtensions
+{
+    GObject parent;
+
+    /*< private >*/
+    GsmShellExtensionsPrivate *priv;
+};
+
+struct _GsmShellExtensionsClass
+{
+    GObjectClass parent_class;
+};
+
+GType gsm_shell_extensions_get_type                   (void) G_GNUC_CONST;
+
+gboolean gsm_shell_extensions_disable_all             (GsmShellExtensions *self);
+
+guint gsm_shell_extensions_n_extensions               (GsmShellExtensions *self);
+
+G_END_DECLS
+
+#endif /* __GSM_SHELL_EXTENSIONS_H */
diff --git a/gnome-session/gsm-system.c b/gnome-session/gsm-system.c
index dc49a1af..be224244 100644
--- a/gnome-session/gsm-system.c
+++ b/gnome-session/gsm-system.c
@@ -73,6 +73,7 @@ typedef GObject GsmSystemNull;
 typedef GObjectClass GsmSystemNullClass;
 
 static void do_nothing (void) { }
+static gboolean return_true (void) { return TRUE; }
 static gboolean return_false (void) { return FALSE; }
 
 static void
@@ -90,9 +91,11 @@ gsm_system_null_init_iface (GsmSystemInterface *iface)
         iface->suspend           = (void *) do_nothing;
         iface->hibernate         = (void *) do_nothing;
         iface->set_session_idle  = (void *) do_nothing;
+        iface->is_login_session  = (void *) return_true;
         iface->set_inhibitors    = (void *) do_nothing;
         iface->prepare_shutdown  = (void *) do_nothing;
         iface->complete_shutdown = (void *) do_nothing;
+        iface->is_last_session_for_user = (void *) return_false;
 }
 
 static void
@@ -213,6 +216,18 @@ gsm_system_set_inhibitors (GsmSystem        *system,
         GSM_SYSTEM_GET_IFACE (system)->set_inhibitors (system, flags);
 }
 
+gboolean
+gsm_system_is_login_session (GsmSystem *system)
+{
+        return GSM_SYSTEM_GET_IFACE (system)->is_login_session (system);
+}
+
+gboolean
+gsm_system_is_last_session_for_user (GsmSystem *system)
+{
+        return GSM_SYSTEM_GET_IFACE (system)->is_last_session_for_user (system);
+}
+
 /**
  * gsm_system_is_active:
  *
diff --git a/gnome-session/gsm-system.h b/gnome-session/gsm-system.h
index 2f137422..438434cc 100644
--- a/gnome-session/gsm-system.h
+++ b/gnome-session/gsm-system.h
@@ -64,11 +64,13 @@ struct _GsmSystemInterface
         void     (* hibernate)        (GsmSystem *system);
         void     (* set_session_idle) (GsmSystem *system,
                                        gboolean   is_idle);
+        gboolean (* is_login_session) (GsmSystem *system);
         void     (* set_inhibitors)   (GsmSystem        *system,
                                        GsmInhibitorFlag  flags);
         void     (* prepare_shutdown) (GsmSystem   *system,
                                        gboolean     restart);
         void     (* complete_shutdown)(GsmSystem   *system);
+        gboolean (* is_last_session_for_user) (GsmSystem *system);
 };
 
 enum _GsmSystemError {
@@ -108,6 +110,10 @@ void       gsm_system_hibernate        (GsmSystem *system);
 void       gsm_system_set_session_idle (GsmSystem *system,
                                         gboolean   is_idle);
 
+gboolean   gsm_system_is_login_session (GsmSystem *system);
+
+gboolean   gsm_system_is_last_session_for_user (GsmSystem *system);
+
 gboolean   gsm_system_is_active        (GsmSystem *system);
 
 void       gsm_system_set_inhibitors   (GsmSystem        *system,
diff --git a/gnome-session/gsm-systemd.c b/gnome-session/gsm-systemd.c
index 6f5a03d8..9271e7fe 100644
--- a/gnome-session/gsm-systemd.c
+++ b/gnome-session/gsm-systemd.c
@@ -707,6 +707,31 @@ gsm_systemd_can_stop (GsmSystem *system)
         return can_stop;
 }
 
+static gboolean
+gsm_systemd_is_login_session (GsmSystem *system)
+{
+        GsmSystemd *manager = GSM_SYSTEMD (system);
+        int res;
+        gboolean ret;
+        gchar *session_class = NULL;
+
+        ret = FALSE;
+
+        if (manager->priv->session_id == NULL) {
+                return ret;
+        }
+
+        res = sd_session_get_class (manager->priv->session_id, &session_class);
+        if (res < 0) {
+                g_warning ("Could not get session class: %s", strerror (-res));
+                return FALSE;
+        }
+        ret = (g_strcmp0 (session_class, "greeter") == 0);
+        free (session_class);
+
+        return ret;
+}
+
 static gboolean
 gsm_systemd_can_suspend (GsmSystem *system)
 {
@@ -1032,6 +1057,66 @@ gsm_systemd_complete_shutdown (GsmSystem *system)
         drop_delay_inhibitor (systemd);
 }
 
+static gboolean
+gsm_systemd_is_last_session_for_user (GsmSystem *system)
+{
+        char **sessions = NULL;
+        char *session = NULL;
+        gboolean is_last_session;
+        int ret, i;
+
+        if (!gsm_systemd_find_session (&session)) {
+                return FALSE;
+        }
+
+        ret = sd_uid_get_sessions (getuid (), FALSE, &sessions);
+
+        if (ret <= 0) {
+                free (session);
+                return FALSE;
+        }
+
+        is_last_session = TRUE;
+        for (i = 0; sessions[i]; i++) {
+                char *state = NULL;
+                char *type = NULL;
+
+                if (g_strcmp0 (sessions[i], session) == 0)
+                        continue;
+
+                ret = sd_session_get_state (sessions[i], &state);
+
+                if (ret != 0)
+                        continue;
+
+                if (g_strcmp0 (state, "closing") == 0) {
+                        free (state);
+                        continue;
+                }
+                free (state);
+
+                ret = sd_session_get_type (sessions[i], &type);
+
+                if (ret != 0)
+                        continue;
+
+                if (g_strcmp0 (type, "x11") != 0 &&
+                    g_strcmp0 (type, "wayland") != 0) {
+                        free (type);
+                        continue;
+                }
+
+                is_last_session = FALSE;
+        }
+
+        for (i = 0; sessions[i]; i++)
+                free (sessions[i]);
+        free (sessions);
+        free (session);
+
+        return is_last_session;
+}
+
 static void
 gsm_systemd_system_init (GsmSystemInterface *iface)
 {
@@ -1047,9 +1132,11 @@ gsm_systemd_system_init (GsmSystemInterface *iface)
         iface->suspend = gsm_systemd_suspend;
         iface->hibernate = gsm_systemd_hibernate;
         iface->set_session_idle = gsm_systemd_set_session_idle;
+        iface->is_login_session = gsm_systemd_is_login_session;
         iface->set_inhibitors = gsm_systemd_set_inhibitors;
         iface->prepare_shutdown = gsm_systemd_prepare_shutdown;
         iface->complete_shutdown = gsm_systemd_complete_shutdown;
+        iface->is_last_session_for_user = gsm_systemd_is_last_session_for_user;
 }
 
 GsmSystemd *
diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c
index 9dd3e8e1..40a08a35 100644
--- a/gnome-session/gsm-util.c
+++ b/gnome-session/gsm-util.c
@@ -83,6 +83,116 @@ static const char * const variable_unsetlist[] = {
     NULL
 };
 
+char *
+gsm_util_find_desktop_file_for_app_name (const char *name,
+                                         gboolean    autostart_first)
+{
+        char     *app_path;
+        char    **app_dirs;
+        GKeyFile *key_file;
+        char     *desktop_file;
+        int       i;
+
+        app_path = NULL;
+
+        app_dirs = gsm_util_get_desktop_dirs (autostart_first);
+
+        key_file = g_key_file_new ();
+
+        desktop_file = g_strdup_printf ("%s.desktop", name);
+
+        g_debug ("GsmUtil: Looking for file '%s'", desktop_file);
+
+        for (i = 0; app_dirs[i] != NULL; i++) {
+                g_debug ("GsmUtil: Looking in '%s'", app_dirs[i]);
+        }
+
+        g_key_file_load_from_dirs (key_file,
+                                   desktop_file,
+                                   (const char **) app_dirs,
+                                   &app_path,
+                                   G_KEY_FILE_NONE,
+                                   NULL);
+
+        if (app_path != NULL) {
+                g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
+        }
+
+        /* look for gnome vendor prefix */
+        if (app_path == NULL) {
+                g_free (desktop_file);
+                desktop_file = g_strdup_printf ("gnome-%s.desktop", name);
+
+                g_key_file_load_from_dirs (key_file,
+                                           desktop_file,
+                                           (const char **) app_dirs,
+                                           &app_path,
+                                           G_KEY_FILE_NONE,
+                                           NULL);
+                if (app_path != NULL) {
+                        g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path);
+                }
+        }
+
+        g_free (desktop_file);
+        g_key_file_free (key_file);
+
+        g_strfreev (app_dirs);
+
+        return app_path;
+}
+
+static char ** autostart_dirs;
+
+void
+gsm_util_set_autostart_dirs (char ** dirs)
+{
+        autostart_dirs = g_strdupv (dirs);
+}
+
+static char **
+gsm_util_get_standard_autostart_dirs (void)
+{
+        GPtrArray          *dirs;
+        const char * const *system_config_dirs;
+        const char * const *system_data_dirs;
+        int                 i;
+
+        dirs = g_ptr_array_new ();
+
+        g_ptr_array_add (dirs,
+                         g_build_filename (g_get_user_config_dir (),
+                                           "autostart", NULL));
+
+        system_data_dirs = g_get_system_data_dirs ();
+        for (i = 0; system_data_dirs[i]; i++) {
+                g_ptr_array_add (dirs,
+                                 g_build_filename (system_data_dirs[i],
+                                                   "gnome", "autostart", NULL));
+        }
+
+        system_config_dirs = g_get_system_config_dirs ();
+        for (i = 0; system_config_dirs[i]; i++) {
+                g_ptr_array_add (dirs,
+                                 g_build_filename (system_config_dirs[i],
+                                                   "autostart", NULL));
+        }
+
+        g_ptr_array_add (dirs, NULL);
+
+        return (char **) g_ptr_array_free (dirs, FALSE);
+}
+
+char **
+gsm_util_get_autostart_dirs ()
+{
+        if (autostart_dirs) {
+                return g_strdupv ((char **)autostart_dirs);
+        }
+
+        return gsm_util_get_standard_autostart_dirs ();
+}
+
 char **
 gsm_util_get_app_dirs ()
 {
@@ -110,6 +220,71 @@ gsm_util_get_app_dirs ()
         return (char **) g_ptr_array_free (dirs, FALSE);
 }
 
+char **
+gsm_util_get_desktop_dirs (gboolean autostart_first)
+{
+        char **apps;
+        char **autostart;
+        char **standard_autostart;
+        char **result;
+        int    size;
+        int    i;
+
+        apps = gsm_util_get_app_dirs ();
+        autostart = gsm_util_get_autostart_dirs ();
+
+        /* Still, check the standard autostart dirs for things like fulfilling session reqs,
+         * if using a non-standard autostart dir for autostarting */
+        if (autostart_dirs != NULL)
+                standard_autostart = gsm_util_get_standard_autostart_dirs ();
+        else
+                standard_autostart = NULL;
+
+        size = 0;
+        for (i = 0; apps[i] != NULL; i++) { size++; }
+        for (i = 0; autostart[i] != NULL; i++) { size++; }
+        if (standard_autostart != NULL)
+                for (i = 0; standard_autostart[i] != NULL; i++) { size++; }
+
+        result = g_new (char *, size + 1); /* including last NULL */
+
+        size = 0;
+
+        if (autostart_first) {
+                for (i = 0; autostart[i] != NULL; i++, size++) {
+                        result[size] = autostart[i];
+                }
+                if (standard_autostart != NULL) {
+                        for (i = 0; standard_autostart[i] != NULL; i++, size++) {
+                                result[size] = standard_autostart[i];
+                        }
+                }
+                for (i = 0; apps[i] != NULL; i++, size++) {
+                        result[size] = apps[i];
+                }
+        } else {
+                for (i = 0; apps[i] != NULL; i++, size++) {
+                        result[size] = apps[i];
+                }
+                if (standard_autostart != NULL) {
+                        for (i = 0; standard_autostart[i] != NULL; i++, size++) {
+                                result[size] = standard_autostart[i];
+                        }
+                }
+                for (i = 0; autostart[i] != NULL; i++, size++) {
+                        result[size] = autostart[i];
+                }
+        }
+
+        g_free (apps);
+        g_free (autostart);
+        g_free (standard_autostart);
+
+        result[size] = NULL;
+
+        return result;
+}
+
 gboolean
 gsm_util_text_is_blank (const char *str)
 {
@@ -176,6 +351,63 @@ gsm_util_init_error (gboolean    fatal,
         }
 }
 
+/**
+ * gsm_util_generate_startup_id:
+ *
+ * Generates a new SM client ID.
+ *
+ * Return value: an SM client ID.
+ **/
+char *
+gsm_util_generate_startup_id (void)
+{
+        static int     sequence = -1;
+        static guint   rand1 = 0;
+        static guint   rand2 = 0;
+        static pid_t   pid = 0;
+        struct timeval tv;
+
+        /* The XSMP spec defines the ID as:
+         *
+         * Version: "1"
+         * Address type and address:
+         *   "1" + an IPv4 address as 8 hex digits
+         *   "2" + a DECNET address as 12 hex digits
+         *   "6" + an IPv6 address as 32 hex digits
+         * Time stamp: milliseconds since UNIX epoch as 13 decimal digits
+         * Process-ID type and process-ID:
+         *   "1" + POSIX PID as 10 decimal digits
+         * Sequence number as 4 decimal digits
+         *
+         * XSMP client IDs are supposed to be globally unique: if
+         * SmsGenerateClientID() is unable to determine a network
+         * address for the machine, it gives up and returns %NULL.
+         * GNOME and KDE have traditionally used a fourth address
+         * format in this case:
+         *   "0" + 16 random hex digits
+         *
+         * We don't even bother trying SmsGenerateClientID(), since the
+         * user's IP address is probably "192.168.1.*" anyway, so a random
+         * number is actually more likely to be globally unique.
+         */
+
+        if (!rand1) {
+                rand1 = g_random_int ();
+                rand2 = g_random_int ();
+                pid = getpid ();
+        }
+
+        sequence = (sequence + 1) % 10000;
+        gettimeofday (&tv, NULL);
+        return g_strdup_printf ("10%.04x%.04x%.10lu%.3u%.10lu%.4d",
+                                rand1,
+                                rand2,
+                                (unsigned long) tv.tv_sec,
+                                (unsigned) tv.tv_usec,
+                                (unsigned long) pid,
+                                sequence);
+}
+
 static gboolean
 gsm_util_update_activation_environment (const char  *variable,
                                         const char  *value,
@@ -439,6 +671,139 @@ gsm_util_update_user_environment (const char  *variable,
         return environment_updated;
 }
 
+gboolean
+gsm_util_systemd_unit_is_active (const char  *unit,
+                                 GError     **error)
+{
+        g_autoptr(GDBusProxy) proxy = NULL;
+        g_autoptr(GVariant) result = NULL;
+        g_autofree gchar *object_path = NULL;
+        g_autofree gchar *active_state = NULL;
+        g_autoptr(GDBusProxy) unit_proxy = NULL;
+
+        proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                               G_DBUS_PROXY_FLAGS_NONE,
+                                               NULL,
+                                               "org.freedesktop.systemd1",
+                                               "/org/freedesktop/systemd1",
+                                               "org.freedesktop.systemd1.Manager",
+                                               NULL,
+                                               error);
+
+        if (proxy == NULL) {
+                return FALSE;
+        }
+
+        result = g_dbus_proxy_call_sync (proxy,
+                                         "GetUnit",
+                                         g_variant_new ("(s)", unit),
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1,
+                                         NULL,
+                                         error);
+
+        if (result == NULL) {
+                if (error && *error) {
+                        g_autofree char *remote_error = g_dbus_error_get_remote_error (*error);
+
+                        if (g_strcmp0 (remote_error, "org.freedesktop.systemd1.NoSuchUnit") == 0) {
+                                g_clear_error (error);
+                        }
+                }
+                return FALSE;
+        }
+
+        g_variant_get (result, "(o)", &object_path);
+        g_clear_pointer (&result, g_variant_unref);
+
+        unit_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                                    G_DBUS_PROXY_FLAGS_NONE,
+                                                    NULL,
+                                                    "org.freedesktop.systemd1",
+                                                    object_path,
+                                                    "org.freedesktop.systemd1.Unit",
+                                                    NULL,
+                                                    error);
+
+        if (unit_proxy == NULL) {
+            return FALSE;
+        }
+
+        result = g_dbus_proxy_get_cached_property (unit_proxy, "ActiveState");
+
+        if (result == NULL) {
+                g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Error getting ActiveState property");
+                return FALSE;
+        }
+
+        g_variant_get (result, "s", &active_state);
+
+        return g_str_equal (active_state, "active");
+}
+
+gboolean
+gsm_util_start_systemd_unit (const char  *unit,
+                             const char  *mode,
+                             GError     **error)
+{
+        g_autoptr(GDBusConnection) connection = NULL;
+        g_autoptr(GVariant)        reply = NULL;
+        GError          *bus_error = NULL;
+
+        connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+        if (connection == NULL)
+                return FALSE;
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.systemd1",
+                                             "/org/freedesktop/systemd1",
+                                             "org.freedesktop.systemd1.Manager",
+                                             "StartUnit",
+                                             g_variant_new ("(ss)",
+                                                            unit, mode),
+                                             NULL,
+                                             G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                                             -1, NULL, &bus_error);
+
+        if (bus_error != NULL) {
+                g_propagate_error (error, bus_error);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+gboolean
+gsm_util_systemd_reset_failed (GError **error)
+{
+        g_autoptr(GDBusConnection) connection = NULL;
+        g_autoptr(GVariant)        reply = NULL;
+        GError *bus_error = NULL;
+
+        connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
+
+        if (connection == NULL)
+                return FALSE;
+
+        reply = g_dbus_connection_call_sync (connection,
+                                             "org.freedesktop.systemd1",
+                                             "/org/freedesktop/systemd1",
+                                             "org.freedesktop.systemd1.Manager",
+                                             "ResetFailed",
+                                             NULL,
+                                             NULL,
+                                             G_DBUS_CALL_FLAGS_NO_AUTO_START,
+                                             -1, NULL, &bus_error);
+
+        if (bus_error != NULL) {
+                g_propagate_error (error, bus_error);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
 void
 gsm_util_setenv (const char *variable,
                  const char *value)
@@ -472,8 +837,6 @@ gsm_util_setenv (const char *variable,
 const char * const *
 gsm_util_listenv (void)
 {
-        if (child_environment == NULL)
-                child_environment = g_listenv ();
         return (const char * const *) child_environment;
 
 }
diff --git a/gnome-session/gsm-util.h b/gnome-session/gsm-util.h
index 2cc129f9..ec1f5ce7 100644
--- a/gnome-session/gsm-util.h
+++ b/gnome-session/gsm-util.h
@@ -24,13 +24,23 @@ G_BEGIN_DECLS
 
 #define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
 
+char *      gsm_util_find_desktop_file_for_app_name (const char *app_name,
+                                                     gboolean    autostart_first);
+
 gchar**     gsm_util_get_app_dirs                   (void);
 
+gchar**     gsm_util_get_autostart_dirs             (void);
+void        gsm_util_set_autostart_dirs             (char **dirs);
+
+gchar **    gsm_util_get_desktop_dirs               (gboolean autostart_first);
+
 gboolean    gsm_util_text_is_blank                  (const char *str);
 
 void        gsm_util_init_error                     (gboolean    fatal,
                                                      const char *format, ...) G_GNUC_PRINTF (2, 3);
 
+char *      gsm_util_generate_startup_id            (void);
+
 void        gsm_util_setenv                         (const char *variable,
                                                      const char *value);
 const char * const * gsm_util_listenv               (void);
@@ -38,6 +48,15 @@ const char * const * gsm_util_get_variable_blacklist(void);
 
 gboolean    gsm_util_export_activation_environment  (GError     **error);
 gboolean    gsm_util_export_user_environment        (GError     **error);
+gboolean    gsm_util_systemd_unit_is_active         (const char  *unit,
+                                                     GError     **error);
+gboolean    gsm_util_start_systemd_unit             (const char  *unit,
+                                                     const char  *mode,
+                                                     GError     **error);
+gboolean    gsm_util_systemd_reset_failed           (GError     **error);
+
+
+void        gsm_quit                                (void);
 
 G_END_DECLS
 
diff --git a/gnome-session/main.c b/gnome-session/main.c
new file mode 100644
index 00000000..47abd43e
--- /dev/null
+++ b/gnome-session/main.c
@@ -0,0 +1,635 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <libintl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib/gi18n.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib-unix.h>
+#include <gio/gio.h>
+
+#include "gdm-log.h"
+
+#include "gsm-util.h"
+#include "gsm-manager.h"
+#include "gsm-session-fill.h"
+#include "gsm-store.h"
+#include "gsm-system.h"
+#include "gsm-fail-whale.h"
+
+#include <systemd/sd-journal.h>
+
+#define GSM_DBUS_NAME "org.gnome.SessionManager"
+
+static gboolean systemd_service = FALSE;
+static gboolean failsafe = FALSE;
+static gboolean show_version = FALSE;
+static gboolean debug = FALSE;
+static gboolean please_fail = FALSE;
+static gboolean disable_acceleration_check = FALSE;
+static const char *session_name = NULL;
+static GsmManager *manager = NULL;
+static char *gl_renderer = NULL;
+
+static GMainLoop *loop;
+
+void
+gsm_quit (void)
+{
+        g_main_loop_quit (loop);
+}
+
+static void
+gsm_main (void)
+{
+        if (loop == NULL)
+                loop = g_main_loop_new (NULL, TRUE);
+
+        g_main_loop_run (loop);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const char *name,
+              gpointer    data)
+{
+        if (connection == NULL) {
+                if (gsm_manager_get_dbus_disconnected (manager))
+                        gsm_quit ();
+                else {
+                        g_warning ("Lost name on bus: %s", name);
+                        gsm_fail_whale_dialog_we_failed (TRUE, TRUE, NULL);
+                }
+        } else {
+                g_debug ("Calling name lost callback function");
+
+                /*
+                 * When the signal handler gets a shutdown signal, it calls
+                 * this function to inform GsmManager to not restart
+                 * applications in the off chance a handler is already queued
+                 * to dispatch following the below call to gtk_main_quit.
+                 */
+                gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_EXIT);
+
+                gsm_quit ();
+        }
+}
+
+static gboolean
+term_or_int_signal_cb (gpointer data)
+{
+        g_autoptr(GError) error = NULL;
+        GsmManager *manager = (GsmManager *)data;
+
+        /* let the fatal signals interrupt us */
+        g_debug ("Caught SIGINT/SIGTERM, shutting down normally.");
+
+        if (!gsm_manager_logout (manager, GSM_MANAGER_LOGOUT_MODE_FORCE, &error)) {
+                if (g_error_matches (error, GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_NOT_IN_RUNNING)) {
+                    gsm_quit ();
+                    return FALSE;
+                }
+
+                g_critical ("Failed to log out: %s", error->message);
+        }
+
+        return FALSE;
+}
+
+static gboolean
+sigusr2_cb (gpointer data)
+{
+        g_debug ("-------- MARK --------");
+        return TRUE;
+}
+
+static gboolean
+sigusr1_cb (gpointer data)
+{
+        gdm_log_toggle_debug ();
+        return TRUE;
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const char *name,
+                  gpointer data)
+{
+        gsm_manager_start (manager);
+}
+
+static void
+create_manager (void)
+{
+        GsmStore *client_store;
+
+        client_store = gsm_store_new ();
+        manager = gsm_manager_new (client_store, failsafe, systemd_service);
+        g_object_unref (client_store);
+
+        g_unix_signal_add (SIGTERM, term_or_int_signal_cb, manager);
+        g_unix_signal_add (SIGINT, term_or_int_signal_cb, manager);
+        g_unix_signal_add (SIGUSR1, sigusr1_cb, manager);
+        g_unix_signal_add (SIGUSR2, sigusr2_cb, manager);
+
+        if (IS_STRING_EMPTY (session_name)) {
+                session_name = _gsm_manager_get_default_session (manager);
+        }
+
+        if (!gsm_session_fill (manager, session_name)) {
+                gsm_fail_whale_dialog_we_failed (FALSE, TRUE, NULL);
+        }
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+                 const char *name,
+                 gpointer data)
+{
+        create_manager ();
+}
+
+static guint
+acquire_name (void)
+{
+        return g_bus_own_name (G_BUS_TYPE_SESSION,
+                               GSM_DBUS_NAME,
+                               G_BUS_NAME_OWNER_FLAGS_NONE,
+                               on_bus_acquired,
+                               on_name_acquired,
+                               on_name_lost,
+                               NULL, NULL);
+}
+
+static void
+initialize_gio (void)
+{
+        char *disable_fuse = NULL;
+        char *use_vfs = NULL;
+
+        disable_fuse = g_strdup (g_getenv ("GVFS_DISABLE_FUSE"));
+        use_vfs = g_strdup (g_getenv ("GIO_USE_VFS"));
+
+        g_setenv ("GVFS_DISABLE_FUSE", "1", TRUE);
+        g_setenv ("GIO_USE_VFS", "local", TRUE);
+        g_vfs_get_default ();
+
+        if (use_vfs) {
+                g_setenv ("GIO_USE_VFS", use_vfs, TRUE);
+                g_free (use_vfs);
+        } else {
+                g_unsetenv ("GIO_USE_VFS");
+        }
+
+        if (disable_fuse) {
+                g_setenv ("GVFS_DISABLE_FUSE", disable_fuse, TRUE);
+                g_free (disable_fuse);
+        } else {
+                g_unsetenv ("GVFS_DISABLE_FUSE");
+        }
+}
+
+static gboolean
+leader_term_or_int_signal_cb (gpointer data)
+{
+        gint fifo_fd = GPOINTER_TO_INT (data);
+
+        g_debug ("Session termination requested");
+
+        /* Start a shutdown explicitly. */
+        gsm_util_start_systemd_unit ("gnome-session-shutdown.target", "replace-irreversibly", NULL);
+
+        if (fifo_fd >= 0) {
+                int res;
+
+                /* If we have a fifo, try to signal the other side. */
+                res = write (fifo_fd, "S", 1);
+                if (res < 0) {
+                        g_warning ("Error signaling shutdown to monitoring process: %m");
+                        gsm_quit ();
+                }
+        } else {
+                /* Otherwise quit immediately as we cannot wait on systemd */
+                gsm_quit ();
+        }
+
+        return G_SOURCE_REMOVE;
+}
+
+static void
+graphical_session_pre_state_changed_cb (GDBusProxy *proxy,
+                                        GVariant   *changed_properties)
+{
+        const char *state;
+        g_autoptr (GVariant) value = NULL;
+
+        value = g_variant_lookup_value (changed_properties, "ActiveState", NULL);
+
+        if (value == NULL)
+                return;
+
+        g_variant_get (value, "&s", &state);
+        if (g_strcmp0 (state, "inactive") == 0) {
+                g_debug ("Session services now inactive, quitting");
+                gsm_quit ();
+                return;
+        }
+}
+
+static gboolean
+monitor_hangup_cb (int          fd,
+                   GIOCondition condition,
+                   gpointer     user_data)
+{
+        g_autoptr (GDBusConnection) connection = NULL;
+        g_autoptr (GVariant) unit = NULL;
+        g_autoptr (GVariant) value = NULL;
+        g_autoptr (GError) error = NULL;
+        GDBusProxy *proxy = NULL;
+        const char *unit_path = NULL;
+
+        g_debug ("Services have begun stopping, waiting for them to finish stopping");
+
+        connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+        if (!connection) {
+                g_debug ("Could not get bus connection: %s", error->message);
+                gsm_quit ();
+                return G_SOURCE_REMOVE;
+        }
+
+        unit = g_dbus_connection_call_sync (connection,
+                                            "org.freedesktop.systemd1",
+                                            "/org/freedesktop/systemd1",
+                                            "org.freedesktop.systemd1.Manager",
+                                            "GetUnit",
+                                            g_variant_new ("(s)", "graphical-session-pre.target"),
+                                            G_VARIANT_TYPE ("(o)"),
+                                            G_DBUS_CALL_FLAGS_NONE,
+                                            -1,
+                                            NULL,
+                                            &error);
+        if (!unit) {
+                g_debug ("Could not get unit for graphical-session-pre.target: %s", error->message);
+                gsm_quit ();
+                return G_SOURCE_REMOVE;
+        }
+
+        g_variant_get (unit, "(&o)", &unit_path);
+
+        proxy = g_dbus_proxy_new_sync (connection,
+                                       G_DBUS_PROXY_FLAGS_NONE,
+                                       NULL,
+                                       "org.freedesktop.systemd1",
+                                       unit_path,
+                                       "org.freedesktop.systemd1.Unit",
+                                       NULL,
+                                       &error);
+        if (!proxy) {
+                g_debug ("Could not get proxy for graphical-session-pre.target unit: %s", error->message);
+                gsm_quit ();
+                return G_SOURCE_REMOVE;
+        }
+
+        value = g_dbus_proxy_get_cached_property (proxy, "ActiveState");
+
+        if (value) {
+                const char *state;
+
+                g_variant_get (value, "&s", &state);
+
+                if (g_strcmp0 (state, "inactive") == 0) {
+                        g_debug ("State of graphical-session-pre.target unit already inactive quitting");
+                        gsm_quit ();
+                        return G_SOURCE_REMOVE;
+                }
+                g_debug ("State of graphical-session-pre.target unit is '%s', waiting for it to go inactive", state);
+        } else {
+                g_debug ("State of graphical-session-pre.target unit is unknown, waiting for it to go inactive");
+        }
+        g_signal_connect (proxy,
+                          "g-properties-changed",
+                          G_CALLBACK (graphical_session_pre_state_changed_cb),
+                          NULL);
+
+        return G_SOURCE_REMOVE;
+}
+
+/**
+ * systemd_leader_run:
+ *
+ * This is the session leader when running under systemd, i.e. it is the only
+ * process that is *not* managed by the systemd user instance, this process
+ * is the one executed by the GDM helpers and is part of the session scope in
+ * the system systemd instance.
+ *
+ * This process works together with a service running in the user systemd
+ * instance (currently gnome-session-ctl@monitor.service):
+ *
+ * - It needs to signal shutdown to the user session when receiving SIGTERM
+ *
+ * - It needs to quit just after the user session is done
+ *
+ * - The monitor instance needs to know if this process was killed
+ *
+ * All this is achieved by opening a named fifo in a well known location.
+ * If this process receives SIGTERM or SIGINT then it will write a single byte
+ * causing the monitor service to signal STOPPING=1 to systemd, triggering a
+ * clean shutdown, solving the first item. The other two items are solved by
+ * waiting for EOF/HUP on both sides and quitting immediately when receiving
+ * that signal.
+ *
+ * As an example, a shutdown might look as follows:
+ *
+ * - session-X.scope for user is stopped
+ * - Leader process receive SIGTERM
+ * - Leader sends single byte
+ * - Monitor process receives byte and signals STOPPING=1
+ * - Systemd user instance starts session teardown
+ * - Session is torn down, last job run is stopping monitor process (SIGTERM)
+ * - Monitor process quits, closing FD in the process
+ * - Leader process receives HUP and quits
+ * - GDM shuts down its processes in the users scope
+ *
+ * The result is that the session is stopped cleanly.
+ */
+static void
+systemd_leader_run(void)
+{
+        g_autofree char *fifo_name = NULL;
+        int res;
+        int fifo_fd;
+
+        fifo_name = g_strdup_printf ("%s/gnome-session-leader-fifo",
+                                     g_get_user_runtime_dir ());
+        res = mkfifo (fifo_name, 0666);
+        if (res < 0 && errno != EEXIST)
+                g_warning ("Error creating FIFO: %m");
+
+        fifo_fd = g_open (fifo_name, O_WRONLY | O_CLOEXEC, 0666);
+        if (fifo_fd >= 0) {
+                struct stat buf;
+
+                res = fstat (fifo_fd, &buf);
+                if (res < 0) {
+                        g_warning ("Unable to watch systemd session: fstat failed with %m");
+                        close (fifo_fd);
+                        fifo_fd = -1;
+                } else if (!(buf.st_mode & S_IFIFO)) {
+                        g_warning ("Unable to watch systemd session: FD is not a FIFO");
+                        close (fifo_fd);
+                        fifo_fd = -1;
+                } else {
+                        g_unix_fd_add (fifo_fd, G_IO_HUP, (GUnixFDSourceFunc) monitor_hangup_cb, NULL);
+                }
+        } else {
+                g_warning ("Unable to watch systemd session: Opening FIFO failed with %m");
+        }
+
+        g_unix_signal_add (SIGHUP, leader_term_or_int_signal_cb, GINT_TO_POINTER (fifo_fd));
+        g_unix_signal_add (SIGTERM, leader_term_or_int_signal_cb, GINT_TO_POINTER (fifo_fd));
+        g_unix_signal_add (SIGINT, leader_term_or_int_signal_cb, GINT_TO_POINTER (fifo_fd));
+
+        /* Sleep until we receive HUP or are killed. */
+        gsm_main ();
+        exit(0);
+}
+
+int
+main (int argc, char **argv)
+{
+        GError           *error = NULL;
+        static char     **override_autostart_dirs = NULL;
+        static char      *opt_session_name = NULL;
+        const char       *debug_string = NULL;
+        const char       *env_override_autostart_dirs = NULL;
+        g_auto(GStrv)     env_override_autostart_dirs_v = NULL;
+        guint             name_owner_id;
+        GOptionContext   *options;
+        static GOptionEntry entries[] = {
+                { "systemd-service", 0, 0, G_OPTION_ARG_NONE, &systemd_service, N_("Running as systemd service"), NULL },
+                { "autostart", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &override_autostart_dirs, N_("Override standard autostart directories"), N_("AUTOSTART_DIR") },
+                { "session", 0, 0, G_OPTION_ARG_STRING, &opt_session_name, N_("Session to use"), N_("SESSION_NAME") },
+                { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
+                { "failsafe", 'f', 0, G_OPTION_ARG_NONE, &failsafe, N_("Do not load user-specified applications"), NULL },
+                { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+                /* Translators: the 'fail whale' is the black dialog we show when something goes seriously wrong */
+                { "whale", 0, 0, G_OPTION_ARG_NONE, &please_fail, N_("Show the fail whale dialog for testing"), NULL },
+                { "disable-acceleration-check", 0, 0, G_OPTION_ARG_NONE, &disable_acceleration_check, N_("This option is ignored"), NULL },
+                { NULL, 0, 0, 0, NULL, NULL, NULL }
+        };
+
+        /* Make sure that we have a session bus */
+        if (!g_getenv ("DBUS_SESSION_BUS_ADDRESS"))
+                g_error ("No session bus running! Cannot continue");
+
+        /* From 3.14 GDM sets XDG_CURRENT_DESKTOP. For compatibility with
+         * older versions of GDM,  other display managers, and startx,
+         * set a fallback value if we don't find it set.
+         */
+        if (g_getenv ("XDG_CURRENT_DESKTOP") == NULL) {
+            g_setenv("XDG_CURRENT_DESKTOP", "GNOME", TRUE);
+            gsm_util_setenv ("XDG_CURRENT_DESKTOP", "GNOME");
+        }
+
+        /* Make sure we initialize gio in a way that does not autostart any daemon */
+        initialize_gio ();
+
+        setlocale (LC_ALL, "");
+        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+        textdomain (GETTEXT_PACKAGE);
+
+        debug_string = g_getenv ("GNOME_SESSION_DEBUG");
+        if (debug_string != NULL) {
+                debug = atoi (debug_string) == 1;
+        }
+
+        error = NULL;
+        options = g_option_context_new (_(" — the GNOME session manager"));
+        g_option_context_add_main_entries (options, entries, GETTEXT_PACKAGE);
+        g_option_context_parse (options, &argc, &argv, &error);
+        if (error != NULL) {
+                g_warning ("%s", error->message);
+                exit (1);
+        }
+
+        g_option_context_free (options);
+
+        if (show_version) {
+                g_print ("%s %s\n", argv [0], VERSION);
+                exit (0);
+        }
+
+        /* Rebind stdout/stderr to the journal explicitly, so that
+         * journald picks ups the nicer "gnome-session" as the program
+         * name instead of whatever shell script GDM happened to use.
+         */
+        if (!debug) {
+                int journalfd;
+
+                journalfd = sd_journal_stream_fd (PACKAGE, LOG_INFO, 0);
+                if (journalfd >= 0) {
+                        dup2(journalfd, 1);
+                        dup2(journalfd, 2);
+                }
+        }
+
+        gdm_log_init ();
+        gdm_log_set_debug (debug);
+
+        if (please_fail) {
+                gsm_fail_whale_dialog_we_failed (TRUE, TRUE, NULL);
+                gsm_main ();
+                exit (1);
+        }
+
+        env_override_autostart_dirs = g_getenv ("GNOME_SESSION_AUTOSTART_DIR");
+
+        if (env_override_autostart_dirs != NULL && env_override_autostart_dirs[0] != '\0') {
+                env_override_autostart_dirs_v = g_strsplit (env_override_autostart_dirs, ":", 0);
+                gsm_util_set_autostart_dirs (env_override_autostart_dirs_v);
+        } else {
+                gsm_util_set_autostart_dirs (override_autostart_dirs);
+
+                /* Export the override autostart dirs parameter to the environment
+                 * in case we are running on systemd. */
+                if (override_autostart_dirs) {
+                        g_autofree char *autostart_dirs = NULL;
+                        autostart_dirs = g_strjoinv (":", override_autostart_dirs);
+                        g_setenv ("GNOME_SESSION_AUTOSTART_DIR", autostart_dirs, TRUE);
+                }
+        }
+
+        gsm_util_export_activation_environment (&error);
+        if (error) {
+                g_warning ("Failed to upload environment to DBus: %s", error->message);
+                g_clear_error (&error);
+        }
+
+        session_name = opt_session_name;
+
+        gsm_util_export_user_environment (&error);
+        if (error && !g_getenv ("RUNNING_UNDER_GDM"))
+                g_warning ("Failed to upload environment to systemd: %s", error->message);
+        g_clear_error (&error);
+
+        if (!systemd_service) {
+                const gchar *session_type = NULL;
+                g_autofree gchar *gnome_session_target = NULL;
+
+                session_type = g_getenv ("XDG_SESSION_TYPE");
+
+                /* We really need to resolve the session name at this point,
+                 * which requires talking to GSettings internally. */
+                if (IS_STRING_EMPTY (session_name)) {
+                        session_name = _gsm_manager_get_default_session (NULL);
+                }
+
+                /* We don't escape the name (i.e. we leave any '-' intact). */
+                gnome_session_target = g_strdup_printf ("gnome-session-%s@%s.target", session_type, session_name);
+
+                if (!gsm_util_systemd_unit_is_active (gnome_session_target, &error)) {
+                        if (error != NULL) {
+                                g_warning ("Could not check if unit %s is active: %s",
+                                           gnome_session_target, error->message);
+                                g_clear_error (&error);
+                        }
+                        /* Reset all failed units; we are going to start a lof ot things and
+                         * really do not want to run into errors because units have failed
+                         * in a previous session
+                         */
+                        gsm_util_systemd_reset_failed (&error);
+                        if (error && !g_getenv ("RUNNING_UNDER_GDM"))
+                                g_warning ("Failed to reset failed state of units: %s", error->message);
+                        g_clear_error (&error);
+
+                        if (gsm_util_start_systemd_unit (gnome_session_target, "fail", &error)) {
+                                /* We started the unit, open fifo and sleep forever. */
+                                systemd_leader_run ();
+                                exit (0);
+                        }
+
+                        /* We could not start the unit, fall back. */
+                        if (g_getenv ("RUNNING_UNDER_GDM"))
+                                g_message ("Falling back to non-systemd startup procedure. This is expected to happen for GDM sessions.");
+                        else
+                                g_warning ("Falling back to non-systemd startup procedure due to error: %s", error->message);
+                        g_clear_error (&error);
+                } else {
+                        g_warning ("Session manager already running!");
+                        exit (1);
+                }
+        }
+
+        {
+                gchar *ibus_path;
+
+                ibus_path = g_find_program_in_path("ibus-daemon");
+
+                if (ibus_path) {
+                        const gchar *p;
+                        p = g_getenv ("QT_IM_MODULE");
+                        if (!p || !*p)
+                                p = "ibus";
+                        gsm_util_setenv ("QT_IM_MODULE", p);
+                        p = g_getenv ("XMODIFIERS");
+                        if (!p || !*p)
+                                p = "@im=ibus";
+                        gsm_util_setenv ("XMODIFIERS", p);
+                }
+
+                g_free (ibus_path);
+        }
+
+        /* We want to use the GNOME menus which has the designed categories.
+         */
+        gsm_util_setenv ("XDG_MENU_PREFIX", "gnome-");
+
+        /* Talk to logind before acquiring a name, since it does synchronous
+         * calls at initialization time that invoke a main loop and if we
+         * already owned a name, then we would service too early during
+         * that main loop.
+         */
+        g_object_unref (gsm_get_system ());
+
+        name_owner_id  = acquire_name ();
+
+        gsm_main ();
+
+        gsm_util_export_user_environment (NULL);
+
+        g_clear_object (&manager);
+        g_free (gl_renderer);
+
+        g_bus_unown_name (name_owner_id);
+        gdm_log_shutdown ();
+
+        return 0;
+}
diff --git a/gnome-session/meson.build b/gnome-session/meson.build
index 5861fa47..75a69c07 100644
--- a/gnome-session/meson.build
+++ b/gnome-session/meson.build
@@ -1,22 +1,21 @@
-sources = files(
-  'gsm-util.c',
-  'leader-main.c'
-)
+script_conf = configuration_data()
+script_conf.set('libexecdir', session_libexecdir)
 
-cflags = [
-  '-DLOCALE_DIR="@0@"'.format(session_localedir),
-  '-DDATA_DIR="@0@"'.format(session_pkgdatadir),
-  '-DLIBEXECDIR="@0@"'.format(session_libexecdir)
-]
+script = 'gnome-session'
 
-executable(
-  meson.project_name(),
-  sources,
-  include_directories: top_inc,
-  dependencies: session_bin_deps,
-  c_args: cflags,
+configure_file(
+  input: script + '.in',
+  output: script,
   install: true,
-  install_dir: session_bindir
+  install_dir: session_bindir,
+  configuration: script_conf
+)
+
+libgsmutil = static_library(
+  'gsmutil',
+  sources: 'gsm-util.c',
+  include_directories: top_inc,
+  dependencies: session_deps
 )
 
 sources = files(
@@ -29,29 +28,35 @@
   sources,
   include_directories: top_inc,
   dependencies: session_bin_deps,
-  c_args: cflags,
   install: true,
   install_dir: session_libexecdir
 )
 
 sources = files(
-  'gsm-app.c',
+'gsm-app.c',
+  'gsm-autostart-app.c',
   'gsm-client.c',
+  'gsm-dbus-client.c',
+  'gsm-fail-whale.c',
   'gsm-inhibitor.c',
+  'gdm-log.c',
   'gsm-manager.c',
   'gsm-presence.c',
+  'gsm-process-helper.c',
   'gsm-session-fill.c',
+  'gsm-shell-extensions.c',
   'gsm-shell.c',
   'gsm-store.c',
   'gsm-system.c',
   'gsm-systemd.c',
-  'gsm-util.c',
-  'service-main.c'
+  'main.c'
 )
 
 dbus_ifaces = [
   'org.gnome.SessionManager',
+  'org.gnome.SessionManager.Client',
   'org.gnome.SessionManager.ClientPrivate',
+  'org.gnome.SessionManager.App',
   'org.gnome.SessionManager.Inhibitor',
   'org.gnome.SessionManager.Presence',
 ]
@@ -70,18 +75,40 @@
   xml_dbus_docs += out[2] # docbook XML
 endforeach
 
+cflags = [
+  '-DLOCALE_DIR="@0@"'.format(session_localedir),
+  '-DDATA_DIR="@0@"'.format(session_pkgdatadir),
+  '-DLIBEXECDIR="@0@"'.format(session_libexecdir)
+]
+
 executable(
-  meson.project_name() + '-service',
+  meson.project_name() + '-binary',
   sources,
   include_directories: top_inc,
   dependencies: session_bin_deps,
   c_args: cflags,
+  link_with: libgsmutil,
+  install: true,
+  install_dir: session_libexecdir
+)
+
+sources = files('gsm-fail-whale-dialog.c')
+
+cflags = '-DLOCALE_DIR="@0@"'.format(session_localedir)
+
+executable(
+  meson.project_name() + '-failed',
+  sources,
+  include_directories: top_inc,
+  dependencies: gtk_dep,
+  c_args: cflags,
   install: true,
   install_dir: session_libexecdir
 )
 
 units = [
   ['test-client-dbus', [], [gio_dep]],
+  ['test-process-helper', files('gsm-process-helper.c'), [gio_dep]]
 ]
 
 foreach unit: units

diff --git a/gnome-session/org.gnome.SessionManager.App.xml b/gnome-session/org.gnome.SessionManager.App.xml
new file mode 100644
index 00000000..25949c8a
--- /dev/null
+++ b/gnome-session/org.gnome.SessionManager.App.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<!--
+  org.gnome.SessionManager.App:
+  @short_description: Apps to Start on Login
+
+  This interface represents the apps that <command>gnome-session</command> is
+  configured to start on login.
+-->
+<node>
+  <interface name="org.gnome.SessionManager.App">
+    <annotation name="org.gtk.GDBus.C.Name" value="ExportedApp"/>
+
+    <!--
+      GetAppId:
+      @app_id: The identifier for the application
+
+      Return the application ID.
+    -->
+    <method name="GetAppId">
+      <arg type="s" name="app_id" direction="out"/>
+    </method>
+
+    <!--
+      GetStartupId:
+      @startup_id: The startup identifier
+
+      Return the startup ID associated with this application.
+    -->
+    <method name="GetStartupId">
+      <arg type="s" name="startup_id" direction="out"/>
+    </method>
+
+    <!--
+      GetPhase:
+      @phase: The application startup phase
+
+      Return the startup phase of this application.
+    -->
+    <method name="GetPhase">
+      <arg type="u" name="phase" direction="out"/>
+    </method>
+
+  </interface>
+</node>
diff --git a/gnome-session/org.gnome.SessionManager.Client.xml b/gnome-session/org.gnome.SessionManager.Client.xml
new file mode 100644
index 00000000..71f1323b
--- /dev/null
+++ b/gnome-session/org.gnome.SessionManager.Client.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<!--
+  org.gnome.SessionManager.Client:
+  @short_description: Registered Clients
+
+  This interface exports the clients that have registered themselves with
+  <command>gnome-session</command>, either via the
+  <link linkend='gdbus-method-org-gnome-SessionManager.RegisterClient'>D-Bus API</link>
+  or via the X11 Session Management Protocol.
+  You can obtain a list of clients by calling the manager's
+  <link linkend='gdbus-method-org-gnome-SessionManager.GetClients'>GetClients()</link>
+  method.
+-->
+<node>
+  <interface name="org.gnome.SessionManager.Client">
+    <annotation name="org.gtk.GDBus.C.Name" value="ExportedClient"/>
+
+    <!--
+      GetAppId:
+      @app_id: The identifier for the associated application
+
+      Return the application ID associated with this client.
+    -->
+    <method name="GetAppId">
+      <arg type="s" name="app_id" direction="out"/>
+    </method>
+
+    <!--
+      GetStartupId:
+      @startup_id: The startup identifier
+
+      Return the startup ID associated with this client.
+    -->
+    <method name="GetStartupId">
+      <arg type="s" name="startup_id" direction="out"/>
+    </method>
+
+    <!--
+      GetRestartStyleHint:
+      @hint: The restart style hint
+
+      Return the restart style hint for this client.
+    -->
+    <method name="GetRestartStyleHint">
+      <arg type="u" name="hint" direction="out"/>
+    </method>
+
+    <!--
+      GetUnixProcessId:
+      @pid: The Unix process identifier
+
+      Return the Unix process identifier for this client.
+    -->
+    <method name="GetUnixProcessId">
+      <arg type="u" name="pid" direction="out"/>
+    </method>
+
+    <!--
+      GetStatus:
+      @status: The client status
+
+      Return the status of this client.
+    -->
+    <method name="GetStatus">
+      <arg type="u" name="status" direction="out"/>
+    </method>
+
+    <!--
+      Stop:
+
+      Initiate a request to terminate this application via XSMP.
+    -->
+    <method name="Stop"/>
+  </interface>
+</node>
diff --git a/gnome-session/org.gnome.SessionManager.Inhibitor.xml b/gnome-session/org.gnome.SessionManager.Inhibitor.xml
index 7546b167..c663cd9f 100644
--- a/gnome-session/org.gnome.SessionManager.Inhibitor.xml
+++ b/gnome-session/org.gnome.SessionManager.Inhibitor.xml
@@ -56,5 +56,16 @@
       <arg type="u" name="flags" direction="out"/>
     </method>
 
+    <!--
+      GetToplevelXid
+      @xid: An X11 toplevel window identifier
+
+      Return the X11 toplevel window identifier associated with this inhibitor.
+      Returns zero if an XID is unavailable, such as on Wayland.
+    -->
+    <method name="GetToplevelXid">
+      <arg type="u" name="xid" direction="out"/>
+    </method>
+
   </interface>
 </node>
diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml
index 77a37ba5..365fdd3e 100644
--- a/gnome-session/org.gnome.SessionManager.xml
+++ b/gnome-session/org.gnome.SessionManager.xml
@@ -63,14 +63,14 @@
     <!--
       RegisterClient:
       @app_id: The application identifier
-      @ignored: An ignored parameter, which exists only to make this API backwards-compatible with older versions of GNOME
+      @client_startup_id: Client startup identifier
       @client_id: The object path of the newly registered client
 
       Register the caller as a Session Management client.
     -->
     <method name="RegisterClient">
       <arg type="s" name="app_id" direction="in"/>
-      <arg type="s" name="ignored" direction="in"/>
+      <arg type="s" name="client_startup_id" direction="in"/>
       <arg type="o" name="client_id" direction="out"/>
     </method>
 
@@ -87,7 +87,7 @@
     <!--
       Inhibit:
       @app_id: The application identifier, optional
-      @ignored: An ignored parameter, which exists only to make this API backwards-compatible with older versions of GNOME
+      @toplevel_xid: The toplevel X window identifier
       @reason: The reason for the inhibit
       @flags: Flags that specify what should be inhibited
       @inhibit_cookie: The cookie
@@ -103,6 +103,8 @@
       to take place. If @app_id is not specified, the user might not be informed
       of this inhibitor when prompted to force the action.
 
+      On Wayland, or other display servers, pass 0 for @toplevel_xid.
+
       The @flags parameter must include at least one of the following flags,
       bitwise or'ed together:
 
@@ -135,7 +137,7 @@
     -->
     <method name="Inhibit">
       <arg type="s" name="app_id" direction="in"/>
-      <arg type="u" name="ignored" direction="in"/>
+      <arg type="u" name="toplevel_xid" direction="in"/>
       <arg type="s" name="reason" direction="in"/>
       <arg type="u" name="flags" direction="in"/>
       <arg type="u" name="inhibit_cookie" direction="out"/>
@@ -166,6 +168,19 @@
       <arg type="b" name="is_inhibited" direction="out"/>
     </method>
 
+    <!--
+      GetClients:
+      @clients: an array of client IDs
+
+      This gets a list of all the clients that are currently known to the
+      session manager. Each client ID is a D-Bus object path to the object
+      that implements the <link linkend='gdbus-org.gnome.SessionManager.Client'>
+      Client interface</link>
+    -->
+    <method name="GetClients">
+      <arg name="clients" direction="out" type="ao"/>
+    </method>
+
     <!--
       GetInhibitors:
       @inhibitors: an array of inhibitor IDs
@@ -179,6 +194,19 @@
       <arg name="inhibitors" direction="out" type="ao"/>
     </method>
 
+    <!--
+      IsAutostartConditionHandled:
+      @condition: The autostart condition string
+      @handled: True if condition is handled, false otherwise
+
+      Allows the caller to determine whether the session manager is handling
+      changes to the specified autostart condition.
+    -->
+    <method name="IsAutostartConditionHandled">
+      <arg name="condition" direction="in" type="s"/>
+      <arg name="handled" direction="out" type="b"/>
+    </method>
+
     <!--
       Shutdown:
 
@@ -254,10 +282,10 @@
 
     <!--
       IsSessionRunning:
-      @running: True if the session is running, false otherwise.
+      @running: True if the session has entered the Running phase, false otherwise
 
-      Allows the caller to determine whether the session is running or not.
-      Returns false if the session is initializing, or shutting down.
+      Allows the caller to determine whether the session manager has entered
+      the Running phase, in case the client missed the SessionRunning signal.
     -->
     <method name="IsSessionRunning">
       <arg name="running" direction="out" type="b"/>
@@ -308,7 +336,7 @@
     <!--
       SessionRunning:
 
-      Emitted when the session is done initializing.
+      Emitted when the session enters the Running phase.
     -->
     <signal name="SessionRunning"/>
 
diff --git a/gnome-session/service-main.c b/gnome-session/service-main.c
deleted file mode 100644
index eae1dee0..00000000
--- a/gnome-session/service-main.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2006 Novell, Inc.
- * Copyright (C) 2008 Red Hat, Inc.
- *
- * This program 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.
- *
- * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-
-#include <locale.h>
-
-#include <glib/gi18n.h>
-#include <glib.h>
-#include <glib-unix.h>
-#include <gio/gio.h>
-
-#include "gsm-util.h"
-#include "gsm-manager.h"
-#include "gsm-session-fill.h"
-#include "gsm-store.h"
-#include "gsm-system.h"
-
-static char *session_name = NULL;
-
-static GsmManager *manager = NULL;
-static GMainLoop *loop = NULL;
-
-void
-gsm_quit (void)
-{
-        g_main_loop_quit (loop);
-}
-
-static void
-on_name_lost (GDBusConnection *connection,
-              const char *name,
-              gpointer    data)
-{
-        if (connection == NULL) {
-                if (!gsm_manager_get_dbus_disconnected (manager))
-                        g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
-                                          "MESSAGE_ID", GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID,
-                                          "MESSAGE", "Lost bus", name);
-        } else {
-                g_debug ("Calling name lost callback function");
-
-                /*
-                 * When the signal handler gets a shutdown signal, it calls
-                 * this function to inform GsmManager to not restart
-                 * applications in the off chance a handler is already queued
-                 * to dispatch following the below call to gtk_main_quit.
-                 */
-                gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_EXIT);
-        }
-
-        gsm_quit ();
-}
-
-static gboolean
-term_or_int_signal_cb (gpointer data)
-{
-        g_autoptr(GError) error = NULL;
-        GsmManager *manager = (GsmManager *)data;
-
-        /* let the fatal signals interrupt us */
-        g_debug ("Caught SIGINT/SIGTERM, shutting down normally.");
-
-        if (!gsm_manager_logout (manager, GSM_MANAGER_LOGOUT_MODE_FORCE, &error)) {
-                if (g_error_matches (error, GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_NOT_IN_RUNNING)) {
-                    gsm_quit ();
-                    return FALSE;
-                }
-
-                g_critical ("Failed to log out: %s", error->message);
-        }
-
-        return FALSE;
-}
-
-static gboolean
-sigusr2_cb (gpointer data)
-{
-        g_debug ("-------- MARK --------");
-        return TRUE;
-}
-
-static gboolean
-sigusr1_cb (gpointer data)
-{
-        g_log_set_debug_enabled (!g_log_get_debug_enabled ());
-        return TRUE;
-}
-
-static void
-on_name_acquired (GDBusConnection *connection,
-                  const char *name,
-                  gpointer data)
-{
-        gsm_manager_start (manager);
-}
-
-static void
-create_manager (void)
-{
-        manager = gsm_manager_new ();
-
-        g_unix_signal_add (SIGTERM, term_or_int_signal_cb, manager);
-        g_unix_signal_add (SIGINT, term_or_int_signal_cb, manager);
-        g_unix_signal_add (SIGUSR1, sigusr1_cb, manager);
-        g_unix_signal_add (SIGUSR2, sigusr2_cb, manager);
-
-        if (!gsm_session_fill (manager, session_name)) {
-                g_log_structured (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
-                                  "MESSAGE_ID", GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID,
-                                  "MESSAGE", "Failed to fill session");
-                gsm_quit ();
-        }
-}
-
-static void
-on_bus_acquired (GDBusConnection *connection,
-                 const char *name,
-                 gpointer data)
-{
-        create_manager ();
-}
-
-int
-main (int argc, char **argv)
-{
-        g_autoptr (GError) error = NULL;
-        g_autoptr (GOptionContext) options = NULL;
-        const char *debug = NULL;
-        guint name_owner_id;
-        static GOptionEntry entries[] = {
-                { "session", 0, 0, G_OPTION_ARG_STRING, &session_name, N_("Session to use"), N_("SESSION_NAME") },
-                { NULL, 0, 0, 0, NULL, NULL, NULL }
-        };
-
-        setlocale (LC_ALL, "");
-        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
-        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-        textdomain (GETTEXT_PACKAGE);
-
-        options = g_option_context_new (_(" — the GNOME session manager"));
-        g_option_context_add_main_entries (options, entries, GETTEXT_PACKAGE);
-        if (!g_option_context_parse (options, &argc, &argv, &error))
-                g_error ("%s", error->message);
-
-        debug = g_getenv ("GNOME_SESSION_DEBUG");
-        if (debug != NULL)
-                g_log_set_debug_enabled (atoi (debug) == 1);
-
-        /* Talk to logind before acquiring a name, since it does synchronous
-         * calls at initialization time that invoke a main loop and if we
-         * already owned a name, then we would service too early during
-         * that main loop. */
-        g_object_unref (gsm_get_system ());
-
-        name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
-                                        "org.gnome.SessionManager",
-                                        G_BUS_NAME_OWNER_FLAGS_NONE,
-                                        on_bus_acquired,
-                                        on_name_acquired,
-                                        on_name_lost,
-                                        NULL, NULL);
-
-        loop = g_main_loop_new (NULL, TRUE);
-        g_main_loop_run (loop);
-
-        g_clear_object (&manager);
-        g_free (session_name);
-        g_bus_unown_name (name_owner_id);
-        return 0;
-}
diff --git a/gnome-session/test-client-dbus.c b/gnome-session/test-client-dbus.c
index 316d3833..7d6f9e4e 100644
--- a/gnome-session/test-client-dbus.c
+++ b/gnome-session/test-client-dbus.c
@@ -143,16 +143,22 @@ register_client (void)
 {
         GError     *error;
         GVariant   *object_path_variant;
+        const char *startup_id;
         const char *app_id;
 
         app_id = "gedit";
 
+        startup_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+        if (!startup_id) {
+                startup_id = "";
+        }
+
         error = NULL;
         object_path_variant = g_dbus_proxy_call_sync (sm_proxy,
                                                       "RegisterClient",
                                                       g_variant_new ("(ss)",
                                                                      app_id,
-                                                                     ""),
+                                                                     startup_id),
                                                       G_DBUS_CALL_FLAGS_NONE,
                                                       -1, NULL, &error);
 
diff --git a/gnome-session/test-process-helper.c b/gnome-session/test-process-helper.c
new file mode 100644
index 00000000..e48caa8c
--- /dev/null
+++ b/gnome-session/test-process-helper.c
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include "gsm-process-helper.h"
+
+int
+main (int   argc,
+      char *argv[])
+{
+        char *command_line = "xeyes";
+        int   timeout = 500;
+        GError *error = NULL;
+
+        if (argc > 3) {
+                g_printerr ("Too many arguments.\n");
+                g_printerr ("Usage: %s [COMMAND] [TIMEOUT]\n", argv[0]);
+                return 1;
+        }
+
+        if (argc >= 2)
+                command_line = argv[1];
+        if (argc >= 3) {
+                int i = atoi (argv[2]);
+                if (i > 0)
+                        timeout = i;
+        }
+
+        if (!gsm_process_helper (command_line, timeout, &error)) {
+                g_warning ("%s", error->message);
+                g_clear_error (&error);
+        } else {
+                g_print ("Command exited successfully.\n");
+        }
+
+        return 0;
+}
diff --git a/meson.build b/meson.build
index 216e4880..361b388c 100644
--- a/meson.build
+++ b/meson.build
@@ -78,7 +78,7 @@ endif
 
 add_project_arguments(common_flags + compiler_flags, language: 'c')
 
-glib_req_version = '>= 2.82.0'
+glib_req_version = '>= 2.46.0'
 
 gio_dep = dependency('gio-2.0', version: glib_req_version)
 gtk_dep = dependency('gtk4')
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1c1ff5a1..bf7c9839 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,14 +1,15 @@
 # List of source files containing translatable strings.
 # Please keep this file sorted alphabetically.
 data/gnome.desktop.in.in
-data/gnome.session.desktop
+data/gnome.session.desktop.in.in
 data/gnome-wayland.desktop.in.in
 data/gnome-xorg.desktop.in.in
 data/org.gnome.SessionManager.gschema.xml
+gnome-session/gsm-fail-whale-dialog.c
 gnome-session/gsm-manager.c
+gnome-session/gsm-process-helper.c
 gnome-session/gsm-util.c
-gnome-session/leader-main.c
-gnome-session/service-main.c
+gnome-session/main.c
 tools/gnome-session-ctl.c
 tools/gnome-session-inhibit.c
 tools/gnome-session-quit.c
diff --git a/tools/gnome-session-ctl.c b/tools/gnome-session-ctl.c
index e413cd52..edae5062 100644
--- a/tools/gnome-session-ctl.c
+++ b/tools/gnome-session-ctl.c
@@ -248,7 +248,7 @@ main (int argc, char *argv[])
                 { "monitor", '\0', 0, G_OPTION_ARG_NONE, &opt_monitor, N_("Start gnome-session-shutdown.target when receiving EOF or a single byte on stdin"), NULL },
                 { "signal-init", '\0', 0, G_OPTION_ARG_NONE, &opt_signal_init, N_("Signal initialization done to gnome-session"), NULL },
                 { "restart-dbus", '\0', 0, G_OPTION_ARG_NONE, &opt_restart_dbus, N_("Restart dbus.service if it is running"), NULL },
-                { "exec-stop-check", '\0', 0, G_OPTION_ARG_NONE, &opt_exec_stop_check, N_("Run from ExecStopPost to start gnome-session-shutdown.target on service failure"), NULL },
+                { "exec-stop-check", '\0', 0, G_OPTION_ARG_NONE, &opt_exec_stop_check, N_("Run from ExecStopPost to start gnome-session-failed.target on service failure"), NULL },
                 { NULL },
         };
 
@@ -296,7 +296,7 @@ main (int argc, char *argv[])
         } else if (opt_exec_stop_check) {
                 /* Start failed target if the restart limit was hit */
                 if (g_strcmp0 ("start-limit-hit", g_getenv ("SERVICE_RESULT")) == 0) {
-                        do_start_unit ("gnome-session-shutdown.target", "fail");
+                        do_start_unit ("gnome-session-failed.target", "fail");
                 }
        } else {
                 g_assert_not_reached ();
