]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Allow greeters to run in-session
authorRobert Ancell <robert.ancell@canonical.com>
Tue, 28 Jun 2016 04:28:47 +0000 (16:28 +1200)
committerRobert Ancell <robert.ancell@canonical.com>
Tue, 28 Jun 2016 04:28:47 +0000 (16:28 +1200)
19 files changed:
liblightdm-gobject/greeter.c
src/Makefile.am
src/greeter-session.c
src/greeter-socket.c [new file with mode: 0644]
src/greeter-socket.h [new file with mode: 0644]
src/greeter.c
src/greeter.h
src/seat.c
src/session-config.c
src/session-config.h
src/session.c
src/session.h
tests/Makefile.am
tests/data/sessions/greeter.desktop [new file with mode: 0644]
tests/scripts/session-greeter.conf [new file with mode: 0644]
tests/src/Makefile.am
tests/src/libsystem.c
tests/src/test-session.c
tests/test-session-greeter [new file with mode: 0755]

index 33562bce6a792653b2489b2778df91fabcb981a5..49da7d44d1b61ea88dd6b2540b55f91e4383293d 100644 (file)
@@ -12,6 +12,8 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <gio/gio.h>
+#include <gio/gunixsocketaddress.h>
 #include <security/pam_appl.h>
 
 #include "lightdm/greeter.h"
@@ -52,6 +54,9 @@ typedef struct
     /* TRUE if the daemon can reuse this greeter */
     gboolean resettable;
 
+    /* Socket connection to daemon */
+    GSocket *socket;
+
     /* Channel to write to daemon */
     GIOChannel *to_server_channel;
 
@@ -400,22 +405,45 @@ static gboolean
 connect_to_daemon (LightDMGreeter *greeter, GError **error)
 {
     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
-    const gchar *to_server_fd, *from_server_fd;
+    const gchar *to_server_fd, *from_server_fd, *pipe_path;
 
     if (priv->to_server_channel || priv->from_server_channel)
         return TRUE;
 
+    /* Use private connection if one exists */  
     to_server_fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
     from_server_fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
-    if (!to_server_fd || !from_server_fd)
+    pipe_path = g_getenv ("LIGHTDM_GREETER_PIPE");
+    if (to_server_fd && from_server_fd)
+    {
+        priv->to_server_channel = g_io_channel_unix_new (atoi (to_server_fd));
+        priv->from_server_channel = g_io_channel_unix_new (atoi (from_server_fd));
+    }
+    else if (pipe_path)
+    {
+        GSocketAddress *address;
+        gboolean result;
+
+        priv->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, error);
+        if (!priv->socket)
+            return FALSE;
+
+        address = g_unix_socket_address_new (pipe_path);
+        result = g_socket_connect (priv->socket, address, NULL, error);
+        g_object_unref (address);
+        if (!result)
+            return FALSE;
+
+        priv->from_server_channel = g_io_channel_unix_new (g_socket_get_fd (priv->socket));
+        priv->to_server_channel = g_io_channel_ref (priv->from_server_channel);
+    }
+    else
     {
         g_set_error_literal (error, LIGHTDM_GREETER_ERROR, LIGHTDM_GREETER_ERROR_CONNECTION_FAILED,
                              "Unable to determine socket to daemon");
         return FALSE;
     }
 
-    priv->to_server_channel = g_io_channel_unix_new (atoi (to_server_fd));
-    priv->from_server_channel = g_io_channel_unix_new (atoi (from_server_fd));
     g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
 
     if (!g_io_channel_set_encoding (priv->to_server_channel, NULL, error) ||
@@ -1848,6 +1876,7 @@ lightdm_greeter_finalize (GObject *object)
     LightDMGreeter *self = LIGHTDM_GREETER (object);
     LightDMGreeterPrivate *priv = GET_PRIVATE (self);
 
+    g_clear_object (&priv->socket);
     if (priv->to_server_channel)
         g_io_channel_unref (priv->to_server_channel);
     if (priv->from_server_channel)
index abf506425a380eacd5ad11a362010c61bde668b0..7cc0b01a5d689c0609b2c0eb84adb0040cecce27 100644 (file)
@@ -14,6 +14,8 @@ lightdm_SOURCES = \
        greeter.h \
        greeter-session.c \
        greeter-session.h \
+       greeter-socket.c \
+       greeter-socket.h \        
        guest-account.c \
        guest-account.h \
        lightdm.c \
index a7adbacacde6318e80c78568059f8033be4ad91e..8ebac6f26f55baf9c2ad4d3ff2b355a5da68cbd1 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <string.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #include "greeter-session.h"
 
@@ -38,27 +39,46 @@ greeter_session_get_greeter (GreeterSession *session)
 }
 
 static gboolean
-setup_cb (Greeter *greeter, int input_fd, int output_fd, gpointer user_data)
+greeter_session_start (Session *session)
 {
-    Session *session = user_data;
+    GreeterSession *s = GREETER_SESSION (session);
+    int to_greeter_pipe[2], from_greeter_pipe[2];
+    int to_greeter_input, to_greeter_output, from_greeter_input, from_greeter_output;
     gchar *value;
+    gboolean result;
+
+    /* Create a pipe to talk with the greeter */
+    if (pipe (to_greeter_pipe) != 0 || pipe (from_greeter_pipe) != 0)
+    {
+        g_warning ("Failed to create pipes: %s", strerror (errno));
+        return FALSE;
+    }
+
+    to_greeter_input = to_greeter_pipe[1];  
+    to_greeter_output = to_greeter_pipe[0];
+    from_greeter_input = from_greeter_pipe[1];
+    from_greeter_output = from_greeter_pipe[0];  
+    greeter_set_file_descriptors (s->priv->greeter, to_greeter_input, from_greeter_output);
+
+    /* Don't allow the daemon end of the pipes to be accessed in child processes */
+    fcntl (to_greeter_input, F_SETFD, FD_CLOEXEC);
+    fcntl (from_greeter_output, F_SETFD, FD_CLOEXEC);
 
     /* Let the greeter session know how to communicate with the daemon */
-    value = g_strdup_printf ("%d", input_fd);
+    value = g_strdup_printf ("%d", from_greeter_input);
     session_set_env (session, "LIGHTDM_TO_SERVER_FD", value);
     g_free (value);
-    value = g_strdup_printf ("%d", output_fd);
+    value = g_strdup_printf ("%d", to_greeter_output);
     session_set_env (session, "LIGHTDM_FROM_SERVER_FD", value);
     g_free (value);
 
-    return SESSION_CLASS (greeter_session_parent_class)->start (session);
-}
+    result = SESSION_CLASS (greeter_session_parent_class)->start (session);
 
