display.h \
display-manager.c \
display-manager.h \
+ greeter.c \
+ greeter.h \
lightdm.c \
ldm-marshal.c \
ldm-marshal.h \
#include "pam-session.h"
#include "theme.h"
#include "ldm-marshal.h"
-#include "greeter-protocol.h"
+#include "greeter.h"
/* Length of time in milliseconds to wait for a session to load */
#define USER_SESSION_TIMEOUT 5000
-/* Length of time in milliseconds to wait for a greeter to quit */
-#define GREETER_QUIT_TIMEOUT 1000
-
enum {
START_GREETER,
END_GREETER,
/* PAM service to authenticate against */
gchar *pam_service;
- /* Pipe to communicate to greeter */
- int greeter_pipe[2];
- gchar *read_buffer;
- gsize n_read;
-
/* Greeter session process */
- Session *greeter_session;
- gboolean greeter_connected;
- guint greeter_quit_timeout;
+ Greeter *greeter_session;
PAMSession *greeter_pam_session;
gchar *greeter_ck_cookie;
session_command = g_strdup_printf ("%s '%s'", display->priv->session_wrapper, session_command);
g_free (old_command);
}
- display->priv->user_session = session_new (pam_session_get_username (display->priv->user_pam_session), session_command);
+ display->priv->user_session = session_new ();
+ session_set_username (display->priv->user_session, pam_session_get_username (display->priv->user_pam_session));
+ session_set_command (display->priv->user_session, session_command);
g_signal_connect (G_OBJECT (display->priv->user_session), "exited", G_CALLBACK (user_session_exited_cb), display);
g_signal_connect (G_OBJECT (display->priv->user_session), "terminated", G_CALLBACK (user_session_terminated_cb), display);
start_user_session (display, session, language);
}
-static void
-end_greeter_session (Display *display, gboolean clean_exit)
-{
- gboolean greeter_connected;
-
- if (display->priv->greeter_quit_timeout)
- {
- g_source_remove (display->priv->greeter_quit_timeout);
- display->priv->greeter_quit_timeout = 0;
- }
-
- g_signal_emit (display, signals[END_GREETER], 0, display->priv->greeter_session);
-
- greeter_connected = display->priv->greeter_connected;
-
- g_object_unref (display->priv->greeter_session);
- display->priv->greeter_session = NULL;
- display->priv->greeter_connected = FALSE;
-
- pam_session_end (display->priv->greeter_pam_session);
- g_object_unref (display->priv->greeter_pam_session);
- display->priv->greeter_pam_session = NULL;
-
- end_ck_session (display->priv->greeter_ck_cookie);
- g_free (display->priv->greeter_ck_cookie);
- display->priv->greeter_ck_cookie = NULL;
-
- if (!clean_exit)
- g_warning ("Greeter failed");
- else if (!greeter_connected)
- g_warning ("Greeter quit before connecting");
- else if (!display->priv->user_session)
- g_warning ("Greeter quit before session started");
- else
- return;
-
- // FIXME: Issue with greeter, don't want to start a new one, report error to user
-}
-
-static void
-write_int (Display *display, guint32 value)
-{
- g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (display->priv->greeter_session)), (const gchar *) &value, sizeof (value), NULL, NULL);
-}
-
-static void
-write_string (Display *display, const gchar *value)
-{
- write_int (display, strlen (value));
- g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (display->priv->greeter_session)), value, -1, NULL, NULL);
-}
-
-static void
-write_header (Display *display, guint32 id, guint32 length)
-{
- write_int (display, id);
- write_int (display, length);
-}
-
-static guint32
-int_length ()
-{
- return sizeof (guint32);
-}
-
-static guint32
-string_length (const gchar *value)
-{
- return int_length () + strlen (value);
-}
-
-static void
-flush (Display *display)
-{
- g_io_channel_flush (child_process_get_to_child_channel (CHILD_PROCESS (display->priv->greeter_session)), NULL);
-}
-
-static void
-handle_connect (Display *display)
-{
- gchar *theme;
-
- if (!display->priv->greeter_connected)
- {
- display->priv->greeter_connected = TRUE;
- g_debug ("Greeter connected");
- }
-
- theme = g_build_filename (THEME_DIR, display->priv->greeter_theme, "index.theme", NULL);
-
- write_header (display, GREETER_MESSAGE_CONNECTED, string_length (theme) + string_length (display->priv->default_layout) + string_length (display->priv->default_session) + string_length (display->priv->default_user ? display->priv->default_user : "") + int_length ());
- write_string (display, theme);
- write_string (display, display->priv->default_layout);
- write_string (display, display->priv->default_session);
- write_string (display, display->priv->default_user ? display->priv->default_user : "");
- write_int (display, display->priv->timeout);
- flush (display);
-
- g_free (theme);
-}
-
-static void
-pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Display *display)
-{
- int i;
- guint32 size;
-
- /* Respond to d-bus query with messages */
- g_debug ("Prompt greeter with %d message(s)", num_msg);
- size = int_length ();
- for (i = 0; i < num_msg; i++)
- size += int_length () + string_length (msg[i]->msg);
- write_header (display, GREETER_MESSAGE_PROMPT_AUTHENTICATION, size);
- write_int (display, num_msg);
- for (i = 0; i < num_msg; i++)
- {
- write_int (display, msg[i]->msg_style);
- write_string (display, msg[i]->msg);
- }
- flush (display);
-}
-
-static void
-authenticate_result_cb (PAMSession *session, int result, Display *display)
-{
- g_debug ("Authenticate result for user %s: %s", pam_session_get_username (display->priv->user_pam_session), pam_session_strerror (display->priv->user_pam_session, result));
-
- if (result == PAM_SUCCESS)
- {
- run_script ("PostLogin");
- pam_session_authorize (session);
- }
-
- /* Respond to D-Bus request */
- write_header (display, GREETER_MESSAGE_END_AUTHENTICATION, int_length ());
- write_int (display, result);
- flush (display);
-}
-
-static void
-session_started_cb (PAMSession *session, Display *display)
-{
- display->priv->user_ck_cookie = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
-}
-
-static void
-handle_start_authentication (Display *display, const gchar *username)
-{
- GError *error = NULL;
-
- if (!display->priv->greeter_session || display->priv->user_session)
- return;
-
- /* Abort existing authentication */
- if (display->priv->user_pam_session)
- {
- g_signal_handlers_disconnect_matched (display->priv->user_pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
- pam_session_end (display->priv->user_pam_session);
- g_object_unref (display->priv->user_pam_session);
- }
-
- g_debug ("Greeter start authentication for %s", username);
-
- display->priv->user_pam_session = pam_session_new (display->priv->pam_service, username);
- g_signal_connect (G_OBJECT (display->priv->user_pam_session), "got-messages", G_CALLBACK (pam_messages_cb), display);
- g_signal_connect (G_OBJECT (display->priv->user_pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), display);
- g_signal_connect (G_OBJECT (display->priv->user_pam_session), "started", G_CALLBACK (session_started_cb), display);
-
- if (!pam_session_start (display->priv->user_pam_session, &error))
- g_warning ("Failed to start authentication: %s", error->message);
-}
-
-static void
-handle_continue_authentication (Display *display, gchar **secrets)
-{
- int num_messages;
- const struct pam_message **messages;
- struct pam_response *response;
- int i, j, n_secrets = 0;
-
- /* Not connected */
- if (!display->priv->greeter_connected)
- return;
-
- /* Not in authorization */
- if (display->priv->user_pam_session == NULL)
- return;
-
- num_messages = pam_session_get_num_messages (display->priv->user_pam_session);
- messages = pam_session_get_messages (display->priv->user_pam_session);
-
- /* Check correct number of responses */
- for (i = 0; i < num_messages; i++)
- {
- int msg_style = messages[i]->msg_style;
- if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
- n_secrets++;
- }
- if (g_strv_length (secrets) != n_secrets)
- {
- pam_session_end (display->priv->user_pam_session);
- return;
- }
-
- g_debug ("Continue authentication");
-
- /* Build response */
- response = calloc (num_messages, sizeof (struct pam_response));
- for (i = 0, j = 0; i < num_messages; i++)
- {
- int msg_style = messages[i]->msg_style;
- if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
- {
- response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
- j++;
- }
- }
-
- pam_session_respond (display->priv->user_pam_session, response);
-}
-
-static gboolean
-quit_greeter_cb (gpointer data)
-{
- Display *display = data;
- g_warning ("Greeter did not quit, sending kill signal");
- session_stop (display->priv->greeter_session);
- display->priv->greeter_quit_timeout = 0;
- return TRUE;
-}
-
-static void
-quit_greeter (Display *display)
-{
- write_header (display, GREETER_MESSAGE_QUIT, 0);
- flush (display);
-
- if (display->priv->greeter_quit_timeout)
- g_source_remove (display->priv->greeter_quit_timeout);
- display->priv->greeter_quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, display);
-}
-
static gboolean
session_timeout_cb (Display *display)
{
g_warning ("Session has not indicated it is ready, stopping greeter anyway");
/* Stop the greeter */
- quit_greeter (display);
+ greeter_quit (display->priv->greeter_session);
display->priv->user_session_timer = 0;
return FALSE;
}
static void
-handle_login (Display *display, gchar *username, gchar *session, gchar *language)
+greeter_login_cb (Greeter *greeter, const gchar *username, const gchar *session, const gchar *language, Display *display)
{
- if (display->priv->user_session != NULL)
- {
- g_warning ("Ignoring request to log in when already logged in");
- return;
- }
-
- g_debug ("Greeter login for user %s on session %s", username, session);
-
/* Default session requested */
if (strcmp (session, "") == 0)
session = display->priv->default_session;
/* Default language requested */
if (strcmp (language, "") == 0)
language = NULL;
+
+ display->priv->user_pam_session = greeter_get_pam_session (greeter);
+ display->priv->user_ck_cookie = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
if (display->priv->default_user && strcmp (username, display->priv->default_user) == 0)
start_default_session (display, session, language);
if (display->priv->supports_transitions)
display->priv->user_session_timer = g_timeout_add (USER_SESSION_TIMEOUT, (GSourceFunc) session_timeout_cb, display);
else
- quit_greeter (display);
-}
-
-#define HEADER_SIZE (sizeof (guint32) * 2)
-
-static guint32
-read_int (Display *display, gsize *offset)
-{
- guint32 *value;
- if (display->priv->n_read - *offset < sizeof (guint32))
- {
- g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), display->priv->n_read - *offset);
- return 0;
- }
- value = (guint32 *) (display->priv->read_buffer + *offset);
- *offset += sizeof (guint32);
- return *value;
-}
-
-static gchar *
-read_string (Display *display, gsize *offset)
-{
- guint32 length;
- gchar *value;
-
- length = read_int (display, offset);
- if (display->priv->n_read - *offset < length)
- {
- g_warning ("Not enough space for string, need %u, got %zu", length, display->priv->n_read - *offset);
- return g_strdup ("");
- }
-
- value = g_malloc (sizeof (gchar *) * (length + 1));
- memcpy (value, display->priv->read_buffer + *offset, length);
- value[length] = '\0';
- *offset += length;
-
- return value;
+ greeter_quit (display->priv->greeter_session);
}
static void
-greeter_data_cb (Session *session, Display *display)
+greeter_quit_cb (Greeter *greeter, Display *display)
{
- gsize n_to_read, n_read, offset;
- GIOStatus status;
- int message, n_secrets, i;
- gchar *username, *session_name, *language;
- gchar **secrets;
- GError *error = NULL;
-
- n_to_read = HEADER_SIZE;
- if (display->priv->n_read >= HEADER_SIZE)
- n_to_read += ((guint32 *) display->priv->read_buffer)[1];
-
- status = g_io_channel_read_chars (child_process_get_from_child_channel (CHILD_PROCESS (session)),
- display->priv->read_buffer + display->priv->n_read,
- n_to_read - display->priv->n_read,
- &n_read,
- &error);
- if (status != G_IO_STATUS_NORMAL)
- g_warning ("Error reading from greeter: %s", error->message);
- g_clear_error (&error);
- if (status != G_IO_STATUS_NORMAL)
- return;
-
- display->priv->n_read += n_read;
- if (display->priv->n_read != n_to_read)
- return;
+ g_signal_emit (greeter, signals[END_GREETER], 0, display->priv->greeter_session);
- /* If have header, rerun for content */
- if (display->priv->n_read == HEADER_SIZE)
- {
- n_to_read = ((guint32 *) display->priv->read_buffer)[1];
- if (n_to_read > 0)
- {
- display->priv->read_buffer = g_realloc (display->priv->read_buffer, HEADER_SIZE + n_to_read);
- greeter_data_cb (session, display);
- return;
- }
- }
-
- offset = 0;
- message = read_int (display, &offset);
- read_int (display, &offset);
- switch (message)
- {
- case GREETER_MESSAGE_CONNECT:
- handle_connect (display);
- break;
- case GREETER_MESSAGE_START_AUTHENTICATION:
- username = read_string (display, &offset);
- handle_start_authentication (display, username);
- g_free (username);
- break;
- case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
- n_secrets = read_int (display, &offset);
- secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
- for (i = 0; i < n_secrets; i++)
- secrets[i] = read_string (display, &offset);
- secrets[i] = NULL;
- handle_continue_authentication (display, secrets);
- g_strfreev (secrets);
- break;
- case GREETER_MESSAGE_LOGIN:
- username = read_string (display, &offset);
- session_name = read_string (display, &offset);
- language = read_string (display, &offset);
- handle_login (display, username, session_name, language);
- g_free (username);
- g_free (session_name);
- g_free (language);
- break;
- default:
- g_warning ("Unknown message from greeter: %d", message);
- break;
- }
-
- display->priv->n_read = 0;
-}
+ pam_session_end (display->priv->greeter_pam_session);
+ g_object_unref (display->priv->greeter_pam_session);
+ display->priv->greeter_pam_session = NULL;
-static void
-greeter_session_exited_cb (Session *session, gint status, Display *display)
-{
- end_greeter_session (display, status == 0);
-}
+ g_object_unref (display->priv->greeter_session);
+ display->priv->greeter_session = NULL;
-static void
-greeter_session_terminated_cb (Session *session, gint signum, Display *display)
-{
- end_greeter_session (display, FALSE);
+ end_ck_session (display->priv->greeter_ck_cookie);
+ g_free (display->priv->greeter_ck_cookie);
+ display->priv->greeter_ck_cookie = NULL;
}
static void
pam_session_authorize (display->priv->greeter_pam_session);
display->priv->greeter_ck_cookie = start_ck_session (display,
- "LoginWindow",
- username);
-
- display->priv->greeter_connected = FALSE;
- display->priv->greeter_session = session_new (username, command);
- g_signal_connect (G_OBJECT (display->priv->greeter_session), "got-data", G_CALLBACK (greeter_data_cb), display);
- g_signal_connect (G_OBJECT (display->priv->greeter_session), "exited", G_CALLBACK (greeter_session_exited_cb), display);
- g_signal_connect (G_OBJECT (display->priv->greeter_session), "terminated", G_CALLBACK (greeter_session_terminated_cb), display);
+ "LoginWindow",
+ username);
+
+ display->priv->greeter_session = greeter_new ();
+ greeter_set_theme (display->priv->greeter_session, display->priv->greeter_theme);
+ greeter_set_default_user (display->priv->greeter_session, display->priv->default_user, display->priv->timeout);
+ greeter_set_layout (display->priv->greeter_session, display->priv->default_layout);
+ greeter_set_session (display->priv->greeter_session, display->priv->default_session);
+ g_signal_connect (G_OBJECT (display->priv->greeter_session), "login", G_CALLBACK (greeter_login_cb), display);
+ g_signal_connect (G_OBJECT (display->priv->greeter_session), "quit", G_CALLBACK (greeter_quit_cb), display);
+ session_set_username (SESSION (display->priv->greeter_session), username);
+ session_set_command (SESSION (display->priv->greeter_session), command);
child_process_set_env (CHILD_PROCESS (display->priv->greeter_session), "DISPLAY", xserver_get_address (display->priv->xserver));
if (display->priv->greeter_ck_cookie)
child_process_set_env (CHILD_PROCESS (display->priv->greeter_session), "XDG_SESSION_COOKIE", display->priv->greeter_ck_cookie);
- set_env_from_pam_session (display->priv->greeter_session, display->priv->greeter_pam_session);
+ set_env_from_pam_session (SESSION (display->priv->greeter_session), display->priv->greeter_pam_session);
g_signal_emit (display, signals[START_GREETER], 0, display->priv->greeter_session);
- session_start (display->priv->greeter_session, TRUE);
+ session_start (SESSION (display->priv->greeter_session), TRUE);
g_free (command);
g_key_file_free (theme);
display->priv->greeter_theme = g_strdup (GREETER_THEME);
display->priv->default_layout = g_strdup ("us"); // FIXME: Is there a better default to get?
display->priv->default_session = g_strdup (DEFAULT_SESSION);
- display->priv->read_buffer = g_malloc (HEADER_SIZE);
}
static void
--- /dev/null
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * 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 <string.h>
+
+#include "greeter.h"
+#include "ldm-marshal.h"
+#include "greeter-protocol.h"
+
+/* Length of time in milliseconds to wait for a greeter to quit */
+#define GREETER_QUIT_TIMEOUT 1000
+
+enum {
+ LOGIN,
+ QUIT,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct GreeterPrivate
+{
+ gboolean connected;
+
+ /* Pipe to communicate to greeter */
+ int pipe[2];
+ gchar *read_buffer;
+ gsize n_read;
+
+ gchar *theme;
+ gchar *layout;
+ gchar *session;
+ gchar *default_user;
+ gint autologin_timeout;
+
+ guint quit_timeout;
+
+ PAMSession *pam_session;
+};
+
+G_DEFINE_TYPE (Greeter, greeter, SESSION_TYPE);
+
+Greeter *
+greeter_new (void)
+{
+ return g_object_new (GREETER_TYPE, NULL);
+}
+
+void
+greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout)
+{
+ g_free (greeter->priv->default_user);
+ greeter->priv->default_user = g_strdup (username);
+ greeter->priv->autologin_timeout = timeout;
+}
+
+void
+greeter_set_theme (Greeter *greeter, const gchar *theme)
+{
+ g_free (greeter->priv->theme);
+ greeter->priv->theme = g_strdup (theme);
+}
+
+void
+greeter_set_layout (Greeter *greeter, const gchar *layout)
+{
+ g_free (greeter->priv->layout);
+ greeter->priv->layout = g_strdup (layout);
+}
+
+const gchar *
+greeter_get_layout (Greeter *greeter)
+{
+ return greeter->priv->layout;
+}
+
+void
+greeter_set_session (Greeter *greeter, const gchar *session)
+{
+ g_free (greeter->priv->session);
+ greeter->priv->session = g_strdup (session);
+}
+
+const gchar *
+greeter_get_session (Greeter *greeter)
+{
+ return greeter->priv->session;
+}
+
+PAMSession *
+greeter_get_pam_session (Greeter *greeter)
+{
+ return greeter->priv->pam_session;
+}
+
+static void
+write_int (Greeter *greeter, guint32 value)
+{
+ g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), (const gchar *) &value, sizeof (value), NULL, NULL);
+}
+
+static void
+write_string (Greeter *greeter, const gchar *value)
+{
+ write_int (greeter, strlen (value));
+ g_io_channel_write_chars (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), value, -1, NULL, NULL);
+}
+
+static void
+write_header (Greeter *greeter, guint32 id, guint32 length)
+{
+ write_int (greeter, id);
+ write_int (greeter, length);
+}
+
+static guint32
+int_length ()
+{
+ return sizeof (guint32);
+}
+
+static guint32
+string_length (const gchar *value)
+{
+ return int_length () + strlen (value);
+}
+
+static void
+flush (Greeter *greeter)
+{
+ g_io_channel_flush (child_process_get_to_child_channel (CHILD_PROCESS (greeter)), NULL);
+}
+
+static void
+handle_connect (Greeter *greeter)
+{
+ gchar *theme;
+
+ if (!greeter->priv->connected)
+ {
+ greeter->priv->connected = TRUE;
+ g_debug ("Greeter connected");
+ }
+
+ theme = g_build_filename (THEME_DIR, greeter->priv->theme, "index.theme", NULL);
+
+ write_header (greeter, GREETER_MESSAGE_CONNECTED, string_length (theme) + string_length (greeter->priv->layout) + string_length (greeter->priv->session) + string_length (greeter->priv->default_user ? greeter->priv->default_user : "") + int_length ());
+ write_string (greeter, theme);
+ write_string (greeter, greeter->priv->layout);
+ write_string (greeter, greeter->priv->session);
+ write_string (greeter, greeter->priv->default_user ? greeter->priv->default_user : "");
+ write_int (greeter, greeter->priv->autologin_timeout);
+ flush (greeter);
+
+ g_free (theme);
+}
+
+static void
+pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Greeter *greeter)
+{
+ int i;
+ guint32 size;
+
+ /* Respond to d-bus query with messages */
+ g_debug ("Prompt greeter with %d message(s)", num_msg);
+ size = int_length ();
+ for (i = 0; i < num_msg; i++)
+ size += int_length () + string_length (msg[i]->msg);
+ write_header (greeter, GREETER_MESSAGE_PROMPT_AUTHENTICATION, size);
+ write_int (greeter, num_msg);
+ for (i = 0; i < num_msg; i++)
+ {
+ write_int (greeter, msg[i]->msg_style);
+ write_string (greeter, msg[i]->msg);
+ }
+ flush (greeter);
+}
+
+static void
+authenticate_result_cb (PAMSession *session, int result, Greeter *greeter)
+{
+ g_debug ("Authenticate result for user %s: %s", pam_session_get_username (greeter->priv->pam_session), pam_session_strerror (greeter->priv->pam_session, result));
+
+ if (result == PAM_SUCCESS)
+ {
+ //run_script ("PostLogin");
+ pam_session_authorize (session);
+ }
+
+ /* Respond to D-Bus request */
+ write_header (greeter, GREETER_MESSAGE_END_AUTHENTICATION, int_length ());
+ write_int (greeter, result);
+ flush (greeter);
+}
+
+static void
+handle_start_authentication (Greeter *greeter, const gchar *username)
+{
+ GError *error = NULL;
+
+ // FIXME
+ //if (greeter->priv->user_session)
+ // return;
+
+ /* Abort existing authentication */
+ if (greeter->priv->pam_session)
+ {
+ g_signal_handlers_disconnect_matched (greeter->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, greeter);
+ pam_session_end (greeter->priv->pam_session);
+ g_object_unref (greeter->priv->pam_session);
+ }
+
+ g_debug ("Greeter start authentication for %s", username);
+
+ greeter->priv->pam_session = pam_session_new ("lightdm"/*FIXMEgreeter->priv->pam_service*/, username);
+ g_signal_connect (G_OBJECT (greeter->priv->pam_session), "got-messages", G_CALLBACK (pam_messages_cb), greeter);
+ g_signal_connect (G_OBJECT (greeter->priv->pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), greeter);
+
+ if (!pam_session_start (greeter->priv->pam_session, &error))
+ g_warning ("Failed to start authentication: %s", error->message);
+}
+
+static void
+handle_continue_authentication (Greeter *greeter, gchar **secrets)
+{
+ int num_messages;
+ const struct pam_message **messages;
+ struct pam_response *response;
+ int i, j, n_secrets = 0;
+
+ /* Not connected */
+ if (!greeter->priv->connected)
+ return;
+
+ /* Not in authorization */
+ if (greeter->priv->pam_session == NULL)
+ return;
+
+ num_messages = pam_session_get_num_messages (greeter->priv->pam_session);
+ messages = pam_session_get_messages (greeter->priv->pam_session);
+
+ /* Check correct number of responses */
+ for (i = 0; i < num_messages; i++)
+ {
+ int msg_style = messages[i]->msg_style;
+ if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
+ n_secrets++;
+ }
+ if (g_strv_length (secrets) != n_secrets)
+ {
+ pam_session_end (greeter->priv->pam_session);
+ return;
+ }
+
+ g_debug ("Continue authentication");
+
+ /* Build response */
+ response = calloc (num_messages, sizeof (struct pam_response));
+ for (i = 0, j = 0; i < num_messages; i++)
+ {
+ int msg_style = messages[i]->msg_style;
+ if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
+ {
+ response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
+ j++;
+ }
+ }
+
+ pam_session_respond (greeter->priv->pam_session, response);
+}
+
+static gboolean
+quit_greeter_cb (gpointer data)
+{
+ Greeter *greeter = data;
+ g_warning ("Greeter did not quit, sending kill signal");
+ session_stop (SESSION (greeter));
+ greeter->priv->quit_timeout = 0;
+ return TRUE;
+}
+
+void
+greeter_quit (Greeter *greeter)
+{
+ write_header (greeter, GREETER_MESSAGE_QUIT, 0);
+ flush (greeter);
+
+ if (greeter->priv->quit_timeout)
+ g_source_remove (greeter->priv->quit_timeout);
+ greeter->priv->quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, greeter);
+}
+
+static void
+handle_login (Greeter *greeter, gchar *username, gchar *session, gchar *language)
+{
+ /*if (greeter->priv->user_session != NULL)
+ {
+ g_warning ("Ignoring request to log in when already logged in");
+ return;
+ }*/
+
+ g_debug ("Greeter login for user %s on session %s", username, session);
+
+ g_signal_emit (greeter, signals[LOGIN], 0, username, session, language);
+}
+
+#define HEADER_SIZE (sizeof (guint32) * 2)
+
+static guint32
+read_int (Greeter *greeter, gsize *offset)
+{
+ guint32 *value;
+ if (greeter->priv->n_read - *offset < sizeof (guint32))
+ {
+ g_warning ("Not enough space for int, need %zu, got %zu", sizeof (guint32), greeter->priv->n_read - *offset);
+ return 0;
+ }
+ value = (guint32 *) (greeter->priv->read_buffer + *offset);
+ *offset += sizeof (guint32);
+ return *value;
+}
+
+static gchar *
+read_string (Greeter *greeter, gsize *offset)
+{
+ guint32 length;
+ gchar *value;
+
+ length = read_int (greeter, offset);
+ if (greeter->priv->n_read - *offset < length)
+ {
+ g_warning ("Not enough space for string, need %u, got %zu", length, greeter->priv->n_read - *offset);
+ return g_strdup ("");
+ }
+
+ value = g_malloc (sizeof (gchar *) * (length + 1));
+ memcpy (value, greeter->priv->read_buffer + *offset, length);
+ value[length] = '\0';
+ *offset += length;
+
+ return value;
+}
+
+static void
+got_data_cb (Greeter *greeter)
+{
+ gsize n_to_read, n_read, offset;
+ GIOStatus status;
+ int message, n_secrets, i;
+ gchar *username, *session_name, *language;
+ gchar **secrets;
+ GError *error = NULL;
+
+ n_to_read = HEADER_SIZE;
+ if (greeter->priv->n_read >= HEADER_SIZE)
+ n_to_read += ((guint32 *) greeter->priv->read_buffer)[1];
+
+ status = g_io_channel_read_chars (child_process_get_from_child_channel (CHILD_PROCESS (greeter)),
+ greeter->priv->read_buffer + greeter->priv->n_read,
+ n_to_read - greeter->priv->n_read,
+ &n_read,
+ &error);
+ if (status != G_IO_STATUS_NORMAL)
+ g_warning ("Error reading from greeter: %s", error->message);
+ g_clear_error (&error);
+ if (status != G_IO_STATUS_NORMAL)
+ return;
+
+ greeter->priv->n_read += n_read;
+ if (greeter->priv->n_read != n_to_read)
+ return;
+
+ /* If have header, rerun for content */
+ if (greeter->priv->n_read == HEADER_SIZE)
+ {
+ n_to_read = ((guint32 *) greeter->priv->read_buffer)[1];
+ if (n_to_read > 0)
+ {
+ greeter->priv->read_buffer = g_realloc (greeter->priv->read_buffer, HEADER_SIZE + n_to_read);
+ got_data_cb (greeter);
+ return;
+ }
+ }
+
+ offset = 0;
+ message = read_int (greeter, &offset);
+ read_int (greeter, &offset);
+ switch (message)
+ {
+ case GREETER_MESSAGE_CONNECT:
+ handle_connect (greeter);
+ break;
+ case GREETER_MESSAGE_START_AUTHENTICATION:
+ username = read_string (greeter, &offset);
+ handle_start_authentication (greeter, username);
+ g_free (username);
+ break;
+ case GREETER_MESSAGE_CONTINUE_AUTHENTICATION:
+ n_secrets = read_int (greeter, &offset);
+ secrets = g_malloc (sizeof (gchar *) * (n_secrets + 1));
+ for (i = 0; i < n_secrets; i++)
+ secrets[i] = read_string (greeter, &offset);
+ secrets[i] = NULL;
+ handle_continue_authentication (greeter, secrets);
+ g_strfreev (secrets);
+ break;
+ case GREETER_MESSAGE_LOGIN:
+ username = read_string (greeter, &offset);
+ session_name = read_string (greeter, &offset);
+ language = read_string (greeter, &offset);
+ handle_login (greeter, username, session_name, language);
+ g_free (username);
+ g_free (session_name);
+ g_free (language);
+ break;
+ default:
+ g_warning ("Unknown message from greeter: %d", message);
+ break;
+ }
+
+ greeter->priv->n_read = 0;
+}
+
+static void
+end_session (Greeter *greeter, gboolean clean_exit)
+{
+ if (greeter->priv->quit_timeout)
+ {
+ g_source_remove (greeter->priv->quit_timeout);
+ greeter->priv->quit_timeout = 0;
+ }
+
+ /*if (!clean_exit)
+ g_warning ("Greeter failed");
+ else if (!greeter_connected)
+ g_warning ("Greeter quit before connecting");
+ else if (!display->priv->user_session)
+ g_warning ("Greeter quit before session started");
+ else
+ return;*/
+
+ // FIXME: Issue with greeter, don't want to start a new one, report error to user
+
+ g_signal_emit (greeter, signals[QUIT], 0);
+}
+
+static void
+session_exited_cb (Greeter *greeter, gint status)
+{
+ end_session (greeter, status == 0);
+}
+
+static void
+session_terminated_cb (Greeter *greeter, gint signum)
+{
+ end_session (greeter, FALSE);
+}
+
+static void
+greeter_init (Greeter *greeter)
+{
+ greeter->priv = G_TYPE_INSTANCE_GET_PRIVATE (greeter, GREETER_TYPE, GreeterPrivate);
+ greeter->priv->read_buffer = g_malloc (HEADER_SIZE);
+ g_signal_connect (G_OBJECT (greeter), "got-data", G_CALLBACK (got_data_cb), NULL);
+ g_signal_connect (G_OBJECT (greeter), "exited", G_CALLBACK (session_exited_cb), NULL);
+ g_signal_connect (G_OBJECT (greeter), "terminated", G_CALLBACK (session_terminated_cb), NULL);
+}
+
+static void
+greeter_finalize (GObject *object)
+{
+ Greeter *self;
+
+ self = GREETER (object);
+
+ g_free (self->priv->read_buffer);
+ g_free (self->priv->theme);
+ g_free (self->priv->layout);
+ g_free (self->priv->session);
+ g_free (self->priv->default_user);
+}
+
+static void
+greeter_class_init (GreeterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = greeter_finalize;
+
+ signals[LOGIN] =
+ g_signal_new ("login",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GreeterClass, login),
+ NULL, NULL,
+ ldm_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ signals[QUIT] =
+ g_signal_new ("quit",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GreeterClass, quit),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (klass, sizeof (GreeterPrivate));
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * 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_H_
+#define _GREETER_H_
+
+#include "session.h"
+#include "pam-session.h"
+
+G_BEGIN_DECLS
+
+#define GREETER_TYPE (greeter_get_type())
+#define GREETER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GREETER_TYPE, Greeter))
+
+typedef struct GreeterPrivate GreeterPrivate;
+
+typedef struct
+{
+ Session parent_instance;
+ GreeterPrivate *priv;
+} Greeter;
+
+typedef struct
+{
+ SessionClass parent_class;
+ void (*login)(Greeter *greeter, const gchar *username, const gchar *session, const gchar *language);
+ void (*quit)(Greeter *greeter);
+} GreeterClass;
+
+GType greeter_get_type (void);
+
+Greeter *greeter_new (void);
+
+void greeter_set_default_user (Greeter *greeter, const gchar *username, gint timeout);
+
+void greeter_set_theme (Greeter *greeter, const gchar *theme);
+
+void greeter_set_layout (Greeter *greeter, const gchar *layout);
+
+const gchar *greeter_get_layout (Greeter *greeter);
+
+void greeter_set_session (Greeter *greeter, const gchar *session);
+
+const gchar *greeter_get_session (Greeter *greeter);
+
+PAMSession *greeter_get_pam_session (Greeter *greeter);
+
+void greeter_quit (Greeter *greeter);
+
+G_END_DECLS
+
+#endif /* _GREETER_H_ */
BOOLEAN:OBJECT
VOID:INT,POINTER
VOID:OBJECT,BOOLEAN
+VOID:STRING,STRING,STRING
G_DEFINE_TYPE (Session, session, CHILD_PROCESS_TYPE);
Session *
-session_new (const char *username, const char *command)
+session_new ()
{
- Session *self = g_object_new (SESSION_TYPE, NULL);
-
- self->priv->username = g_strdup (username);
- self->priv->command = g_strdup (command);
+ return g_object_new (SESSION_TYPE, NULL);
+}
- return self;
+void
+session_set_username (Session *session, const gchar *username)
+{
+ session->priv->username = g_strdup (username);
}
const gchar *
return session->priv->username;
}
+void
+session_set_command (Session *session, const gchar *command)
+{
+ session->priv->command = g_strdup (command);
+}
+
const gchar *
session_get_command (Session *session)
{
GError *error = NULL;
//g_return_val_if_fail (session->priv->pid == 0, FALSE);
+ g_return_val_if_fail (session->priv->command != NULL, FALSE);
errno = 0;
if (session->priv->username)
G_BEGIN_DECLS
#define SESSION_TYPE (session_get_type())
-#define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SESSION_TYPE, Session));
+#define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SESSION_TYPE, Session))
typedef struct SessionPrivate SessionPrivate;
GType session_get_type (void);
-Session *session_new (const char *username, const char *command);
+Session *session_new (void);
+
+void session_set_username (Session *session, const gchar *username);
const gchar *session_get_username (Session *session);
+void session_set_command (Session *session, const gchar *command);
+
const gchar *session_get_command (Session *session);
void session_set_authorization (Session *session, XAuthorization *authorization, const gchar *path);