diff options
author | Robert Ancell <robert.ancell@canonical.com> | 2016-06-29 15:15:44 +1200 |
---|---|---|
committer | Robert Ancell <robert.ancell@canonical.com> | 2016-06-29 15:15:44 +1200 |
commit | 56d22cc50e4b8ecd96e6c828b2d4e3c3a5efae51 (patch) | |
tree | bc0b9334e111f05ec9efca2646b53b3d83c641e8 | |
parent | 420353d31b89352674a08d0e7dd48f0486019e5c (diff) | |
download | lightdm-git-56d22cc50e4b8ecd96e6c828b2d4e3c3a5efae51.tar.gz |
Allow greeters to run in-session
-rw-r--r-- | liblightdm-gobject/greeter.c | 113 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/greeter-session.c | 42 | ||||
-rw-r--r-- | src/greeter-socket.c | 152 | ||||
-rw-r--r-- | src/greeter-socket.h | 50 | ||||
-rw-r--r-- | src/greeter.c | 37 | ||||
-rw-r--r-- | src/greeter.h | 8 | ||||
-rw-r--r-- | src/seat.c | 70 | ||||
-rw-r--r-- | src/session-config.c | 11 | ||||
-rw-r--r-- | src/session-config.h | 2 | ||||
-rw-r--r-- | src/session.c | 57 | ||||
-rw-r--r-- | src/session.h | 15 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/data/sessions/greeter.desktop | 5 | ||||
-rw-r--r-- | tests/scripts/session-greeter.conf | 59 | ||||
-rw-r--r-- | tests/src/Makefile.am | 3 | ||||
-rw-r--r-- | tests/src/libsystem.c | 106 | ||||
-rw-r--r-- | tests/src/test-session.c | 65 | ||||
-rwxr-xr-x | tests/test-session-greeter | 2 |
19 files changed, 665 insertions, 137 deletions
diff --git a/liblightdm-gobject/greeter.c b/liblightdm-gobject/greeter.c index 5e6996d3..6c279b2c 100644 --- a/liblightdm-gobject/greeter.c +++ b/liblightdm-gobject/greeter.c @@ -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" @@ -50,6 +52,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; @@ -141,6 +146,8 @@ static void request_iface_init (GAsyncResultIface *iface); #define REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), request_get_type (), Request)) G_DEFINE_TYPE_WITH_CODE (Request, request, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, request_iface_init)); +static gboolean from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data); + GType lightdm_prompt_type_get_type (void) { @@ -347,6 +354,66 @@ get_message_length (guint8 *message, gsize message_length) } static gboolean +connect_to_daemon (LightDMGreeter *greeter) +{ + LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); + const gchar *to_server_fd, *from_server_fd, *pipe_path; + GError *error = NULL; + + 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"); + 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) + { + g_warning ("Failed to connect to greeter socket %s: %s", pipe_path, error->message); + g_clear_error (&error); + 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_warning ("Unable to determine socket to daemon"); + return FALSE; + } + + 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) || + !g_io_channel_set_encoding (priv->from_server_channel, NULL, &error)) + { + g_warning ("Failed to set encoding on from server channel to binary: %s", error->message); + g_clear_error (&error); + return FALSE; + } + + return TRUE; +} + +static gboolean send_message (LightDMGreeter *greeter, guint8 *message, gsize message_length) { LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); @@ -355,7 +422,7 @@ send_message (LightDMGreeter *greeter, guint8 *message, gsize message_length) GError *error = NULL; guint32 stated_length; - if (!priv->to_server_channel) + if (!connect_to_daemon (greeter)) return FALSE; /* Double check that we're sending well-formed messages. If we say we're @@ -381,6 +448,8 @@ send_message (LightDMGreeter *greeter, guint8 *message, gsize message_length) if (error) g_warning ("Error writing to daemon: %s", error->message); g_clear_error (&error); + if (status == G_IO_STATUS_AGAIN) + continue; if (status != G_IO_STATUS_NORMAL) return FALSE; data_length -= n_written; @@ -660,8 +729,8 @@ recv_message (LightDMGreeter *greeter, gsize *length, gboolean block) guint8 *buffer; GError *error = NULL; - if (!priv->from_server_channel) - return NULL; + if (!connect_to_daemon (greeter)) + return FALSE; /* Read the header, or the whole message if we already have that */ n_to_read = HEADER_SIZE; @@ -679,7 +748,12 @@ recv_message (LightDMGreeter *greeter, gsize *length, gboolean block) if (error) g_warning ("Error reading from server: %s", error->message); g_clear_error (&error); - if (status != G_IO_STATUS_NORMAL) + if (status == G_IO_STATUS_AGAIN) + { + if (block) + continue; + } + else if (status != G_IO_STATUS_NORMAL) break; g_debug ("Read %zi bytes from daemon", n_read); @@ -1590,39 +1664,9 @@ static void lightdm_greeter_init (LightDMGreeter *greeter) { LightDMGreeterPrivate *priv = GET_PRIVATE (greeter); - const gchar *fd; priv->read_buffer = g_malloc (HEADER_SIZE); priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - fd = g_getenv ("LIGHTDM_TO_SERVER_FD"); - if (fd) - { - GError *error = NULL; - - priv->to_server_channel = g_io_channel_unix_new (atoi (fd)); - g_io_channel_set_encoding (priv->to_server_channel, NULL, &error); - if (error) - g_warning ("Failed to set encoding on to server channel to binary: %s\n", error->message); - g_clear_error (&error); - } - else - g_warning ("No LIGHTDM_TO_SERVER_FD environment variable"); - - fd = g_getenv ("LIGHTDM_FROM_SERVER_FD"); - if (fd) - { - GError *error = NULL; - - priv->from_server_channel = g_io_channel_unix_new (atoi (fd)); - g_io_channel_set_encoding (priv->from_server_channel, NULL, &error); - if (error) - g_warning ("Failed to set encoding on from server channel to binary: %s\n", error->message); - g_clear_error (&error); - g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter); - } - else - g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable"); } static void @@ -1699,6 +1743,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) diff --git a/src/Makefile.am b/src/Makefile.am index dc6d40dc..849ba1d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/greeter-session.c b/src/greeter-session.c index 0241dbdf..862253a0 100644 --- a/src/greeter-session.c +++ b/src/greeter-session.c @@ -13,6 +13,7 @@ #include <string.h> #include <errno.h> +#include <fcntl.h> #include "greeter-session.h" @@ -42,27 +43,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 index 00000000..4e2b7a33 --- /dev/null +++ b/src/greeter-socket.c @@ -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 index 00000000..da490bad --- /dev/null +++ b/src/greeter-socket.h @@ -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_ */ diff --git a/src/greeter.c b/src/greeter.c index 72eaa2e7..4534ee2f 100644 --- a/src/greeter.c +++ b/src/greeter.c @@ -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 diff --git a/src/greeter.h b/src/greeter.h index a48a6d43..19ddf86c 100644 --- a/src/greeter.h +++ b/src/greeter.h @@ -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); @@ -1243,34 +1243,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 * @@ -1873,9 +1875,35 @@ seat_real_create_greeter_session (Seat *seat) } 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 diff --git a/src/session-config.c b/src/session-config.c index 34f3a970..43073ea9 100644 --- a/src/session-config.c +++ b/src/session-config.c @@ -24,6 +24,9 @@ struct SessionConfigPrivate /* Compositor command to run (for type mir-container) */ gchar *compositor_command; + + /* TRUE if can run a greeter inside the session */ + gboolean allow_greeter; }; G_DEFINE_TYPE (SessionConfig, session_config, G_TYPE_OBJECT); @@ -68,6 +71,7 @@ session_config_new_from_file (const gchar *filename, const gchar *default_sessio } } config->priv->compositor_command = g_key_file_get_string (desktop_file, G_KEY_FILE_DESKTOP_GROUP, "X-LightDM-System-Compositor-Command", 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); @@ -102,6 +106,13 @@ session_config_get_compositor_command (SessionConfig *config) return config->priv->compositor_command; } +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) { diff --git a/src/session-config.h b/src/session-config.h index aea59599..827d3f5e 100644 --- a/src/session-config.h +++ b/src/session-config.h @@ -46,6 +46,8 @@ gchar **session_config_get_desktop_names (SessionConfig *config); const gchar *session_config_get_compositor_command (SessionConfig *config); +gboolean session_config_get_allow_greeter (SessionConfig *config); + G_END_DECLS #endif /* SESSION_CONFIG_H_ */ diff --git a/src/session.c b/src/session.c index 86cc0ae6..92a59bbd 100644 --- a/src/session.c +++ b/src/session.c @@ -27,8 +27,10 @@ #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), diff --git a/src/session.h b/src/session.h index 5f57885a..320ac496 100644 --- a/src/session.h +++ b/src/session.h @@ -18,12 +18,19 @@ 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); diff --git a/tests/Makefile.am b/tests/Makefile.am index 88b91b71..e46019ac 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -166,6 +166,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 \ @@ -382,6 +383,7 @@ EXTRA_DIST = \ data/keys.conf \ data/sessions/alternative.desktop \ data/sessions/default.desktop \ + data/sessions/greeter.desktop \ data/sessions/mir.desktop \ data/sessions/mir-container.desktop \ data/sessions/named.desktop \ @@ -536,6 +538,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 index 00000000..9937c0c4 --- /dev/null +++ b/tests/data/sessions/greeter.desktop @@ -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 index 00000000..b18d4d50 --- /dev/null +++ b/tests/scripts/session-greeter.conf @@ -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=.*/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 diff --git a/tests/src/Makefile.am b/tests/src/Makefile.am index d769f027..bd42022e 100644 --- a/tests/src/Makefile.am +++ b/tests/src/Makefile.am @@ -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) diff --git a/tests/src/libsystem.c b/tests/src/libsystem.c index 43d83fb7..78e77bb4 100644 --- a/tests/src/libsystem.c +++ b/tests/src/libsystem.c @@ -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> @@ -616,35 +617,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; } @@ -654,20 +668,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; @@ -676,27 +690,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: @@ -704,9 +733,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; } @@ -718,23 +747,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: @@ -742,9 +784,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; } diff --git a/tests/src/test-session.c b/tests/src/test-session.c index 1455d73f..88be15ec 100644 --- a/tests/src/test-session.c +++ b/tests/src/test-session.c @@ -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) { @@ -41,8 +44,36 @@ sigterm_cb (gpointer user_data) } 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,40 @@ 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", session_id); + } + + else if (strcmp (name, "GREETER-AUTHENTICATE") == 0) + { + lightdm_greeter_authenticate (greeter, g_hash_table_lookup (params, "USERNAME")); + } + + else if (strcmp (name, "GREETER-RESPOND") == 0) + { + lightdm_greeter_respond (greeter, g_hash_table_lookup (params, "TEXT")); + } + + 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 index 00000000..6b53f429 --- /dev/null +++ b/tests/test-session-greeter @@ -0,0 +1,2 @@ +#!/bin/sh +./src/dbus-env ./src/test-runner session-greeter test-gobject-greeter |