-static gboolean
-greeter_session_start (Session *session)
-{
-    GreeterSession *s = GREETER_SESSION (session);
-    return greeter_start (s->priv->greeter, setup_cb, session);
+    /* Close the session ends of the pipe */
+    close (from_greeter_input);
+    close (to_greeter_output);
+
+    return result;
 }
 
 static void
diff --git a/src/greeter-socket.c b/src/greeter-socket.c
new file mode 100644 (file)
index 0000000..4e2b7a3
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010-2016 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 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#include <config.h>
+
+#include <gio/gio.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "greeter-socket.h"
+
+enum {
+    CREATE_GREETER,
+    LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct GreeterSocketPrivate
+{
+    /* Path of socket to use */
+    gchar *path;
+
+    /* Listening UNIX socket */
+    GSocket *socket;
+
+    /* Source for listening for connections */
+    GSource *source;
+
+    /* Socket to greeter */
+    GSocket *greeter_socket;
+
+    /* Greeter connected on this socket */
+    Greeter *greeter;
+};
+
+G_DEFINE_TYPE (GreeterSocket, greeter_socket, G_TYPE_OBJECT);
+
+GreeterSocket *
+greeter_socket_new (const gchar *path)
+{
+    GreeterSocket *socket;
+
+    socket = g_object_new (GREETER_SOCKET_TYPE, NULL);
+    socket->priv->path = g_strdup (path);
+
+    return socket;
+}
+
+static gboolean
+greeter_connect_cb (GSocket *s, GIOCondition condition, GreeterSocket *socket)
+{
+    GSocket *new_socket;
+    GError *error = NULL;
+
+    new_socket = g_socket_accept (socket->priv->socket, NULL, &error);
+    if (error)
+        g_warning ("Failed to accept greeter connection: %s", error->message);
+    g_clear_error (&error);
+    if (!new_socket)
+        return G_SOURCE_CONTINUE;
+
+    /* Greeter already connected */
+    if (socket->priv->greeter)
+    {
+        g_socket_close (new_socket, NULL);
+        g_object_unref (new_socket);
+        return G_SOURCE_CONTINUE;
+    }
+
+    socket->priv->greeter_socket = new_socket;
+    g_signal_emit (socket, signals[CREATE_GREETER], 0, &socket->priv->greeter);
+    greeter_set_file_descriptors (socket->priv->greeter, g_socket_get_fd (new_socket), g_socket_get_fd (new_socket));
+
+    return G_SOURCE_CONTINUE;
+}
+
+gboolean
+greeter_socket_start (GreeterSocket *socket, GError **error)
+{
+    GSocketAddress *address;
+    gboolean result;
+
+    g_return_val_if_fail (socket != NULL, FALSE);
+    g_return_val_if_fail (socket->priv->socket == NULL, FALSE);  
+
+    socket->priv->socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, error);
+    if (!socket->priv->socket)
+        return FALSE;
+
+    unlink (socket->priv->path);  
+    address = g_unix_socket_address_new (socket->priv->path);
+    result = g_socket_bind (socket->priv->socket, address, FALSE, error);
+    g_object_unref (address);
+    if (!result)
+        return FALSE;
+    if (!g_socket_listen (socket->priv->socket, error))
+        return FALSE;
+
+    socket->priv->source = g_socket_create_source (socket->priv->socket, G_IO_IN, NULL);
+    g_source_set_callback (socket->priv->source, (GSourceFunc) greeter_connect_cb, socket, NULL);
+    g_source_attach (socket->priv->source, NULL);
+
+    return TRUE;
+}
+
+static void
+greeter_socket_init (GreeterSocket *socket)
+{
+    socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket, GREETER_SOCKET_TYPE, GreeterSocketPrivate);
+}
+
+static void
+greeter_socket_finalize (GObject *object)
+{
+    GreeterSocket *self = GREETER_SOCKET (object);
+
+    if (self->priv->path)
+        unlink (self->priv->path); 
+    g_free (self->priv->path);
+    g_clear_object (&self->priv->socket);
+    g_clear_object (&self->priv->source);
+    g_clear_object (&self->priv->greeter_socket);
+    g_clear_object (&self->priv->greeter);
+
+    G_OBJECT_CLASS (greeter_socket_parent_class)->finalize (object);
+}
+
+static void
+greeter_socket_class_init (GreeterSocketClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = greeter_socket_finalize;
+
+    signals[CREATE_GREETER] =
+        g_signal_new (GREETER_SOCKET_SIGNAL_CREATE_GREETER,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (GreeterSocketClass, create_greeter),
+                      g_signal_accumulator_first_wins,
+                      NULL,
+                      NULL,
+                      GREETER_TYPE, 0);
+
+    g_type_class_add_private (klass, sizeof (GreeterSocketPrivate));
+}
diff --git a/src/greeter-socket.h b/src/greeter-socket.h
new file mode 100644 (file)
index 0000000..da490ba
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010-2016 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 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef GREETER_SOCKET_H_
+#define GREETER_SOCKET_H_
+
+#include <glib-object.h>
+
+#include "greeter.h"
+
+G_BEGIN_DECLS
+
+#define GREETER_SOCKET_TYPE           (greeter_socket_get_type())
+#define GREETER_SOCKET(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GREETER_SOCKET_TYPE, GreeterSocket))
+#define GREETER_SOCKET_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), GREETER_SOCKET_TYPE, GreeterSocketClass))
+#define GREETER_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GREETER_SOCKET_TYPE, GreeterSocketClass))
+#define IS_GREETER_SOCKET(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GREETER_SOCKET_TYPE))
+
+#define GREETER_SOCKET_SIGNAL_CREATE_GREETER "create-greeter"
+
+typedef struct GreeterSocketPrivate GreeterSocketPrivate;
+
+typedef struct
+{
+    GObject               parent_instance;
+    GreeterSocketPrivate *priv;
+} GreeterSocket;
+
+typedef struct
+{
+    GObjectClass parent_class;
+    Greeter *(*create_greeter)(GreeterSocket *socket);
+} GreeterSocketClass;
+
+GType greeter_socket_get_type (void);
+
+GreeterSocket *greeter_socket_new (const gchar *path);
+
+gboolean greeter_socket_start (GreeterSocket *socket, GError **error);
+
+G_END_DECLS
+
+#endif /* GREETER_SOCKET_H_ */
index 72eaa2e7dc57668b1c131de3206bb39b14e61f83..4534ee2fc0f175e7293936573361d29da0febc52 100644 (file)
@@ -12,8 +12,6 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-#include <fcntl.h>
 #include <gcrypt.h>
 
 #include "greeter.h"
