summaryrefslogtreecommitdiff
path: root/daemon
diff options
context:
space:
mode:
Diffstat (limited to 'daemon')
-rw-r--r--daemon/Makefile.am25
-rw-r--r--daemon/gdm-greeter-proxy.c12
-rw-r--r--daemon/gdm-greeter-server.c4
-rw-r--r--daemon/gdm-product-slave.c83
-rw-r--r--daemon/gdm-session-worker-job.c478
-rw-r--r--daemon/gdm-session-worker-job.h66
-rw-r--r--daemon/gdm-session-worker.c1672
-rw-r--r--daemon/gdm-session-worker.h74
-rw-r--r--daemon/gdm-session.c4183
-rw-r--r--daemon/gdm-session.h20
-rw-r--r--daemon/gdm-simple-slave.c21
-rw-r--r--daemon/session-worker-main.c179
-rw-r--r--daemon/test-session.c50
13 files changed, 3395 insertions, 3472 deletions
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 58686e29..75aa1262 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -70,18 +70,21 @@ noinst_PROGRAMS = \
test_session_SOURCES = \
test-session.c \
gdm-session.c \
+ gdm-session-worker-job.c\
$(NULL)
test_session_LDADD = \
$(GLIB_LIBS) \
$(GOBJECT_LIBS) \
$(PAM_LIBS) \
+ $(DBUS_LIBS) \
$(NULL)
libexec_PROGRAMS = \
gdm-simple-slave \
gdm-factory-slave \
gdm-product-slave \
+ gdm-session-worker \
$(NULL)
gdm_simple_slave_SOURCES = \
@@ -94,6 +97,8 @@ gdm_simple_slave_SOURCES = \
gdm-server.h \
gdm-session.c \
gdm-session.h \
+ gdm-session-worker-job.c \
+ gdm-session-worker-job.h \
gdm-slave.c \
gdm-slave.h \
gdm-simple-slave.c \
@@ -132,8 +137,6 @@ gdm_factory_slave_SOURCES = \
gdm-server.h \
gdm-session-relay.c \
gdm-session-relay.h \
- gdm-session.c \
- gdm-session.h \
gdm-slave.c \
gdm-slave.h \
gdm-factory-slave.c \
@@ -168,6 +171,8 @@ gdm_product_slave_SOURCES = \
gdm-server.h \
gdm-session.c \
gdm-session.h \
+ gdm-session-worker-job.c \
+ gdm-session-worker-job.h \
gdm-slave.c \
gdm-slave.h \
gdm-product-slave.c \
@@ -196,6 +201,22 @@ gdm_product_slave_LDADD = \
-lXext \
$(NULL)
+gdm_session_worker_SOURCES = \
+ session-worker-main.c \
+ gdm-session-worker.c \
+ $(NULL)
+
+gdm_session_worker_LDFLAGS = \
+ $(PAM_LIBS) \
+ $(NULL)
+
+gdm_session_worker_LDADD = \
+ $(GLIB_LIBS) \
+ $(GOBJECT_LIBS) \
+ $(top_builddir)/common/libgdmcommon.la \
+ $(DBUS_LIBS) \
+ $(NULL)
+
sbin_PROGRAMS = \
gdm-binary \
$(NULL)
diff --git a/daemon/gdm-greeter-proxy.c b/daemon/gdm-greeter-proxy.c
index dbad1f3d..c3167f88 100644
--- a/daemon/gdm-greeter-proxy.c
+++ b/daemon/gdm-greeter-proxy.c
@@ -32,10 +32,6 @@
#include <pwd.h>
#include <grp.h>
-#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD)
-#include <sched.h>
-#endif
-
#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
@@ -43,10 +39,7 @@
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
-#include <X11/Xlib.h> /* for Display */
-
#include "gdm-common.h"
-#include "filecheck.h"
#include "gdm-greeter-proxy.h"
@@ -77,7 +70,6 @@ struct GdmGreeterProxyPrivate
guint child_watch_id;
char *server_address;
- DBusConnection *greeter_connection;
};
enum {
@@ -611,8 +603,8 @@ gdm_greeter_proxy_constructor (GType type,
klass = GDM_GREETER_PROXY_CLASS (g_type_class_peek (GDM_TYPE_GREETER_PROXY));
greeter_proxy = GDM_GREETER_PROXY (G_OBJECT_CLASS (gdm_greeter_proxy_parent_class)->constructor (type,
- n_construct_properties,
- construct_properties));
+ n_construct_properties,
+ construct_properties));
return G_OBJECT (greeter_proxy);
}
diff --git a/daemon/gdm-greeter-server.c b/daemon/gdm-greeter-server.c
index a30809be..624ef0ab 100644
--- a/daemon/gdm-greeter-server.c
+++ b/daemon/gdm-greeter-server.c
@@ -226,8 +226,8 @@ generate_address (void)
static DBusHandlerResult
handle_answer_query (GdmGreeterServer *greeter_server,
- DBusConnection *connection,
- DBusMessage *message)
+ DBusConnection *connection,
+ DBusMessage *message)
{
DBusMessage *reply;
DBusError error;
diff --git a/daemon/gdm-product-slave.c b/daemon/gdm-product-slave.c
index 6ccc3597..b67b42c1 100644
--- a/daemon/gdm-product-slave.c
+++ b/daemon/gdm-product-slave.c
@@ -79,6 +79,7 @@ struct GdmProductSlavePrivate
/* user selected */
char *selected_session;
char *selected_language;
+ char *selected_user;
GdmServer *server;
GdmSession *session;
@@ -392,11 +393,28 @@ relay_session_started (GdmProductSlave *slave)
}
static void
+on_open (GdmSession *session,
+ GdmProductSlave *slave)
+{
+ GError *error;
+ gboolean res;
+
+ g_debug ("session open");
+ res = gdm_session_begin_verification (session,
+ slave->priv->selected_user,
+ &error);
+ if (! res) {
+ g_warning ("Unable to begin verification: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
on_session_started (GdmSession *session,
GPid pid,
GdmProductSlave *slave)
{
- g_debug ("session started on pid %d\n", (int) pid);
+ g_debug ("session started on pid %d", (int) pid);
g_signal_emit (slave, signals [SESSION_STARTED], 0, pid);
relay_session_started (slave);
}
@@ -406,7 +424,7 @@ on_session_exited (GdmSession *session,
int exit_code,
GdmProductSlave *slave)
{
- g_debug ("session exited with code %d\n", exit_code);
+ g_debug ("session exited with code %d", exit_code);
g_signal_emit (slave, signals [SESSION_EXITED], 0, exit_code);
}
@@ -573,11 +591,8 @@ on_user_verified (GdmSession *session,
GdmProductSlave *slave)
{
char *username;
- int argc;
- char **argv;
char *command;
char *filename;
- GError *error;
gboolean res;
/*gdm_greeter_server_stop (slave->priv->greeter);*/
@@ -603,20 +618,10 @@ on_user_verified (GdmSession *session,
return;
}
- error = NULL;
- res = g_shell_parse_argv (command, &argc, &argv, &error);
- if (! res) {
- g_warning ("Could not parse command: %s", error->message);
- g_error_free (error);
- }
-
- gdm_session_start_program (session,
- argc,
- (const char **)argv);
+ gdm_session_start_program (session, command);
g_free (filename);
g_free (command);
- g_strfreev (argv);
}
static void
@@ -786,32 +791,39 @@ on_relay_language_selected (DBusGProxy *proxy,
slave->priv->selected_language = g_strdup (text);
}
+static gboolean
+reset_session (GdmProductSlave *slave)
+{
+ gboolean res;
+ GError *error;
+
+ gdm_session_close (slave->priv->session);
+ res = gdm_session_open (slave->priv->session,
+ "gdm",
+ "",
+ "/dev/console",
+ &error);
+ if (! res) {
+ g_warning ("Unable to open session: %s", error->message);
+ g_error_free (error);
+ }
+
+ return res;
+}
+
static void
on_relay_user_selected (DBusGProxy *proxy,
const char *text,
gpointer data)
{
GdmProductSlave *slave = GDM_PRODUCT_SLAVE (data);
- gboolean res;
- GError *error;
g_debug ("User: %s", text);
- gdm_session_close (slave->priv->session);
+ g_free (slave->priv->selected_user);
+ slave->priv->selected_user = g_strdup (text);
- error = NULL;
- res = gdm_session_open_for_user (slave->priv->session,
- "gdm",
- text,
- NULL /* hostname */,
- "/dev/console",
- STDOUT_FILENO,
- STDERR_FILENO,
- &error);
- if (! res) {
- g_warning ("Unable to open session: %s", error->message);
- g_error_free (error);
- }
+ reset_session (slave);
}
static void
@@ -829,8 +841,6 @@ on_relay_open (DBusGProxy *proxy,
"gdm",
NULL /* hostname */,
"/dev/console",
- STDOUT_FILENO,
- STDERR_FILENO,
&error);
if (! res) {
g_warning ("Unable to open session: %s", error->message);
@@ -844,6 +854,11 @@ create_new_session (GdmProductSlave *slave)
slave->priv->session = gdm_session_new ();
g_signal_connect (slave->priv->session,
+ "open",
+ G_CALLBACK (on_open),
+ slave);
+
+ g_signal_connect (slave->priv->session,
"info",
G_CALLBACK (on_info),
slave);
diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c
new file mode 100644
index 00000000..dfa5cfcc
--- /dev/null
+++ b/daemon/gdm-session-worker-job.c
@@ -0,0 +1,478 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gdm-common.h"
+
+#include "gdm-session-worker-job.h"
+
+#define GDM_SESSION_SERVER_DBUS_PATH "/org/gnome/DisplayManager/SessionServer"
+#define GDM_SESSION_SERVER_DBUS_INTERFACE "org.gnome.DisplayManager.SessionServer"
+
+extern char **environ;
+
+#define GDM_SESSION_WORKER_JOB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SESSION_WORKER_JOB, GdmSessionWorkerJobPrivate))
+
+struct GdmSessionWorkerJobPrivate
+{
+ char *command;
+ GPid pid;
+
+ guint child_watch_id;
+
+ char *server_address;
+};
+
+enum {
+ PROP_0,
+ PROP_SERVER_ADDRESS,
+};
+
+enum {
+ STARTED,
+ STOPPED,
+ EXITED,
+ DIED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_session_worker_job_class_init (GdmSessionWorkerJobClass *klass);
+static void gdm_session_worker_job_init (GdmSessionWorkerJob *session_worker_job);
+static void gdm_session_worker_job_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmSessionWorkerJob, gdm_session_worker_job, G_TYPE_OBJECT)
+
+static void
+session_worker_job_child_setup (GdmSessionWorkerJob *session_worker_job)
+{
+}
+
+static void
+session_worker_job_child_watch (GPid pid,
+ int status,
+ GdmSessionWorkerJob *job)
+{
+ g_debug ("child (pid:%d) done (%s:%d)",
+ (int) pid,
+ WIFEXITED (status) ? "status"
+ : WIFSIGNALED (status) ? "signal"
+ : "unknown",
+ WIFEXITED (status) ? WEXITSTATUS (status)
+ : WIFSIGNALED (status) ? WTERMSIG (status)
+ : -1);
+ if (WIFEXITED (status)) {
+ int code = WEXITSTATUS (status);
+ g_signal_emit (job, signals [EXITED], 0, code);
+ } else if (WIFSIGNALED (status)) {
+ int num = WTERMSIG (status);
+ g_signal_emit (job, signals [DIED], 0, num);
+ }
+
+ g_spawn_close_pid (job->priv->pid);
+ job->priv->pid = -1;
+}
+
+static void
+listify_hash (const char *key,
+ const char *value,
+ GPtrArray *env)
+{
+ char *str;
+ str = g_strdup_printf ("%s=%s", key, value);
+ g_ptr_array_add (env, str);
+}
+
+static GPtrArray *
+get_job_environment (GdmSessionWorkerJob *job)
+{
+ GPtrArray *env;
+ GHashTable *hash;
+
+ env = g_ptr_array_new ();
+
+ /* create a hash table of current environment, then update keys has necessary */
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ g_hash_table_insert (hash, g_strdup ("GDM_SESSION_DBUS_ADDRESS"), g_strdup (job->priv->server_address));
+
+ g_hash_table_foreach (hash, (GHFunc)listify_hash, env);
+ g_hash_table_destroy (hash);
+
+ g_ptr_array_add (env, NULL);
+
+ return env;
+}
+
+static gboolean
+gdm_session_worker_job_spawn (GdmSessionWorkerJob *session_worker_job)
+{
+ gchar **argv;
+ GError *error;
+ gboolean ret;
+ GPtrArray *env;
+
+ ret = FALSE;
+
+ g_debug ("Running session_worker_job process: %s", session_worker_job->priv->command);
+
+ argv = NULL;
+ if (! g_shell_parse_argv (session_worker_job->priv->command, NULL, &argv, &error)) {
+ g_warning ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ env = get_job_environment (session_worker_job);
+
+ error = NULL;
+ ret = g_spawn_async_with_pipes (NULL,
+ argv,
+ (char **)env->pdata,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ (GSpawnChildSetupFunc)session_worker_job_child_setup,
+ session_worker_job,
+ &session_worker_job->priv->pid,
+ NULL,
+ NULL,
+ NULL,
+ &error);
+
+ g_ptr_array_foreach (env, (GFunc)g_free, NULL);
+ g_ptr_array_free (env, TRUE);
+
+ if (! ret) {
+ g_warning ("Could not start command '%s': %s",
+ session_worker_job->priv->command,
+ error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("gdm_slave_session_worker_job: SessionWorkerJob on pid %d", (int)session_worker_job->priv->pid);
+ }
+
+ session_worker_job->priv->child_watch_id = g_child_watch_add (session_worker_job->priv->pid,
+ (GChildWatchFunc)session_worker_job_child_watch,
+ session_worker_job);
+
+ g_strfreev (argv);
+ out:
+
+ return ret;
+}
+
+/**
+ * gdm_session_worker_job_start:
+ * @disp: Pointer to a GdmDisplay structure
+ *
+ * Starts a local X session_worker_job. Handles retries and fatal errors properly.
+ */
+gboolean
+gdm_session_worker_job_start (GdmSessionWorkerJob *session_worker_job)
+{
+ gboolean res;
+
+ g_debug ("Starting worker...");
+
+ res = gdm_session_worker_job_spawn (session_worker_job);
+
+ if (res) {
+
+ }
+
+
+ return res;
+}
+
+static int
+signal_pid (int pid,
+ int signal)
+{
+ int status = -1;
+
+ /* perhaps block sigchld */
+
+ status = kill (pid, signal);
+
+ if (status < 0) {
+ if (errno == ESRCH) {
+ g_warning ("Child process %lu was already dead.",
+ (unsigned long) pid);
+ } else {
+ g_warning ("Couldn't kill child process %lu: %s",
+ (unsigned long) pid,
+ g_strerror (errno));
+ }
+ }
+
+ /* perhaps unblock sigchld */
+
+ return status;
+}
+
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+ wait_again:
+ if (waitpid (pid, &status, 0) < 0) {
+ if (errno == EINTR) {
+ goto wait_again;
+ } else if (errno == ECHILD) {
+ ; /* do nothing, child already reaped */
+ } else {
+ g_debug ("waitpid () should not fail");
+ }
+ }
+
+ return status;
+}
+
+static void
+session_worker_job_died (GdmSessionWorkerJob *session_worker_job)
+{
+ int exit_status;
+
+ g_debug ("Waiting on process %d", session_worker_job->priv->pid);
+ exit_status = wait_on_child (session_worker_job->priv->pid);
+
+ if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) {
+ g_debug ("Wait on child process failed");
+ } else {
+ /* exited normally */
+ }
+
+ g_spawn_close_pid (session_worker_job->priv->pid);
+ session_worker_job->priv->pid = -1;
+
+ g_debug ("SessionWorkerJob died");
+}
+
+gboolean
+gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job)
+{
+
+ if (session_worker_job->priv->pid <= 1) {
+ return TRUE;
+ }
+
+ /* remove watch source before we can wait on child */
+ if (session_worker_job->priv->child_watch_id > 0) {
+ g_source_remove (session_worker_job->priv->child_watch_id);
+ session_worker_job->priv->child_watch_id = 0;
+ }
+
+ g_debug ("Stopping session_worker_job");
+
+ signal_pid (session_worker_job->priv->pid, SIGTERM);
+ session_worker_job_died (session_worker_job);
+
+ return TRUE;
+}
+
+void
+gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_job,
+ const char *address)
+{
+ g_return_if_fail (GDM_IS_SESSION_WORKER_JOB (session_worker_job));
+
+ g_free (session_worker_job->priv->server_address);
+ session_worker_job->priv->server_address = g_strdup (address);
+}
+
+static void
+gdm_session_worker_job_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionWorkerJob *self;
+
+ self = GDM_SESSION_WORKER_JOB (object);
+
+ switch (prop_id) {
+ case PROP_SERVER_ADDRESS:
+ gdm_session_worker_job_set_server_address (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_session_worker_job_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionWorkerJob *self;
+
+ self = GDM_SESSION_WORKER_JOB (object);
+
+ switch (prop_id) {
+ case PROP_SERVER_ADDRESS:
+ g_value_set_string (value, self->priv->server_address);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gdm_session_worker_job_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmSessionWorkerJob *session_worker_job;
+ GdmSessionWorkerJobClass *klass;
+
+ klass = GDM_SESSION_WORKER_JOB_CLASS (g_type_class_peek (GDM_TYPE_SESSION_WORKER_JOB));
+
+ session_worker_job = GDM_SESSION_WORKER_JOB (G_OBJECT_CLASS (gdm_session_worker_job_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (session_worker_job);
+}
+
+static void
+gdm_session_worker_job_class_init (GdmSessionWorkerJobClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_session_worker_job_get_property;
+ object_class->set_property = gdm_session_worker_job_set_property;
+ object_class->constructor = gdm_session_worker_job_constructor;
+ object_class->finalize = gdm_session_worker_job_finalize;
+
+ g_type_class_add_private (klass, sizeof (GdmSessionWorkerJobPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_SERVER_ADDRESS,
+ g_param_spec_string ("server-address",
+ "server address",
+ "server address",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ signals [STARTED] =
+ g_signal_new ("started",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmSessionWorkerJobClass, started),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [STOPPED] =
+ g_signal_new ("stopped",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmSessionWorkerJobClass, stopped),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [EXITED] =
+ g_signal_new ("exited",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmSessionWorkerJobClass, exited),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ signals [DIED] =
+ g_signal_new ("died",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmSessionWorkerJobClass, died),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);}
+
+static void
+gdm_session_worker_job_init (GdmSessionWorkerJob *session_worker_job)
+{
+
+ session_worker_job->priv = GDM_SESSION_WORKER_JOB_GET_PRIVATE (session_worker_job);
+
+ session_worker_job->priv->pid = -1;
+
+ session_worker_job->priv->command = g_strdup (LIBEXECDIR "/gdm-session-worker");
+}
+
+static void
+gdm_session_worker_job_finalize (GObject *object)
+{
+ GdmSessionWorkerJob *session_worker_job;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_SESSION_WORKER_JOB (object));
+
+ session_worker_job = GDM_SESSION_WORKER_JOB (object);
+
+ g_return_if_fail (session_worker_job->priv != NULL);
+
+ gdm_session_worker_job_stop (session_worker_job);
+
+ G_OBJECT_CLASS (gdm_session_worker_job_parent_class)->finalize (object);
+}
+
+GdmSessionWorkerJob *
+gdm_session_worker_job_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_SESSION_WORKER_JOB,
+ NULL);
+
+ return GDM_SESSION_WORKER_JOB (object);
+}
diff --git a/daemon/gdm-session-worker-job.h b/daemon/gdm-session-worker-job.h
new file mode 100644
index 00000000..5c35fe16
--- /dev/null
+++ b/daemon/gdm-session-worker-job.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __GDM_SESSION_WORKER_JOB_H
+#define __GDM_SESSION_WORKER_JOB_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SESSION_WORKER_JOB (gdm_session_worker_job_get_type ())
+#define GDM_SESSION_WORKER_JOB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_SESSION_WORKER_JOB, GdmSessionWorkerJob))
+#define GDM_SESSION_WORKER_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_SESSION_WORKER_JOB, GdmSessionWorkerJobClass))
+#define GDM_IS_SESSION_WORKER_JOB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_SESSION_WORKER_JOB))
+#define GDM_IS_SESSION_WORKER_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_SESSION_WORKER_JOB))
+#define GDM_SESSION_WORKER_JOB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_SESSION_WORKER_JOB, GdmSessionWorkerJobClass))
+
+typedef struct GdmSessionWorkerJobPrivate GdmSessionWorkerJobPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmSessionWorkerJobPrivate *priv;
+} GdmSessionWorkerJob;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* started) (GdmSessionWorkerJob *session_worker_job);
+ void (* stopped) (GdmSessionWorkerJob *session_worker_job);
+ void (* exited) (GdmSessionWorkerJob *session_worker_job,
+ int exit_code);
+
+ void (* died) (GdmSessionWorkerJob *session_worker_job,
+ int signal_number);
+} GdmSessionWorkerJobClass;
+
+GType gdm_session_worker_job_get_type (void);
+GdmSessionWorkerJob * gdm_session_worker_job_new (void);
+void gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_job,
+ const char *server_address);
+gboolean gdm_session_worker_job_start (GdmSessionWorkerJob *session_worker_job);
+gboolean gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job);
+
+G_END_DECLS
+
+#endif /* __GDM_SESSION_WORKER_JOB_H */
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
new file mode 100644
index 00000000..4e1d8f42
--- /dev/null
+++ b/daemon/gdm-session-worker.c
@@ -0,0 +1,1672 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+
+#include <security/pam_appl.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gdm-session-worker.h"
+#include "gdm-marshal.h"
+
+#define GDM_SESSION_WORKER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SESSION_WORKER, GdmSessionWorkerPrivate))
+
+#define GDM_SESSION_DBUS_PATH "/org/gnome/DisplayManager/Session"
+#define GDM_SESSION_DBUS_INTERFACE "org.gnome.DisplayManager.Session"
+
+#ifndef GDM_PASSWD_AUXILLARY_BUFFER_SIZE
+#define GDM_PASSWD_AUXILLARY_BUFFER_SIZE 1024
+#endif
+
+#ifndef GDM_SESSION_DEFAULT_PATH
+#define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin"
+#endif
+
+#ifndef GDM_SESSION_ROOT_UID
+#define GDM_SESSION_ROOT_UID 0
+#endif
+
+struct GdmSessionWorkerPrivate
+{
+ int exit_code;
+
+ pam_handle_t *pam_handle;
+
+ GPid child_pid;
+ guint child_watch_id;
+
+ char *username;
+ char **arguments;
+
+ GHashTable *environment;
+
+ guint32 credentials_are_established : 1;
+ guint32 is_running : 1;
+
+ char *server_address;
+ DBusGConnection *connection;
+ DBusGProxy *server_proxy;
+};
+
+enum {
+ PROP_0,
+ PROP_SERVER_ADDRESS,
+};
+
+
+enum {
+ USER_VERIFIED = 0,
+ USER_VERIFICATION_ERROR,
+ INFO,
+ PROBLEM,
+ INFO_QUERY,
+ SECRET_INFO_QUERY,
+ SESSION_STARTED,
+ SESSION_STARTUP_ERROR,
+ SESSION_EXITED,
+ SESSION_DIED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_session_worker_class_init (GdmSessionWorkerClass *klass);
+static void gdm_session_worker_init (GdmSessionWorker *session_worker);
+static void gdm_session_worker_finalize (GObject *object);
+
+typedef int (* GdmSessionWorkerPamNewMessagesFunc) (int,
+ const struct pam_message **,
+ struct pam_response **,
+ gpointer);
+
+G_DEFINE_TYPE (GdmSessionWorker, gdm_session_worker, G_TYPE_OBJECT)
+
+GQuark
+gdm_session_worker_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (error_quark == 0)
+ error_quark = g_quark_from_static_string ("gdm-session-worker");
+
+ return error_quark;
+}
+
+/* adapted from glib script_execute */
+static void
+script_execute (const gchar *file,
+ char **argv,
+ char **envp,
+ gboolean search_path)
+{
+ /* Count the arguments. */
+ int argc = 0;
+
+ while (argv[argc])
+ ++argc;
+
+ /* Construct an argument list for the shell. */
+ {
+ char **new_argv;
+
+ new_argv = g_new0 (gchar*, argc + 2); /* /bin/sh and NULL */
+
+ new_argv[0] = (char *) "/bin/sh";
+ new_argv[1] = (char *) file;
+ while (argc > 0) {
+ new_argv[argc + 1] = argv[argc];
+ --argc;
+ }
+
+ /* Execute the shell. */
+ if (envp)
+ execve (new_argv[0], new_argv, envp);
+ else
+ execv (new_argv[0], new_argv);
+
+ g_free (new_argv);
+ }
+}
+
+static char *
+my_strchrnul (const char *str, char c)
+{
+ char *p = (char*) str;
+ while (*p && (*p != c))
+ ++p;
+
+ return p;
+}
+
+/* adapted from glib g_execute */
+static gint
+gdm_session_execute (const char *file,
+ char **argv,
+ char **envp,
+ gboolean search_path)
+{
+ if (*file == '\0') {
+ /* We check the simple case first. */
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (!search_path || strchr (file, '/') != NULL) {
+ /* Don't search when it contains a slash. */
+ if (envp)
+ execve (file, argv, envp);
+ else
+ execv (file, argv);
+
+ if (errno == ENOEXEC)
+ script_execute (file, argv, envp, FALSE);
+ } else {
+ gboolean got_eacces = 0;
+ const char *path, *p;
+ char *name, *freeme;
+ gsize len;
+ gsize pathlen;
+
+ path = g_getenv ("PATH");
+ if (path == NULL) {
+ /* There is no `PATH' in the environment. The default
+ * search path in libc is the current directory followed by
+ * the path `confstr' returns for `_CS_PATH'.
+ */
+
+ /* In GLib we put . last, for security, and don't use the
+ * unportable confstr(); UNIX98 does not actually specify
+ * what to search if PATH is unset. POSIX may, dunno.
+ */
+
+ path = "/bin:/usr/bin:.";
+ }
+
+ len = strlen (file) + 1;
+ pathlen = strlen (path);
+ freeme = name = g_malloc (pathlen + len + 1);
+
+ /* Copy the file name at the top, including '\0' */
+ memcpy (name + pathlen + 1, file, len);
+ name = name + pathlen;
+ /* And add the slash before the filename */
+ *name = '/';
+
+ p = path;
+ do {
+ char *startp;
+
+ path = p;
+ p = my_strchrnul (path, ':');
+
+ if (p == path)
+ /* Two adjacent colons, or a colon at the beginning or the end
+ * of `PATH' means to search the current directory.
+ */
+ startp = name + 1;
+ else
+ startp = memcpy (name - (p - path), path, p - path);
+
+ /* Try to execute this name. If it works, execv will not return. */
+ if (envp)
+ execve (startp, argv, envp);
+ else
+ execv (startp, argv);
+
+ if (errno == ENOEXEC)
+ script_execute (startp, argv, envp, search_path);
+
+ switch (errno) {
+ case EACCES:
+ /* Record the we got a `Permission denied' error. If we end
+ * up finding no executable we can use, we want to diagnose
+ * that we did find one but were denied access.
+ */
+ got_eacces = TRUE;
+
+ /* FALL THRU */
+
+ case ENOENT:
+#ifdef ESTALE
+ case ESTALE:
+#endif
+#ifdef ENOTDIR
+ case ENOTDIR:
+#endif
+ /* Those errors indicate the file is missing or not executable
+ * by us, in which case we want to just try the next path
+ * directory.
+ */
+ break;
+
+ default:
+ /* Some other error means we found an executable file, but
+ * something went wrong executing it; return the error to our
+ * caller.
+ */
+ g_free (freeme);
+ return -1;
+ }
+ } while (*p++ != '\0');
+
+ /* We tried every element and none of them worked. */
+ if (got_eacces)
+ /* At least one failure was due to permissions, so report that
+ * error.
+ */
+ errno = EACCES;
+
+ g_free (freeme);
+ }
+
+ /* Return the error from the last attempt (probably ENOENT). */
+ return -1;
+}
+
+static void
+send_user_verified (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "Verified",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send Verified: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+send_startup_failed (GdmSessionWorker *worker,
+ const char *message)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "StartupFailed",
+ &error,
+ G_TYPE_STRING, message,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send StartupFailed: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+send_username_changed (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "UsernameChanged",
+ &error,
+ G_TYPE_STRING, worker->priv->username,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send UsernameChanged: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+send_user_verification_error (GdmSessionWorker *worker,
+ const char *message)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "VerificationFailed",
+ &error,
+ G_TYPE_STRING, message,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send VerificationFailed: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+send_session_started (GdmSessionWorker *worker,
+ GPid pid)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "SessionStarted",
+ &error,
+ G_TYPE_INT, (int)pid,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send SessionStarted: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+gdm_session_worker_get_username (GdmSessionWorker *worker,
+ char **username)
+{
+ gconstpointer item;
+
+ g_assert (worker->priv->pam_handle != NULL);
+
+ if (pam_get_item (worker->priv->pam_handle, PAM_USER, &item) == PAM_SUCCESS) {
+ if (username) {
+ *username = g_strdup ((char *) item);
+ g_debug ("username is '%s'",
+ *username != NULL ? *username :
+ "<unset>");
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gdm_session_worker_update_username (GdmSessionWorker *worker)
+{
+ char *username;
+ gboolean res;
+
+ username = NULL;
+ res = gdm_session_worker_get_username (worker, &username);
+ if (res) {
+ if ((worker->priv->username == username) ||
+ ((worker->priv->username != NULL) && (username != NULL) &&
+ (strcmp (worker->priv->username, username) == 0)))
+ goto out;
+
+ g_debug ("setting username to '%s'", username);
+
+ g_free (worker->priv->username);
+ worker->priv->username = username;
+ username = NULL;
+
+ send_username_changed (worker);
+ }
+
+ out:
+ g_free (username);
+}
+
+static char *
+gdm_session_worker_ask_question (GdmSessionWorker *worker,
+ const char *question)
+{
+ GError *error;
+ gboolean res;
+ char *answer;
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "InfoQuery",
+ &error,
+ G_TYPE_STRING, question,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &answer,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send InfoQuery: %s", error->message);
+ g_error_free (error);
+ }
+
+ return answer;
+}
+
+static char *
+gdm_session_worker_ask_for_secret (GdmSessionWorker *worker,
+ const char *secret)
+{
+ GError *error;
+ gboolean res;
+ char *answer;
+
+ g_debug ("Secret info query: %s", secret);
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "SecretInfoQuery",
+ &error,
+ G_TYPE_STRING, secret,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &answer,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send SecretInfoQuery: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_debug ("answer to secret question '%s' is '%s'", secret, answer);
+ return answer;
+}
+
+static void
+gdm_session_worker_report_info (GdmSessionWorker *worker,
+ const char *info)
+{
+ GError *error;
+ gboolean res;
+
+ g_debug ("Info: %s", info);
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "Info",
+ &error,
+ G_TYPE_STRING, info,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send Info: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+gdm_session_worker_report_problem (GdmSessionWorker *worker,
+ const char *problem)
+{
+ GError *error;
+ gboolean res;
+
+ g_debug ("Problem: %s", problem);
+
+ error = NULL;
+ res = dbus_g_proxy_call (worker->priv->server_proxy,
+ "Problem",
+ &error,
+ G_TYPE_STRING, problem,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ if (! res) {
+ g_warning ("Unable to send Problem: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static char *
+convert_to_utf8 (const char *str)
+{
+ char *utf8;
+ utf8 = g_locale_to_utf8 (str,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+
+ /* if we couldn't convert text from locale then
+ * assume utf-8 and hope for the best */
+ if (utf8 == NULL) {
+ char *p;
+ char *q;
+
+ utf8 = g_strdup (str);
+
+ p = utf8;
+ while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q)) {
+ *q = '?';
+ p = q + 1;
+ }
+ }
+
+ return utf8;
+}
+
+static gboolean
+gdm_session_worker_process_pam_message (GdmSessionWorker *worker,
+ const struct pam_message *query,
+ char **response_text)
+{
+ char *user_answer;
+ gboolean was_processed;
+ char *utf8_msg;
+
+
+ g_debug ("received pam message of type %u with payload '%s'",
+ query->msg_style, query->msg);
+
+ utf8_msg = convert_to_utf8 (query->msg);
+
+ user_answer = NULL;
+ was_processed = FALSE;
+ switch (query->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ user_answer = gdm_session_worker_ask_question (worker, utf8_msg);
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ user_answer = gdm_session_worker_ask_for_secret (worker, utf8_msg);
+ break;
+ case PAM_TEXT_INFO:
+ gdm_session_worker_report_info (worker, utf8_msg);
+ was_processed = TRUE;
+ break;
+ case PAM_ERROR_MSG:
+ gdm_session_worker_report_problem (worker, utf8_msg);
+ was_processed = TRUE;
+ break;
+ default:
+ g_debug ("unknown query of type %u\n", query->msg_style);
+ break;
+ }
+
+ if (user_answer != NULL) {
+ /* we strdup and g_free to make sure we return malloc'd
+ * instead of g_malloc'd memory
+ */
+ if (response_text != NULL) {
+ *response_text = strdup (user_answer);
+ }
+
+ g_free (user_answer);
+
+ g_debug ("trying to get updated username");
+ gdm_session_worker_update_username (worker);
+ was_processed = TRUE;
+ }
+
+ g_free (utf8_msg);
+
+ return was_processed;
+}
+
+static int
+gdm_session_worker_pam_new_messages_handler (int number_of_messages,
+ const struct pam_message **messages,
+ struct pam_response **responses,
+ GdmSessionWorker *worker)
+{
+ struct pam_response *replies;
+ int return_value;
+ int i;
+
+ g_debug ("%d new messages received from pam\n", number_of_messages);
+
+ return_value = PAM_CONV_ERR;
+
+ if (number_of_messages < 0) {
+ return PAM_CONV_ERR;
+ }
+
+ if (number_of_messages == 0) {
+ if (responses) {
+ *responses = NULL;
+ }
+
+ return PAM_SUCCESS;
+ }
+
+ /* we want to generate one reply for every question
+ */
+ replies = (struct pam_response *) calloc (number_of_messages,
+ sizeof (struct pam_response));
+ for (i = 0; i < number_of_messages; i++) {
+ gboolean got_response;
+ char *response_text;
+
+ response_text = NULL;
+ got_response = gdm_session_worker_process_pam_message (worker,
+ messages[i],
+ &response_text);
+ if (!got_response)
+ goto out;
+
+ g_debug ("answered pam message %d with response '%s'",
+ i, response_text);
+ replies[i].resp = response_text;
+ replies[i].resp_retcode = PAM_SUCCESS;
+ }
+
+ return_value = PAM_SUCCESS;
+
+ out:
+ if (return_value != PAM_SUCCESS) {
+ for (i = 0; i < number_of_messages; i++) {
+ if (replies[i].resp != NULL) {
+ memset (replies[i].resp, 0, strlen (replies[i].resp));
+ free (replies[i].resp);
+ }
+ memset (&replies[i], 0, sizeof (replies[i]));
+ }
+ free (replies);
+ replies = NULL;
+ }
+
+ if (responses) {
+ *responses = replies;
+ }
+
+ return return_value;
+}
+
+static void
+gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
+ int error_code)
+{
+ g_debug ("uninitializing PAM");
+
+ if (worker->priv->pam_handle == NULL)
+ return;
+
+ if (worker->priv->credentials_are_established) {
+ pam_setcred (worker->priv->pam_handle, PAM_DELETE_CRED);
+ worker->priv->credentials_are_established = FALSE;
+ }
+
+ if (worker->priv->is_running) {
+ pam_close_session (worker->priv->pam_handle, 0);
+ worker->priv->is_running = FALSE;
+ }
+
+ pam_end (worker->priv->pam_handle, error_code);
+ worker->priv->pam_handle = NULL;
+}
+
+static gboolean
+gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
+ const char *service,
+ const char *username,
+ const char *hostname,
+ const char *console_name,
+ GError **error)
+{
+ struct pam_conv pam_conversation;
+ int error_code;
+
+ g_assert (worker->priv->pam_handle == NULL);
+
+ g_debug ("initializing PAM");
+
+ pam_conversation.conv = (GdmSessionWorkerPamNewMessagesFunc) gdm_session_worker_pam_new_messages_handler;
+ pam_conversation.appdata_ptr = worker;
+
+ error_code = pam_start (service,
+ username,
+ &pam_conversation,
+ &worker->priv->pam_handle);
+
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("could not initialize pam");
+ /* we don't use pam_strerror here because it requires a valid
+ * pam handle, and if pam_start fails pam_handle is undefined
+ */
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ _("error initiating conversation with authentication system - %s"),
+ error_code == PAM_ABORT? _("general failure") :
+ error_code == PAM_BUF_ERR? _("out of memory") :
+ error_code == PAM_SYSTEM_ERR? _("application programmer error") :
+ _("unscoped error"));
+
+ goto out;
+ }
+
+ if (username == NULL) {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_USER_PROMPT, _("Username:"));
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ _("error informing authentication system of preferred username prompt - %s"),
+ pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+ }
+
+ if (hostname != NULL) {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_RHOST, hostname);
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ _("error informing authentication system of user's hostname - %s"),
+ pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+ }
+
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_TTY, console_name);
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ _("error informing authentication system of user's console - %s"),
+ pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+
+ out:
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_authenticate_user (GdmSessionWorker *worker,
+ gboolean password_is_required,
+ GError **error)
+{
+ int error_code;
+ int authentication_flags;
+
+ g_debug ("authenticating user");
+
+ authentication_flags = 0;
+
+ if (password_is_required) {
+ authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
+ }
+
+ /* blocking call, does the actual conversation
+ */
+ error_code = pam_authenticate (worker->priv->pam_handle, authentication_flags);
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ "%s", pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+
+ out:
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_authorize_user (GdmSessionWorker *worker,
+ gboolean password_is_required,
+ GError **error)
+{
+ int error_code;
+ int authentication_flags;
+
+ g_debug ("determining if authenticated user is authorized to session");
+
+ authentication_flags = 0;
+
+ if (password_is_required) {
+ authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
+ }
+
+ /* check that the account isn't disabled or expired
+ */
+ error_code = pam_acct_mgmt (worker->priv->pam_handle, authentication_flags);
+
+ /* it's possible that the user needs to change their password or pin code
+ */
+ if (error_code == PAM_NEW_AUTHTOK_REQD)
+ error_code = pam_chauthtok (worker->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
+
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("user is not authorized to log in: %s",
+ pam_strerror (worker->priv->pam_handle, error_code));
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHORIZING,
+ "%s", pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+
+ out:
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gdm_session_worker_set_environment_variable (GdmSessionWorker *worker,
+ const char *key,
+ const char *value)
+{
+ /* FIXME: maybe we should use use pam_putenv instead of our
+ * own hash table, so pam can override our choices if it knows
+ * better?
+ */
+ g_hash_table_replace (worker->priv->environment,
+ g_strdup (key),
+ g_strdup (value));
+}
+
+static void
+gdm_session_worker_update_environment_from_passwd_entry (GdmSessionWorker *worker,
+ struct passwd *passwd_entry)
+{
+ gdm_session_worker_set_environment_variable (worker, "LOGNAME", worker->priv->username);
+ gdm_session_worker_set_environment_variable (worker, "USER", worker->priv->username);
+ gdm_session_worker_set_environment_variable (worker, "USERNAME", worker->priv->username);
+ gdm_session_worker_set_environment_variable (worker, "HOME", passwd_entry->pw_dir);
+ gdm_session_worker_set_environment_variable (worker, "SHELL", passwd_entry->pw_shell);
+}
+
+static gboolean
+gdm_session_worker_environment_variable_is_set (GdmSessionWorker *worker,
+ const char *name)
+{
+ return g_hash_table_lookup (worker->priv->environment, name) != NULL;
+}
+
+static gboolean
+gdm_session_worker_give_user_credentials (GdmSessionWorker *worker,
+ GError **error)
+{
+ int error_code;
+ struct passwd *passwd_entry, passwd_buffer;
+ char *aux_buffer;
+ long required_aux_buffer_size;
+ gsize aux_buffer_size;
+
+ aux_buffer = NULL;
+ aux_buffer_size = 0;
+
+ if (worker->priv->username == NULL) {
+ error_code = PAM_USER_UNKNOWN;
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ _("no user account available"));
+ goto out;
+ }
+
+ required_aux_buffer_size = sysconf (_SC_GETPW_R_SIZE_MAX);
+
+ if (required_aux_buffer_size < 0)
+ aux_buffer_size = GDM_PASSWD_AUXILLARY_BUFFER_SIZE;
+ else
+ aux_buffer_size = (gsize) required_aux_buffer_size;
+
+ aux_buffer = g_slice_alloc0 (aux_buffer_size);
+
+ /* we use the _r variant of getpwnam()
+ * (with its weird semantics) so that the
+ * passwd_entry doesn't potentially get stomped on
+ * by a PAM module
+ */
+ passwd_entry = NULL;
+ errno = getpwnam_r (worker->priv->username,
+ &passwd_buffer,
+ aux_buffer,
+ (size_t) aux_buffer_size,
+ &passwd_entry);
+
+ if (errno != 0) {
+ error_code = PAM_SYSTEM_ERR;
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ "%s",
+ g_strerror (errno));
+ goto out;
+ }
+
+ if (passwd_entry == NULL) {
+ error_code = PAM_USER_UNKNOWN;
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ _("user account not available on system"));
+ goto out;
+ }
+
+ gdm_session_worker_update_environment_from_passwd_entry (worker, passwd_entry);
+
+ /* Let's give the user a default PATH if he doesn't already have one
+ */
+ if (!gdm_session_worker_environment_variable_is_set (worker, "PATH")) {
+ gdm_session_worker_set_environment_variable (worker, "PATH", GDM_SESSION_DEFAULT_PATH);
+ }
+
+ /* pam_setcred wants to be called as the authenticated user
+ * but pam_open_session needs to be called as super-user.
+ *
+ * Set the real uid and gid to the user and give the user a
+ * temporary super-user effective id.
+ */
+ if (setreuid (passwd_entry->pw_uid, GDM_SESSION_ROOT_UID) < 0) {
+ error_code = PAM_SYSTEM_ERR;
+ g_set_error (error, GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ "%s", g_strerror (errno));
+ goto out;
+ }
+
+ if (setgid (passwd_entry->pw_gid) < 0) {
+ error_code = PAM_SYSTEM_ERR;
+ g_set_error (error, GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ "%s", g_strerror (errno));
+ goto out;
+ }
+
+ if (initgroups (passwd_entry->pw_name, passwd_entry->pw_gid) < 0) {
+ error_code = PAM_SYSTEM_ERR;
+ g_set_error (error, GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ "%s", g_strerror (errno));
+ goto out;
+ }
+
+ error_code = pam_setcred (worker->priv->pam_handle, PAM_ESTABLISH_CRED);
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ "%s",
+ pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+
+ worker->priv->credentials_are_established = TRUE;
+
+ out:
+ if (aux_buffer != NULL) {
+ g_assert (aux_buffer_size > 0);
+ g_slice_free1 (aux_buffer_size, aux_buffer);
+ }
+
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_verify_user (GdmSessionWorker *worker,
+ const char *service_name,
+ const char *hostname,
+ const char *console_name,
+ const char *username,
+ gboolean password_is_required,
+ GError **error)
+{
+ GError *pam_error;
+ gboolean res;
+
+ g_debug ("Verifying user: %s host: %s service: %s tty: %s", username, hostname, service_name, console_name);
+
+ pam_error = NULL;
+ res = gdm_session_worker_initialize_pam (worker,
+ service_name,
+ username,
+ hostname,
+ console_name,
+ &pam_error);
+ if (! res) {
+ g_propagate_error (error, pam_error);
+ return FALSE;
+ }
+
+ /* find out who the user is and ensure they are who they say they are
+ */
+ res = gdm_session_worker_authenticate_user (worker,
+ password_is_required,
+ &pam_error);
+ if (! res) {
+ g_propagate_error (error, pam_error);
+ return FALSE;
+ }
+
+ /* we're authenticated. Let's make sure we've been given
+ * a valid username for the system
+ */
+ g_debug ("trying to get updated username");
+ gdm_session_worker_update_username (worker);
+
+ /* make sure the user is allowed to log in to this system
+ */
+ res = gdm_session_worker_authorize_user (worker,
+ password_is_required,
+ &pam_error);
+ if (! res) {
+ g_propagate_error (error, pam_error);
+ return FALSE;
+ }
+
+ /* get kerberos tickets, setup group lists, etc
+ */
+ res = gdm_session_worker_give_user_credentials (worker, &pam_error);
+ if (! res) {
+ g_propagate_error (error, pam_error);
+ return FALSE;
+ }
+
+ g_debug ("verification process completed, creating reply...");
+
+ send_user_verified (worker);
+
+ return TRUE;
+}
+
+static void
+gdm_session_worker_update_environment_from_pam (GdmSessionWorker *worker)
+{
+ char **environment;
+ gsize i;
+
+ environment = pam_getenvlist (worker->priv->pam_handle);
+
+ for (i = 0; environment[i] != NULL; i++) {
+ char **key_and_value;
+
+ key_and_value = g_strsplit (environment[i], "=", 2);
+
+ gdm_session_worker_set_environment_variable (worker, key_and_value[0], key_and_value[1]);
+
+ g_strfreev (key_and_value);
+ }
+
+ for (i = 0; environment[i]; i++) {
+ free (environment[i]);
+ }
+
+ free (environment);
+}
+
+static void
+gdm_session_worker_fill_environment_array (const char *key,
+ const char *value,
+ GPtrArray *environment)
+{
+ char *variable;
+
+ if (value == NULL)
+ return;
+
+ variable = g_strdup_printf ("%s=%s", key, value);
+
+ g_ptr_array_add (environment, variable);
+}
+
+static char **
+gdm_session_worker_get_environment (GdmSessionWorker *worker)
+{
+ GPtrArray *environment;
+
+ environment = g_ptr_array_new ();
+ g_hash_table_foreach (worker->priv->environment,
+ (GHFunc) gdm_session_worker_fill_environment_array,
+ environment);
+ g_ptr_array_add (environment, NULL);
+
+ return (char **) g_ptr_array_free (environment, FALSE);
+}
+
+static void
+session_worker_child_watch (GPid pid,
+ int status,
+ GdmSessionWorker *worker)
+{
+ g_debug ("child (pid:%d) done (%s:%d)",
+ (int) pid,
+ WIFEXITED (status) ? "status"
+ : WIFSIGNALED (status) ? "signal"
+ : "unknown",
+ WIFEXITED (status) ? WEXITSTATUS (status)
+ : WIFSIGNALED (status) ? WTERMSIG (status)
+ : -1);
+
+ if (WIFEXITED (status)) {
+ int code = WEXITSTATUS (status);
+ /*g_signal_emit (job, signals [EXITED], 0, code);*/
+ } else if (WIFSIGNALED (status)) {
+ int num = WTERMSIG (status);
+ /*g_signal_emit (job, signals [DIED], 0, num);*/
+ }
+
+ worker->priv->child_pid = -1;
+}
+
+static void
+gdm_session_worker_watch_child (GdmSessionWorker *worker)
+{
+
+ worker->priv->child_watch_id = g_child_watch_add (worker->priv->child_pid,
+ (GChildWatchFunc)session_worker_child_watch,
+ worker);
+
+}
+
+static gboolean
+gdm_session_worker_open_user_session (GdmSessionWorker *worker,
+ GError **error)
+{
+ int error_code;
+ pid_t session_pid;
+
+ g_assert (!worker->priv->is_running);
+ g_assert (geteuid () == 0);
+ error_code = pam_open_session (worker->priv->pam_handle, 0);
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ "%s", pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+ worker->priv->is_running = TRUE;
+
+ g_debug ("querying pam for user environment");
+ gdm_session_worker_update_environment_from_pam (worker);
+
+ g_debug ("opening user session with program '%s'",
+ worker->priv->arguments[0]);
+
+ session_pid = fork ();
+
+ if (session_pid < 0) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ "%s", g_strerror (errno));
+ error_code = PAM_ABORT;
+ goto out;
+ }
+
+ if (session_pid == 0) {
+ char **environment;
+ char *home_dir;
+
+ if (setuid (getuid ()) < 0) {
+ g_debug ("could not reset uid - %s", g_strerror (errno));
+ _exit (1);
+ }
+
+ if (setsid () < 0) {
+ g_debug ("could not set pid '%u' as leader of new session and process group - %s",
+ (guint) getpid (), g_strerror (errno));
+ _exit (2);
+ }
+
+ environment = gdm_session_worker_get_environment (worker);
+
+ g_assert (geteuid () == getuid ());
+
+ home_dir = g_hash_table_lookup (worker->priv->environment,
+ "HOME");
+
+ if ((home_dir == NULL) || g_chdir (home_dir) < 0) {
+ g_chdir ("/");
+ }
+
+ gdm_session_execute (worker->priv->arguments[0],
+ worker->priv->arguments,
+ environment,
+ TRUE);
+
+ g_debug ("child '%s' could not be started - %s",
+ worker->priv->arguments[0],
+ g_strerror (errno));
+ g_strfreev (environment);
+
+ _exit (127);
+ }
+
+ worker->priv->child_pid = session_pid;
+
+ g_debug ("session opened creating reply...");
+ g_assert (sizeof (GPid) <= sizeof (int));
+
+ send_session_started (worker, session_pid);
+
+ gdm_session_worker_watch_child (worker);
+
+ out:
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+static gboolean
+gdm_session_worker_open (GdmSessionWorker *worker,
+ const char *service_name,
+ const char *hostname,
+ const char *console_name,
+ const char *username,
+ GError **error)
+{
+ GError *verification_error;
+ gboolean res;
+ gboolean ret;
+
+ ret = FALSE;
+
+ verification_error = NULL;
+ res = gdm_session_worker_verify_user (worker,
+ service_name,
+ hostname,
+ console_name,
+ username,
+ TRUE /* password is required */,
+ &verification_error);
+ if (! res) {
+ g_assert (verification_error != NULL);
+
+ g_message ("%s", verification_error->message);
+
+ g_propagate_error (error, verification_error);
+
+ goto out;
+ }
+
+ /* Did start_program get called early? if so, process it now,
+ * otherwise we'll do it asynchronously later.
+ */
+ if ((worker->priv->arguments != NULL) &&
+ !gdm_session_worker_open_user_session (worker, &verification_error)) {
+ g_assert (verification_error != NULL);
+
+ g_message ("%s", verification_error->message);
+
+ g_propagate_error (error, verification_error);
+
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static gboolean
+gdm_session_worker_start_program (GdmSessionWorker *worker,
+ const char *command)
+{
+ GError *start_error;
+ GError *error;
+ gboolean res;
+
+ if (worker->priv->arguments != NULL)
+ g_strfreev (worker->priv->arguments);
+
+ error = NULL;
+ if (! g_shell_parse_argv (command, NULL, &worker->priv->arguments, &error)) {
+ g_warning ("Unable to parse command: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /* Did start_program get called early? if so, we will process the request
+ * later, synchronously after getting credentials
+ */
+ if (!worker->priv->credentials_are_established) {
+ return FALSE;
+ }
+
+ start_error = NULL;
+ res = gdm_session_worker_open_user_session (worker, &start_error);
+ if (! res) {
+ g_assert (start_error != NULL);
+
+ g_warning ("%s", start_error->message);
+
+ send_startup_failed (worker, start_error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gdm_session_worker_set_server_address (GdmSessionWorker *worker,
+ const char *address)
+{
+ g_free (worker->priv->server_address);
+ worker->priv->server_address = g_strdup (address);
+}
+
+static void
+gdm_session_worker_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionWorker *self;
+
+ self = GDM_SESSION_WORKER (object);
+
+ switch (prop_id) {
+ case PROP_SERVER_ADDRESS:
+ gdm_session_worker_set_server_address (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_session_worker_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionWorker *self;
+
+ self = GDM_SESSION_WORKER (object);
+
+ switch (prop_id) {
+ case PROP_SERVER_ADDRESS:
+ g_value_set_string (value, self->priv->server_address);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+on_set_environment_variable (DBusGProxy *proxy,
+ const char *key,
+ const char *value,
+ gpointer data)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (data);
+
+ g_debug ("set env: %s = %s", key, value);
+
+ gdm_session_worker_set_environment_variable (worker, key, value);
+}
+
+static void
+on_start_program (DBusGProxy *proxy,
+ const char *text,
+ gpointer data)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (data);
+
+ g_debug ("start program: %s", text);
+
+ gdm_session_worker_start_program (worker, text);
+}
+
+static void
+on_begin_verification (DBusGProxy *proxy,
+ const char *service,
+ const char *console,
+ const char *hostname,
+ gpointer data)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (data);
+ GError *error;
+ gboolean res;
+
+ g_debug ("begin verification: %s %s", service, console);
+
+ error = NULL;
+ res = gdm_session_worker_open (worker, service, console, hostname, NULL, &error);
+ if (! res) {
+ g_debug ("Verification failed: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+on_begin_verification_for_user (DBusGProxy *proxy,
+ const char *service,
+ const char *console,
+ const char *hostname,
+ const char *username,
+ gpointer data)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (data);
+ GError *error;
+ gboolean res;
+
+ g_debug ("begin verification: %s %s", service, console);
+
+ error = NULL;
+ res = gdm_session_worker_open (worker, service, console, hostname, username, &error);
+ if (! res) {
+ g_debug ("Verification failed: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static GObject *
+gdm_session_worker_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmSessionWorker *worker;
+ GdmSessionWorkerClass *klass;
+ GError *error;
+
+ klass = GDM_SESSION_WORKER_CLASS (g_type_class_peek (GDM_TYPE_SESSION_WORKER));
+
+ worker = GDM_SESSION_WORKER (G_OBJECT_CLASS (gdm_session_worker_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ g_debug ("connecting to address: %s", worker->priv->server_address);
+
+ error = NULL;
+ worker->priv->connection = dbus_g_connection_open (worker->priv->server_address, &error);
+ if (worker->priv->connection == NULL) {
+ if (error != NULL) {
+ g_warning ("error opening connection: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Unable to open connection");
+ }
+ exit (1);
+ }
+
+ g_debug ("creating proxy for peer: %s", GDM_SESSION_DBUS_PATH);
+ worker->priv->server_proxy = dbus_g_proxy_new_for_peer (worker->priv->connection,
+ GDM_SESSION_DBUS_PATH,
+ GDM_SESSION_DBUS_INTERFACE);
+ if (worker->priv->server_proxy == NULL) {
+ g_warning ("Unable to create proxy for peer");
+ exit (1);
+ }
+
+ /*g_signal_connect (worker->priv->server_proxy, "destroy", G_CALLBACK (proxy_destroyed), NULL);*/
+
+ dbus_g_object_register_marshaller (gdm_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_object_register_marshaller (gdm_marshal_VOID__STRING_STRING_STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+
+ /* FIXME: not sure why introspection isn't working */
+ dbus_g_proxy_add_signal (worker->priv->server_proxy, "StartProgram", G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (worker->priv->server_proxy, "SetEnvironmentVariable", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (worker->priv->server_proxy, "BeginVerification", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (worker->priv->server_proxy, "BeginVerificationForUser", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (worker->priv->server_proxy,
+ "StartProgram",
+ G_CALLBACK (on_start_program),
+ worker,
+ NULL);
+ dbus_g_proxy_connect_signal (worker->priv->server_proxy,
+ "SetEnvironmentVariable",
+ G_CALLBACK (on_set_environment_variable),
+ worker,
+ NULL);
+ dbus_g_proxy_connect_signal (worker->priv->server_proxy,
+ "BeginVerification",
+ G_CALLBACK (on_begin_verification),
+ worker,
+ NULL);
+ dbus_g_proxy_connect_signal (worker->priv->server_proxy,
+ "BeginVerificationForUser",
+ G_CALLBACK (on_begin_verification_for_user),
+ worker,
+ NULL);
+
+ return G_OBJECT (worker);
+}
+
+static void
+gdm_session_worker_class_init (GdmSessionWorkerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_session_worker_get_property;
+ object_class->set_property = gdm_session_worker_set_property;
+ object_class->constructor = gdm_session_worker_constructor;
+ object_class->finalize = gdm_session_worker_finalize;
+
+ g_type_class_add_private (klass, sizeof (GdmSessionWorkerPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_SERVER_ADDRESS,
+ g_param_spec_string ("server-address",
+ "server address",
+ "server address",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+gdm_session_worker_init (GdmSessionWorker *worker)
+{
+
+ worker->priv = GDM_SESSION_WORKER_GET_PRIVATE (worker);
+ worker->priv->environment = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+}
+
+static void
+gdm_session_worker_unwatch_child (GdmSessionWorker *worker)
+{
+ if (worker->priv->child_watch_id == 0)
+ return;
+
+ g_source_remove (worker->priv->child_watch_id);
+ worker->priv->child_watch_id = 0;
+}
+
+
+static void
+gdm_session_worker_finalize (GObject *object)
+{
+ GdmSessionWorker *worker;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_SESSION_WORKER (object));
+
+ worker = GDM_SESSION_WORKER (object);
+
+ g_return_if_fail (worker->priv != NULL);
+
+ gdm_session_worker_unwatch_child (worker);
+
+ if (worker->priv->username != NULL) {
+ g_free (worker->priv->username);
+ worker->priv->username = NULL;
+ }
+
+ if (worker->priv->arguments != NULL) {
+ g_strfreev (worker->priv->arguments);
+ worker->priv->arguments = NULL;
+ }
+
+ if (worker->priv->environment != NULL) {
+ g_hash_table_destroy (worker->priv->environment);
+ worker->priv->environment = NULL;
+ }
+
+ G_OBJECT_CLASS (gdm_session_worker_parent_class)->finalize (object);
+}
+
+GdmSessionWorker *
+gdm_session_worker_new (const char *address)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_SESSION_WORKER,
+ "server-address", address,
+ NULL);
+
+ return GDM_SESSION_WORKER (object);
+}
diff --git a/daemon/gdm-session-worker.h b/daemon/gdm-session-worker.h
new file mode 100644
index 00000000..308c3b35
--- /dev/null
+++ b/daemon/gdm-session-worker.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GDM_SESSION_H
+#define __GDM_SESSION_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SESSION_WORKER (gdm_session_worker_get_type ())
+#define GDM_SESSION_WORKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SESSION_WORKER, GdmSessionWorker))
+#define GDM_SESSION_WORKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SESSION_WORKER, GdmSessionWorkerClass))
+#define GDM_IS_SESSION_WORKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SESSION_WORKER))
+#define GDM_IS_SESSION_WORKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SESSION_WORKER))
+#define GDM_SESSION_WORKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SESSION_WORKER, GdmSessionWorkerClass))
+
+#define GDM_SESSION_WORKER_ERROR (gdm_session_worker_error_quark ())
+
+typedef enum _GdmSessionWorkerError {
+ GDM_SESSION_WORKER_ERROR_GENERIC = 0,
+ GDM_SESSION_WORKER_ERROR_WITH_SESSION_COMMAND,
+ GDM_SESSION_WORKER_ERROR_FORKING,
+ GDM_SESSION_WORKER_ERROR_OPENING_MESSAGE_PIPE,
+ GDM_SESSION_WORKER_ERROR_COMMUNICATING,
+ GDM_SESSION_WORKER_ERROR_WORKER_DIED,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ GDM_SESSION_WORKER_ERROR_AUTHORIZING,
+ GDM_SESSION_WORKER_ERROR_OPENING_LOG_FILE,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS
+} GdmSessionWorkerError;
+
+typedef struct GdmSessionWorkerPrivate GdmSessionWorkerPrivate;
+
+typedef struct
+{
+ GObject parent;
+
+ /*< private > */
+ GdmSessionWorkerPrivate *priv;
+} GdmSessionWorker;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+} GdmSessionWorkerClass;
+
+GType gdm_session_worker_get_type (void);
+GQuark gdm_session_worker_error_quark (void);
+
+GdmSessionWorker * gdm_session_worker_new (const char *server_address) G_GNUC_MALLOC;
+
+G_END_DECLS
+
+#endif /* GDM_SESSION_WORKER_H */
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 996c4067..bafc03ab 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -3,6 +3,7 @@
* session.c - authenticates and authorizes users with system
*
* Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,8 +33,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <grp.h>
-#include <pwd.h>
+
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
@@ -44,14 +44,20 @@
#include <unistd.h>
#include <utmp.h>
-#include <security/pam_appl.h>
-
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <glib-object.h>
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
#include "gdm-session.h"
+#include "gdm-session-worker-job.h"
+
+#define GDM_SESSION_DBUS_PATH "/org/gnome/DisplayManager/Session"
+#define GDM_SESSION_DBUS_INTERFACE "org.gnome.DisplayManager.Session"
+
#ifndef GDM_BAD_SESSION_RECORDS_FILE
#define GDM_BAD_SESSION_RECORDS_FILE "/var/log/btmp"
@@ -89,59 +95,6 @@
#define GDM_MAX_LOG_FILENAME_SIZE 1024
#endif
-#ifndef GDM_PASSWD_AUXILLARY_BUFFER_SIZE
-#define GDM_PASSWD_AUXILLARY_BUFFER_SIZE 1024
-#endif
-
-#ifndef GDM_SESSION_MESSAGE_SENTINAL
-#define GDM_SESSION_MESSAGE_SENTINAL "sEnTInAL"
-#endif
-
-#ifndef GDM_SESSION_WORKER_MESSAGE_SENTINAL
-#define GDM_SESSION_WORKER_MESSAGE_SENTINAL "WoRkeR sEnTInAL"
-#endif
-
-#ifndef GDM_SESSION_DEFAULT_PATH
-#define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin"
-#endif
-
-#ifndef GDM_SESSION_ROOT_UID
-#define GDM_SESSION_ROOT_UID 0
-#endif
-
-typedef struct _GdmSessionMessage GdmSessionMessage;
-typedef struct _GdmSessionVerificationMessage GdmSessionVerificationMessage;
-typedef struct _GdmSessionStartProgramMessage GdmSessionStartProgramMessage;
-typedef struct _GdmSessionSetEnvironmentVariableMessage GdmSessionSetEnvironmentVariableMessage;
-typedef struct _GdmSessionInfoReplyMessage GdmSessionInfoReplyMessage;
-typedef struct _GdmSessionSecretInfoReplyMessage GdmSessionSecretInfoReplyMessage;
-typedef struct _GdmSessionWorker GdmSessionWorker;
-typedef struct _GdmSessionWorkerMessage GdmSessionWorkerMessage;
-typedef struct _GdmSessionWorkerVerifiedMessage GdmSessionWorkerVerifiedMessage;
-typedef struct _GdmSessionWorkerVerificationFailedMessage GdmSessionWorkerVerificationFailedMessage;
-typedef struct _GdmSessionWorkerUsernameChangedMessage GdmSessionWorkerUsernameChangedMessage;
-typedef struct _GdmSessionWorkerInfoRequestMessage GdmSessionWorkerInfoRequestMessage;
-typedef struct _GdmSessionWorkerSecretInfoRequestMessage GdmSessionWorkerSecretInfoRequestMessage;
-typedef struct _GdmSessionWorkerInfoMessage GdmSessionWorkerInfoMessage;
-typedef struct _GdmSessionWorkerProblemMessage GdmSessionWorkerProblemMessage;
-typedef struct _GdmSessionWorkerSessionStartedMessage GdmSessionWorkerSessionStartedMessage;
-typedef struct _GdmSessionWorkerSessionStartupFailedMessage GdmSessionWorkerSessionStartupFailedMessage;
-typedef struct _GdmSessionWorkerSessionExitedMessage GdmSessionWorkerSessionExitedMessage;
-typedef struct _GdmSessionWorkerSessionDiedMessage GdmSessionWorkerSessionDiedMessage;
-
-typedef int (* GdmSessionWorkerPamNewMessagesFunc) (int,
- const struct pam_message **,
- struct pam_response **,
- gpointer);
-typedef enum _GdmSessionMessageType {
- GDM_SESSION_MESSAGE_TYPE_INVALID = 0,
- GDM_SESSION_MESSAGE_TYPE_VERIFICATION = 0xc001deed,
- GDM_SESSION_MESSAGE_TYPE_START_PROGRAM,
- GDM_SESSION_MESSAGE_TYPE_SET_ENVIRONMENT_VARIABLE,
- GDM_SESSION_MESSAGE_TYPE_NEW_LOG_FILENAME,
- GDM_SESSION_MESSAGE_TYPE_INFO_REPLY,
- GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY
-} GdmSessionMessageType;
typedef enum _GdmSessionRecordType {
GDM_SESSION_RECORD_TYPE_LOGIN,
@@ -149,241 +102,28 @@ typedef enum _GdmSessionRecordType {
GDM_SESSION_RECORD_TYPE_LOGOUT,
} GdmSessionRecordType;
-typedef enum _GdmSessionWorkerMessageType {
- GDM_SESSION_WORKER_MESSAGE_TYPE_INVALID = 0,
- GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFIED = 0xdeadbeef,
- GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFICATION_FAILED,
- GDM_SESSION_WORKER_MESSAGE_TYPE_USERNAME_CHANGED,
- GDM_SESSION_WORKER_MESSAGE_TYPE_INFO_REQUEST,
- GDM_SESSION_WORKER_MESSAGE_TYPE_SECRET_INFO_REQUEST,
- GDM_SESSION_WORKER_MESSAGE_TYPE_INFO,
- GDM_SESSION_WORKER_MESSAGE_TYPE_PROBLEM,
- GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTED,
- GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTUP_FAILED,
- GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_EXITED,
- GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_DIED,
-} GdmSessionWorkerMessageType;
-
struct _GdmSessionPrivate
{
- GPid pid;
-
- char *service_name;
- char **arguments;
- char *username;
- char *hostname;
- char *console_name;
-
- int standard_output_fd;
- int standard_error_fd;
-
- int worker_message_pipe_fd;
- GSource *worker_message_pipe_source;
-
- GMainContext *context;
-
- GPid worker_pid;
- GSource *child_watch_source;
-
- GdmSessionMessageType next_expected_message;
-
- /* variable is only alive briefly to store user
- * responses set in callbacks to authentication queries
- */
- char *query_answer;
-
- guint32 is_verified : 1;
- guint32 is_running : 1;
-};
-
-struct _GdmSessionMessage
-{
- GdmSessionMessageType type;
- gsize size;
-};
-
-struct _GdmSessionVerificationMessage
-{
- GdmSessionMessage header;
-
- char service_name[GDM_MAX_SERVICE_NAME_SIZE];
- char console_name[GDM_MAX_CONSOLE_NAME_SIZE];
- char hostname[GDM_MAX_HOSTNAME_SIZE];
-
- int standard_output_fd;
- int standard_error_fd;
-
- guint32 hostname_is_provided : 1;
-
- gssize username_size;
- char username[0];
-};
-
-struct _GdmSessionStartProgramMessage
-{
- GdmSessionMessage header;
-
- gsize arguments_size;
- char arguments[0];
-};
-
-struct _GdmSessionSetEnvironmentVariableMessage
-{
- GdmSessionMessage header;
-
- gsize environment_variable_size;
- char environment_variable[0];
-};
-
-struct _GdmSessionInfoReplyMessage
-{
- GdmSessionMessage header;
-
- gssize answer_size;
- char answer[0];
-};
-
-struct _GdmSessionSecretInfoReplyMessage
-{
- GdmSessionMessage header;
-
- gssize answer_size;
- char answer[0];
-};
-
-struct _GdmSessionWorker
-{
- GMainLoop *event_loop;
- int exit_code;
-
- pam_handle_t *pam_handle;
-
- int message_pipe_fd;
- GSource *message_pipe_source;
-
- GSList *inherited_fd_list;
-
- GPid child_pid;
- GSource *child_watch_source;
-
- char *username;
- char **arguments;
-
- GHashTable *environment;
-
- int standard_output_fd;
- int standard_error_fd;
-
- guint32 credentials_are_established : 1;
- guint32 is_running : 1;
-};
-
-struct _GdmSessionWorkerMessage
-{
- GdmSessionWorkerMessageType type;
- gsize size;
-};
-
-struct _GdmSessionWorkerInfoRequestMessage
-{
- GdmSessionWorkerMessage header;
-
- gssize question_size;
- char question[0];
-};
-
-struct _GdmSessionWorkerUsernameChangedMessage
-{
- GdmSessionWorkerMessage header;
-
- gssize username_size;
- char username[0];
-};
-
-struct _GdmSessionWorkerSecretInfoRequestMessage
-{
- GdmSessionWorkerMessage header;
+ GdmSessionWorkerJob *job;
+ GPid session_pid;
- gssize question_size;
- char question[0];
-};
-
-struct _GdmSessionWorkerInfoMessage
-{
- GdmSessionWorkerMessage header;
-
- gssize info_size;
- char info[0];
-};
-
-struct _GdmSessionWorkerProblemMessage
-{
- GdmSessionWorkerMessage header;
-
- gssize problem_size;
- char problem[0];
-};
-
-struct _GdmSessionWorkerSessionStartedMessage
-{
- GdmSessionWorkerMessage header;
- GPid pid;
-};
-
-struct _GdmSessionWorkerSessionStartupFailedMessage
-{
- GdmSessionWorkerMessage header;
- GQuark error_domain;
- int error_code;
-
- gssize error_message_size;
- char error_message[0];
-};
-
-struct _GdmSessionWorkerSessionExitedMessage
-{
- GdmSessionWorkerMessage header;
-
- int exit_code;
-};
+ char *service_name;
+ char *username;
+ char *hostname;
+ char *console_name;
-struct _GdmSessionWorkerSessionDiedMessage
-{
- GdmSessionWorkerMessage header;
+ DBusMessage *message_pending_reply;
- int signal_number;
-};
-
-struct _GdmSessionWorkerVerifiedMessage
-{
- GdmSessionWorkerMessage header;
-};
+ GHashTable *environment;
-struct _GdmSessionWorkerVerificationFailedMessage
-{
- GdmSessionWorkerMessage header;
- GQuark error_domain;
- int error_code;
+ DBusServer *server;
+ char *server_address;
+ DBusConnection *worker_connection;
- gssize error_message_size;
- char error_message[0];
+ guint32 is_verified : 1;
+ guint32 is_running : 1;
};
-static GdmSessionWorkerMessage *gdm_session_worker_verified_message_new (void);
-static GdmSessionWorkerMessage *gdm_session_worker_verification_failed_message_new (GError *error);
-static GdmSessionWorkerMessage *gdm_session_worker_info_request_message_new (const char *question);
-static GdmSessionWorkerMessage *gdm_session_worker_username_changed_message_new (const char *new_username);
-static GdmSessionWorkerMessage *gdm_session_worker_secret_info_request_message_new (const char *question);
-static GdmSessionWorkerMessage *gdm_session_worker_info_message_new (const char *info);
-static GdmSessionWorkerMessage *gdm_session_worker_problem_message_new (const char *problem);
-static GdmSessionWorkerMessage *gdm_session_worker_session_started_message_new (GPid pid);
-static GdmSessionWorkerMessage *gdm_session_worker_session_exited_message_new (int exit_code);
-static GdmSessionWorkerMessage *gdm_session_worker_session_died_message_new (int signal_number);
-static void gdm_session_worker_message_free (GdmSessionWorkerMessage *message);
-
-static gboolean gdm_session_worker_process_asynchronous_message (GdmSessionWorker *worker,
- GdmSessionMessage *message);
-
enum {
USER_VERIFIED = 0,
USER_VERIFICATION_ERROR,
@@ -395,6 +135,8 @@ enum {
SESSION_STARTUP_ERROR,
SESSION_EXITED,
SESSION_DIED,
+ OPENED,
+ CLOSED,
LAST_SIGNAL
};
@@ -413,6 +155,74 @@ gdm_session_error_quark (void)
return error_quark;
}
+static gboolean
+send_dbus_message (DBusConnection *connection,
+ DBusMessage *message)
+{
+ gboolean is_connected;
+ gboolean sent;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+
+ if (connection == NULL) {
+ g_warning ("There is no valid connection");
+ return FALSE;
+ }
+
+ is_connected = dbus_connection_get_is_connected (connection);
+ if (! is_connected) {
+ g_warning ("Not connected!");
+ return FALSE;
+ }
+
+ sent = dbus_connection_send (connection, message, NULL);
+
+ return sent;
+}
+
+static void
+send_dbus_string_signal (GdmSession *session,
+ const char *name,
+ const char *text)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+
+ g_return_if_fail (session != NULL);
+
+ message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
+ GDM_SESSION_DBUS_INTERFACE,
+ name);
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &text);
+
+ if (! send_dbus_message (session->priv->worker_connection, message)) {
+ g_debug ("Could not send %s signal", name);
+ }
+
+ dbus_message_unref (message);
+}
+
+static void
+send_dbus_void_signal (GdmSession *session,
+ const char *name)
+{
+ DBusMessage *message;
+
+ g_return_if_fail (session != NULL);
+
+ message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
+ GDM_SESSION_DBUS_INTERFACE,
+ name);
+
+ if (! send_dbus_message (session->priv->worker_connection, message)) {
+ g_debug ("Could not send %s signal", name);
+ }
+
+ dbus_message_unref (message);
+}
+
static void
gdm_session_write_record (GdmSession *session,
GdmSessionRecordType record_type)
@@ -500,10 +310,9 @@ gdm_session_write_record (GdmSession *session,
session_record.ut_type = USER_PROCESS;
g_debug ("using type USER_PROCESS");
- if (session->priv->pid != 0)
- session_record.ut_pid = session->priv->pid;
- else
- session_record.ut_pid = session->priv->worker_pid;
+ if (session->priv->session_pid != 0) {
+ session_record.ut_pid = session->priv->session_pid;
+ }
g_debug ("using pid %d", (int) session_record.ut_pid);
@@ -565,6 +374,27 @@ gdm_session_class_install_signals (GdmSessionClass *session_class)
object_class = G_OBJECT_CLASS (session_class);
+ gdm_session_signals[OPENED] =
+ g_signal_new ("opened",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmSessionClass, opened),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ gdm_session_signals[CLOSED] =
+ g_signal_new ("closed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmSessionClass, closed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ session_class->user_verified = NULL;
gdm_session_signals[USER_VERIFIED] =
g_signal_new ("user-verified",
G_OBJECT_CLASS_TYPE (object_class),
@@ -685,7 +515,7 @@ gdm_session_class_install_signals (GdmSessionClass *session_class)
g_signal_new ("session-died",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (GdmSessionClass, session_exited),
+ G_STRUCT_OFFSET (GdmSessionClass, session_died),
NULL,
NULL,
g_cclosure_marshal_VOID__INT,
@@ -703,11 +533,15 @@ gdm_session_finalize (GObject *object)
session = GDM_SESSION (object);
- g_strfreev (session->priv->arguments);
g_free (session->priv->username);
parent_class = G_OBJECT_CLASS (gdm_session_parent_class);
+ if (session->priv->environment != NULL) {
+ g_hash_table_destroy (session->priv->environment);
+ session->priv->environment = NULL;
+ }
+
if (parent_class->finalize != NULL)
parent_class->finalize (object);
}
@@ -727,3292 +561,937 @@ gdm_session_class_init (GdmSessionClass *session_class)
sizeof (GdmSessionPrivate));
}
-static void
-gdm_session_init (GdmSession *session)
-{
- session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session,
- GDM_TYPE_SESSION,
- GdmSessionPrivate);
-}
-
-static gboolean
-gdm_read_message (int socket_fd,
- gpointer *message_data,
- gsize *message_data_size,
- char **error_message)
-{
- struct msghdr message = { 0 };
- struct iovec data_block = { 0 };
- const int flags = MSG_NOSIGNAL;
-
- gssize num_bytes_received;
-
- message.msg_iov = &data_block;
- message.msg_iovlen = 1;
-
- data_block.iov_base = g_malloc0 (GDM_MAX_MESSAGE_SIZE);
- data_block.iov_len = (size_t) GDM_MAX_MESSAGE_SIZE;
-
- num_bytes_received = 0;
- do {
- num_bytes_received = recvmsg (socket_fd, &message, flags);
-
- if (num_bytes_received > 0) {
- g_debug ("read '%lu' bytes over socket '%d'",
- (gulong) num_bytes_received,
- socket_fd);
- }
- }
- while (!(num_bytes_received > 0) && (errno == EINTR));
-
- if ((num_bytes_received < 0) && (error_message != NULL)) {
- const char *error;
- error = g_strerror (errno);
- g_debug ("message was not received: %s", error);
- *error_message = g_strdup (error);
- return FALSE;
- } else if (num_bytes_received == 0) {
- g_debug ("message was not received: premature eof");
- *error_message = g_strdup ("premature eof");
- return FALSE;
- }
-
- g_debug ("message was received!");
- if (message_data != NULL) {
- *message_data = g_realloc (data_block.iov_base, num_bytes_received);
- *message_data_size = (gsize) num_bytes_received;
- } else {
- g_free (message.msg_iov);
- }
-
- return TRUE;
-}
-
-static GdmSessionMessage *
-gdm_session_verification_message_new (const char *service_name,
- const char *username,
- const char *hostname,
- const char *console_name,
- int standard_output_fd,
- int standard_error_fd)
+static DBusHandlerResult
+gdm_session_handle_verified (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GdmSessionVerificationMessage *message;
- gsize size, username_size;
-
- username_size = (username != NULL? strlen (username) + 1 : 0);
- size = sizeof (GdmSessionVerificationMessage) +
- username_size +
- sizeof (GDM_SESSION_MESSAGE_SENTINAL);
+ DBusMessage *reply;
+ DBusError error;
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_MESSAGE_TYPE_VERIFICATION;
- message->header.size = size;
-
- g_strlcpy (message->service_name, service_name,
- sizeof (message->service_name));
-
- if (username != NULL) {
- g_strlcpy (message->username, username, username_size);
- message->username_size = username_size;
- } else {
- message->username_size = -1;
- }
-
- if (hostname != NULL) {
- g_strlcpy (message->hostname, hostname, sizeof (message->hostname));
- message->hostname_is_provided = TRUE;
- } else {
- message->hostname_is_provided = FALSE;
- }
-
- g_strlcpy (message->console_name, console_name, sizeof (message->console_name));
-
- message->standard_output_fd = standard_output_fd;
- message->standard_error_fd = standard_error_fd;
-
- g_strlcpy ((char *) (char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_MESSAGE_SENTINAL),
- GDM_SESSION_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_MESSAGE_SENTINAL));
-
- return (GdmSessionMessage *) message;
-}
-
-static char *
-gdm_session_flatten_arguments (const char * const *argv,
- gsize *arguments_size)
-{
- char *arguments;
- gsize i, total_size, argument_size;
-
- total_size = 0;
- for (i = 0; argv[i] != NULL; i++) {
- argument_size = strlen (argv[i]) + 1;
- total_size += argument_size;
- }
- total_size++;
-
- arguments = g_slice_alloc0 (total_size);
-
- total_size = 0;
- for (i = 0; argv[i] != NULL; i++) {
- argument_size = strlen (argv[i]) + 1;
- g_strlcpy (arguments + total_size,
- argv[i], argument_size);
+ g_debug ("Emitting 'user-verified' signal");
- total_size += argument_size;
- }
- total_size++;
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- if (arguments_size)
- *arguments_size = total_size;
+ session->priv->is_verified = TRUE;
+ g_signal_emit (session,
+ gdm_session_signals[USER_VERIFIED],
+ 0);
- return arguments;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static char **
-gdm_session_unflatten_arguments (const char *arguments)
+static DBusHandlerResult
+gdm_session_handle_verification_failed (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GPtrArray *array;
- char *argument;
+ DBusMessage *reply;
+ DBusError error;
+ const char *text;
- array = g_ptr_array_new ();
-
- argument = (char *) arguments;
- while (*argument != '\0') {
- g_ptr_array_add (array, g_strdup (argument));
- argument += strlen (argument) + 1;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- g_ptr_array_add (array, NULL);
-
- return (char **) g_ptr_array_free (array, FALSE);
-}
-
-static GdmSessionMessage *
-gdm_session_start_program_message_new (const char * const * args)
-{
- GdmSessionStartProgramMessage *message;
- char *arguments;
- gsize size;
- gsize arguments_size;
-
- g_assert (args != NULL);
- arguments_size = 0;
- arguments = gdm_session_flatten_arguments (args, &arguments_size);
-
- size = sizeof (GdmSessionStartProgramMessage) +
- arguments_size +
- sizeof (GDM_SESSION_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_MESSAGE_TYPE_START_PROGRAM;
- message->header.size = size;
-
- memcpy (message->arguments, arguments, arguments_size);
- g_slice_free1 (arguments_size, arguments);
- message->arguments_size = arguments_size;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_MESSAGE_SENTINAL),
- GDM_SESSION_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_MESSAGE_SENTINAL));
-
- return (GdmSessionMessage *) message;
-}
-
-static GdmSessionMessage *
-gdm_session_set_environment_variable_message_new (const char *name,
- const char *value)
-{
- GdmSessionSetEnvironmentVariableMessage *message;
- char *environment_variable;
- gsize size;
- gsize environment_variable_size;
- int length;
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- g_assert (name != NULL);
- g_assert (strchr (name, '=') == NULL);
- g_assert (value != NULL);
-
- environment_variable = g_strdup_printf ("%s=%s%n", name, value, &length);
- environment_variable_size = length + 1;
-
- size = sizeof (GdmSessionSetEnvironmentVariableMessage) +
- environment_variable_size +
- sizeof (GDM_SESSION_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_MESSAGE_TYPE_SET_ENVIRONMENT_VARIABLE;
- message->header.size = size;
-
- g_strlcpy (message->environment_variable,
- environment_variable, environment_variable_size);
- g_free (environment_variable);
- message->environment_variable_size = environment_variable_size;
+ g_debug ("Emitting 'verification-failed' signal");
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_MESSAGE_SENTINAL),
- GDM_SESSION_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_MESSAGE_SENTINAL));
+ g_signal_emit (session,
+ gdm_session_signals[USER_VERIFICATION_ERROR],
+ 0, text);
- return (GdmSessionMessage *) message;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static GdmSessionMessage *
-gdm_session_info_reply_message_new (const char *answer)
+static DBusHandlerResult
+gdm_session_handle_username_changed (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GdmSessionInfoReplyMessage *message;
- gsize size;
- gsize answer_size;
-
- answer_size = (answer != NULL? strlen (answer) + 1 : 0);
- size = sizeof (GdmSessionInfoReplyMessage) +
- answer_size +
- sizeof (GDM_SESSION_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
+ DBusMessage *reply;
+ DBusError error;
+ const char *text;
- message->header.type = GDM_SESSION_MESSAGE_TYPE_INFO_REPLY;
- message->header.size = size;
-
- if (answer != NULL) {
- g_strlcpy (message->answer, answer, answer_size);
- message->answer_size = answer_size;
- } else {
- message->answer_size = -1;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_MESSAGE_SENTINAL),
- GDM_SESSION_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_MESSAGE_SENTINAL));
-
- return (GdmSessionMessage *) message;
-}
-
-static GdmSessionMessage *
-gdm_session_secret_info_reply_message_new (const char *answer)
-{
- GdmSessionSecretInfoReplyMessage *message;
- gsize size;
- gsize answer_size;
-
- answer_size = (answer != NULL? strlen (answer) + 1 : 0);
- size = sizeof (GdmSessionSecretInfoReplyMessage) +
- answer_size +
- sizeof (GDM_SESSION_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY;
- message->header.size = size;
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- if (answer != NULL) {
- g_strlcpy (message->answer, answer, answer_size);
- message->answer_size = answer_size;
- } else {
- message->answer_size = -1;
- }
+ g_debug ("changing username from '%s' to '%s'",
+ session->priv->username != NULL ? session->priv->username : "<unset>",
+ (strlen (text)) ? text : "<unset>");
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_MESSAGE_SENTINAL),
- GDM_SESSION_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_MESSAGE_SENTINAL));
+ g_free (session->priv->username);
+ session->priv->username = (strlen (text) > 0) ? g_strdup (text) : NULL;
- return (GdmSessionMessage *) message;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
static void
-gdm_session_message_free (GdmSessionMessage *message)
-{
- g_slice_free1 (message->size, message);
-}
-
-
-GdmSession *
-gdm_session_new (void)
+answer_pending_query (GdmSession *session,
+ const char *answer)
{
- GdmSession *session;
+ DBusMessage *reply;
+ DBusMessageIter iter;
- session = g_object_new (GDM_TYPE_SESSION, NULL);
+ reply = dbus_message_new_method_return (session->priv->message_pending_reply);
+ dbus_message_iter_init_append (reply, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &answer);
- return session;
-}
-
-static void
-gdm_session_clear_child_watch_source (GdmSession *session)
-{
- session->priv->child_watch_source = NULL;
-}
+ dbus_connection_send (session->priv->worker_connection, reply, NULL);
+ dbus_message_unref (reply);
-static void
-gdm_session_clear_message_pipe_source (GdmSession *session)
-{
- session->priv->worker_message_pipe_source = NULL;
+ dbus_message_unref (session->priv->message_pending_reply);
+ session->priv->message_pending_reply = NULL;
}
static void
-gdm_session_on_child_exited (GPid pid,
- int status,
- GdmSession *session)
+set_pending_query (GdmSession *session,
+ DBusMessage *message)
{
- GError *error;
-
- if (WIFEXITED (status)) {
- if (!session->priv->is_verified) {
- error = g_error_new (GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_WORKER_DIED,
- _("worker exited with status %d"),
- WEXITSTATUS (status));
-
- g_signal_emit (session,
- gdm_session_signals[USER_VERIFICATION_ERROR],
- 0, error);
- g_error_free (error);
- } else if (session->priv->is_running) {
- g_signal_emit (session,
- gdm_session_signals[SESSION_EXITED],
- 0, WEXITSTATUS (status));
- }
- } else if (WIFSIGNALED (status)) {
- if (!session->priv->is_verified) {
- error = g_error_new (GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_WORKER_DIED,
- _("worker got signal '%s' and was subsequently killed"),
- g_strsignal (WTERMSIG (status)));
- g_signal_emit (session,
- gdm_session_signals[USER_VERIFICATION_ERROR],
- 0, error);
- g_error_free (error);
- } else if (session->priv->is_running) {
- g_signal_emit (session,
- gdm_session_signals[SESSION_EXITED],
- 0, WTERMSIG (status));
- }
- } else {
- /* WIFSTOPPED (status) || WIFCONTINUED (status) */
- }
+ g_assert (session->priv->message_pending_reply == NULL);
- g_spawn_close_pid (pid);
- session->priv->worker_pid = 0;
+ session->priv->message_pending_reply = dbus_message_ref (message);
}
-static gboolean
-gdm_session_validate_message_size (GdmSession *session,
- GdmSessionWorkerMessage *message,
- GError **error)
+static DBusHandlerResult
+gdm_session_handle_info_query (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- gsize expected_size;
-
- return TRUE;
-
- switch (message->type) {
- case GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFIED:
- expected_size = (gsize) sizeof (GdmSessionWorkerVerifiedMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFICATION_FAILED:
- expected_size = (gsize) sizeof (GdmSessionWorkerVerificationFailedMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_USERNAME_CHANGED:
- expected_size = (gsize) sizeof (GdmSessionWorkerUsernameChangedMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_INFO_REQUEST:
- expected_size = (gsize) sizeof (GdmSessionWorkerInfoRequestMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SECRET_INFO_REQUEST:
- expected_size = (gsize) sizeof (GdmSessionWorkerSecretInfoRequestMessage);
- break;
+ DBusError error;
+ const char *text;
- case GDM_SESSION_WORKER_MESSAGE_TYPE_INFO:
- expected_size = (gsize) sizeof (GdmSessionWorkerInfoMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_PROBLEM:
- expected_size = (gsize) sizeof (GdmSessionWorkerProblemMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTED:
- expected_size = (gsize) sizeof (GdmSessionWorkerSessionStartedMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTUP_FAILED:
- expected_size = (gsize) sizeof (GdmSessionWorkerSessionStartupFailedMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_EXITED:
- expected_size = (gsize) sizeof (GdmSessionWorkerSessionExitedMessage);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_DIED:
- expected_size = (gsize) sizeof (GdmSessionWorkerSessionDiedMessage);
- break;
-
- default:
- g_debug ("do not know about message type '0x%x'", (guint) message->type);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- _("do not know about message type '0x%x'"),
- (guint) message->type);
- return FALSE;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- if (message->size != expected_size) {
- g_debug ("message size was '%lu', but message was supposed "
- "to be '%lu'", (gulong) message->size, (gulong) expected_size);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- "message size was '%lu', but message was supposed "
- "to be '%lu'", (gulong) message->size,
- (gulong) expected_size);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_get_incoming_message (GdmSession *session,
- GError **error)
-{
- GError *size_error;
- char *error_message;
- GdmSessionWorkerMessage *message;
- gsize message_size;
- gboolean res;
-
- g_debug ("attemping to read message from worker...");
- error_message = NULL;
- res = gdm_read_message (session->priv->worker_message_pipe_fd,
- (gpointer) &message,
- &message_size,
- &error_message);
- if (! res) {
- g_debug ("could not read message from worker: %s", error_message);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- "%s", error_message);
- g_free (error_message);
- return NULL;
- }
- g_debug ("message type is '0x%x'", message->type);
-
- if (message_size != message->size) {
- g_debug ("message reports to be '%ld' bytes but is actually '%ld'",
- (glong) message->size, (glong) message_size);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- _("specified message size does not match size of message "
- "received"));
- return NULL;
- }
- g_debug ("message size is '%lu'", (gulong) message_size);
-
- g_debug ("validating that message size is right for message "
- "type...");
- size_error = NULL;
- res = gdm_session_validate_message_size (session, message, &size_error);
- if (! res) {
- g_propagate_error (error, size_error);
- return NULL;
- }
- g_debug ("message size is valid");
-
- switch (message->type) {
- case GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFIED:
- g_debug ("received valid 'verified' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFICATION_FAILED:
- g_debug ("received valid 'verification failed' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_USERNAME_CHANGED:
- g_debug ("received valid 'username changed' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_INFO_REQUEST:
- g_debug ("received valid 'info request' message from worker");
- g_debug ("***********MESSAGE QUESTION IS '%s'********",
- ((GdmSessionWorkerInfoRequestMessage *) message)->question);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SECRET_INFO_REQUEST:
- g_debug ("received valid 'secret info request' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_INFO:
- g_debug ("received valid 'info' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_PROBLEM:
- g_debug ("received valid 'problem' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTED:
- g_debug ("received valid 'session started' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTUP_FAILED:
- g_debug ("received valid 'session startup failed' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_EXITED:
- g_debug ("received valid 'session exited' message from worker");
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_DIED:
- g_debug ("received valid 'session died' message from worker");
- break;
-
- default:
- g_debug ("received unknown message of type '0x%x'",
- (guint) message->type);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- _("received unknown message"));
- gdm_session_worker_message_free (message);
- message = NULL;
- break;
- }
-
- return message;
-}
-
-static void
-gdm_session_handle_verified_message (GdmSession *session,
- GdmSessionWorkerVerifiedMessage *message)
-{
- g_debug ("Emitting 'user-verified' signal");
-
- session->priv->is_verified = TRUE;
- g_signal_emit (session,
- gdm_session_signals[USER_VERIFIED],
- 0);
-}
-
-static void
-gdm_session_handle_verification_failed_message (GdmSession *session,
- GdmSessionWorkerVerificationFailedMessage *message)
-{
- GError *error;
-
- g_debug ("Emitting 'verification-failed' signal");
-
- error = g_error_new (message->error_domain,
- message->error_code,
- "%s", message->error_message);
-
- g_signal_emit (session,
- gdm_session_signals[USER_VERIFICATION_ERROR],
- 0, error);
- g_error_free (error);
-}
-
-static void
-gdm_session_handle_username_changed_message (GdmSession *session,
- GdmSessionWorkerUsernameChangedMessage *message)
-{
- g_debug ("changing username from '%s' to '%s'",
- session->priv->username != NULL? session->priv->username : "<unset>",
- (message->username_size >= 0)? message->username : "<unset>");
- g_free (session->priv->username);
- session->priv->username = (message->username_size >= 0)? g_strdup (message->username) : NULL;
-}
-
-static void
-gdm_session_handle_info_request_message (GdmSession*session,
- GdmSessionWorkerInfoRequestMessage *message)
-{
- g_assert (session->priv->query_answer == NULL);
-
- session->priv->next_expected_message = GDM_SESSION_MESSAGE_TYPE_INFO_REPLY;
+ set_pending_query (session, message);
g_debug ("Emitting 'info-query' signal");
g_signal_emit (session,
gdm_session_signals[INFO_QUERY],
- 0, message->question);
-}
-
-static void
-gdm_session_handle_secret_info_request_message (GdmSession *session,
- GdmSessionWorkerSecretInfoRequestMessage *message)
-{
- g_assert (session->priv->query_answer == NULL);
+ 0, text);
- session->priv->next_expected_message = GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY;
-
- g_debug ("Emitting 'secret-info-query' signal");
-
- g_signal_emit (session,
- gdm_session_signals[SECRET_INFO_QUERY],
- 0, message->question);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static void
-gdm_session_handle_info_message (GdmSession *session,
- GdmSessionWorkerInfoMessage *message)
+static DBusHandlerResult
+gdm_session_handle_secret_info_query (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- g_debug ("Emitting 'info' signal");
- g_signal_emit (session,
- gdm_session_signals[INFO],
- 0, message->info);
+ DBusMessage *reply;
+ const char *text;
-}
-
-static void
-gdm_session_handle_problem_message (GdmSession *session,
- GdmSessionWorkerProblemMessage *message)
-{
- g_debug ("Emitting 'problem' signal");
- g_signal_emit (session,
- gdm_session_signals[PROBLEM],
- 0, message->problem);
-}
-
-static void
-gdm_session_handle_session_started_message (GdmSession *session,
- GdmSessionWorkerSessionStartedMessage *message)
-{
- g_debug ("Emitting 'session-started' signal with pid '%d'",
- (int) message->pid);
-
- session->priv->pid = message->pid;
- session->priv->is_running = TRUE;
-
- g_signal_emit (session,
- gdm_session_signals[SESSION_STARTED],
- 0, (int) message->pid);
-}
-
-static void
-gdm_session_handle_session_startup_failed_message (GdmSession *session,
- GdmSessionWorkerSessionStartupFailedMessage *message)
-{
- GError *error;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
+ }
- g_debug ("Emitting 'session-startup-error' signal");
+ set_pending_query (session, message);
- error = g_error_new (message->error_domain,
- message->error_code,
- "%s", message->error_message);
- g_signal_emit (session,
- gdm_session_signals[SESSION_STARTUP_ERROR],
- 0, error);
- g_error_free (error);
-}
-
-static void
-gdm_session_handle_session_exited_message (GdmSession *session,
- GdmSessionWorkerSessionExitedMessage *message)
-{
- g_debug ("Emitting 'session-exited' signal with exit code '%d'",
- message->exit_code);
+ g_debug ("Emitting 'secret-info-query' signal");
- session->priv->is_running = FALSE;
g_signal_emit (session,
- gdm_session_signals[SESSION_EXITED],
- 0, message->exit_code);
-}
-
-static void
-gdm_session_handle_session_died_message (GdmSession *session,
- GdmSessionWorkerSessionDiedMessage *message)
-{
- g_debug ("Emitting 'session-died' signal with signal number '%d'",
- message->signal_number);
+ gdm_session_signals[SECRET_INFO_QUERY],
+ 0, text);
- session->priv->is_running = FALSE;
- g_signal_emit (session,
- gdm_session_signals[SESSION_DIED],
- 0, message->signal_number);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static void
-gdm_session_incoming_message_handler (GdmSession *session)
+static DBusHandlerResult
+gdm_session_handle_info (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GdmSessionWorkerMessage *message;
- GError *error;
-
- error = NULL;
- message = gdm_session_get_incoming_message (session, &error);
+ DBusMessage *reply;
+ DBusError error;
+ const char *text;
- if (message == NULL) {
- g_assert (error != NULL);
- g_warning ("could not receive message from parent: %s\n", error->message);
- g_error_free (error);
-
- /* FIXME: figure out what to do here
- */
- return;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- switch (message->type) {
- case GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFIED:
- g_debug ("worker successfully verified user");
- gdm_session_handle_verified_message (session,
- (GdmSessionWorkerVerifiedMessage *)
- message);
-
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFICATION_FAILED:
- g_debug ("worker could not verify user");
- gdm_session_handle_verification_failed_message (session,
- (GdmSessionWorkerVerificationFailedMessage *)
- message);
-
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_USERNAME_CHANGED:
- g_debug ("received changed username from worker");
- gdm_session_handle_username_changed_message (session,
- (GdmSessionWorkerUsernameChangedMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_INFO_REQUEST:
- g_debug ("received info request from worker");
- gdm_session_handle_info_request_message (session,
- (GdmSessionWorkerInfoRequestMessage *) message);
- break;
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SECRET_INFO_REQUEST:
- g_debug ("received secret info request from worker");
- gdm_session_handle_secret_info_request_message (session,
- (GdmSessionWorkerSecretInfoRequestMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_INFO:
- g_debug ("received new info from worker");
- gdm_session_handle_info_message (session,
- (GdmSessionWorkerInfoMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_PROBLEM:
- g_debug ("received problem from worker");
- gdm_session_handle_problem_message (session,
- (GdmSessionWorkerProblemMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTED:
- g_debug ("received session started message from worker");
- gdm_session_handle_session_started_message (session,
- (GdmSessionWorkerSessionStartedMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTUP_FAILED:
- g_debug ("received session startup failed message from worker");
- gdm_session_handle_session_startup_failed_message (session,
- (GdmSessionWorkerSessionStartupFailedMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_EXITED:
- g_debug ("received session exited message from worker");
- gdm_session_handle_session_exited_message (session,
- (GdmSessionWorkerSessionExitedMessage *) message);
- break;
-
- case GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_DIED:
- g_debug ("received session died message from worker");
- gdm_session_handle_session_died_message (session,
- (GdmSessionWorkerSessionDiedMessage *) message);
- break;
-
- default:
- g_debug ("received unknown message of type '0x%x' from worker",
- message->type);
- break;
- }
+ g_debug ("Emitting 'info' signal");
+ g_signal_emit (session,
+ gdm_session_signals[INFO],
+ 0, text);
- gdm_session_worker_message_free (message);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static gboolean
-gdm_session_data_on_message_pipe_handler (GIOChannel *channel,
- GIOCondition condition,
- GdmSession *session)
+static DBusHandlerResult
+gdm_session_handle_problem (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- if ((condition & G_IO_IN) || (condition & G_IO_PRI)) {
- g_debug ("got message from message pipe");
- gdm_session_incoming_message_handler (session);
- }
+ DBusMessage *reply;
+ DBusError error;
+ const char *text;
- if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) {
- g_debug ("got disconnected message from message pipe");
- g_error("not implemented");
- /*gdm_session_disconnected_handler (session);*/
- return FALSE;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- return TRUE;
-}
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
-static void
-gdm_session_worker_clear_child_watch_source (GdmSessionWorker *worker)
-{
- worker->child_watch_source = NULL;
+ g_debug ("Emitting 'problem' signal");
+ g_signal_emit (session,
+ gdm_session_signals[PROBLEM],
+ 0, text);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static void
-gdm_session_watch_child (GdmSession *session)
+static DBusHandlerResult
+gdm_session_handle_session_started (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GIOChannel *io_channel;
- GIOFlags channel_flags;
-
- g_assert (session->priv->child_watch_source == NULL);
-
- session->priv->child_watch_source = g_child_watch_source_new (session->priv->worker_pid);
- g_source_set_callback (session->priv->child_watch_source,
- (GChildWatchFunc) gdm_session_on_child_exited,
- session,
- (GDestroyNotify) gdm_session_clear_child_watch_source);
- g_source_attach (session->priv->child_watch_source, session->priv->context);
- g_source_unref (session->priv->child_watch_source);
-
- io_channel = g_io_channel_unix_new (session->priv->worker_message_pipe_fd);
-
- channel_flags = g_io_channel_get_flags (io_channel);
- g_io_channel_set_flags (io_channel,
- channel_flags | G_IO_FLAG_NONBLOCK,
- NULL);
-
- g_assert (session->priv->worker_message_pipe_source == NULL);
-
- session->priv->worker_message_pipe_source = g_io_create_watch (io_channel,
- G_IO_IN | G_IO_HUP);
- g_io_channel_unref (io_channel);
-
- g_source_set_callback (session->priv->worker_message_pipe_source,
- (GIOFunc) gdm_session_data_on_message_pipe_handler,
- session,
- (GDestroyNotify) gdm_session_clear_message_pipe_source);
- g_source_attach (session->priv->worker_message_pipe_source, session->priv->context);
- g_source_unref (session->priv->worker_message_pipe_source);
-}
+ DBusMessage *reply;
+ DBusError error;
+ int pid;
-static void
-gdm_session_unwatch_child (GdmSession *session)
-{
- if (session->priv->child_watch_source == NULL) {
- return;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- g_source_destroy (session->priv->child_watch_source);
- session->priv->child_watch_source = NULL;
-
- g_assert (session->priv->worker_message_pipe_source != NULL);
-
- g_source_destroy (session->priv->worker_message_pipe_source);
- session->priv->worker_message_pipe_source = NULL;
-}
-
-static void
-gdm_session_worker_clear_message_pipe_source (GdmSessionWorker *worker)
-{
- worker->message_pipe_source = NULL;
-}
-
-static void
-gdm_session_worker_disconnected_handler (GdmSessionWorker *worker)
-{
- g_debug ("exiting...");
- worker->exit_code = 127;
- g_main_loop_quit (worker->event_loop);
-}
-
-static gboolean
-gdm_session_worker_validate_message_size (GdmSessionWorker *worker,
- GdmSessionMessage *message,
- GError **error)
-{
- gsize expected_size;
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- return TRUE;
-
- switch (message->type) {
- case GDM_SESSION_MESSAGE_TYPE_VERIFICATION:
- expected_size = (gsize) sizeof (GdmSessionVerificationMessage);
- break;
- case GDM_SESSION_MESSAGE_TYPE_START_PROGRAM:
- expected_size = (gsize) sizeof (GdmSessionStartProgramMessage);
- break;
- case GDM_SESSION_MESSAGE_TYPE_SET_ENVIRONMENT_VARIABLE:
- expected_size = (gsize) sizeof (GdmSessionSetEnvironmentVariableMessage);
- break;
- case GDM_SESSION_MESSAGE_TYPE_INFO_REPLY:
- expected_size = (gsize) sizeof (GdmSessionInfoReplyMessage);
- break;
- case GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY:
- expected_size = (gsize) sizeof (GdmSessionSecretInfoReplyMessage);
- break;
-
- default:
- g_debug ("do not know about message type '0x%x'",
- (guint) message->type);
+ g_debug ("Emitting 'session-started' signal with pid '%d'",
+ pid);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- _("do not know about message type '0x%x'"),
- (guint) message->type);
- return FALSE;
- }
+ session->priv->session_pid = pid;
+ session->priv->is_running = TRUE;
- if (message->size != expected_size) {
- g_debug ("message size was '%lu', but message was supposed "
- "to be '%lu'",
- (gulong) message->size,
- (gulong) expected_size);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- "message size was '%lu', but message was supposed "
- "to be '%lu'", (gulong) message->size,
- (gulong) expected_size);
- return FALSE;
- }
+ g_signal_emit (session,
+ gdm_session_signals[SESSION_STARTED],
+ 0, pid);
- return TRUE;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static GdmSessionMessage *
-gdm_session_worker_get_incoming_message (GdmSessionWorker *worker,
- GError **error)
+static DBusHandlerResult
+gdm_session_handle_startup_failed (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GError *size_error;
- char *error_message;
- GdmSessionMessage *message;
- gsize message_size;
- gboolean res;
-
- g_debug ("attemping to read message from parent...");
- error_message = NULL;
- res = gdm_read_message (worker->message_pipe_fd,
- (gpointer) &message,
- &message_size,
- &error_message);
- if (! res) {
- g_debug ("could not read message from parent: %s", error_message);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- "%s", error_message);
- g_free (error_message);
- return NULL;
- }
- g_debug ("message type is '0x%x'", message->type);
-
- if (message_size != message->size) {
- g_debug ("message reports to be '%ld' bytes but is actually '%ld'",
- (glong) message->size,
- (glong) message_size);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- _("specified message size does not match size of message "
- "received"));
- return NULL;
- }
- g_debug ("message size is '%lu'", (gulong) message_size);
+ DBusMessage *reply;
+ DBusError error;
+ const char *text;
- g_debug ("validating that message size is right for message "
- "type...");
-
- size_error = NULL;
- res = gdm_session_worker_validate_message_size (worker, message, &size_error);
- if (! res) {
- g_propagate_error (error, size_error);
- return NULL;
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- g_debug ("message size is valid");
- switch (message->type) {
- case GDM_SESSION_MESSAGE_TYPE_VERIFICATION:
- g_debug ("user session verification request received");
- break;
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- case GDM_SESSION_MESSAGE_TYPE_START_PROGRAM:
- g_debug ("session arguments received");
- break;
-
- case GDM_SESSION_MESSAGE_TYPE_SET_ENVIRONMENT_VARIABLE:
- g_debug ("environment variable received");
- break;
-
- case GDM_SESSION_MESSAGE_TYPE_INFO_REPLY:
- g_debug ("user session info reply received");
- break;
-
- case GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY:
- g_debug ("user session secret info reply received");
- break;
+ g_debug ("Emitting 'session-startup-error' signal");
- default:
- g_debug ("do not know about message type '0x%x'",
- (guint) message->type);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_COMMUNICATING,
- _("received unknown message type"));
- gdm_session_message_free (message);
- message = NULL;
- break;
- }
+ g_signal_emit (session,
+ gdm_session_signals[SESSION_STARTUP_ERROR],
+ 0, text);
- return message;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static gboolean
-gdm_write_message (int socket_fd,
- gpointer message_data,
- gsize message_data_size,
- char **error_message)
+static DBusHandlerResult
+gdm_session_handle_session_exited (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- struct msghdr message = { 0 };
- struct iovec data_block = { 0 };
- const int flags = MSG_NOSIGNAL;
- gboolean message_was_sent;
-
- message.msg_iov = &data_block;
- message.msg_iovlen = 1;
-
- g_assert (message_data_size <= GDM_MAX_MESSAGE_SIZE);
-
- data_block.iov_base = message_data;
- data_block.iov_len = (size_t) message_data_size;
+ DBusMessage *reply;
+ DBusError error;
+ int code;
- message_was_sent = FALSE;
- do {
- gssize num_bytes_sent;
- num_bytes_sent = sendmsg (socket_fd, &message, flags);
-
- if (num_bytes_sent > 0) {
- g_debug ("sent '%lu' bytes over socket '%d'",
- (gulong) num_bytes_sent,
- socket_fd);
- message_was_sent = TRUE;
- }
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_INT32, &code,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- while (! message_was_sent && (errno == EINTR));
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- if (! message_was_sent && (error_message != NULL)) {
- const char *error;
- error = g_strerror (errno);
- g_debug ("message was not sent: %s", error);
- *error_message = g_strdup (error);
- } else if (message_was_sent) {
- g_debug ("message was sent!");
- }
+ g_debug ("Emitting 'session-exited' signal with exit code '%d'",
+ code);
- g_free (message.msg_control);
+ session->priv->is_running = FALSE;
+ g_signal_emit (session,
+ gdm_session_signals[SESSION_EXITED],
+ 0, code);
- return message_was_sent;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static char *
-gdm_session_worker_ask_question (GdmSessionWorker *worker,
- const char *question)
+static DBusHandlerResult
+gdm_session_handle_session_died (GdmSession *session,
+ DBusConnection *connection,
+ DBusMessage *message)
{
- GdmSessionWorkerMessage *message;
- GdmSessionMessage *reply;
- GdmSessionInfoReplyMessage *info_reply;
- char *answer;
- GError *error;
-
- message = gdm_session_worker_info_request_message_new (question);
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
+ DBusMessage *reply;
+ DBusError error;
+ int code;
- error = NULL;
- do {
- gboolean res;
-
- reply = gdm_session_worker_get_incoming_message (worker, &error);
-
- if (reply == NULL) {
- g_warning ("could not receive message from parent: %s", error->message);
- g_error_free (error);
-
- /* FIXME: figure out what to do here
- */
- return NULL;
- }
-
- res = gdm_session_worker_process_asynchronous_message (worker, reply);
- if (res) {
- gdm_session_message_free (reply);
- reply = NULL;
- }
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_INT32, &code,
+ DBUS_TYPE_INVALID)) {
+ g_warning ("ERROR: %s", error.message);
}
- while (reply == NULL);
- /* FIXME: we have to do something better here. Messages aren't gauranteed to
- * be delivered synchronously. We need to either fix that, or fix this to not
- * expect them to be.
- */
- if (reply->type != GDM_SESSION_MESSAGE_TYPE_INFO_REPLY) {
- g_debug ("discarding unexpected message of type 0x%x", reply->type);
- gdm_session_message_free (reply);
- reply = NULL;
- return NULL;
- }
+ reply = dbus_message_new_method_return (message);
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
- info_reply = (GdmSessionInfoReplyMessage *) reply;
- if (info_reply->answer_size >= 0)
- answer = g_strdup (info_reply->answer);
- else
- answer = NULL;
- gdm_session_message_free (reply);
- reply = NULL;
+ g_debug ("Emitting 'session-died' signal with signal number '%d'",
+ code);
- return answer;
-}
+ session->priv->is_running = FALSE;
+ g_signal_emit (session,
+ gdm_session_signals[SESSION_DIED],
+ 0, code);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+session_worker_message (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GdmSession *session = GDM_SESSION (user_data);
+
+ if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Verified")) {
+ return gdm_session_handle_verified (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "VerificationFailed")) {
+ return gdm_session_handle_verification_failed (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "UsernameChanged")) {
+ return gdm_session_handle_username_changed (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "InfoQuery")) {
+ return gdm_session_handle_info_query (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SecretInfoQuery")) {
+ return gdm_session_handle_secret_info_query (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Info")) {
+ return gdm_session_handle_info (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Problem")) {
+ return gdm_session_handle_problem (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionStarted")) {
+ return gdm_session_handle_session_started (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "StartupFailed")) {
+ return gdm_session_handle_startup_failed (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionExited")) {
+ return gdm_session_handle_session_exited (session, connection, message);
+ } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionDied")) {
+ return gdm_session_handle_session_died (session, connection, message);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+do_introspect (DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ GString *xml;
+ char *xml_string;
+
+ g_debug ("Do introspect");
+
+ /* standard header */
+ xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+ "<node>\n"
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " </interface>\n");
+
+ /* interface */
+ xml = g_string_append (xml,
+ " <interface name=\"org.gnome.DisplayManager.Session\">\n"
+ " <method name=\"Verified\">\n"
+ " </method>\n"
+ " <method name=\"VerificationFailed\">\n"
+ " <arg name=\"message\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"InfoQuery\">\n"
+ " <arg name=\"query\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"answer\" direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"SecretInfoQuery\">\n"
+ " <arg name=\"query\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"answer\" direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"Info\">\n"
+ " <arg name=\"text\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"Problem\">\n"
+ " <arg name=\"text\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"UsernameChanged\">\n"
+ " <arg name=\"text\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"StartupFailed\">\n"
+ " <arg name=\"message\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <method name=\"SessionStarted\">\n"
+ " <arg name=\"pid\" direction=\"in\" type=\"i\"/>\n"
+ " </method>\n"
+ " <method name=\"SessionExited\">\n"
+ " <arg name=\"code\" direction=\"in\" type=\"i\"/>\n"
+ " </method>\n"
+ " <method name=\"SessionDied\">\n"
+ " <arg name=\"signal\" direction=\"in\" type=\"i\"/>\n"
+ " </method>\n"
+ " <signal name=\"BeginVerification\">\n"
+ " <arg name=\"service_name\" type=\"s\"/>\n"
+ " <arg name=\"hostname\" type=\"s\"/>\n"
+ " <arg name=\"console\" type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"BeginVerificationForUser\">\n"
+ " <arg name=\"service_name\" type=\"s\"/>\n"
+ " <arg name=\"hostname\" type=\"s\"/>\n"
+ " <arg name=\"console\" type=\"s\"/>\n"
+ " <arg name=\"username\" type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"StartProgram\">\n"
+ " <arg name=\"command\" type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"SetEnvironmentVariable\">\n"
+ " <arg name=\"name\" type=\"s\"/>\n"
+ " <arg name=\"value\" type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"Reset\">\n"
+ " </signal>\n"
+ " </interface>\n");
+
+ reply = dbus_message_new_method_return (message);
+
+ xml = g_string_append (xml, "</node>\n");
+ xml_string = g_string_free (xml, FALSE);
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &xml_string,
+ DBUS_TYPE_INVALID);
+
+ g_free (xml_string);
+
+ if (reply == NULL) {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL)) {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+session_message_handler (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ g_debug ("session_message_handler: destination=%s obj_path=%s interface=%s method=%s",
+ dbus_message_get_destination (message),
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message));
+
+
+ if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ if (reply == NULL) {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL)) {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ } else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
+ strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
+
+ /*dbus_connection_unref (connection);*/
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ } else if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+ return do_introspect (connection, message);
+ } else {
+ return session_worker_message (connection, message, user_data);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+/* Note: Use abstract sockets like dbus does by default on Linux. Abstract
+ * sockets are only available on Linux.
+ */
static char *
-gdm_session_worker_ask_for_secret (GdmSessionWorker *worker,
- const char *secret)
+generate_address (void)
{
- GdmSessionWorkerMessage *message;
- GdmSessionMessage *reply;
- GdmSessionSecretInfoReplyMessage *secret_info_reply;
- char *answer;
- GError *error;
-
- message = gdm_session_worker_secret_info_request_message_new (secret);
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
+ char *path;
+#if defined (__linux__)
+ int i;
+ char tmp[9];
- error = NULL;
- do {
- gboolean res;
-
- reply = gdm_session_worker_get_incoming_message (worker, &error);
-
- if (reply == NULL) {
- g_warning ("could not receive message from parent: %s", error->message);
- g_error_free (error);
-
- /* FIXME: figure out what to do here
- */
- return NULL;
- }
-
- res = gdm_session_worker_process_asynchronous_message (worker, reply);
- if (res) {
- gdm_session_message_free (reply);
- reply = NULL;
+ for (i = 0; i < 8; i++) {
+ if (g_random_int_range (0, 2) == 0) {
+ tmp[i] = g_random_int_range ('a', 'z' + 1);
+ } else {
+ tmp[i] = g_random_int_range ('A', 'Z' + 1);
}
}
- while (reply == NULL);
-
- /* FIXME: we have to do something better here. Messages
- * aren't gauranteed to be delivered synchronously. We need
- * to either fix that, or fix this to not expect them to be.
- */
- if (reply->type != GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY) {
- g_debug ("discarding unexpected message of type 0x%x", reply->type);
- gdm_session_message_free (reply);
- return NULL;
- }
- secret_info_reply = (GdmSessionSecretInfoReplyMessage *) reply;
+ tmp[8] = '\0';
- answer = g_strdup (secret_info_reply->answer);
- gdm_session_message_free (reply);
-
- g_debug ("answer to secret question '%s' is '%s'", secret, answer);
- return answer;
-}
+ path = g_strdup_printf ("unix:abstract=/tmp/gdm-session-%s", tmp);
+#else
+ path = g_strdup ("unix:tmpdir=/tmp/gdm-session");
+#endif
-static void
-gdm_session_worker_report_info (GdmSessionWorker *worker,
- const char *info)
-{
- GdmSessionWorkerMessage *message;
- message = gdm_session_worker_info_message_new (info);
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
+ return path;
}
static void
-gdm_session_worker_report_problem (GdmSessionWorker *worker,
- const char *problem)
-{
- GdmSessionWorkerMessage *message;
- message = gdm_session_worker_problem_message_new (problem);
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
-}
-
-static gboolean
-gdm_session_worker_get_username (GdmSessionWorker *worker,
- char **username)
+session_unregister_handler (DBusConnection *connection,
+ void *user_data)
{
- gconstpointer item;
-
- g_assert (worker->pam_handle != NULL);
-
- if (pam_get_item (worker->pam_handle, PAM_USER, &item) == PAM_SUCCESS) {
- if (username) {
- *username = g_strdup ((char *) item);
- g_debug ("username is '%s'",
- *username != NULL ? *username :
- "<unset>");
- }
- return TRUE;
- }
-
- return FALSE;
+ g_debug ("session_unregister_handler");
}
-static void
-gdm_session_worker_update_username (GdmSessionWorker *worker)
+static DBusHandlerResult
+connection_filter_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
{
- char *username;
- gboolean res;
+ GdmSession *session = GDM_SESSION (user_data);
+ const char *path;
- username = NULL;
- res = gdm_session_worker_get_username (worker, &username);
- if (res) {
- GdmSessionWorkerMessage *message;
+ path = dbus_message_get_path (message);
- if ((worker->username == username) ||
- ((worker->username != NULL) && (username != NULL) &&
- (strcmp (worker->username, username) == 0)))
- goto out;
+ g_debug ("obj_path=%s interface=%s method=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message));
- g_debug ("setting username to '%s'", username);
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
+ && strcmp (path, DBUS_PATH_LOCAL) == 0) {
- g_free (worker->username);
- worker->username = username;
- username = NULL;
+ g_debug ("Disconnected");
- message = gdm_session_worker_username_changed_message_new (worker->username);
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
- }
+ dbus_connection_unref (connection);
+ session->priv->worker_connection = NULL;
- out:
- g_free (username);
-}
+ g_signal_emit (session, gdm_session_signals [CLOSED], 0);
+ } else if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
-static gboolean
-gdm_session_worker_process_pam_message (GdmSessionWorker *worker,
- const struct pam_message *query,
- char **response_text)
-{
- char *user_answer;
- gboolean was_processed;
- g_debug ("received pam message of type %u with payload '%s'",
- query->msg_style, query->msg);
-
- user_answer = NULL;
- was_processed = FALSE;
- switch (query->msg_style) {
- case PAM_PROMPT_ECHO_ON:
- user_answer = gdm_session_worker_ask_question (worker, query->msg);
- break;
- case PAM_PROMPT_ECHO_OFF:
- user_answer = gdm_session_worker_ask_for_secret (worker, query->msg);
- break;
- case PAM_TEXT_INFO:
- gdm_session_worker_report_info (worker, query->msg);
- was_processed = TRUE;
- break;
- case PAM_ERROR_MSG:
- gdm_session_worker_report_problem (worker, query->msg);
- was_processed = TRUE;
- break;
- default:
- g_debug ("unknown query of type %u\n", query->msg_style);
- break;
- }
-
- if (user_answer != NULL) {
- /* we strdup and g_free to make sure we return malloc'd
- * instead of g_malloc'd memory
- */
- if (response_text != NULL) {
- *response_text = strdup (user_answer);
- }
-
- g_free (user_answer);
-
- g_debug ("trying to get updated username");
- gdm_session_worker_update_username (worker);
- was_processed = TRUE;
+ } else {
+ return session_message_handler (connection, message, user_data);
}
- return was_processed;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
-static int
-gdm_session_worker_pam_new_messages_handler (int number_of_messages,
- const struct pam_message **messages,
- struct pam_response **responses,
- GdmSessionWorker *worker)
+static dbus_bool_t
+allow_user_function (DBusConnection *connection,
+ unsigned long uid,
+ void *data)
{
- struct pam_response *replies;
- int return_value;
- int i;
-
- g_debug ("%d new messages received from pam\n", number_of_messages);
-
- return_value = PAM_CONV_ERR;
-
- if (number_of_messages < 0) {
- return PAM_CONV_ERR;
- }
-
- if (number_of_messages == 0) {
- if (responses) {
- *responses = NULL;
- }
-
- return PAM_SUCCESS;
- }
-
- /* we want to generate one reply for every question
- */
- replies = (struct pam_response *) calloc (number_of_messages,
- sizeof (struct pam_response));
- for (i = 0; i < number_of_messages; i++) {
- gboolean got_response;
- char *response_text;
-
- response_text = NULL;
- got_response = gdm_session_worker_process_pam_message (worker,
- messages[i],
- &response_text);
- if (!got_response)
- goto out;
-
- g_debug ("answered pam message %d with response '%s'",
- i, response_text);
- replies[i].resp = response_text;
- replies[i].resp_retcode = PAM_SUCCESS;
- }
-
- return_value = PAM_SUCCESS;
-
- out:
- if (return_value != PAM_SUCCESS) {
- for (i = 0; i < number_of_messages; i++) {
- if (replies[i].resp != NULL) {
- memset (replies[i].resp, 0, strlen (replies[i].resp));
- free (replies[i].resp);
- }
- memset (&replies[i], 0, sizeof (replies[i]));
- }
- free (replies);
- replies = NULL;
+ if (0 == uid) {
+ return TRUE;
}
- if (responses) {
- *responses = replies;
- }
+ g_debug ("User not allowed");
- return return_value;
+ return FALSE;
}
static void
-gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
- int error_code)
+handle_connection (DBusServer *server,
+ DBusConnection *new_connection,
+ void *user_data)
{
- g_debug ("uninitializing PAM");
-
- if (worker->pam_handle == NULL)
- return;
+ GdmSession *session = GDM_SESSION (user_data);
- if (worker->credentials_are_established) {
- pam_setcred (worker->pam_handle, PAM_DELETE_CRED);
- worker->credentials_are_established = FALSE;
- }
-
- if (worker->is_running) {
- pam_close_session (worker->pam_handle, 0);
- worker->is_running = FALSE;
- }
-
- pam_end (worker->pam_handle, error_code);
- worker->pam_handle = NULL;
-}
-
-static gboolean
-gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
- const char *service,
- const char *username,
- const char *hostname,
- const char *console_name,
- GError **error)
-{
- struct pam_conv pam_conversation;
- int error_code;
-
- g_assert (worker->pam_handle == NULL);
-
- g_debug ("initializing PAM");
-
- pam_conversation.conv = (GdmSessionWorkerPamNewMessagesFunc) gdm_session_worker_pam_new_messages_handler;
- pam_conversation.appdata_ptr = worker;
-
- error_code = pam_start (service,
- username,
- &pam_conversation,
- &worker->pam_handle);
-
- if (error_code != PAM_SUCCESS) {
- g_debug ("could not initialize pam");
- /* we don't use pam_strerror here because it requires a valid
- * pam handle, and if pam_start fails pam_handle is undefined
- */
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_AUTHENTICATING,
- _("error initiating conversation with authentication system - %s"),
- error_code == PAM_ABORT? _("general failure") :
- error_code == PAM_BUF_ERR? _("out of memory") :
- error_code == PAM_SYSTEM_ERR? _("application programmer error") :
- _("unscoped error"));
+ g_debug ("Handing new connection");
- goto out;
- }
+ if (session->priv->worker_connection == NULL) {
+ DBusObjectPathVTable vtable = { &session_unregister_handler,
+ &session_message_handler,
+ NULL, NULL, NULL, NULL
+ };
- if (username == NULL) {
- error_code = pam_set_item (worker->pam_handle, PAM_USER_PROMPT, _("Username:"));
-
- if (error_code != PAM_SUCCESS) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_AUTHENTICATING,
- _("error informing authentication system of preferred username prompt - %s"),
- pam_strerror (worker->pam_handle, error_code));
- goto out;
- }
- }
+ session->priv->worker_connection = new_connection;
+ dbus_connection_ref (new_connection);
+ dbus_connection_setup_with_g_main (new_connection, NULL);
- if (hostname != NULL) {
- error_code = pam_set_item (worker->pam_handle, PAM_RHOST, hostname);
+ g_debug ("worker connection is %p", new_connection);
- if (error_code != PAM_SUCCESS) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_AUTHENTICATING,
- _("error informing authentication system of user's hostname - %s"),
- pam_strerror (worker->pam_handle, error_code));
- goto out;
- }
- }
+ dbus_connection_add_filter (new_connection,
+ connection_filter_function,
+ session,
+ NULL);
- error_code = pam_set_item (worker->pam_handle, PAM_TTY, console_name);
+ dbus_connection_set_unix_user_function (new_connection,
+ allow_user_function,
+ session,
+ NULL);
- if (error_code != PAM_SUCCESS) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_AUTHENTICATING,
- _("error informing authentication system of user's console - %s"),
- pam_strerror (worker->pam_handle, error_code));
- goto out;
- }
+ dbus_connection_register_object_path (new_connection,
+ "/",
+ &vtable,
+ session);
- out:
- if (error_code != PAM_SUCCESS) {
- gdm_session_worker_uninitialize_pam (worker, error_code);
- return FALSE;
+ g_debug ("Emitting opened signal");
+ g_signal_emit (session, gdm_session_signals [OPENED], 0);
}
-
- return TRUE;
}
static gboolean
-gdm_session_worker_authenticate_user (GdmSessionWorker *worker,
- gboolean password_is_required,
- GError **error)
+setup_server (GdmSession *session)
{
- int error_code;
- int authentication_flags;
+ DBusError error;
+ gboolean ret;
+ char *address;
+ const char *auth_mechanisms[] = {"EXTERNAL", NULL};
- g_debug ("authenticating user");
+ ret = FALSE;
- authentication_flags = 0;
+ g_debug ("Creating D-Bus server for session");
- if (password_is_required) {
- authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
- }
+ address = generate_address ();
- /* blocking call, does the actual conversation
- */
- error_code = pam_authenticate (worker->pam_handle, authentication_flags);
+ dbus_error_init (&error);
+ session->priv->server = dbus_server_listen (address, &error);
+ g_free (address);
- if (error_code != PAM_SUCCESS) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_AUTHENTICATING,
- "%s", pam_strerror (worker->pam_handle, error_code));
+ if (session->priv->server == NULL) {
+ g_warning ("Cannot create D-BUS server for the session: %s", error.message);
goto out;
}
- out:
- if (error_code != PAM_SUCCESS) {
- gdm_session_worker_uninitialize_pam (worker, error_code);
- return FALSE;
- }
+ dbus_server_setup_with_g_main (session->priv->server, NULL);
+ dbus_server_set_auth_mechanisms (session->priv->server, auth_mechanisms);
+ dbus_server_set_new_connection_function (session->priv->server,
+ handle_connection,
+ session,
+ NULL);
+ ret = TRUE;
- return TRUE;
-}
-
-static gboolean
-gdm_session_worker_authorize_user (GdmSessionWorker *worker,
- gboolean password_is_required,
- GError **error)
-{
- int error_code;
- int authentication_flags;
-
- g_debug ("determining if authenticated user is authorized to session");
+ g_free (session->priv->server_address);
+ session->priv->server_address = dbus_server_get_address (session->priv->server);
- authentication_flags = 0;
-
- if (password_is_required) {
- authentication_flags |= PAM_DISALLOW_NULL_AUTHTOK;
- }
-
- /* check that the account isn't disabled or expired
- */
- error_code = pam_acct_mgmt (worker->pam_handle, authentication_flags);
-
- /* it's possible that the user needs to change their password or pin code
- */
- if (error_code == PAM_NEW_AUTHTOK_REQD)
- error_code = pam_chauthtok (worker->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
-
- if (error_code != PAM_SUCCESS) {
- g_debug ("user is not authorized to log in: %s",
- pam_strerror (worker->pam_handle, error_code));
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_AUTHORIZING,
- "%s", pam_strerror (worker->pam_handle, error_code));
- goto out;
- }
+ g_debug ("D-Bus server listening on %s", session->priv->server_address);
out:
- if (error_code != PAM_SUCCESS) {
- gdm_session_worker_uninitialize_pam (worker, error_code);
- return FALSE;
- }
- return TRUE;
+ return ret;
}
static void
-gdm_session_worker_set_environment_variable (GdmSessionWorker *worker,
- const char *environment_variable)
-{
- char **key_and_value;
-
- key_and_value = g_strsplit (environment_variable, "=", 2);
-
- /* FIXME: maybe we should use use pam_putenv instead of our
- * own hash table, so pam can override our choices if it knows
- * better?
- */
- g_hash_table_replace (worker->environment,
- key_and_value[0],
- key_and_value[1]);
-
- /* We are calling g_free instead of g_strfreev because the
- * hash table is taking over ownership of the individual
- * elements above
- */
- g_free (key_and_value);
-}
-
-static void
-gdm_session_worker_update_environment_from_passwd_entry (GdmSessionWorker *worker,
- struct passwd *passwd_entry)
-{
- char *environment_variable;
-
- environment_variable = g_strdup_printf ("LOGNAME=%s", worker->username);
- gdm_session_worker_set_environment_variable (worker, environment_variable);
- g_free (environment_variable);
-
- environment_variable = g_strdup_printf ("USER=%s", worker->username);
- gdm_session_worker_set_environment_variable (worker, environment_variable);
- g_free (environment_variable);
-
- environment_variable = g_strdup_printf ("USERNAME=%s", worker->username);
- gdm_session_worker_set_environment_variable (worker, environment_variable);
- g_free (environment_variable);
-
- environment_variable = g_strdup_printf ("HOME=%s", passwd_entry->pw_dir);
- gdm_session_worker_set_environment_variable (worker, environment_variable);
- g_free (environment_variable);
-
- environment_variable = g_strdup_printf ("SHELL=%s", passwd_entry->pw_shell);
- gdm_session_worker_set_environment_variable (worker, environment_variable);
- g_free (environment_variable);
-}
-
-static gboolean
-gdm_session_worker_environment_variable_is_set (GdmSessionWorker *worker,
- const char *name)
-{
- return g_hash_table_lookup (worker->environment, name) != NULL;
-}
-
-static gboolean
-gdm_session_worker_give_user_credentials (GdmSessionWorker *worker,
- GError **error)
+gdm_session_init (GdmSession *session)
{
- int error_code;
- struct passwd *passwd_entry, passwd_buffer;
- char *aux_buffer;
- long required_aux_buffer_size;
- gsize aux_buffer_size;
-
- aux_buffer = NULL;
- aux_buffer_size = 0;
-
- if (worker->username == NULL) {
- error_code = PAM_USER_UNKNOWN;
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- _("no user account available"));
- goto out;
- }
-
- required_aux_buffer_size = sysconf (_SC_GETPW_R_SIZE_MAX);
-
- if (required_aux_buffer_size < 0)
- aux_buffer_size = GDM_PASSWD_AUXILLARY_BUFFER_SIZE;
- else
- aux_buffer_size = (gsize) required_aux_buffer_size;
-
- aux_buffer = g_slice_alloc0 (aux_buffer_size);
-
- /* we use the _r variant of getpwnam()
- * (with its weird semantics) so that the
- * passwd_entry doesn't potentially get stomped on
- * by a PAM module
- */
- passwd_entry = NULL;
- errno = getpwnam_r (worker->username, &passwd_buffer,
- aux_buffer, (size_t) aux_buffer_size,
- &passwd_entry);
-
- if (errno != 0) {
- error_code = PAM_SYSTEM_ERR;
- g_set_error (error, GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- "%s", g_strerror (errno));
- goto out;
- }
-
- if (passwd_entry == NULL) {
- error_code = PAM_USER_UNKNOWN;
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- _("user account not available on system"));
- goto out;
- }
-
- gdm_session_worker_update_environment_from_passwd_entry (worker, passwd_entry);
-
- /* Let's give the user a default PATH if he doesn't already have one
- */
- if (!gdm_session_worker_environment_variable_is_set (worker, "PATH"))
- gdm_session_worker_set_environment_variable (worker,
- "PATH=" GDM_SESSION_DEFAULT_PATH);
-
- /* pam_setcred wants to be called as the authenticated user
- * but pam_open_session needs to be called as super-user.
- *
- * Set the real uid and gid to the user and give the user a
- * temporary super-user effective id.
- */
- if (setreuid (passwd_entry->pw_uid, GDM_SESSION_ROOT_UID) < 0) {
- error_code = PAM_SYSTEM_ERR;
- g_set_error (error, GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- "%s", g_strerror (errno));
- goto out;
- }
-
- if (setgid (passwd_entry->pw_gid) < 0) {
- error_code = PAM_SYSTEM_ERR;
- g_set_error (error, GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- "%s", g_strerror (errno));
- goto out;
- }
-
- if (initgroups (passwd_entry->pw_name, passwd_entry->pw_gid) < 0) {
- error_code = PAM_SYSTEM_ERR;
- g_set_error (error, GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- "%s", g_strerror (errno));
- goto out;
- }
-
- error_code = pam_setcred (worker->pam_handle, PAM_ESTABLISH_CRED);
-
- if (error_code != PAM_SUCCESS) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_GIVING_CREDENTIALS,
- "%s", pam_strerror (worker->pam_handle, error_code));
- goto out;
- }
+ session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session,
+ GDM_TYPE_SESSION,
+ GdmSessionPrivate);
- worker->credentials_are_established = TRUE;
+ session->priv->environment = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
- out:
- if (aux_buffer != NULL) {
- g_assert (aux_buffer_size > 0);
- g_slice_free1 (aux_buffer_size, aux_buffer);
- }
-
- if (error_code != PAM_SUCCESS) {
- gdm_session_worker_uninitialize_pam (worker, error_code);
- return FALSE;
- }
-
- return TRUE;
+ setup_server (session);
}
-static gboolean
-gdm_session_worker_verify_user (GdmSessionWorker *worker,
- const char *service_name,
- const char *username,
- const char *hostname,
- const char *console_name,
- gboolean password_is_required,
- GError **error)
+GdmSession *
+gdm_session_new (void)
{
- GError *pam_error;
- GdmSessionWorkerMessage *reply;
- char *error_message;
- gboolean res;
-
- g_debug ("Verifying user: %s host: %s service: %s tty: %s", username, hostname, service_name, console_name);
-
- pam_error = NULL;
- res = gdm_session_worker_initialize_pam (worker,
- service_name,
- username,
- hostname,
- console_name,
- &pam_error);
- if (! res) {
- g_propagate_error (error, pam_error);
- return FALSE;
- }
-
- /* find out who the user is and ensure they are who they say they are
- */
- res = gdm_session_worker_authenticate_user (worker,
- password_is_required,
- &pam_error);
- if (! res) {
- g_propagate_error (error, pam_error);
- return FALSE;
- }
-
- /* we're authenticated. Let's make sure we've been given
- * a valid username for the system
- */
- g_debug ("trying to get updated username");
- gdm_session_worker_update_username (worker);
-
- /* make sure the user is allowed to log in to this system
- */
- res = gdm_session_worker_authorize_user (worker,
- password_is_required,
- &pam_error);
- if (! res) {
- g_propagate_error (error, pam_error);
- return FALSE;
- }
-
- /* get kerberos tickets, setup group lists, etc
- */
- res = gdm_session_worker_give_user_credentials (worker, &pam_error);
- if (! res) {
- g_propagate_error (error, pam_error);
- return FALSE;
- }
-
- g_debug ("verification process completed, creating reply...");
- reply = gdm_session_worker_verified_message_new ();
-
- error_message = NULL;
- res = gdm_write_message (worker->message_pipe_fd,
- reply,
- reply->size,
- &error_message);
- if (! res) {
- g_warning ("could not send 'verified' reply to parent: %s\n",
- error_message);
- g_free (error_message);
- }
+ GdmSession *session;
- gdm_session_worker_message_free (reply);
+ session = g_object_new (GDM_TYPE_SESSION, NULL);
- return TRUE;
+ return session;
}
static void
-gdm_session_worker_update_environment_from_pam (GdmSessionWorker *worker)
+worker_stopped (GdmSessionWorkerJob *job,
+ GdmSession *session)
{
- char **environment;
- gsize i;
-
- environment = pam_getenvlist (worker->pam_handle);
-
- for (i = 0; environment[i] != NULL; i++)
- gdm_session_worker_set_environment_variable (worker, environment[i]);
-
- for (i = 0; environment[i]; i++)
- free (environment[i]);
- free (environment);
+ g_debug ("Worker job stopped");
}
static void
-gdm_session_worker_fill_environment_array (const char *key,
- const char *value,
- GPtrArray *environment)
+worker_started (GdmSessionWorkerJob *job,
+ GdmSession *session)
{
- char *variable;
-
- if (value == NULL)
- return;
-
- variable = g_strdup_printf ("%s=%s", key, value);
-
- g_ptr_array_add (environment, variable);
-}
-
-static char **
-gdm_session_worker_get_environment (GdmSessionWorker *worker)
-{
- GPtrArray *environment;
-
- environment = g_ptr_array_new ();
- g_hash_table_foreach (worker->environment,
- (GHFunc) gdm_session_worker_fill_environment_array,
- environment);
- g_ptr_array_add (environment, NULL);
-
- return (char **) g_ptr_array_free (environment, FALSE);
+ g_debug ("Worker job started");
}
static void
-gdm_session_worker_on_child_exited (GPid pid,
- int status,
- GdmSessionWorker *worker)
+worker_exited (GdmSessionWorkerJob *job,
+ int code,
+ GdmSession *session)
{
- GdmSessionWorkerMessage *message;
+ g_debug ("Worker job exited: %d", code);
- message = NULL;
+ if (!session->priv->is_verified) {
+ GError *error;
- if (WIFEXITED (status))
- message = gdm_session_worker_session_exited_message_new (WEXITSTATUS (status));
- else if (WIFSIGNALED (status))
- message = gdm_session_worker_session_died_message_new (WTERMSIG (status));
+ error = g_error_new (GDM_SESSION_ERROR,
+ GDM_SESSION_ERROR_WORKER_DIED,
+ _("worker exited with status %d"),
+ code);
- if (message != NULL) {
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
+ g_signal_emit (session,
+ gdm_session_signals [USER_VERIFICATION_ERROR],
+ 0, error);
+ g_error_free (error);
+ } else if (session->priv->is_running) {
+ g_signal_emit (session,
+ gdm_session_signals [SESSION_EXITED],
+ 0, code);
}
-
- g_spawn_close_pid (pid);
- worker->child_pid = 0;
-
- worker->exit_code = 0;
- g_main_loop_quit (worker->event_loop);
-}
-
-static void
-gdm_session_worker_watch_child (GdmSessionWorker *worker)
-{
- worker->child_watch_source = g_child_watch_source_new (worker->child_pid);
- g_source_set_callback (worker->child_watch_source,
- (GSourceFunc) (GChildWatchFunc)
- gdm_session_worker_on_child_exited,
- worker,
- (GDestroyNotify)
- gdm_session_worker_clear_child_watch_source);
- g_source_attach (worker->child_watch_source,
- g_main_loop_get_context (worker->event_loop));
- g_source_unref (worker->child_watch_source);
}
-/* adapted from glib script_execute */
static void
-script_execute (const gchar *file,
- char **argv,
- char **envp,
- gboolean search_path)
-{
- /* Count the arguments. */
- int argc = 0;
-
- while (argv[argc])
- ++argc;
-
- /* Construct an argument list for the shell. */
- {
- char **new_argv;
-
- new_argv = g_new0 (gchar*, argc + 2); /* /bin/sh and NULL */
-
- new_argv[0] = (char *) "/bin/sh";
- new_argv[1] = (char *) file;
- while (argc > 0) {
- new_argv[argc + 1] = argv[argc];
- --argc;
- }
-
- /* Execute the shell. */
- if (envp)
- execve (new_argv[0], new_argv, envp);
- else
- execv (new_argv[0], new_argv);
-
- g_free (new_argv);
- }
-}
-
-static char *
-my_strchrnul (const char *str, char c)
-{
- char *p = (char*) str;
- while (*p && (*p != c))
- ++p;
-
- return p;
-}
-
-/* adapted from glib g_execute */
-static gint
-gdm_session_execute (const char *file,
- char **argv,
- char **envp,
- gboolean search_path)
-{
- if (*file == '\0') {
- /* We check the simple case first. */
- errno = ENOENT;
- return -1;
- }
-
- if (!search_path || strchr (file, '/') != NULL) {
- /* Don't search when it contains a slash. */
- if (envp)
- execve (file, argv, envp);
- else
- execv (file, argv);
-
- if (errno == ENOEXEC)
- script_execute (file, argv, envp, FALSE);
- } else {
- gboolean got_eacces = 0;
- const char *path, *p;
- char *name, *freeme;
- gsize len;
- gsize pathlen;
-
- path = g_getenv ("PATH");
- if (path == NULL) {
- /* There is no `PATH' in the environment. The default
- * search path in libc is the current directory followed by
- * the path `confstr' returns for `_CS_PATH'.
- */
-
- /* In GLib we put . last, for security, and don't use the
- * unportable confstr(); UNIX98 does not actually specify
- * what to search if PATH is unset. POSIX may, dunno.
- */
-
- path = "/bin:/usr/bin:.";
- }
-
- len = strlen (file) + 1;
- pathlen = strlen (path);
- freeme = name = g_malloc (pathlen + len + 1);
-
- /* Copy the file name at the top, including '\0' */
- memcpy (name + pathlen + 1, file, len);
- name = name + pathlen;
- /* And add the slash before the filename */
- *name = '/';
-
- p = path;
- do {
- char *startp;
-
- path = p;
- p = my_strchrnul (path, ':');
-
- if (p == path)
- /* Two adjacent colons, or a colon at the beginning or the end
- * of `PATH' means to search the current directory.
- */
- startp = name + 1;
- else
- startp = memcpy (name - (p - path), path, p - path);
-
- /* Try to execute this name. If it works, execv will not return. */
- if (envp)
- execve (startp, argv, envp);
- else
- execv (startp, argv);
-
- if (errno == ENOEXEC)
- script_execute (startp, argv, envp, search_path);
-
- switch (errno) {
- case EACCES:
- /* Record the we got a `Permission denied' error. If we end
- * up finding no executable we can use, we want to diagnose
- * that we did find one but were denied access.
- */
- got_eacces = TRUE;
-
- /* FALL THRU */
-
- case ENOENT:
-#ifdef ESTALE
- case ESTALE:
-#endif
-#ifdef ENOTDIR
- case ENOTDIR:
-#endif
- /* Those errors indicate the file is missing or not executable
- * by us, in which case we want to just try the next path
- * directory.
- */
- break;
-
- default:
- /* Some other error means we found an executable file, but
- * something went wrong executing it; return the error to our
- * caller.
- */
- g_free (freeme);
- return -1;
- }
- } while (*p++ != '\0');
-
- /* We tried every element and none of them worked. */
- if (got_eacces)
- /* At least one failure was due to permissions, so report that
- * error.
- */
- errno = EACCES;
-
- g_free (freeme);
+worker_died (GdmSessionWorkerJob *job,
+ int signum,
+ GdmSession *session)
+{
+ g_debug ("Worker job died: %d", signum);
+
+ if (!session->priv->is_verified) {
+ GError *error;
+ error = g_error_new (GDM_SESSION_ERROR,
+ GDM_SESSION_ERROR_WORKER_DIED,
+ _("worker got signal '%s' and was subsequently killed"),
+ g_strsignal (signum));
+ g_signal_emit (session,
+ gdm_session_signals[USER_VERIFICATION_ERROR],
+ 0, error);
+ g_error_free (error);
+ } else if (session->priv->is_running) {
+ g_signal_emit (session,
+ gdm_session_signals[SESSION_EXITED],
+ 0, signum);
}
-
- /* Return the error from the last attempt (probably ENOENT). */
- return -1;
}
static gboolean
-gdm_session_worker_open_user_session (GdmSessionWorker *worker,
- GError **error)
+start_worker (GdmSession *session)
{
- int error_code;
- pid_t session_pid;
- GdmSessionWorkerMessage *message;
- char *error_message;
-
- g_assert (!worker->is_running);
- g_assert (geteuid () == 0);
- error_code = pam_open_session (worker->pam_handle, 0);
-
- if (error_code != PAM_SUCCESS) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_OPENING_SESSION,
- "%s", pam_strerror (worker->pam_handle, error_code));
- goto out;
- }
- worker->is_running = TRUE;
-
- g_debug ("querying pam for user environment");
- gdm_session_worker_update_environment_from_pam (worker);
-
- g_debug ("opening user session with program '%s'",
- worker->arguments[0]);
-
- session_pid = fork ();
-
- if (session_pid < 0) {
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_OPENING_SESSION,
- "%s", g_strerror (errno));
- error_code = PAM_ABORT;
- goto out;
- }
-
- if (session_pid == 0) {
- char **environment;
- char *home_dir;
- int fd;
-
- worker->inherited_fd_list = g_slist_append (NULL,
- GINT_TO_POINTER (worker->standard_output_fd));
- worker->inherited_fd_list = g_slist_append (worker->inherited_fd_list,
- GINT_TO_POINTER (worker->standard_error_fd));
-
-#if 0
- gdm_session_worker_close_open_fds (worker);
-#endif
-
- if (setuid (getuid ()) < 0) {
- g_debug ("could not reset uid - %s", g_strerror (errno));
- _exit (1);
- }
-
- if (setsid () < 0) {
- g_debug ("could not set pid '%u' as leader of new session and process group - %s",
- (guint) getpid (), g_strerror (errno));
- _exit (2);
- }
-
-#if 0
- fd = gdm_open_dev_null (O_RDWR);
-
- if (worker->standard_output_fd >= 0) {
- dup2 (worker->standard_output_fd, STDOUT_FILENO);
- close (worker->standard_output_fd);
- worker->standard_output_fd = -1;
- } else {
- dup2 (fd, STDOUT_FILENO);
- }
-
- if (worker->standard_error_fd >= 0) {
- dup2 (worker->standard_error_fd, STDERR_FILENO);
- close (worker->standard_error_fd);
- worker->standard_error_fd = -1;
- } else {
- dup2 (fd, STDERR_FILENO);
- }
-
- dup2 (fd, STDIN_FILENO);
- close (fd);
-#endif
-
- environment = gdm_session_worker_get_environment (worker);
-
- g_assert (geteuid () == getuid ());
-
- home_dir = g_hash_table_lookup (worker->environment,
- "HOME");
-
- if ((home_dir == NULL) || g_chdir (home_dir) < 0) {
- g_chdir ("/");
- }
-
- gdm_session_execute (worker->arguments[0],
- worker->arguments,
- environment,
- TRUE);
-
- g_debug ("child '%s' could not be started - %s",
- worker->arguments[0],
- g_strerror (errno));
- g_strfreev (environment);
-
- _exit (127);
- }
-
- worker->child_pid = session_pid;
-
- g_debug ("session opened creating reply...");
- g_assert (sizeof (GPid) <= sizeof (int));
-
- message = gdm_session_worker_session_started_message_new ((GPid) session_pid);
-
- error_message = NULL;
- if (!gdm_write_message (worker->message_pipe_fd,
- message, message->size,
- &error_message)) {
- g_warning ("could not send 'session started' reply to parent: %s\n",
- error_message);
- g_free (error_message);
- }
- gdm_session_worker_message_free (message);
-
- gdm_session_worker_watch_child (worker);
-
- out:
- if (error_code != PAM_SUCCESS) {
- gdm_session_worker_uninitialize_pam (worker, error_code);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_session_startup_failed_message_new (GError *error)
-{
- GdmSessionWorkerSessionStartupFailedMessage *message;
- gsize error_message_size, size;
-
- error_message_size = (error != NULL? strlen (error->message) + 1 : 0);
- size = sizeof (GdmSessionWorkerSessionStartupFailedMessage) +
- error_message_size +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTUP_FAILED;
- message->header.size = size;
-
- if (error != NULL) {
- message->error_domain = error->domain;
- message->error_code = error->code;
- g_strlcpy (message->error_message, error->message,
- error_message_size);
- message->error_message_size = error_message_size;
- } else {
- message->error_message_size = -1;
- }
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static void
-gdm_session_worker_handle_verification_message (GdmSessionWorker *worker,
- GdmSessionVerificationMessage *message)
-{
- GError *verification_error;
- GdmSessionWorkerMessage *reply;
- gboolean res;
-
- worker->standard_output_fd = message->standard_output_fd;
- worker->standard_error_fd = message->standard_error_fd;
-
- verification_error = NULL;
- res = gdm_session_worker_verify_user (worker,
- message->service_name,
- (message->username_size >= 0) ? message->username : NULL,
- message->hostname_is_provided ? message->hostname : NULL,
- message->console_name,
- TRUE /* password is required */,
- &verification_error);
- if (! res) {
- g_assert (verification_error != NULL);
-
- g_message ("%s", verification_error->message);
-
- reply = gdm_session_worker_verification_failed_message_new (verification_error);
-
- g_error_free (verification_error);
-
- gdm_write_message (worker->message_pipe_fd, reply, reply->size, NULL);
- gdm_session_worker_message_free (reply);
- goto out;
- }
-
- /* Did start_program get called early? if so, process it now,
- * otherwise we'll do it asynchronously later.
- */
- if ((worker->arguments != NULL) &&
- !gdm_session_worker_open_user_session (worker, &verification_error)) {
- g_assert (verification_error != NULL);
-
- g_message ("%s", verification_error->message);
-
- reply = gdm_session_worker_session_startup_failed_message_new (verification_error);
-
- g_error_free (verification_error);
-
- gdm_write_message (worker->message_pipe_fd, reply, reply->size, NULL);
- gdm_session_worker_message_free (reply);
- goto out;
- }
-
- out:
- ;
-}
-
-static void
-gdm_session_worker_handle_start_program_message (GdmSessionWorker *worker,
- GdmSessionStartProgramMessage *message)
-{
- GError *start_error;
gboolean res;
- if (worker->arguments != NULL)
- g_strfreev (worker->arguments);
-
- worker->arguments = gdm_session_unflatten_arguments (message->arguments);
-
- /* Did start_program get called early? if so, we will process the request
- * later, synchronously after getting credentials
- */
- if (!worker->credentials_are_established)
- return;
-
- start_error = NULL;
- res = gdm_session_worker_open_user_session (worker, &start_error);
- if (! res) {
- GdmSessionWorkerMessage *message;
-
- g_assert (start_error != NULL);
-
- g_warning ("%s", start_error->message);
-
- message = gdm_session_worker_session_startup_failed_message_new (start_error);
-
- g_error_free (start_error);
-
- gdm_write_message (worker->message_pipe_fd, message, message->size, NULL);
- gdm_session_worker_message_free (message);
- }
+ session->priv->job = gdm_session_worker_job_new ();
+ gdm_session_worker_job_set_server_address (session->priv->job, session->priv->server_address);
+ g_signal_connect (session->priv->job,
+ "stopped",
+ G_CALLBACK (worker_stopped),
+ session);
+ g_signal_connect (session->priv->job,
+ "started",
+ G_CALLBACK (worker_started),
+ session);
+ g_signal_connect (session->priv->job,
+ "exited",
+ G_CALLBACK (worker_exited),
+ session);
+ g_signal_connect (session->priv->job,
+ "died",
+ G_CALLBACK (worker_died),
+ session);
+
+ res = gdm_session_worker_job_start (session->priv->job);
+
+ return res;
}
-static void
-gdm_session_worker_handle_set_environment_variable_message (GdmSessionWorker *worker,
- GdmSessionSetEnvironmentVariableMessage *message)
-{
- gdm_session_worker_set_environment_variable (worker,
- message->environment_variable);
-}
-
-static gboolean
-gdm_session_worker_process_asynchronous_message (GdmSessionWorker *worker,
- GdmSessionMessage *message)
+gboolean
+gdm_session_open (GdmSession *session,
+ const char *service_name,
+ const char *hostname,
+ const char *console_name,
+ GError **error)
{
- switch (message->type) {
- case GDM_SESSION_MESSAGE_TYPE_VERIFICATION:
- g_debug ("received verification request from parent");
- gdm_session_worker_handle_verification_message (worker,
- (GdmSessionVerificationMessage *) message);
- return TRUE;
+ gboolean res;
- case GDM_SESSION_MESSAGE_TYPE_START_PROGRAM:
- g_debug ("received new session arguments from parent");
- gdm_session_worker_handle_start_program_message (worker,
- (GdmSessionStartProgramMessage *)
- message);
- return TRUE;
+ g_return_val_if_fail (session != NULL, FALSE);
+ g_return_val_if_fail (service_name != NULL, FALSE);
+ g_return_val_if_fail (console_name != NULL, FALSE);
+ g_return_val_if_fail (hostname != NULL, FALSE);
- case GDM_SESSION_MESSAGE_TYPE_SET_ENVIRONMENT_VARIABLE:
- g_debug ("received new environment variable from parent");
- gdm_session_worker_handle_set_environment_variable_message (worker,
- (GdmSessionSetEnvironmentVariableMessage *)
- message);
- return TRUE;
+ res = start_worker (session);
- default:
- g_debug ("received unknown message with type '0x%x' from parent",
- message->type);
- return FALSE;
- }
+ session->priv->service_name = g_strdup (service_name);
+ session->priv->hostname = g_strdup (hostname);
+ session->priv->console_name = g_strdup (console_name);
- return FALSE;
+ return res;
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
static void
-gdm_session_worker_unwatch_child (GdmSessionWorker *worker)
+send_begin_verification (GdmSession *session)
{
- if (worker->child_watch_source == NULL)
- return;
+ DBusMessage *message;
+ DBusMessageIter iter;
- g_source_destroy (worker->child_watch_source);
- worker->child_watch_source = NULL;
-}
+ message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
+ GDM_SESSION_DBUS_INTERFACE,
+ "BeginVerification");
-static int
-gdm_get_max_open_fds (void)
-{
- struct rlimit open_fd_limit;
- const int fallback_limit = GDM_MAX_OPEN_FILE_DESCRIPTORS;
-
- if (getrlimit (RLIMIT_NOFILE, &open_fd_limit) < 0) {
- g_debug ("could not get file descriptor limit: %s",
- g_strerror (errno));
- g_debug ("returning fallback file descriptor limit of %d",
- fallback_limit);
- return fallback_limit;
- }
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->service_name);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->hostname);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->console_name);
- if (open_fd_limit.rlim_cur == RLIM_INFINITY) {
- g_debug ("currently no file descriptor limit, returning fallback limit of %d",
- fallback_limit);
- return fallback_limit;
+ if (! send_dbus_message (session->priv->worker_connection, message)) {
+ g_debug ("Could not send %s signal", "BeginVerification");
}
- return (int) open_fd_limit.rlim_cur;
+ dbus_message_unref (message);
}
static void
-gdm_session_worker_close_all_fds (GdmSessionWorker *worker)
+send_begin_verification_for_user (GdmSession *session)
{
- int max_open_fds, fd;
-
- max_open_fds = gdm_get_max_open_fds ();
- g_debug ("closing all file descriptors except those that are specifically "
- "excluded");
-
- for (fd = 0; fd < max_open_fds; fd++) {
- GSList *node;
- for (node = worker->inherited_fd_list;
- node != NULL;
- node = node->next) {
- if (fd == GPOINTER_TO_INT (node->data))
- break;
- }
+ DBusMessage *message;
+ DBusMessageIter iter;
- if (node == NULL) {
- g_debug ("closing file descriptor '%d'", fd);
- close (fd);
- }
- }
+ message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
+ GDM_SESSION_DBUS_INTERFACE,
+ "BeginVerificationForUser");
- g_debug ("closed first '%d' file descriptors", max_open_fds);
-}
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->service_name);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->hostname);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->console_name);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session->priv->username);
-static void
-gdm_session_worker_close_open_fds (GdmSessionWorker *worker)
-{
- /* using DIR instead of GDir because we need access to dirfd so
- * that we can iterate through the fds and close them in one sweep.
- * (if we just closed all of them then we would close the one we're using
- * for reading the directory!)
- */
- DIR *dir;
- struct dirent *entry;
- int fd, opendir_fd;
- gboolean should_use_fallback;
-
- should_use_fallback = FALSE;
- opendir_fd = -1;
- return;
-
- dir = opendir (GDM_OPEN_FILE_DESCRIPTORS_DIR);
-
- if (dir != NULL) {
- opendir_fd = dirfd (dir);
- g_debug ("opened '"GDM_OPEN_FILE_DESCRIPTORS_DIR"' on file descriptor '%d'", opendir_fd);
+ if (! send_dbus_message (session->priv->worker_connection, message)) {
+ g_debug ("Could not send %s signal", "BeginVerificationForUser");
}
- if ((dir == NULL) || (opendir_fd < 0)) {
- g_debug ("could not open "GDM_OPEN_FILE_DESCRIPTORS_DIR": %s", g_strerror (errno));
- should_use_fallback = TRUE;
- } else {
- g_debug ("reading files in '"GDM_OPEN_FILE_DESCRIPTORS_DIR"'");
- while ((entry = readdir (dir)) != NULL) {
- GSList *node;
- glong filename_as_number;
- char *byte_after_number;
-
- if (entry->d_name[0] == '.')
- continue;
-
- g_debug ("scanning filename '%s' for file descriptor number",
- entry->d_name);
- fd = -1;
- filename_as_number = strtol (entry->d_name, &byte_after_number, 10);
-
- g_assert (byte_after_number != NULL);
-
- if ((*byte_after_number != '\0') ||
- (filename_as_number < 0) ||
- (filename_as_number >= G_MAXINT)) {
- g_debug ("filename '%s' does not appear to represent a "
- "file descriptor: %s",
- entry->d_name, strerror (errno));
- should_use_fallback = TRUE;
- } else {
- fd = (int) filename_as_number;
- g_debug ("filename '%s' represents file descriptor '%d'",
- entry->d_name, fd);
- should_use_fallback = FALSE;
- }
-
- for (node = worker->inherited_fd_list;
- node != NULL;
- node = node->next) {
- if (fd == GPOINTER_TO_INT (node->data))
- break;
- }
-
- if ((node == NULL) &&
- (fd != opendir_fd)) {
- g_debug ("closing file descriptor '%d'", fd);
- close (fd);
- } else {
- g_debug ("will not close file descriptor '%d' because it "
- "is still neded", fd);
- }
- }
- g_debug ("closing directory '"GDM_OPEN_FILE_DESCRIPTORS_DIR"'");
- closedir (dir);
- }
-
- /* if /proc isn't mounted or something else is screwy,
- * fall back to closing everything
- */
- if (should_use_fallback) {
- gdm_session_worker_close_all_fds (worker);
- }
+ dbus_message_unref (message);
}
-
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_verified_message_new (void)
-{
- GdmSessionWorkerVerifiedMessage *message;
- gsize size;
-
- size = sizeof (GdmSessionWorkerVerifiedMessage) +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFIED;
- message->header.size = size;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_verification_failed_message_new (GError *error)
-{
- GdmSessionWorkerVerificationFailedMessage *message;
- gsize error_message_size, size;
-
- error_message_size = (error != NULL? strlen (error->message) + 1 : 0);
- size = sizeof (GdmSessionWorkerVerificationFailedMessage) +
- error_message_size +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_VERIFICATION_FAILED;
- message->header.size = size;
-
- if (error != NULL) {
- message->error_domain = error->domain;
- message->error_code = error->code;
- g_strlcpy (message->error_message, error->message,
- error_message_size);
- message->error_message_size = error_message_size;
- } else {
- message->error_message_size = -1;
- }
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_info_request_message_new (const char *question)
-{
- GdmSessionWorkerInfoRequestMessage *message;
- gsize question_size, size;
-
- g_assert (question != NULL);
-
- question_size = strlen (question) + 1;
- size = sizeof (GdmSessionWorkerInfoRequestMessage) +
- question_size +
- sizeof (GDM_SESSION_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_INFO_REQUEST;
- message->header.size = size;
-
- g_strlcpy (message->question, question, question_size);
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_username_changed_message_new (const char *new_username)
+gboolean
+gdm_session_begin_verification (GdmSession *session,
+ const char *username,
+ GError **error)
{
- GdmSessionWorkerUsernameChangedMessage *message;
- gsize username_size, size;
-
- username_size = (new_username != NULL? strlen (new_username) + 1 : 0);
-
- size = sizeof (GdmSessionWorkerUsernameChangedMessage) +
- username_size +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
+ g_return_val_if_fail (session != NULL, FALSE);
+ g_return_val_if_fail (dbus_connection_get_is_connected (session->priv->worker_connection), FALSE);
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_USERNAME_CHANGED;
- message->header.size = size;
+ session->priv->username = g_strdup (username);
- if (new_username != NULL) {
- g_strlcpy (message->username, new_username, username_size);
- message->username_size = username_size;
+ if (username == NULL) {
+ send_begin_verification (session);
} else {
- message->username_size = -1;
- }
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_secret_info_request_message_new (const char *question)
-{
- GdmSessionWorkerSecretInfoRequestMessage *message;
- gsize question_size, size;
-
- g_assert (question != NULL);
-
- question_size = strlen (question) + 1;
-
- size = sizeof (GdmSessionWorkerSecretInfoRequestMessage) +
- question_size +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_SECRET_INFO_REQUEST;
- message->header.size = size;
-
- g_strlcpy (message->question, question, question_size);
- message->question_size = question_size;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_info_message_new (const char *info)
-{
- GdmSessionWorkerInfoMessage *message;
- gsize info_size, size;
-
- g_assert (info != NULL);
-
- info_size = strlen (info) + 1;
-
- size = sizeof (GdmSessionWorkerInfoRequestMessage) +
- info_size +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_INFO;
- message->header.size = size;
-
- g_strlcpy (message->info, info, info_size);
- message->info_size = info_size;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_problem_message_new (const char *problem)
-{
- GdmSessionWorkerProblemMessage *message;
- gsize problem_size, size;
-
- g_assert (problem != NULL);
-
- problem_size = strlen (problem) + 1;
-
- size = sizeof (GdmSessionWorkerProblemMessage) +
- problem_size +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_PROBLEM;
- message->header.size = size;
-
- g_strlcpy (message->problem, problem, problem_size);
- message->problem_size = problem_size;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_session_started_message_new (GPid pid)
-{
- GdmSessionWorkerSessionStartedMessage *message;
- gsize size;
-
- size = sizeof (GdmSessionWorkerSessionStartedMessage) +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_STARTED;
- message->header.size = size;
-
- message->pid = pid;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_session_exited_message_new (int exit_code)
-{
- GdmSessionWorkerSessionExitedMessage *message;
- gsize size;
-
- size = sizeof (GdmSessionWorkerSessionExitedMessage) +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_EXITED;
- message->header.size = size;
-
- message->exit_code = exit_code;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static GdmSessionWorkerMessage *
-gdm_session_worker_session_died_message_new (int signal_number)
-{
- GdmSessionWorkerSessionDiedMessage *message;
- gsize size;
-
- size = sizeof (GdmSessionWorkerSessionDiedMessage) +
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL);
-
- message = g_slice_alloc0 (size);
-
- message->header.type = GDM_SESSION_WORKER_MESSAGE_TYPE_SESSION_EXITED;
- message->header.size = size;
-
- message->signal_number = signal_number;
-
- g_strlcpy ((char *) ((guint *) message) + size -
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL),
- GDM_SESSION_WORKER_MESSAGE_SENTINAL,
- sizeof (GDM_SESSION_WORKER_MESSAGE_SENTINAL));
-
- return (GdmSessionWorkerMessage *) message;
-}
-
-static void
-gdm_session_worker_message_free (GdmSessionWorkerMessage *message)
-{
- g_slice_free1 (message->size, message);
-}
-
-static void
-gdm_session_worker_incoming_message_handler (GdmSessionWorker *worker)
-{
- GdmSessionMessage *message;
- GError *error;
-
- error = NULL;
- message = gdm_session_worker_get_incoming_message (worker, &error);
-
- if (message == NULL) {
- g_assert (error != NULL);
- g_warning ("could not receive message from parent: %s\n",
- error->message);
- g_error_free (error);
-
- /* FIXME: figure out what to do here
- */
- return;
- }
-
- gdm_session_worker_process_asynchronous_message (worker, message);
-
- gdm_session_message_free (message);
-}
-
-static gboolean
-gdm_session_worker_data_on_message_pipe_handler (GIOChannel *channel,
- GIOCondition condition,
- GdmSessionWorker *worker)
-{
- if ((condition & G_IO_IN) || (condition & G_IO_PRI)) {
- g_debug ("got message from message pipe");
- gdm_session_worker_incoming_message_handler (worker);
- }
-
- if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) {
- g_debug ("got disconnected message from message pipe");
- gdm_session_worker_disconnected_handler (worker);
- return FALSE;
+ send_begin_verification_for_user (session);
}
return TRUE;
}
static void
-gdm_session_worker_watch_message_pipe (GdmSessionWorker *worker)
-{
- GIOChannel *io_channel;
-
- io_channel = g_io_channel_unix_new (worker->message_pipe_fd);
- g_io_channel_set_close_on_unref (io_channel, TRUE);
-
- worker->message_pipe_source = g_io_create_watch (io_channel,
- G_IO_IN | G_IO_HUP);
- g_io_channel_unref (io_channel);
-
- g_source_set_callback (worker->message_pipe_source,
- (GSourceFunc) (GIOFunc)
- gdm_session_worker_data_on_message_pipe_handler,
- worker,
- (GDestroyNotify)
- gdm_session_worker_clear_message_pipe_source);
- g_source_attach (worker->message_pipe_source,
- g_main_loop_get_context (worker->event_loop));
- g_source_unref (worker->message_pipe_source);
-}
-
-static void
-gdm_session_worker_wait_for_messages (GdmSessionWorker *worker)
-{
- gdm_session_worker_watch_message_pipe (worker);
-
- g_main_loop_run (worker->event_loop);
-}
-
-static gboolean
-gdm_open_bidirectional_pipe (int *fd1,
- int *fd2,
- char **error_message)
+send_environment_variable (const char *key,
+ const char *value,
+ GdmSession *session)
{
- int pipe_fds[2];
-
- g_assert (fd1 != NULL);
- g_assert (fd2 != NULL);
+ DBusMessage *message;
+ DBusMessageIter iter;
- if (socketpair (AF_UNIX, SOCK_DGRAM, 0, pipe_fds) < 0) {
- if (error_message != NULL)
- *error_message = g_strdup_printf ("%s",
- g_strerror (errno));
- return FALSE;
- }
+ message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
+ GDM_SESSION_DBUS_INTERFACE,
+ "SetEnvironmentVariable");
- if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) {
- if (error_message != NULL)
- *error_message = g_strdup_printf ("%s",
- g_strerror (errno));
- close (pipe_fds[0]);
- close (pipe_fds[1]);
- return FALSE;
- }
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);
- if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) {
- if (error_message != NULL)
- *error_message = g_strdup_printf ("%s",
- g_strerror (errno));
- close (pipe_fds[0]);
- close (pipe_fds[1]);
- return FALSE;
+ if (! send_dbus_message (session->priv->worker_connection, message)) {
+ g_debug ("Could not send %s signal", "SetEnvironmentVariable");
}
- *fd1 = pipe_fds[0];
- *fd2 = pipe_fds[1];
-
- return TRUE;
-}
-
-static GdmSessionWorker *
-gdm_session_worker_new (void)
-{
- GdmSessionWorker *worker;
- GMainContext *context;
-
- worker = g_slice_new0 (GdmSessionWorker);
-
- context = g_main_context_new ();
- worker->event_loop = g_main_loop_new (context, FALSE);
- g_main_context_unref (context);
-
- worker->pam_handle = NULL;
-
- worker->message_pipe_fd = -1;
- worker->message_pipe_source = NULL;
-
- worker->inherited_fd_list = NULL;
-
- worker->username = NULL;
- worker->arguments = NULL;
-
- worker->environment = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) g_free);
- worker->standard_output_fd = -1;
- worker->standard_error_fd = -1;
-
- worker->exit_code = 127;
-
- worker->credentials_are_established = FALSE;
- worker->is_running = FALSE;
-
- return worker;
+ dbus_message_unref (message);
}
static void
-gdm_session_worker_free (GdmSessionWorker *worker)
+send_environment (GdmSession *session)
{
- if (worker == NULL)
- return;
-
- gdm_session_worker_unwatch_child (worker);
-
- if (worker->message_pipe_fd != -1) {
- close (worker->message_pipe_fd);
- worker->message_pipe_fd = -1;
- }
-
- if (worker->username != NULL) {
- g_free (worker->username);
- worker->username = NULL;
- }
-
- if (worker->arguments != NULL) {
- g_strfreev (worker->arguments);
- worker->arguments = NULL;
- }
-
- if (worker->environment != NULL) {
- g_hash_table_destroy (worker->environment);
- worker->environment = NULL;
- }
-
- if (worker->standard_output_fd >= 0) {
- close (worker->standard_output_fd);
- worker->standard_output_fd = -1;
- }
-
- if (worker->standard_error_fd >= 0) {
- close (worker->standard_error_fd);
- worker->standard_error_fd = -1;
- }
- if (worker->event_loop != NULL) {
- g_main_loop_unref (worker->event_loop);
- worker->event_loop = NULL;
- }
-
- g_slice_free (GdmSessionWorker, worker);
-}
-
-static gboolean
-gdm_session_create_worker (GdmSession *session,
- int standard_output_fd,
- int standard_error_fd,
- int *worker_fd,
- GPid *worker_pid,
- GError **error)
-{
- GdmSessionWorker *worker;
- int session_message_pipe_fd, worker_message_pipe_fd;
- char *error_message;
- pid_t child_pid;
-
- session_message_pipe_fd = -1;
- worker_message_pipe_fd = -1;
- error_message = NULL;
- if (!gdm_open_bidirectional_pipe (&session_message_pipe_fd,
- &worker_message_pipe_fd,
- &error_message)) {
- g_debug ("unable open message pipe: %s", error_message);
- g_set_error (error,
- GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_OPENING_MESSAGE_PIPE,
- "%s", error_message);
- g_free (error_message);
- return FALSE;
- }
-
- g_assert (session_message_pipe_fd >= 0);
- g_assert (worker_message_pipe_fd >= 0);
-
- child_pid = fork ();
-
- if (child_pid < 0) {
- g_set_error (error, GDM_SESSION_ERROR,
- GDM_SESSION_ERROR_FORKING,
- "%s", g_strerror (errno));
- return FALSE;
- }
-
- if (child_pid == 0) {
- GError *child_error;
- int fd;
-
- setsid ();
-
- worker = gdm_session_worker_new ();
- worker->message_pipe_fd = worker_message_pipe_fd;
- worker->inherited_fd_list =
- g_slist_append (worker->inherited_fd_list,
- GINT_TO_POINTER (worker->message_pipe_fd));
-
- if (standard_output_fd >= 0)
- worker->inherited_fd_list =
- g_slist_append (worker->inherited_fd_list,
- GINT_TO_POINTER (standard_output_fd));
- if (standard_error_fd >= 0)
- worker->inherited_fd_list =
- g_slist_append (worker->inherited_fd_list,
- GINT_TO_POINTER (standard_error_fd));
-
-#if 0
- gdm_session_worker_close_open_fds (worker);
-
- fd = gdm_open_dev_null (O_RDWR);
- dup2 (fd, STDIN_FILENO);
-
- if (standard_output_fd >= 0 &&
- standard_output_fd != STDOUT_FILENO)
- dup2 (standard_output_fd, STDOUT_FILENO);
- else
- dup2 (fd, STDOUT_FILENO);
-
- if (standard_error_fd >= 0 &&
- standard_error_fd != STDERR_FILENO)
- dup2 (standard_error_fd, STDERR_FILENO);
- else
- dup2 (fd, STDERR_FILENO);
- close (fd);
-#endif
-
- g_debug ("waiting for messages from parent");
- child_error = NULL;
-
- gdm_session_worker_wait_for_messages (worker);
-
- g_debug ("exiting with code '%d'", worker->exit_code);
- gdm_session_worker_free (worker);
- _exit (worker->exit_code);
- }
- close (worker_message_pipe_fd);
-
- g_assert (child_pid != 0);
- if (worker_pid != NULL)
- *worker_pid = (GPid) child_pid;
-
- if (worker_fd != NULL)
- *worker_fd = session_message_pipe_fd;
-
- return TRUE;
-}
-
-static gboolean
-gdm_session_open_with_worker (GdmSession *session,
- const char *service_name,
- const char *username,
- const char *hostname,
- const char *console_name,
- int standard_output_fd,
- int standard_error_fd,
- GError **error)
-{
- GdmSessionMessage *message;
- GError *worker_error;
- int worker_message_pipe_fd;
- GPid worker_pid;
- gboolean worker_is_created;
-
- worker_error = NULL;
- worker_is_created = gdm_session_create_worker (session,
- standard_output_fd,
- standard_error_fd,
- &worker_message_pipe_fd,
- &worker_pid,
- &worker_error);
- if (!worker_is_created) {
- g_debug ("worker could not be created");
- g_propagate_error (error, worker_error);
- return FALSE;
- }
-
- session->priv->service_name = g_strdup (service_name);
-
- session->priv->arguments = NULL;
- session->priv->username = g_strdup (username);
-
- session->priv->hostname = g_strdup (hostname);
- session->priv->console_name = g_strdup (console_name);
- session->priv->standard_output_fd = standard_output_fd;
- session->priv->standard_error_fd = standard_error_fd;
-
- session->priv->worker_message_pipe_fd = worker_message_pipe_fd;
- session->priv->worker_pid = worker_pid;
- session->priv->next_expected_message = GDM_SESSION_MESSAGE_TYPE_VERIFICATION;
- session->priv->query_answer = NULL;
-
- gdm_session_watch_child (session);
-
- message = gdm_session_verification_message_new (service_name,
- username,
- hostname,
- console_name,
- standard_output_fd,
- standard_error_fd);
- gdm_write_message (session->priv->worker_message_pipe_fd, message, message->size,
- NULL);
- gdm_session_message_free (message);
-
- return TRUE;
-}
-
-gboolean
-gdm_session_open (GdmSession *session,
- const char *service_name,
- const char *hostname,
- const char *console_name,
- int standard_output_fd,
- int standard_error_fd,
- GError **error)
-{
- g_return_val_if_fail (session != NULL, FALSE);
- g_return_val_if_fail (service_name != NULL, FALSE);
- g_return_val_if_fail (console_name != NULL, FALSE);
-
- return gdm_session_open_with_worker (session, service_name,
- NULL, hostname, console_name,
- standard_output_fd,
- standard_error_fd, error);
-}
-
-gboolean
-gdm_session_open_for_user (GdmSession *session,
- const char *service_name,
- const char *username,
- const char *hostname,
- const char *console_name,
- int standard_output_fd,
- int standard_error_fd,
- GError **error)
-{
- g_return_val_if_fail (session != NULL, FALSE);
- g_return_val_if_fail (service_name != NULL, FALSE);
- g_return_val_if_fail (username != NULL, FALSE);
- g_return_val_if_fail (console_name != NULL, FALSE);
-
- return gdm_session_open_with_worker (session,
- service_name,
- username, hostname, console_name,
- standard_output_fd,
- standard_error_fd, error);
+ g_hash_table_foreach (session->priv->environment,
+ (GHFunc) send_environment_variable,
+ session);
}
void
-gdm_session_start_program (GdmSession *session,
- int argc,
- const char **argv)
+gdm_session_start_program (GdmSession *session,
+ const char *command)
{
- GdmSessionMessage *message;
- int i;
-
g_return_if_fail (session != NULL);
g_return_if_fail (session != NULL);
g_return_if_fail (gdm_session_is_running (session) == FALSE);
- g_return_if_fail (argv != NULL);
- g_return_if_fail (argv[0] != NULL);
+ g_return_if_fail (command != NULL);
- session->priv->arguments = g_new0 (char *, (argc + 1));
+ send_environment (session);
- for (i = 0; argv[i] != NULL; i++) {
- session->priv->arguments[i] = g_strdup (argv[i]);
- }
-
- message = gdm_session_start_program_message_new (argv);
- gdm_write_message (session->priv->worker_message_pipe_fd,
- message,
- message->size,
- NULL);
- gdm_session_message_free (message);
+ send_dbus_string_signal (session, "StartProgram", command);
}
void
@@ -4020,21 +1499,13 @@ gdm_session_close (GdmSession *session)
{
g_return_if_fail (session != NULL);
- gdm_session_unwatch_child (session);
- session->priv->next_expected_message = GDM_SESSION_MESSAGE_TYPE_VERIFICATION;
-
- if (session->priv->worker_message_pipe_fd > 0) {
- close (session->priv->worker_message_pipe_fd);
- session->priv->worker_message_pipe_fd = -1;
- }
-
- if (session->priv->worker_pid > 0) {
- if (session->priv->is_running)
+ if (session->priv->job != NULL) {
+ if (session->priv->is_running) {
gdm_session_write_record (session,
GDM_SESSION_RECORD_TYPE_LOGOUT);
- kill (-session->priv->worker_pid, SIGTERM);
- waitpid (session->priv->worker_pid, NULL, 0);
- session->priv->worker_pid = -1;
+ }
+
+ gdm_session_worker_job_stop (session->priv->job);
}
session->priv->is_running = FALSE;
@@ -4045,11 +1516,6 @@ gdm_session_close (GdmSession *session)
session->priv->service_name = NULL;
}
- if (session->priv->arguments) {
- g_strfreev (session->priv->arguments);
- session->priv->arguments = NULL;
- }
-
if (session->priv->hostname) {
g_free (session->priv->hostname);
session->priv->hostname = NULL;
@@ -4059,69 +1525,38 @@ gdm_session_close (GdmSession *session)
g_free (session->priv->username);
session->priv->username = NULL;
}
-
}
gboolean
gdm_session_is_running (GdmSession *session)
{
+ g_return_val_if_fail (session != NULL, FALSE);
+
return session->priv->is_running;
}
void
-gdm_session_set_environment_variable (GdmSession *session,
- const char *key,
- const char *value)
+gdm_session_set_environment_variable (GdmSession *session,
+ const char *key,
+ const char *value)
{
- GdmSessionMessage *message;
-
g_return_if_fail (session != NULL);
g_return_if_fail (session != NULL);
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
- message = gdm_session_set_environment_variable_message_new (key, value);
- gdm_write_message (session->priv->worker_message_pipe_fd, message, message->size,
- NULL);
- gdm_session_message_free (message);
+ g_hash_table_replace (session->priv->environment,
+ g_strdup (key),
+ g_strdup (value));
}
void
-gdm_session_answer_query (GdmSession *session,
- const char *answer)
+gdm_session_answer_query (GdmSession *session,
+ const char *answer)
{
- GdmSessionMessage *reply;
-
g_return_if_fail (session != NULL);
- if (session->priv->query_answer != NULL)
- g_free (session->priv->query_answer);
-
- session->priv->query_answer = g_strdup (answer);
-
- reply = NULL;
- switch (session->priv->next_expected_message) {
- case GDM_SESSION_MESSAGE_TYPE_INFO_REPLY:
- reply = gdm_session_info_reply_message_new (session->priv->query_answer);
- break;
-
- case GDM_SESSION_MESSAGE_TYPE_SECRET_INFO_REPLY:
- reply = gdm_session_secret_info_reply_message_new (session->priv->query_answer);
- break;
-
- default:
- break;
- }
- g_free (session->priv->query_answer);
- session->priv->query_answer = NULL;
-
- if (reply != NULL) {
- gdm_write_message (session->priv->worker_message_pipe_fd, reply, reply->size,
- NULL);
- gdm_session_message_free (reply);
- }
-
- session->priv->next_expected_message = GDM_SESSION_MESSAGE_TYPE_INVALID;
+ answer_pending_query (session, answer);
}
char *
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index cd5c0867..a1db5375 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -50,6 +50,9 @@ typedef struct
GObjectClass parent_class;
/* signals */
+ void (* opened) (GdmSession *session);
+ void (* closed) (GdmSession *session);
+
void (* user_verified) (GdmSession *session);
void (* user_verification_error) (GdmSession *session,
@@ -84,7 +87,6 @@ typedef enum _GdmSessionError {
GDM_SESSION_ERROR_GENERIC = 0,
GDM_SESSION_ERROR_WITH_SESSION_COMMAND,
GDM_SESSION_ERROR_FORKING,
- GDM_SESSION_ERROR_OPENING_MESSAGE_PIPE,
GDM_SESSION_ERROR_COMMUNICATING,
GDM_SESSION_ERROR_WORKER_DIED,
GDM_SESSION_ERROR_AUTHENTICATING,
@@ -103,22 +105,13 @@ gboolean gdm_session_open (GdmSession *session,
const char *service_name,
const char *hostname,
const char *console_name,
- int standard_output_fd,
- int standard_error_fd,
GError **error);
-
-gboolean gdm_session_open_for_user (GdmSession *session,
- const char *service_name,
+void gdm_session_close (GdmSession *session);
+gboolean gdm_session_begin_verification (GdmSession *session,
const char *username,
- const char *hostname,
- const char *console_name,
- int standard_output_fd,
- int standard_error_fd,
GError **error);
void gdm_session_start_program (GdmSession *session,
- int argc,
- const char **argv);
-
+ const char *command);
void gdm_session_set_environment_variable (GdmSession *session,
const char *key,
const char *value);
@@ -128,7 +121,6 @@ void gdm_session_answer_query (GdmSession *session,
char * gdm_session_get_username (GdmSession *session);
-void gdm_session_close (GdmSession *session);
gboolean gdm_session_is_running (GdmSession *session);
G_END_DECLS
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index ef6888d3..0be3be9d 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -549,15 +549,12 @@ setup_session_environment (GdmSimpleSlave *slave)
}
static void
-on_user_verified (GdmSession *session,
- GdmSimpleSlave *slave)
+on_user_verified (GdmSession *session,
+ GdmSimpleSlave *slave)
{
char *username;
- int argc;
- char **argv;
char *command;
char *filename;
- GError *error;
gboolean res;
gdm_greeter_proxy_stop (slave->priv->greeter);
@@ -584,20 +581,10 @@ on_user_verified (GdmSession *session,
return;
}
- error = NULL;
- res = g_shell_parse_argv (command, &argc, &argv, &error);
- if (! res) {
- g_warning ("Could not parse command: %s", error->message);
- g_error_free (error);
- }
-
- gdm_session_start_program (session,
- argc,
- (const char **)argv);
+ gdm_session_start_program (session, command);
g_free (filename);
g_free (command);
- g_strfreev (argv);
}
static void
@@ -696,8 +683,6 @@ on_greeter_connected (GdmGreeterServer *greeter_server,
"gdm",
NULL /* hostname */,
"/dev/console",
- STDOUT_FILENO,
- STDERR_FILENO,
NULL);
/* If XDMCP stop pinging */
diff --git a/daemon/session-worker-main.c b/daemon/session-worker-main.c
new file mode 100644
index 00000000..2cb5671b
--- /dev/null
+++ b/daemon/session-worker-main.c
@@ -0,0 +1,179 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gdm-signal-handler.h"
+#include "gdm-log.h"
+#include "gdm-session-worker.h"
+
+#define SERVER_DBUS_PATH "/org/gnome/DisplayManager/SessionServer"
+#define SERVER_DBUS_INTERFACE "org.gnome.DisplayManager.SessionServer"
+
+static gboolean
+signal_cb (int signo,
+ gpointer data)
+{
+ int ret;
+
+ g_debug ("Got callback for signal %d", signo);
+
+ ret = TRUE;
+
+ switch (signo) {
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGILL:
+ case SIGABRT:
+ g_debug ("Caught signal %d.", signo);
+
+ ret = FALSE;
+ break;
+
+ case SIGFPE:
+ case SIGPIPE:
+ /* let the fatal signals interrupt us */
+ g_debug ("Caught signal %d, shutting down abnormally.", signo);
+ ret = FALSE;
+
+ break;
+
+ case SIGINT:
+ case SIGTERM:
+ /* let the fatal signals interrupt us */
+ g_debug ("Caught signal %d, shutting down normally.", signo);
+ ret = FALSE;
+
+ break;
+
+ case SIGHUP:
+ g_debug ("Got HUP signal");
+ /* FIXME:
+ * Reread config stuff like system config files, VPN service files, etc
+ */
+ ret = TRUE;
+
+ break;
+
+ case SIGUSR1:
+ g_debug ("Got USR1 signal");
+ /* FIXME:
+ * Play with log levels or something
+ */
+ ret = TRUE;
+
+ break;
+
+ default:
+ g_debug ("Caught unhandled signal %d", signo);
+ ret = TRUE;
+
+ break;
+ }
+
+ return ret;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GMainLoop *main_loop;
+ GOptionContext *context;
+ GdmSessionWorker *worker;
+ GdmSignalHandler *signal_handler;
+ const char *address;
+ static GOptionEntry entries [] = {
+ { NULL }
+ };
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ setlocale (LC_ALL, "");
+
+ g_type_init ();
+
+ context = g_option_context_new (_("GNOME Display Manager Session Worker"));
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ gdm_log_init ();
+ gdm_log_set_debug (TRUE);
+
+ address = g_getenv ("GDM_SESSION_DBUS_ADDRESS");
+ if (address == NULL) {
+ g_warning ("GDM_SESSION_DBUS_ADDRESS not set");
+ exit (1);
+ }
+
+ worker = gdm_session_worker_new (address);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ signal_handler = gdm_signal_handler_new ();
+ gdm_signal_handler_set_main_loop (signal_handler, main_loop);
+ gdm_signal_handler_add (signal_handler, SIGTERM, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGINT, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGILL, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGBUS, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGFPE, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGHUP, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGSEGV, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGABRT, signal_cb, NULL);
+ gdm_signal_handler_add (signal_handler, SIGUSR1, signal_cb, NULL);
+
+ g_main_loop_run (main_loop);
+
+ if (worker != NULL) {
+ g_object_unref (worker);
+ }
+
+ if (signal_handler != NULL) {
+ g_object_unref (signal_handler);
+ }
+
+ g_main_loop_unref (main_loop);
+
+ out:
+
+ g_debug ("Worker finished");
+
+ return 0;
+}
diff --git a/daemon/test-session.c b/daemon/test-session.c
index 73c7ab4d..24682e0f 100644
--- a/daemon/test-session.c
+++ b/daemon/test-session.c
@@ -33,6 +33,25 @@
static GMainLoop *loop;
static void
+on_open (GdmSession *session,
+ const char *username)
+{
+ GError *error;
+ gboolean res;
+
+ g_debug ("Got opened: begin auth for %s", username);
+
+ error = NULL;
+ res = gdm_session_begin_verification (session,
+ username,
+ &error);
+ if (! res) {
+ g_warning ("Unable to begin verification: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
on_session_started (GdmSession *session,
GPid pid)
{
@@ -61,15 +80,15 @@ static void
on_user_verified (GdmSession *session)
{
char *username;
- const char *argv[] = { "/usr/bin/gedit", "/tmp/foo.log", NULL };
+ const char *command = "/usr/bin/gedit /tmp/foo.log";
username = gdm_session_get_username (session);
g_print ("%s%ssuccessfully authenticated\n",
- username? username : "", username? " " : "");
+ username ? username : "", username ? " " : "");
g_free (username);
- gdm_session_start_program (session, 2, argv);
+ gdm_session_start_program (session, command);
}
static void
@@ -183,25 +202,20 @@ main (int argc,
if (argc <= 1) {
username = NULL;
- gdm_session_open (session,
- "gdm",
- NULL /* hostname */,
- ttyname (STDIN_FILENO),
- STDOUT_FILENO,
- STDERR_FILENO,
- NULL);
} else {
username = argv[1];
- gdm_session_open_for_user (session,
- "gdm",
- username,
- NULL,
- ttyname (STDIN_FILENO),
- STDOUT_FILENO,
- STDERR_FILENO,
- NULL);
}
+ gdm_session_open (session,
+ "gdm",
+ "",
+ ttyname (STDIN_FILENO),
+ NULL);
+
+ g_signal_connect (session, "opened",
+ G_CALLBACK (on_open),
+ username);
+
g_signal_connect (session, "info",
G_CALLBACK (on_info),
NULL);