@@ -118,29 +116,22 @@ greeter_new (void)
     return g_object_new (GREETER_TYPE, NULL);
 }
 
-gboolean
-greeter_start (Greeter *greeter, gboolean (*setup_child_cb)(Greeter *greeter, int input_fd, int output_fd, gpointer user_data), gpointer user_data)
+void
+greeter_set_file_descriptors (Greeter *greeter, int to_greeter_fd, int from_greeter_fd)
 {
-    int to_greeter_pipe[2], from_greeter_pipe[2];
-    int to_greeter_output, from_greeter_input;
-    gboolean result;
     GError *error = NULL;
 
-    /* Create a pipe to talk with the greeter */
-    if (pipe (to_greeter_pipe) != 0 || pipe (from_greeter_pipe) != 0)
-    {
-        g_warning ("Failed to create pipes: %s", strerror (errno));
-        return FALSE;
-    }
-    to_greeter_output = to_greeter_pipe[0];
-    greeter->priv->to_greeter_input = to_greeter_pipe[1];
+    g_return_if_fail (greeter != NULL);
+    g_return_if_fail (greeter->priv->to_greeter_input < 0);
+    g_return_if_fail (greeter->priv->from_greeter_output < 0);
+
+    greeter->priv->to_greeter_input = to_greeter_fd;  
     greeter->priv->to_greeter_channel = g_io_channel_unix_new (greeter->priv->to_greeter_input);
     g_io_channel_set_encoding (greeter->priv->to_greeter_channel, NULL, &error);
     if (error)
         g_warning ("Failed to set encoding on to greeter channel to binary: %s\n", error->message);
     g_clear_error (&error);
-    greeter->priv->from_greeter_output = from_greeter_pipe[0];
-    from_greeter_input = from_greeter_pipe[1];
+    greeter->priv->from_greeter_output = from_greeter_fd;
     greeter->priv->from_greeter_channel = g_io_channel_unix_new (greeter->priv->from_greeter_output);
     g_io_channel_set_encoding (greeter->priv->from_greeter_channel, NULL, &error);
     if (error)
@@ -148,18 +139,6 @@ greeter_start (Greeter *greeter, gboolean (*setup_child_cb)(Greeter *greeter, in
     g_clear_error (&error);
     g_io_channel_set_buffered (greeter->priv->from_greeter_channel, FALSE);
     greeter->priv->from_greeter_watch = g_io_add_watch (greeter->priv->from_greeter_channel, G_IO_IN | G_IO_HUP, read_cb, greeter);
-
-    /* Don't allow the daemon end of the pipes to be accessed in child processes */
-    fcntl (greeter->priv->to_greeter_input, F_SETFD, FD_CLOEXEC);
-    fcntl (greeter->priv->from_greeter_output, F_SETFD, FD_CLOEXEC);
-
-    result = setup_child_cb (greeter, from_greeter_input, to_greeter_output, user_data);
-
-    /* Close the session ends of the pipe */
-    close (from_greeter_input);
-    close (to_greeter_output);
-
-    return result;
 }
 
 void
index a48a6d43d20450690076d8b43cf6c3972eff643b..19ddf86caa00da65e5c9e618cd4ef8d1c164bdeb 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef GREETER_H_
 #define GREETER_H_
 
+typedef struct Greeter Greeter;
+
 #include "session.h"
 
 G_BEGIN_DECLS
@@ -31,11 +33,11 @@ G_BEGIN_DECLS
 
 typedef struct GreeterPrivate GreeterPrivate;
 
-typedef struct
+struct Greeter
 {
     GObject         parent_instance;
     GreeterPrivate *priv;
-} Greeter;
+};
 
 typedef struct
 {
@@ -49,7 +51,7 @@ GType greeter_get_type (void);
 
 Greeter *greeter_new (void);
 
-gboolean greeter_start (Greeter *greeter, gboolean (*setup_child_cb)(Greeter *greeter, int input_fd, int output_fd, gpointer user_data), gpointer user_data);
+void greeter_set_file_descriptors (Greeter *greeter, int to_greeter_fd, int from_greeter_fd);
 
 void greeter_stop (Greeter *greeter);
 
index bc1544cbe194b02cc340f9a24c22c699dc46f255..2b3a00f40829a7bb60782e1e322f686cc22ff803 100644 (file)
@@ -1250,34 +1250,36 @@ greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *sessi
 
     /* If can re-use the display server, stop the greeter first */
     greeter_session = get_greeter_session (seat, greeter);
-    display_server = session_get_display_server (greeter_session);
-    if (!greeter_get_resettable (greeter) &&
-        can_share_display_server (seat, display_server) &&
-        strcmp (display_server_get_session_type (display_server), session_get_session_type (session)) == 0)
+    if (greeter_session)
     {
-        l_debug (seat, "Stopping greeter; display server will be re-used for user session");
+        display_server = session_get_display_server (greeter_session);
+        if (display_server &&
+            !greeter_get_resettable (greeter) &&
+            can_share_display_server (seat, display_server) &&
+            strcmp (display_server_get_session_type (display_server), session_get_session_type (session)) == 0)
+        {
+            l_debug (seat, "Stopping greeter; display server will be re-used for user session");
 
-        /* Run on the same display server after the greeter has stopped */
-        session_set_display_server (session, display_server);
+            /* Run on the same display server after the greeter has stopped */
+            session_set_display_server (session, display_server);
 
-        /* Stop the greeter */
-        session_stop (greeter_session);
+            /* Stop the greeter */
+            session_stop (greeter_session);
 
-        return TRUE;
+            return TRUE;
+        }
     }
+
     /* Otherwise start a new display server for this session */
-    else
+    display_server = create_display_server (seat, session);
+    session_set_display_server (session, display_server);
+    if (!start_display_server (seat, display_server))
     {
-        display_server = create_display_server (seat, session);
-        session_set_display_server (session, display_server);
-        if (!start_display_server (seat, display_server))
-        {
-            l_debug (seat, "Failed to start display server for new session");
-            return FALSE;
-        }
-
-        return TRUE;
+        l_debug (seat, "Failed to start display server for new session");
+        return FALSE;
     }
+
+    return TRUE;
 }
 
 static GreeterSession *
@@ -1879,10 +1881,36 @@ seat_real_create_greeter_session (Seat *seat)
     return greeter_session_new ();
 }
 
+static Session *
+create_session_cb (Greeter *greeter, Seat *seat)
+{
+    return create_session (seat, FALSE);
+}
+
+static Greeter *
+create_greeter_cb (Session *session, Seat *seat)
+{
+    Greeter *greeter;
+
+    greeter = greeter_new ();
+    greeter_set_pam_services (greeter,
+                              seat_get_string_property (seat, "pam-service"),
+                              seat_get_string_property (seat, "pam-autologin-service"));
+    g_signal_connect (greeter, GREETER_SIGNAL_CREATE_SESSION, G_CALLBACK (create_session_cb), seat);
+    g_signal_connect (greeter, GREETER_SIGNAL_START_SESSION, G_CALLBACK (greeter_start_session_cb), seat);
+
+    return greeter;
+}
+
 static Session *
 seat_real_create_session (Seat *seat)
 {
-    return session_new ();
+    Session *session;
+
+    session = session_new ();
+    g_signal_connect (session, SESSION_SIGNAL_CREATE_GREETER, G_CALLBACK (create_greeter_cb), seat);
+
+    return session;
 }
 
 static void
index 31de204b498a112a445376035683c82a62017234..c0476d15027f96cd8c316095670175b9f42287a3 100644 (file)
@@ -21,6 +21,9 @@ struct SessionConfigPrivate
 
     /* Command to run */
     gchar *command;
+
+    /* TRUE if can run a greeter inside the session */
+    gboolean allow_greeter;
 };
 
 G_DEFINE_TYPE (SessionConfig, session_config, G_TYPE_OBJECT);
@@ -64,6 +67,7 @@ session_config_new_from_file (const gchar *filename, const gchar *default_sessio
             config->priv->desktop_names[1] = NULL;
         }
     }
+    config->priv->allow_greeter = g_key_file_get_boolean (desktop_file, G_KEY_FILE_DESKTOP_GROUP, "X-LightDM-Allow-Greeter", NULL);
 
     g_key_file_free (desktop_file);
 
@@ -91,6 +95,13 @@ session_config_get_desktop_names (SessionConfig *config)
     return config->priv->desktop_names;
 }
 
+gboolean
+session_config_get_allow_greeter (SessionConfig *config)
+{
+    g_return_val_if_fail (config != NULL, FALSE);
+    return config->priv->allow_greeter;
+}
+
 static void
 session_config_init (SessionConfig *config)
 {
index 2de65b322d104b513e6fe1681ced7cd1032ccea4..8a86a5fb6b4214a4104f19ceace45ed06a7d76d8 100644 (file)
@@ -44,6 +44,8 @@ const gchar *session_config_get_session_type (SessionConfig *config);
 
 gchar **session_config_get_desktop_names (SessionConfig *config);
 
+gboolean session_config_get_allow_greeter (SessionConfig *config);
+
 G_END_DECLS
 
 #endif /* SESSION_CONFIG_H_ */
index 86cc0ae6c77a4ead70427cf0b77a2136267e3fc2..92a59bbd28c09c641909cd88a240db7f9cbe9f74 100644 (file)
 #include "login1.h"
 #include "guest-account.h"
 #include "shared-data-manager.h"
+#include "greeter-socket.h"
 
 enum {
+    CREATE_GREETER,
     GOT_MESSAGES,
     AUTHENTICATION_COMPLETE,
     STOPPED,
@@ -94,6 +96,9 @@ struct SessionPrivate
     XAuthority *x_authority;
     gboolean x_authority_use_system_location;
 
+    /* Socket to allow greeters to connect to (if allowed) */
+    GreeterSocket *greeter_socket;
+
     /* Remote host this session is being controlled from */
     gchar *remote_host_name;
 
@@ -151,7 +156,7 @@ const gchar *
 session_get_session_type (Session *session)
 {
     g_return_val_if_fail (session != NULL, NULL);
-    return session_config_get_session_type (session_get_config (session));
+    return session_config_get_session_type (session->priv->config);
 }
 
 void
@@ -551,6 +556,14 @@ session_get_is_started (Session *session)
     return session->priv->pid != 0;
 }
 
+static Greeter *
+create_greeter_cb (GreeterSocket *socket, Session *session)
+{
+    Greeter *greeter;
+    g_signal_emit (session, signals[CREATE_GREETER], 0, &greeter);
+    return greeter;
+}
+
 static gboolean
 session_real_start (Session *session)
 {
@@ -589,6 +602,38 @@ session_real_start (Session *session)
             return FALSE;
     }
 
+    /* Open socket to allow in-session greeter */
+    if (session->priv->config && session_config_get_allow_greeter (session->priv->config))
+    {
+        gchar *run_dir, *dir, *path;
+        GError *error = NULL;
+
+        run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
+        dir = g_build_filename (run_dir, session->priv->username, NULL);
+        g_free (run_dir);
+
+        if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
+            l_warning (session, "Failed to create greeter socket dir %s: %s", dir, strerror (errno));
+        if (getuid () == 0)
+        {
+            if (chown (dir, user_get_uid (session_get_user (session)), user_get_gid (session_get_user (session))) < 0)
+                l_warning (session, "Failed to set ownership of greeter socket dir: %s", strerror (errno));
+        }
+
+        path = g_build_filename (dir, "greeter-socket", NULL);
+        session->priv->greeter_socket = greeter_socket_new (path);
+        g_signal_connect (session->priv->greeter_socket, GREETER_SOCKET_SIGNAL_CREATE_GREETER, G_CALLBACK (create_greeter_cb), session);
+        session_set_env (session, "LIGHTDM_GREETER_PIPE", path);
+        g_free (path);
+        g_free (dir);
+
+        if (!greeter_socket_start (session->priv->greeter_socket, &error))
+        {
+            l_warning (session, "Failed to start greeter socket: %s\n", error->message);
+            g_clear_error (&error);
+        }
+    }
+
     /* Run the child */
     arg0 = g_strdup_printf ("%d", to_child_output);
     arg1 = g_strdup_printf ("%d", from_child_input);
@@ -975,6 +1020,16 @@ session_class_init (SessionClass *klass)
 
     g_type_class_add_private (klass, sizeof (SessionPrivate));
 
+    signals[CREATE_GREETER] =
+        g_signal_new (SESSION_SIGNAL_CREATE_GREETER,
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (SessionClass, create_greeter),
+                      g_signal_accumulator_first_wins,
+                      NULL,
+                      NULL,
+                      GREETER_TYPE, 0);
+
     signals[GOT_MESSAGES] =
         g_signal_new (SESSION_SIGNAL_GOT_MESSAGES,
                       G_TYPE_FROM_CLASS (klass),
index 5f57885a31c852a9778e43a1c83d469af7c88c74..320ac4963f988bd7a23e91ad6da5d64ac4a61bbc 100644 (file)
 
 typedef struct Session Session;
 
+typedef enum
+{
+    SESSION_TYPE_LOCAL,
+    SESSION_TYPE_REMOTE
+} SessionType;
+
 #include "session-config.h"
 #include "display-server.h"
 #include "accounts.h"
 #include "x-authority.h"
 #include "logger.h"
 #include "log-file.h"
+#include "greeter.h"
 
 G_BEGIN_DECLS
 
@@ -32,6 +39,7 @@ G_BEGIN_DECLS
 #define SESSION_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), SESSION_TYPE, SessionClass))
 #define SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SESSION_TYPE, SessionClass))
 
+#define SESSION_SIGNAL_CREATE_GREETER          "create-greeter"
 #define SESSION_SIGNAL_GOT_MESSAGES            "got-messages"
 #define SESSION_SIGNAL_AUTHENTICATION_COMPLETE "authentication-complete"
 #define SESSION_SIGNAL_STOPPED                 "stopped"
@@ -52,17 +60,12 @@ typedef struct
     void (*run)(Session *session);
     void (*stop)(Session *session);
 
+    Greeter *(*create_greeter)(Session *session);
     void (*got_messages)(Session *session);
     void (*authentication_complete)(Session *session);
     void (*stopped)(Session *session);
 } SessionClass;
 
-typedef enum
-{
-    SESSION_TYPE_LOCAL,
-    SESSION_TYPE_REMOTE
-} SessionType;
-
 GType session_get_type (void);
 
 Session *session_new (void);
index bd4e8ebf84daba8a3b73a0f3608c238e1fb817ad..fd94c9aa99440ee576b019a5b473bb0f3829a0e2 100644 (file)
@@ -167,6 +167,7 @@ TESTS = \
        test-switch-to-user-logout-inactive \
        test-switch-to-user-resettable \
        test-switch-to-users \
+       test-session-greeter \        
        test-vnc-login \
        test-vnc-command \
        test-vnc-dimensions \
@@ -385,6 +386,7 @@ EXTRA_DIST = \
        data/keys.conf \
        data/sessions/alternative.desktop \
        data/sessions/default.desktop \
+       data/sessions/greeter.desktop \
        data/sessions/mir.desktop \
        data/sessions/named.desktop \
        data/sessions/named-legacy.desktop \
@@ -538,6 +540,7 @@ EXTRA_DIST = \
        scripts/script-hook-greeter-setup-missing.conf \
        scripts/script-hook-session-setup-fail.conf \
        scripts/script-hook-session-setup-missing.conf \
+       scripts/session-greeter.conf \
        scripts/session-stdout.conf \
        scripts/session-stderr.conf \
        scripts/session-stderr-multi-write.conf \
diff --git a/tests/data/sessions/greeter.desktop b/tests/data/sessions/greeter.desktop
new file mode 100644 (file)
index 0000000..9937c0c
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Name=Test Session with greeter
+Comment=LightDM test session that can run a greeter inside
+Exec=test-session
+X-LightDM-Allow-Greeter=true
diff --git a/tests/scripts/session-greeter.conf b/tests/scripts/session-greeter.conf
new file mode 100644 (file)
index 0000000..090e339
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# Check can run greeter inside session
+#
+
+[Seat:*]
+autologin-user=have-password1
+user-session=greeter
+
+#?*START-DAEMON
+#?RUNNER DAEMON-START
+
+# X server starts
+#?XSERVER-0 START VT=7 SEAT=seat0
+
+# Daemon connects when X server is ready
+#?*XSERVER-0 INDICATE-READY
+#?XSERVER-0 INDICATE-READY
+#?XSERVER-0 ACCEPT-CONNECT
+
+# Session starts
+#?SESSION-X-0 START XDG_SEAT=seat0 XDG_VTNR=7 XDG_GREETER_DATA_DIR=.*/have-password1 XDG_SESSION_TYPE=x11 XDG_SESSION_DESKTOP=greeter USER=have-password1
+#?LOGIN1 ACTIVATE-SESSION SESSION=c0
+#?XSERVER-0 ACCEPT-CONNECT
+#?SESSION-X-0 CONNECT-XSERVER
+
+# Start greeter inside session
+#?*SESSION-X-0 GREETER-START
+#?SESSION-X-0 GREETER-STARTED
+
+# Log into account with a password
+#?*SESSION-X-0 GREETER-AUTHENTICATE USERNAME=have-password2
+#?SESSION-X-0 GREETER-SHOW-PROMPT TEXT="Password:"
+#?*SESSION-X-0 GREETER-RESPOND TEXT="password"
+#?SESSION-X-0 GREETER-AUTHENTICATION-COMPLETE USERNAME=have-password2 AUTHENTICATED=TRUE
+#?*SESSION-X-0 GREETER-START-SESSION
+
+# New X server starts
+#?XSERVER-1 START VT=8 SEAT=seat0
+#?*XSERVER-1 INDICATE-READY
+#?XSERVER-1 INDICATE-READY
+#?XSERVER-1 ACCEPT-CONNECT
+
+# New session starts
+#?SESSION-X-1 START XDG_SEAT=seat0 XDG_VTNR=8 XDG_GREETER_DATA_DIR=/home/bob/bzr/lightdm/session-greeter/install/var/lib/lightdm-data/have-password2 XDG_SESSION_TYPE=x11 XDG_SESSION_DESKTOP=greeter USER=have-password2
+#?XSERVER-1 ACCEPT-CONNECT
+#?SESSION-X-1 CONNECT-XSERVER
+
+# Switch to new session
+#?VT ACTIVATE VT=8
+#?LOGIN1 LOCK-SESSION SESSION=c0
+#?LOGIN1 ACTIVATE-SESSION SESSION=c1
+
+# Cleanup
+#?*STOP-DAEMON
+#?SESSION-X-0 TERMINATE SIGNAL=15
+#?XSERVER-1 TERMINATE SIGNAL=15
+#?SESSION-X-1 TERMINATE SIGNAL=15
+#?XSERVER-0 TERMINATE SIGNAL=15
+#?RUNNER DAEMON-EXIT STATUS=0
index d769f0279fccdbb34e32c39081563bbab39de563..bd42022ee52f68e2e8ca6486bbb00c07fc894f6f 100644 (file)
@@ -195,11 +195,14 @@ test_qt5_greeter_LDADD = \
 
 test_session_SOURCES = test-session.c status.c status.h
 test_session_CFLAGS = \
+       -I$(top_srcdir)/liblightdm-gobject \
        $(WARN_CFLAGS) \
        $(GLIB_CFLAGS) \
        $(GIO_UNIX_CFLAGS) \
        $(XCB_CFLAGS)
 test_session_LDADD = \
+       -L$(top_builddir)/liblightdm-gobject \
+       -llightdm-gobject-1 \
        $(GLIB_LIBS) \
        $(GIO_UNIX_LIBS) \
        $(XCB_LIBS)
index c93aa202b160a8801ccb135a30a404ececca1239..d4ec11676b23d482684ac1509fa9971d27718ee1 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <netinet/in.h>
 #include <pwd.h>
 #include <unistd.h>
@@ -608,35 +609,48 @@ int
 bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
 {
     int port = 0, redirected_port = 0;
+    const char *path;
     int (*_bind) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
     const struct sockaddr *modified_addr = addr;
-    struct sockaddr_in temp_addr;
-    struct sockaddr_in6 temp_addr6;
+    struct sockaddr_in temp_addr_in;
+    struct sockaddr_in6 temp_addr_in6;
+    struct sockaddr_un temp_addr_un;
     int retval;
 
     _bind = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "bind");
 
     switch (addr->sa_family)
     {
+    case AF_UNIX:
+        path = ((const struct sockaddr_un *) addr)->sun_path;
+        if (path[0] != '\0')
+        {
+            gchar *new_path = redirect_path (path);
+            memcpy (&temp_addr_un, addr, sizeof (struct sockaddr_un));
+            strncpy (temp_addr_un.sun_path, new_path, sizeof (temp_addr_un.sun_path) - 1);
+            g_free (new_path);
+            modified_addr = (struct sockaddr *) &temp_addr_un;
+        }
+        break;
     case AF_INET:
         port = ntohs (((const struct sockaddr_in *) addr)->sin_port);
         redirected_port = find_port_redirect (port);
-        memcpy (&temp_addr, addr, sizeof (struct sockaddr_in));
-        modified_addr = (struct sockaddr *) &temp_addr;
+        memcpy (&temp_addr_in, addr, sizeof (struct sockaddr_in));
+        modified_addr = (struct sockaddr *) &temp_addr_in;
         if (redirected_port != 0)
-            temp_addr.sin_port = htons (redirected_port);
+            temp_addr_in.sin_port = htons (redirected_port);
         else
-            temp_addr.sin_port = 0;
+            temp_addr_in.sin_port = 0;
         break;
     case AF_INET6:
         port = ntohs (((const struct sockaddr_in6 *) addr)->sin6_port);
         redirected_port = find_port_redirect (port);
-        memcpy (&temp_addr6, addr, sizeof (struct sockaddr_in6));
-        modified_addr = (struct sockaddr *) &temp_addr6;
+        memcpy (&temp_addr_in6, addr, sizeof (struct sockaddr_in6));
+        modified_addr = (struct sockaddr *) &temp_addr_in6;
         if (redirected_port != 0)
-            temp_addr6.sin6_port = htons (redirected_port);
+            temp_addr_in6.sin6_port = htons (redirected_port);
         else
-            temp_addr6.sin6_port = 0;
+            temp_addr_in6.sin6_port = 0;
         break;
     }
 
@@ -646,20 +660,20 @@ bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
     switch (addr->sa_family)
     {
     case AF_INET:
-        temp_addr_len = sizeof (temp_addr);
-        getsockname (sockfd, &temp_addr, &temp_addr_len);
+        temp_addr_len = sizeof (temp_addr_in);
+        getsockname (sockfd, &temp_addr_in, &temp_addr_len);
         if (redirected_port == 0)
         {
-            redirected_port = ntohs (temp_addr.sin_port);
+            redirected_port = ntohs (temp_addr_in.sin_port);
             add_port_redirect (port, redirected_port);
         }
         break;
     case AF_INET6:
-        temp_addr_len = sizeof (temp_addr6);
-        getsockname (sockfd, &temp_addr6, &temp_addr_len);
+        temp_addr_len = sizeof (temp_addr_in6);
+        getsockname (sockfd, &temp_addr_in6, &temp_addr_len);
         if (redirected_port == 0)
         {
-            redirected_port = ntohs (temp_addr6.sin6_port);
+            redirected_port = ntohs (temp_addr_in6.sin6_port);
             add_port_redirect (port, redirected_port);
         }
         break;
@@ -668,27 +682,42 @@ bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
     return retval;
 }
 
+#include <ctype.h>
+
 int
 connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
 {
     int port, redirected_port;
+    const char *path;
     const struct sockaddr *modified_addr = addr;
-    struct sockaddr_in temp_addr;
-    struct sockaddr_in6 temp_addr6;
+    struct sockaddr_in temp_addr_in;
+    struct sockaddr_in6 temp_addr_in6;
+    struct sockaddr_un temp_addr_un;
     int (*_connect) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
 
     _connect = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "connect");
 
     switch (addr->sa_family)
     {
+    case AF_UNIX:
+        path = ((const struct sockaddr_un *) addr)->sun_path;
+        if (path[0] != '\0') 
+        {
+            gchar *new_path = redirect_path (path);
+            memcpy (&temp_addr_un, addr, addrlen);
+            strncpy (temp_addr_un.sun_path, new_path, sizeof (temp_addr_un.sun_path) - 1);
+            g_free (new_path);
+            modified_addr = (struct sockaddr *) &temp_addr_un;
+        }
+        break;
     case AF_INET:
         port = ntohs (((const struct sockaddr_in *) addr)->sin_port);
         redirected_port = find_port_redirect (port);
         if (redirected_port != 0) 
         {
-            memcpy (&temp_addr, addr, sizeof (struct sockaddr_in));
-            temp_addr.sin_port = htons (redirected_port);
-            modified_addr = (struct sockaddr *) &temp_addr;
+            memcpy (&temp_addr_in, addr, sizeof (struct sockaddr_in));
+            temp_addr_in.sin_port = htons (redirected_port);
+            modified_addr = (struct sockaddr *) &temp_addr_in;
         }
         break;
     case AF_INET6:
@@ -696,9 +725,9 @@ connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
         redirected_port = find_port_redirect (port);
         if (redirected_port != 0) 
         {
-            memcpy (&temp_addr6, addr, sizeof (struct sockaddr_in6));
-            temp_addr6.sin6_port = htons (redirected_port);
-            modified_addr = (struct sockaddr *) &temp_addr6;
+            memcpy (&temp_addr_in6, addr, sizeof (struct sockaddr_in6));
+            temp_addr_in6.sin6_port = htons (redirected_port);
+            modified_addr = (struct sockaddr *) &temp_addr_in6;
         }
         break;
     }
@@ -710,23 +739,36 @@ ssize_t
 sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
 {
     int port, redirected_port;
+    const char *path;
     const struct sockaddr *modified_addr = dest_addr;
-    struct sockaddr_in temp_addr;
-    struct sockaddr_in6 temp_addr6;
+    struct sockaddr_in temp_addr_in;
+    struct sockaddr_in6 temp_addr_in6;
+    struct sockaddr_un temp_addr_un;  
     ssize_t (*_sendto) (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
 
     _sendto = (ssize_t (*)(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "sendto");
 
     switch (dest_addr->sa_family)
     {
+    case AF_UNIX:
+        path = ((const struct sockaddr_un *) dest_addr)->sun_path;
+        if (path[0] != '\0') 
+        {
+            gchar *new_path = redirect_path (path);
+            memcpy (&temp_addr_un, dest_addr, sizeof (struct sockaddr_un));
+            strncpy (temp_addr_un.sun_path, new_path, sizeof (temp_addr_un.sun_path) - 1);
+            g_free (new_path);
+            modified_addr = (struct sockaddr *) &temp_addr_un;
+        }
+        break;
     case AF_INET:
         port = ntohs (((const struct sockaddr_in *) dest_addr)->sin_port);
         redirected_port = find_port_redirect (port);
         if (redirected_port != 0) 
         {
-            memcpy (&temp_addr, dest_addr, sizeof (struct sockaddr_in));
-            temp_addr.sin_port = htons (redirected_port);
-            modified_addr = (struct sockaddr *) &temp_addr;
+            memcpy (&temp_addr_in, dest_addr, sizeof (struct sockaddr_in));
+            temp_addr_in.sin_port = htons (redirected_port);
+            modified_addr = (struct sockaddr *) &temp_addr_in;
         }
         break;
     case AF_INET6:
@@ -734,9 +776,9 @@ sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockadd
         redirected_port = find_port_redirect (port);
         if (redirected_port != 0) 
         {
-            memcpy (&temp_addr6, dest_addr, sizeof (struct sockaddr_in6));
-            temp_addr6.sin6_port = htons (redirected_port);
-            modified_addr = (struct sockaddr *) &temp_addr6;
+            memcpy (&temp_addr_in6, dest_addr, sizeof (struct sockaddr_in6));
+            temp_addr_in6.sin6_port = htons (redirected_port);
+            modified_addr = (struct sockaddr *) &temp_addr_in6;
         }
         break;
     }
index 1455d73f6a17634ef3c03b45589f8182585e8dc3..f3270b5d1d636843860a8fd117700d8d9b953a41 100644 (file)
@@ -11,6 +11,7 @@
 #include <gio/gio.h>
 #include <glib-unix.h>
 #include <glib/gstdio.h>
+#include <lightdm/greeter.h>
 
 #include "status.h"
 
@@ -24,6 +25,8 @@ static GKeyFile *config;
 
 static xcb_connection_t *connection;
 
+static LightDMGreeter *greeter = NULL;
+
 static gboolean
 sigint_cb (gpointer user_data)
 {
@@ -40,9 +43,37 @@ sigterm_cb (gpointer user_data)
     return TRUE;
 }
 
+static void
+show_message_cb (LightDMGreeter *greeter, const gchar *text, LightDMMessageType type)
+{
+    status_notify ("%s GREETER-SHOW-MESSAGE TEXT=\"%s\"", session_id, text);
+}
+
+static void
+show_prompt_cb (LightDMGreeter *greeter, const gchar *text, LightDMPromptType type)
+{
+    status_notify ("%s GREETER-SHOW-PROMPT TEXT=\"%s\"", session_id, text);
+}
+
+static void
+authentication_complete_cb (LightDMGreeter *greeter)
+{
+    if (lightdm_greeter_get_authentication_user (greeter))
+        status_notify ("%s GREETER-AUTHENTICATION-COMPLETE USERNAME=%s AUTHENTICATED=%s",
+                       session_id,
+                       lightdm_greeter_get_authentication_user (greeter),
+                       lightdm_greeter_get_is_authenticated (greeter) ? "TRUE" : "FALSE");
+    else
+        status_notify ("%s GREETER-AUTHENTICATION-COMPLETE AUTHENTICATED=%s",
+                       session_id,
+                       lightdm_greeter_get_is_authenticated (greeter) ? "TRUE" : "FALSE");
+}
+
 static void
 request_cb (const gchar *name, GHashTable *params)
 {
+    GError *error = NULL;
+
     if (!name)
     {
         g_main_loop_quit (loop);
@@ -224,6 +255,51 @@ request_cb (const gchar *name, GHashTable *params)
         else
             status_notify ("%s WRITE-SHARED-DATA ERROR=NO_XDG_GREETER_DATA_DIR", session_id);
     }
+
+    else if (strcmp (name, "GREETER-START") == 0)
+    {
+        GError *error = NULL;
+
+        g_assert (greeter == NULL);
+        greeter = lightdm_greeter_new ();
+        g_signal_connect (greeter, LIGHTDM_GREETER_SIGNAL_SHOW_MESSAGE, G_CALLBACK (show_message_cb), NULL);
+        g_signal_connect (greeter, LIGHTDM_GREETER_SIGNAL_SHOW_PROMPT, G_CALLBACK (show_prompt_cb), NULL);
+        g_signal_connect (greeter, LIGHTDM_GREETER_SIGNAL_AUTHENTICATION_COMPLETE, G_CALLBACK (authentication_complete_cb), NULL);
+        if (lightdm_greeter_connect_to_daemon_sync (greeter, &error))
+            status_notify ("%s GREETER-STARTED", session_id);
+        else
+        {
+            status_notify ("%s GREETER-FAILED ERROR=%s", session_id, error->message);
+            g_clear_error (&error);
+        }
+    }
+
+    else if (strcmp (name, "GREETER-AUTHENTICATE") == 0)
+    {
+        if (!lightdm_greeter_authenticate (greeter, g_hash_table_lookup (params, "USERNAME"), &error))
+        {
+            status_notify ("%s FAIL-AUTHENTICATE ERROR=%s", session_id, error->message);
+            g_clear_error (&error);
+        }
+    }
+
+    else if (strcmp (name, "GREETER-RESPOND") == 0)
+    {
+        if (!lightdm_greeter_respond (greeter, g_hash_table_lookup (params, "TEXT"), &error))
+        {
+            status_notify ("%s FAIL-RESPOND ERROR=%s", session_id, error->message);
+            g_clear_error (&error);
+        }
+    }
+
+    else if (strcmp (name, "GREETER-START-SESSION") == 0)
+    {
+        if (!lightdm_greeter_start_session_sync (greeter, g_hash_table_lookup (params, "SESSION"), &error))
+        {
+            status_notify ("%s FAIL-START-SESSION ERROR=%s", session_id, error->message);
+            g_clear_error (&error);          
+        }
+    }
 }
 
 int
diff --git a/tests/test-session-greeter b/tests/test-session-greeter
new file mode 100755 (executable)
index 0000000..6b53f42
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+./src/dbus-env ./src/test-runner session-greeter test-gobject-greeter