summaryrefslogtreecommitdiffstats
path: root/daemon
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:43:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:43:08 +0000
commitd524c8e88f558b9f2aebff39b6fbe77eab51e081 (patch)
tree399d1caf80df6656549115df912f6af2f7369308 /daemon
parentInitial commit. (diff)
downloadgdm3-d524c8e88f558b9f2aebff39b6fbe77eab51e081.tar.xz
gdm3-d524c8e88f558b9f2aebff39b6fbe77eab51e081.zip
Adding upstream version 43.0.upstream/43.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'daemon')
-rw-r--r--daemon/gdm-dbus-util.c199
-rw-r--r--daemon/gdm-dbus-util.h40
-rw-r--r--daemon/gdm-display-access-file.c606
-rw-r--r--daemon/gdm-display-access-file.h67
-rw-r--r--daemon/gdm-display-factory.c242
-rw-r--r--daemon/gdm-display-factory.h59
-rw-r--r--daemon/gdm-display-store.c331
-rw-r--r--daemon/gdm-display-store.h92
-rw-r--r--daemon/gdm-display.c1962
-rw-r--r--daemon/gdm-display.h117
-rw-r--r--daemon/gdm-display.xml23
-rw-r--r--daemon/gdm-launch-environment.c1047
-rw-r--r--daemon/gdm-launch-environment.h89
-rw-r--r--daemon/gdm-legacy-display.c303
-rw-r--r--daemon/gdm-legacy-display.h38
-rw-r--r--daemon/gdm-local-display-factory.c1631
-rw-r--r--daemon/gdm-local-display-factory.h51
-rw-r--r--daemon/gdm-local-display-factory.xml8
-rw-r--r--daemon/gdm-local-display.c166
-rw-r--r--daemon/gdm-local-display.h38
-rw-r--r--daemon/gdm-local-display.xml5
-rw-r--r--daemon/gdm-manager.c2838
-rw-r--r--daemon/gdm-manager.h82
-rw-r--r--daemon/gdm-manager.xml19
-rw-r--r--daemon/gdm-server.c1089
-rw-r--r--daemon/gdm-server.h42
-rw-r--r--daemon/gdm-session-auditor.c322
-rw-r--r--daemon/gdm-session-auditor.h65
-rw-r--r--daemon/gdm-session-enum-types.c.in42
-rw-r--r--daemon/gdm-session-enum-types.h.in24
-rw-r--r--daemon/gdm-session-linux-auditor.c165
-rw-r--r--daemon/gdm-session-linux-auditor.h43
-rw-r--r--daemon/gdm-session-record.c305
-rw-r--r--daemon/gdm-session-record.h50
-rw-r--r--daemon/gdm-session-settings.c425
-rw-r--r--daemon/gdm-session-settings.h72
-rw-r--r--daemon/gdm-session-solaris-auditor.c387
-rw-r--r--daemon/gdm-session-solaris-auditor.h42
-rw-r--r--daemon/gdm-session-worker-common.c57
-rw-r--r--daemon/gdm-session-worker-common.h50
-rw-r--r--daemon/gdm-session-worker-enum-types.c.in42
-rw-r--r--daemon/gdm-session-worker-enum-types.h.in24
-rw-r--r--daemon/gdm-session-worker-job.c590
-rw-r--r--daemon/gdm-session-worker-job.h48
-rw-r--r--daemon/gdm-session-worker.c3621
-rw-r--r--daemon/gdm-session-worker.h68
-rw-r--r--daemon/gdm-session-worker.xml93
-rw-r--r--daemon/gdm-session.c4132
-rw-r--r--daemon/gdm-session.h133
-rw-r--r--daemon/gdm-session.xml146
-rw-r--r--daemon/gdm-wayland-session.c620
-rw-r--r--daemon/gdm-x-session.c997
-rw-r--r--daemon/gdm-xdmcp-chooser-display.c146
-rw-r--r--daemon/gdm-xdmcp-chooser-display.h66
-rw-r--r--daemon/gdm-xdmcp-display-factory.c3486
-rw-r--r--daemon/gdm-xdmcp-display-factory.h51
-rw-r--r--daemon/gdm-xdmcp-display.c295
-rw-r--r--daemon/gdm-xdmcp-display.h52
-rw-r--r--daemon/main.c463
-rw-r--r--daemon/meson.build217
-rw-r--r--daemon/org.freedesktop.DBus.xml12
-rw-r--r--daemon/session-worker-main.c173
-rw-r--r--daemon/test-session-client.c257
63 files changed, 28965 insertions, 0 deletions
diff --git a/daemon/gdm-dbus-util.c b/daemon/gdm-dbus-util.c
new file mode 100644
index 0000000..844d60a
--- /dev/null
+++ b/daemon/gdm-dbus-util.c
@@ -0,0 +1,199 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Giovanni Campagna <scampa.giovanni@gmail.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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gdm-dbus-util.h"
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <gio/gunixsocketaddress.h>
+
+/* a subset of org.freedesktop.DBus interface, to be used by internal servers */
+static const char *dbus_introspection =
+"<node name=\"/org/freedesktop/DBus\">"
+" <interface name=\"org.freedesktop.DBus\">"
+" <method name=\"AddMatch\">"
+" <arg name=\"match_rule\" type=\"s\" direction=\"in\" />"
+" </method>"
+" </interface>"
+"</node>";
+
+static void
+handle_bus_method (GDBusConnection *connection,
+ const char *sender,
+ const char *object_path,
+ const char *interface_name,
+ const char *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static gboolean
+handle_connection (GDBusServer *server,
+ GDBusConnection *new_connection,
+ gpointer user_data)
+{
+ GDBusInterfaceVTable bus_vtable = { handle_bus_method };
+ GDBusNodeInfo *bus_info;
+
+ bus_info = g_dbus_node_info_new_for_xml (dbus_introspection,
+ NULL);
+
+ g_debug ("GdmDBusServer: new connection %p", new_connection);
+
+ g_dbus_connection_register_object (new_connection,
+ "/org/freedesktop/DBus",
+ bus_info->interfaces[0],
+ &bus_vtable,
+ NULL, NULL, NULL);
+ g_dbus_node_info_unref (bus_info);
+
+ /* We're not handling the signal */
+ return FALSE;
+}
+
+GDBusServer *
+gdm_dbus_setup_private_server (GDBusAuthObserver *observer,
+ GError **error)
+{
+ char *guid;
+ const char *client_address;
+ GDBusServer *server;
+
+ guid = g_dbus_generate_guid ();
+
+ server = g_dbus_server_new_sync ("unix:tmpdir=/tmp",
+ G_DBUS_SERVER_FLAGS_NONE,
+ guid,
+ observer,
+ NULL,
+ error);
+
+ client_address = g_dbus_server_get_client_address (server);
+
+ if (g_str_has_prefix (client_address, "unix:path=")) {
+ client_address += strlen("unix:path=");
+ g_chmod (client_address, 0666);
+ }
+
+ g_signal_connect (server, "new-connection",
+ G_CALLBACK (handle_connection),
+ NULL);
+
+ g_free (guid);
+
+ return server;
+}
+
+gboolean
+gdm_dbus_get_pid_for_name (const char *system_bus_name,
+ pid_t *out_pid,
+ GError **error)
+{
+ GDBusConnection *bus;
+ GVariant *reply;
+ gboolean retval = FALSE;
+ unsigned int v;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (bus == NULL) {
+ return FALSE;
+ }
+
+ reply = g_dbus_connection_call_sync (bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixProcessID",
+ g_variant_new ("(s)", system_bus_name),
+ G_VARIANT_TYPE ("(u)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, error);
+ if (reply == NULL) {
+ goto out;
+ }
+
+ g_variant_get (reply, "(u)", &v);
+ *out_pid = v;
+ g_variant_unref (reply);
+
+ retval = TRUE;
+ out:
+ g_object_unref (bus);
+
+ return retval;
+}
+
+gboolean
+gdm_dbus_get_uid_for_name (const char *system_bus_name,
+ uid_t *out_uid,
+ GError **error)
+{
+ GDBusConnection *bus;
+ GVariant *reply;
+ gboolean retval = FALSE;
+ unsigned int v;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (bus == NULL) {
+ return FALSE;
+ }
+
+ reply = g_dbus_connection_call_sync (bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetConnectionUnixUser",
+ g_variant_new ("(s)", system_bus_name),
+ G_VARIANT_TYPE ("(u)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, error);
+ if (reply == NULL) {
+ goto out;
+ }
+
+ g_variant_get (reply, "(u)", &v);
+ *out_uid = v;
+ g_variant_unref (reply);
+
+ retval = TRUE;
+ out:
+ g_object_unref (bus);
+
+ return retval;
+}
+
+void
+gdm_dbus_error_ensure (GQuark domain)
+{
+ /* The primary purpose of this function is to make sure the error quark
+ * is registered internally with gdbus before any bus traffic occurs,
+ * so we get remote errors mapped correctly to their local counterparts.
+ * This error quark registration happens implicitly the first time the
+ * quark is used.
+ * Note that g_debug is never optimized away, only the output is suppressed.
+ */
+ g_debug ("GdmDBusUtils: Registered DBus error domain '%s'",
+ g_quark_to_string (domain));
+}
diff --git a/daemon/gdm-dbus-util.h b/daemon/gdm-dbus-util.h
new file mode 100644
index 0000000..32dc319
--- /dev/null
+++ b/daemon/gdm-dbus-util.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2012 Giovanni Campagna <scampa.giovanni@gmail.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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __GDM_DBUS_UTIL_H
+#define __GDM_DBUS_UTIL_H
+
+#include <gio/gio.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+GDBusServer *gdm_dbus_setup_private_server (GDBusAuthObserver *observer,
+ GError **error);
+
+gboolean gdm_dbus_get_pid_for_name (const char *system_bus_name,
+ pid_t *out_pid,
+ GError **error);
+
+gboolean gdm_dbus_get_uid_for_name (const char *system_bus_name,
+ uid_t *out_uid,
+ GError **error);
+
+void gdm_dbus_error_ensure (GQuark domain);
+#endif
diff --git a/daemon/gdm-display-access-file.c b/daemon/gdm-display-access-file.c
new file mode 100644
index 0000000..217ebbb
--- /dev/null
+++ b/daemon/gdm-display-access-file.c
@@ -0,0 +1,606 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * gdm-display-access-file.c - Abstraction around xauth cookies
+ *
+ * Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+
+#include <X11/Xauth.h>
+
+#include "gdm-display-access-file.h"
+#include "gdm-common.h"
+
+struct _GdmDisplayAccessFile
+{
+ GObject parent;
+
+ char *username;
+ FILE *fp;
+ char *path;
+};
+
+#ifndef GDM_DISPLAY_ACCESS_COOKIE_SIZE
+#define GDM_DISPLAY_ACCESS_COOKIE_SIZE 16
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static void gdm_display_access_file_finalize (GObject * object);
+
+enum
+{
+ PROP_0 = 0,
+ PROP_USERNAME,
+ PROP_PATH
+};
+
+G_DEFINE_TYPE (GdmDisplayAccessFile, gdm_display_access_file, G_TYPE_OBJECT)
+
+static void
+gdm_display_access_file_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmDisplayAccessFile *access_file;
+
+ access_file = GDM_DISPLAY_ACCESS_FILE (object);
+
+ switch (prop_id) {
+ case PROP_USERNAME:
+ g_value_set_string (value, access_file->username);
+ break;
+
+ case PROP_PATH:
+ g_value_set_string (value, access_file->path);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gdm_display_access_file_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmDisplayAccessFile *access_file;
+
+ access_file = GDM_DISPLAY_ACCESS_FILE (object);
+
+ switch (prop_id) {
+ case PROP_USERNAME:
+ g_assert (access_file->username == NULL);
+ access_file->username = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gdm_display_access_file_class_init (GdmDisplayAccessFileClass *access_file_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *param_spec;
+
+ object_class = G_OBJECT_CLASS (access_file_class);
+
+ object_class->finalize = gdm_display_access_file_finalize;
+ object_class->get_property = gdm_display_access_file_get_property;
+ object_class->set_property = gdm_display_access_file_set_property;
+
+ param_spec = g_param_spec_string ("username",
+ "Username",
+ "Owner of Xauthority file",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_USERNAME, param_spec);
+ param_spec = g_param_spec_string ("path",
+ "Path",
+ "Path to Xauthority file",
+ NULL,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_PATH, param_spec);
+}
+
+static void
+gdm_display_access_file_init (GdmDisplayAccessFile *access_file)
+{
+}
+
+static void
+gdm_display_access_file_finalize (GObject *object)
+{
+ GdmDisplayAccessFile *file;
+ GObjectClass *parent_class;
+
+ file = GDM_DISPLAY_ACCESS_FILE (object);
+ parent_class = G_OBJECT_CLASS (gdm_display_access_file_parent_class);
+
+ if (file->fp != NULL) {
+ gdm_display_access_file_close (file);
+ }
+ g_assert (file->path == NULL);
+
+ if (file->username != NULL) {
+ g_free (file->username);
+ file->username = NULL;
+ g_object_notify (object, "username");
+ }
+
+ if (parent_class->finalize != NULL) {
+ parent_class->finalize (object);
+ }
+}
+
+GQuark
+gdm_display_access_file_error_quark (void)
+{
+ static GQuark error_quark = 0;
+
+ if (error_quark == 0) {
+ error_quark = g_quark_from_static_string ("gdm-display-access-file");
+ }
+
+ return error_quark;
+}
+
+GdmDisplayAccessFile *
+gdm_display_access_file_new (const char *username)
+{
+ GdmDisplayAccessFile *access_file;
+ g_return_val_if_fail (username != NULL, NULL);
+
+ access_file = g_object_new (GDM_TYPE_DISPLAY_ACCESS_FILE,
+ "username", username,
+ NULL);
+
+ return access_file;
+}
+
+static gboolean
+_get_uid_and_gid_for_user (const char *username,
+ uid_t *uid,
+ gid_t *gid)
+{
+ struct passwd *passwd_entry;
+
+ g_assert (username != NULL);
+ g_assert (uid != NULL);
+ g_assert (gid != NULL);
+
+ errno = 0;
+ gdm_get_pwent_for_name (username, &passwd_entry);
+
+ if (passwd_entry == NULL) {
+ return FALSE;
+ }
+
+ *uid = passwd_entry->pw_uid;
+ *gid = passwd_entry->pw_gid;
+
+ return TRUE;
+}
+
+static void
+clean_up_stale_auth_subdirs (void)
+{
+ GDir *dir;
+ const char *filename;
+
+ dir = g_dir_open (GDM_XAUTH_DIR, 0, NULL);
+
+ if (dir == NULL) {
+ return;
+ }
+
+ while ((filename = g_dir_read_name (dir)) != NULL) {
+ char *path;
+
+ path = g_build_filename (GDM_XAUTH_DIR, filename, NULL);
+
+ /* Will only succeed if the directory is empty
+ */
+ g_rmdir (path);
+ g_free (path);
+ }
+ g_dir_close (dir);
+}
+
+static FILE *
+_create_xauth_file_for_user (const char *username,
+ char **filename,
+ GError **error)
+{
+ char *template;
+ const char *dir_name;
+ char *auth_filename;
+ int fd;
+ FILE *fp;
+ uid_t uid;
+ gid_t gid;
+
+ g_assert (filename != NULL);
+
+ *filename = NULL;
+
+ template = NULL;
+ auth_filename = NULL;
+ fp = NULL;
+ fd = -1;
+
+ /* Create directory if not exist, then set permission 0711 and ownership root:gdm */
+ if (g_file_test (GDM_XAUTH_DIR, G_FILE_TEST_IS_DIR) == FALSE) {
+ g_remove (GDM_XAUTH_DIR);
+ if (g_mkdir (GDM_XAUTH_DIR, 0711) != 0) {
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ g_strerror (errno));
+ goto out;
+ }
+
+ g_chmod (GDM_XAUTH_DIR, 0711);
+ _get_uid_and_gid_for_user (GDM_USERNAME, &uid, &gid);
+ if (chown (GDM_XAUTH_DIR, 0, gid) != 0) {
+ g_warning ("Unable to change owner of '%s'",
+ GDM_XAUTH_DIR);
+ }
+ } else {
+ /* if it does exist make sure it has correct mode 0711 */
+ g_chmod (GDM_XAUTH_DIR, 0711);
+
+ /* and clean up any stale auth subdirs */
+ clean_up_stale_auth_subdirs ();
+ }
+
+ if (!_get_uid_and_gid_for_user (username, &uid, &gid)) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_USER_INFO,
+ _("could not find user “%s” on system"),
+ username);
+ goto out;
+
+ }
+
+ template = g_strdup_printf (GDM_XAUTH_DIR
+ "/auth-for-%s-XXXXXX",
+ username);
+
+ g_debug ("GdmDisplayAccessFile: creating xauth directory %s", template);
+ /* Initially create with mode 01700 then later chmod after we create database */
+ errno = 0;
+ dir_name = g_mkdtemp (template);
+ if (dir_name == NULL) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "Unable to create temp dir from tempalte '%s': %s",
+ template,
+ g_strerror (errno));
+ goto out;
+ }
+
+ g_debug ("GdmDisplayAccessFile: chowning %s to %u:%u",
+ dir_name, (guint)uid, (guint)gid);
+ errno = 0;
+ if (chown (dir_name, uid, gid) < 0) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "Unable to change permission of '%s': %s",
+ dir_name,
+ g_strerror (errno));
+ goto out;
+ }
+
+ auth_filename = g_build_filename (dir_name, "database", NULL);
+
+ g_debug ("GdmDisplayAccessFile: creating %s", auth_filename);
+ /* mode 00600 */
+ errno = 0;
+ fd = g_open (auth_filename,
+ O_RDWR | O_CREAT | O_EXCL | O_BINARY,
+ S_IRUSR | S_IWUSR);
+
+ if (fd < 0) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "Unable to open '%s': %s",
+ auth_filename,
+ g_strerror (errno));
+ goto out;
+ }
+
+ g_debug ("GdmDisplayAccessFile: chowning %s to %u:%u", auth_filename, (guint)uid, (guint)gid);
+ errno = 0;
+ if (fchown (fd, uid, gid) < 0) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ "Unable to change owner for '%s': %s",
+ auth_filename,
+ g_strerror (errno));
+ close (fd);
+ fd = -1;
+ goto out;
+ }
+
+ /* now open up permissions on per-session directory */
+ g_debug ("GdmDisplayAccessFile: chmoding %s to 0711", dir_name);
+ g_chmod (dir_name, 0711);
+
+ errno = 0;
+ fp = fdopen (fd, "w");
+ if (fp == NULL) {
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ g_strerror (errno));
+ close (fd);
+ fd = -1;
+ goto out;
+ }
+
+ *filename = auth_filename;
+ auth_filename = NULL;
+
+ /* don't close it */
+ fd = -1;
+out:
+ g_free (template);
+ g_free (auth_filename);
+ if (fd != -1) {
+ close (fd);
+ }
+
+ return fp;
+}
+
+gboolean
+gdm_display_access_file_open (GdmDisplayAccessFile *file,
+ GError **error)
+{
+ GError *create_error;
+
+ g_return_val_if_fail (file != NULL, FALSE);
+ g_return_val_if_fail (file->fp == NULL, FALSE);
+ g_return_val_if_fail (file->path == NULL, FALSE);
+
+ create_error = NULL;
+ file->fp = _create_xauth_file_for_user (file->username,
+ &file->path,
+ &create_error);
+
+ if (file->fp == NULL) {
+ g_propagate_error (error, create_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+_get_auth_info_for_display (GdmDisplayAccessFile *file,
+ GdmDisplay *display,
+ unsigned short *family,
+ unsigned short *address_length,
+ char **address,
+ unsigned short *number_length,
+ char **number,
+ unsigned short *name_length,
+ char **name)
+{
+ int display_number;
+ gboolean is_local;
+
+ gdm_display_is_local (display, &is_local, NULL);
+
+ if (is_local) {
+ /* We could just use FamilyWild here except xauth
+ * (and by extension su and ssh) doesn't support it yet
+ *
+ * https://bugs.freedesktop.org/show_bug.cgi?id=43425
+ */
+ char localhost[HOST_NAME_MAX + 1] = "";
+ *family = FamilyLocal;
+ if (gethostname (localhost, HOST_NAME_MAX) == 0) {
+ *address = g_strdup (localhost);
+ } else {
+ *address = g_strdup ("localhost");
+ }
+ } else {
+ *family = FamilyWild;
+ gdm_display_get_remote_hostname (display, address, NULL);
+ }
+ *address_length = strlen (*address);
+
+ gdm_display_get_x11_display_number (display, &display_number, NULL);
+ *number = g_strdup_printf ("%d", display_number);
+ *number_length = strlen (*number);
+
+ *name = g_strdup ("MIT-MAGIC-COOKIE-1");
+ *name_length = strlen (*name);
+}
+
+gboolean
+gdm_display_access_file_add_display (GdmDisplayAccessFile *file,
+ GdmDisplay *display,
+ char **cookie,
+ gsize *cookie_size,
+ GError **error)
+{
+ GError *add_error;
+ gboolean display_added;
+
+ g_return_val_if_fail (file != NULL, FALSE);
+ g_return_val_if_fail (file->path != NULL, FALSE);
+ g_return_val_if_fail (cookie != NULL, FALSE);
+
+ add_error = NULL;
+ *cookie = gdm_generate_random_bytes (GDM_DISPLAY_ACCESS_COOKIE_SIZE,
+ &add_error);
+
+ if (*cookie == NULL) {
+ g_propagate_error (error, add_error);
+ return FALSE;
+ }
+
+ *cookie_size = GDM_DISPLAY_ACCESS_COOKIE_SIZE;
+
+ display_added = gdm_display_access_file_add_display_with_cookie (file, display,
+ *cookie,
+ *cookie_size,
+ &add_error);
+ if (!display_added) {
+ g_free (*cookie);
+ *cookie = NULL;
+ g_propagate_error (error, add_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_access_file_add_display_with_cookie (GdmDisplayAccessFile *file,
+ GdmDisplay *display,
+ const char *cookie,
+ gsize cookie_size,
+ GError **error)
+{
+ Xauth auth_entry;
+ gboolean display_added;
+
+ g_return_val_if_fail (file != NULL, FALSE);
+ g_return_val_if_fail (file->path != NULL, FALSE);
+ g_return_val_if_fail (cookie != NULL, FALSE);
+
+ _get_auth_info_for_display (file, display,
+ &auth_entry.family,
+ &auth_entry.address_length,
+ &auth_entry.address,
+ &auth_entry.number_length,
+ &auth_entry.number,
+ &auth_entry.name_length,
+ &auth_entry.name);
+
+ auth_entry.data = (char *) cookie;
+ auth_entry.data_length = cookie_size;
+
+ /* FIXME: We should lock the file in case the X server is
+ * trying to use it, too.
+ */
+ if (!XauWriteAuth (file->fp, &auth_entry)
+ || fflush (file->fp) == EOF) {
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ g_strerror (errno));
+ display_added = FALSE;
+ } else {
+ display_added = TRUE;
+ }
+
+ /* If we wrote a FamilyLocal entry, we still want a FamilyWild
+ * entry, because it's more resiliant against hostname changes
+ *
+ */
+ if (auth_entry.family == FamilyLocal) {
+ auth_entry.family = FamilyWild;
+
+ if (XauWriteAuth (file->fp, &auth_entry)
+ && fflush (file->fp) != EOF) {
+ display_added = TRUE;
+ }
+ }
+
+ g_free (auth_entry.address);
+ g_free (auth_entry.number);
+ g_free (auth_entry.name);
+
+ return display_added;
+}
+
+void
+gdm_display_access_file_close (GdmDisplayAccessFile *file)
+{
+ char *auth_dir;
+
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (file->fp != NULL);
+ g_return_if_fail (file->path != NULL);
+
+ errno = 0;
+ if (g_unlink (file->path) != 0) {
+ g_warning ("GdmDisplayAccessFile: Unable to remove X11 authority database '%s': %s",
+ file->path,
+ g_strerror (errno));
+ }
+
+ /* still try to remove dir even if file remove failed,
+ may have already been removed by someone else */
+ /* we own the parent directory too */
+ auth_dir = g_path_get_dirname (file->path);
+ if (auth_dir != NULL) {
+ errno = 0;
+ if (g_rmdir (auth_dir) != 0) {
+ g_warning ("GdmDisplayAccessFile: Unable to remove X11 authority directory '%s': %s",
+ auth_dir,
+ g_strerror (errno));
+ }
+ g_free (auth_dir);
+ }
+
+ g_free (file->path);
+ file->path = NULL;
+ g_object_notify (G_OBJECT (file), "path");
+
+ fclose (file->fp);
+ file->fp = NULL;
+}
+
+char *
+gdm_display_access_file_get_path (GdmDisplayAccessFile *access_file)
+{
+ return g_strdup (access_file->path);
+}
diff --git a/daemon/gdm-display-access-file.h b/daemon/gdm-display-access-file.h
new file mode 100644
index 0000000..559d3ed
--- /dev/null
+++ b/daemon/gdm-display-access-file.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * gdm-display-access-file.h - Abstraction around xauth cookies
+ *
+ * Copyright (C) 2007 Ray Strode <rstrode@redhat.com>
+ *
+ * Written by 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef __GDM_DISPLAY_ACCESS_FILE_H__
+#define __GDM_DISPLAY_ACCESS_FILE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-display.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DISPLAY_ACCESS_FILE (gdm_display_access_file_get_type ())
+G_DECLARE_FINAL_TYPE (GdmDisplayAccessFile, gdm_display_access_file, GDM, DISPLAY_ACCESS_FILE, GObject)
+
+#define GDM_DISPLAY_ACCESS_FILE_ERROR (gdm_display_access_file_error_quark ())
+
+typedef enum _GdmDisplayAccessFileError GdmDisplayAccessFileError;
+
+enum _GdmDisplayAccessFileError
+{
+ GDM_DISPLAY_ACCESS_FILE_ERROR_GENERAL = 0,
+ GDM_DISPLAY_ACCESS_FILE_ERROR_FINDING_AUTH_ENTRY
+};
+
+GQuark gdm_display_access_file_error_quark (void);
+
+GdmDisplayAccessFile *gdm_display_access_file_new (const char *username);
+gboolean gdm_display_access_file_open (GdmDisplayAccessFile *file,
+ GError **error);
+gboolean gdm_display_access_file_add_display (GdmDisplayAccessFile *file,
+ GdmDisplay *display,
+ char **cookie,
+ gsize *cookie_size,
+ GError **error);
+gboolean gdm_display_access_file_add_display_with_cookie (GdmDisplayAccessFile *file,
+ GdmDisplay *display,
+ const char *cookie,
+ gsize cookie_size,
+ GError **error);
+
+void gdm_display_access_file_close (GdmDisplayAccessFile *file);
+char *gdm_display_access_file_get_path (GdmDisplayAccessFile *file);
+
+G_END_DECLS
+#endif /* __GDM_DISPLAY_ACCESS_FILE_H__ */
diff --git a/daemon/gdm-display-factory.c b/daemon/gdm-display-factory.c
new file mode 100644
index 0000000..b28d287
--- /dev/null
+++ b/daemon/gdm-display-factory.c
@@ -0,0 +1,242 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-display-factory.h"
+#include "gdm-display-store.h"
+
+typedef struct _GdmDisplayFactoryPrivate
+{
+ GdmDisplayStore *display_store;
+ guint purge_displays_id;
+} GdmDisplayFactoryPrivate;
+
+enum {
+ PROP_0,
+ PROP_DISPLAY_STORE,
+};
+
+static void gdm_display_factory_class_init (GdmDisplayFactoryClass *klass);
+static void gdm_display_factory_init (GdmDisplayFactory *factory);
+static void gdm_display_factory_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdmDisplayFactory, gdm_display_factory, G_TYPE_OBJECT)
+
+GQuark
+gdm_display_factory_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_display_factory_error");
+ }
+
+ return ret;
+}
+
+static gboolean
+purge_display (char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ int status;
+
+ status = gdm_display_get_status (display);
+
+ switch (status) {
+ case GDM_DISPLAY_FINISHED:
+ case GDM_DISPLAY_FAILED:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+purge_displays (GdmDisplayFactory *factory)
+{
+ GdmDisplayFactoryPrivate *priv;
+
+ priv = gdm_display_factory_get_instance_private (factory);
+ priv->purge_displays_id = 0;
+ gdm_display_store_foreach_remove (priv->display_store,
+ (GdmDisplayStoreFunc)purge_display,
+ NULL);
+
+ return G_SOURCE_REMOVE;
+}
+
+void
+gdm_display_factory_queue_purge_displays (GdmDisplayFactory *factory)
+{
+ GdmDisplayFactoryPrivate *priv;
+
+ priv = gdm_display_factory_get_instance_private (factory);
+ if (priv->purge_displays_id == 0) {
+ priv->purge_displays_id = g_idle_add ((GSourceFunc) purge_displays, factory);
+ }
+}
+
+GdmDisplayStore *
+gdm_display_factory_get_display_store (GdmDisplayFactory *factory)
+{
+ GdmDisplayFactoryPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), NULL);
+
+ priv = gdm_display_factory_get_instance_private (factory);
+ return priv->display_store;
+}
+
+gboolean
+gdm_display_factory_start (GdmDisplayFactory *factory)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE);
+
+ g_object_ref (factory);
+ ret = GDM_DISPLAY_FACTORY_GET_CLASS (factory)->start (factory);
+ g_object_unref (factory);
+
+ return ret;
+}
+
+gboolean
+gdm_display_factory_stop (GdmDisplayFactory *factory)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE);
+
+ g_object_ref (factory);
+ ret = GDM_DISPLAY_FACTORY_GET_CLASS (factory)->stop (factory);
+ g_object_unref (factory);
+
+ return ret;
+}
+
+static void
+gdm_display_factory_set_display_store (GdmDisplayFactory *factory,
+ GdmDisplayStore *display_store)
+{
+ GdmDisplayFactoryPrivate *priv;
+
+ priv = gdm_display_factory_get_instance_private (factory);
+ g_clear_object (&priv->display_store);
+
+ if (display_store != NULL) {
+ priv->display_store = g_object_ref (display_store);
+ }
+}
+
+static void
+gdm_display_factory_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmDisplayFactory *self;
+
+ self = GDM_DISPLAY_FACTORY (object);
+
+ switch (prop_id) {
+ case PROP_DISPLAY_STORE:
+ gdm_display_factory_set_display_store (self, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_display_factory_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmDisplayFactory *self;
+ GdmDisplayFactoryPrivate *priv;
+
+ self = GDM_DISPLAY_FACTORY (object);
+ priv = gdm_display_factory_get_instance_private (self);
+
+ switch (prop_id) {
+ case PROP_DISPLAY_STORE:
+ g_value_set_object (value, priv->display_store);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_display_factory_class_init (GdmDisplayFactoryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_display_factory_get_property;
+ object_class->set_property = gdm_display_factory_set_property;
+ object_class->finalize = gdm_display_factory_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_STORE,
+ g_param_spec_object ("display-store",
+ "display store",
+ "display store",
+ GDM_TYPE_DISPLAY_STORE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdm_display_factory_init (GdmDisplayFactory *factory)
+{
+}
+
+static void
+gdm_display_factory_finalize (GObject *object)
+{
+ GdmDisplayFactory *factory;
+ GdmDisplayFactoryPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_DISPLAY_FACTORY (object));
+
+ factory = GDM_DISPLAY_FACTORY (object);
+ priv = gdm_display_factory_get_instance_private (factory);
+
+ g_return_if_fail (priv != NULL);
+
+ if (priv->purge_displays_id != 0) {
+ g_source_remove (priv->purge_displays_id);
+ priv->purge_displays_id = 0;
+ }
+
+ G_OBJECT_CLASS (gdm_display_factory_parent_class)->finalize (object);
+}
diff --git a/daemon/gdm-display-factory.h b/daemon/gdm-display-factory.h
new file mode 100644
index 0000000..b17cb1c
--- /dev/null
+++ b/daemon/gdm-display-factory.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_DISPLAY_FACTORY_H
+#define __GDM_DISPLAY_FACTORY_H
+
+#include <glib-object.h>
+
+#include "gdm-display-store.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DISPLAY_FACTORY (gdm_display_factory_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GdmDisplayFactory, gdm_display_factory, GDM, DISPLAY_FACTORY, GObject)
+
+struct _GdmDisplayFactoryClass
+{
+ GObjectClass parent_class;
+
+ gboolean (*start) (GdmDisplayFactory *factory);
+ gboolean (*stop) (GdmDisplayFactory *factory);
+};
+
+typedef enum
+{
+ GDM_DISPLAY_FACTORY_ERROR_GENERAL
+} GdmDisplayFactoryError;
+
+#define GDM_DISPLAY_FACTORY_ERROR gdm_display_factory_error_quark ()
+
+GQuark gdm_display_factory_error_quark (void);
+GType gdm_display_factory_get_type (void);
+
+gboolean gdm_display_factory_start (GdmDisplayFactory *manager);
+gboolean gdm_display_factory_stop (GdmDisplayFactory *manager);
+GdmDisplayStore * gdm_display_factory_get_display_store (GdmDisplayFactory *manager);
+void gdm_display_factory_queue_purge_displays (GdmDisplayFactory *manager);
+
+G_END_DECLS
+
+#endif /* __GDM_DISPLAY_FACTORY_H */
diff --git a/daemon/gdm-display-store.c b/daemon/gdm-display-store.c
new file mode 100644
index 0000000..7df69d9
--- /dev/null
+++ b/daemon/gdm-display-store.c
@@ -0,0 +1,331 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-display-store.h"
+#include "gdm-display.h"
+
+struct GdmDisplayStorePrivate
+{
+ GHashTable *displays;
+};
+
+typedef struct
+{
+ GdmDisplayStore *store;
+ GdmDisplay *display;
+} StoredDisplay;
+
+enum {
+ DISPLAY_ADDED,
+ DISPLAY_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_display_store_class_init (GdmDisplayStoreClass *klass);
+static void gdm_display_store_init (GdmDisplayStore *display_store);
+static void gdm_display_store_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdmDisplayStore, gdm_display_store, G_TYPE_OBJECT)
+
+static StoredDisplay *
+stored_display_new (GdmDisplayStore *store,
+ GdmDisplay *display)
+{
+ StoredDisplay *stored_display;
+
+ stored_display = g_slice_new (StoredDisplay);
+ stored_display->store = store;
+ stored_display->display = g_object_ref (display);
+
+ return stored_display;
+}
+
+static void
+stored_display_free (StoredDisplay *stored_display)
+{
+ g_signal_emit (G_OBJECT (stored_display->store),
+ signals[DISPLAY_REMOVED],
+ 0,
+ stored_display->display);
+
+ g_debug ("GdmDisplayStore: Unreffing display: %p",
+ stored_display->display);
+ g_object_unref (stored_display->display);
+
+ g_slice_free (StoredDisplay, stored_display);
+}
+
+GQuark
+gdm_display_store_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_display_store_error");
+ }
+
+ return ret;
+}
+
+void
+gdm_display_store_clear (GdmDisplayStore *store)
+{
+ g_return_if_fail (store != NULL);
+ g_debug ("GdmDisplayStore: Clearing display store");
+ g_hash_table_remove_all (store->priv->displays);
+}
+
+static gboolean
+remove_display (char *id,
+ GdmDisplay *display,
+ GdmDisplay *display_to_remove)
+{
+ if (display == display_to_remove) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean
+gdm_display_store_remove (GdmDisplayStore *store,
+ GdmDisplay *display)
+{
+ g_return_val_if_fail (store != NULL, FALSE);
+
+ gdm_display_store_foreach_remove (store,
+ (GdmDisplayStoreFunc)remove_display,
+ display);
+ return FALSE;
+}
+
+typedef struct
+{
+ GdmDisplayStoreFunc predicate;
+ gpointer user_data;
+} FindClosure;
+
+static gboolean
+find_func (const char *id,
+ StoredDisplay *stored_display,
+ FindClosure *closure)
+{
+ return closure->predicate (id,
+ stored_display->display,
+ closure->user_data);
+}
+
+static void
+foreach_func (const char *id,
+ StoredDisplay *stored_display,
+ FindClosure *closure)
+{
+ (void) closure->predicate (id,
+ stored_display->display,
+ closure->user_data);
+}
+
+void
+gdm_display_store_foreach (GdmDisplayStore *store,
+ GdmDisplayStoreFunc func,
+ gpointer user_data)
+{
+ FindClosure closure;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (func != NULL);
+
+ closure.predicate = func;
+ closure.user_data = user_data;
+
+ g_hash_table_foreach (store->priv->displays,
+ (GHFunc) foreach_func,
+ &closure);
+}
+
+GdmDisplay *
+gdm_display_store_lookup (GdmDisplayStore *store,
+ const char *id)
+{
+ StoredDisplay *stored_display;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+
+ stored_display = g_hash_table_lookup (store->priv->displays,
+ id);
+ if (stored_display == NULL) {
+ return NULL;
+ }
+
+ return stored_display->display;
+}
+
+GdmDisplay *
+gdm_display_store_find (GdmDisplayStore *store,
+ GdmDisplayStoreFunc predicate,
+ gpointer user_data)
+{
+ StoredDisplay *stored_display;
+ FindClosure closure;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (predicate != NULL, NULL);
+
+ closure.predicate = predicate;
+ closure.user_data = user_data;
+
+ stored_display = g_hash_table_find (store->priv->displays,
+ (GHRFunc) find_func,
+ &closure);
+
+ if (stored_display == NULL) {
+ return NULL;
+ }
+
+ return stored_display->display;
+}
+
+guint
+gdm_display_store_foreach_remove (GdmDisplayStore *store,
+ GdmDisplayStoreFunc func,
+ gpointer user_data)
+{
+ FindClosure closure;
+ guint ret;
+
+ g_return_val_if_fail (store != NULL, 0);
+ g_return_val_if_fail (func != NULL, 0);
+
+ closure.predicate = func;
+ closure.user_data = user_data;
+
+ ret = g_hash_table_foreach_remove (store->priv->displays,
+ (GHRFunc) find_func,
+ &closure);
+ return ret;
+}
+
+void
+gdm_display_store_add (GdmDisplayStore *store,
+ GdmDisplay *display)
+{
+ char *id;
+ StoredDisplay *stored_display;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (display != NULL);
+
+ gdm_display_get_id (display, &id, NULL);
+
+ g_debug ("GdmDisplayStore: Adding display %s to store", id);
+
+ stored_display = stored_display_new (store, display);
+ g_hash_table_insert (store->priv->displays,
+ id,
+ stored_display);
+
+ g_signal_emit (G_OBJECT (store),
+ signals[DISPLAY_ADDED],
+ 0,
+ id);
+}
+
+static void
+gdm_display_store_class_init (GdmDisplayStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdm_display_store_finalize;
+
+ signals [DISPLAY_ADDED] =
+ g_signal_new ("display-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmDisplayStoreClass, display_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [DISPLAY_REMOVED] =
+ g_signal_new ("display-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmDisplayStoreClass, display_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+}
+
+static void
+gdm_display_store_init (GdmDisplayStore *store)
+{
+
+ store->priv = gdm_display_store_get_instance_private (store);
+
+ store->priv->displays = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify)
+ stored_display_free);
+}
+
+static void
+gdm_display_store_finalize (GObject *object)
+{
+ GdmDisplayStore *store;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_DISPLAY_STORE (object));
+
+ store = GDM_DISPLAY_STORE (object);
+
+ g_return_if_fail (store->priv != NULL);
+
+ g_hash_table_destroy (store->priv->displays);
+
+ G_OBJECT_CLASS (gdm_display_store_parent_class)->finalize (object);
+}
+
+GdmDisplayStore *
+gdm_display_store_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_DISPLAY_STORE,
+ NULL);
+
+ return GDM_DISPLAY_STORE (object);
+}
diff --git a/daemon/gdm-display-store.h b/daemon/gdm-display-store.h
new file mode 100644
index 0000000..0aff8ee
--- /dev/null
+++ b/daemon/gdm-display-store.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_DISPLAY_STORE_H
+#define __GDM_DISPLAY_STORE_H
+
+#include <glib-object.h>
+#include "gdm-display.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DISPLAY_STORE (gdm_display_store_get_type ())
+#define GDM_DISPLAY_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DISPLAY_STORE, GdmDisplayStore))
+#define GDM_DISPLAY_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DISPLAY_STORE, GdmDisplayStoreClass))
+#define GDM_IS_DISPLAY_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DISPLAY_STORE))
+#define GDM_IS_DISPLAY_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DISPLAY_STORE))
+#define GDM_DISPLAY_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DISPLAY_STORE, GdmDisplayStoreClass))
+
+typedef struct GdmDisplayStorePrivate GdmDisplayStorePrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmDisplayStorePrivate *priv;
+} GdmDisplayStore;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* display_added) (GdmDisplayStore *display_store,
+ const char *id);
+ void (* display_removed) (GdmDisplayStore *display_store,
+ GdmDisplay *display);
+} GdmDisplayStoreClass;
+
+typedef enum
+{
+ GDM_DISPLAY_STORE_ERROR_GENERAL
+} GdmDisplayStoreError;
+
+#define GDM_DISPLAY_STORE_ERROR gdm_display_store_error_quark ()
+
+typedef gboolean (*GdmDisplayStoreFunc) (const char *id,
+ GdmDisplay *display,
+ gpointer user_data);
+
+GQuark gdm_display_store_error_quark (void);
+GType gdm_display_store_get_type (void);
+
+GdmDisplayStore * gdm_display_store_new (void);
+
+void gdm_display_store_add (GdmDisplayStore *store,
+ GdmDisplay *display);
+void gdm_display_store_clear (GdmDisplayStore *store);
+gboolean gdm_display_store_remove (GdmDisplayStore *store,
+ GdmDisplay *display);
+void gdm_display_store_foreach (GdmDisplayStore *store,
+ GdmDisplayStoreFunc func,
+ gpointer user_data);
+guint gdm_display_store_foreach_remove (GdmDisplayStore *store,
+ GdmDisplayStoreFunc func,
+ gpointer user_data);
+GdmDisplay * gdm_display_store_lookup (GdmDisplayStore *store,
+ const char *id);
+
+GdmDisplay * gdm_display_store_find (GdmDisplayStore *store,
+ GdmDisplayStoreFunc predicate,
+ gpointer user_data);
+
+
+G_END_DECLS
+
+#endif /* __GDM_DISPLAY_STORE_H */
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
new file mode 100644
index 0000000..46d5a77
--- /dev/null
+++ b/daemon/gdm-display.c
@@ -0,0 +1,1962 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include <xcb/xcb.h>
+#include <X11/Xlib.h>
+
+#include "gdm-common.h"
+#include "gdm-display.h"
+#include "gdm-display-glue.h"
+#include "gdm-display-access-file.h"
+#include "gdm-launch-environment.h"
+
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+#include "gdm-launch-environment.h"
+#include "gdm-dbus-util.h"
+
+#define GNOME_SESSION_SESSIONS_PATH DATADIR "/gnome-session/sessions"
+
+typedef struct _GdmDisplayPrivate
+{
+ GObject parent;
+
+ char *id;
+ char *seat_id;
+ char *session_id;
+ char *session_class;
+ char *session_type;
+
+ char *remote_hostname;
+ int x11_display_number;
+ char *x11_display_name;
+ int status;
+ time_t creation_time;
+
+ char *x11_cookie;
+ gsize x11_cookie_size;
+ GdmDisplayAccessFile *access_file;
+
+ guint finish_idle_id;
+
+ xcb_connection_t *xcb_connection;
+ int xcb_screen_number;
+
+ GDBusConnection *connection;
+ GdmDisplayAccessFile *user_access_file;
+
+ GdmDBusDisplay *display_skeleton;
+ GDBusObjectSkeleton *object_skeleton;
+
+ GDBusProxy *accountsservice_proxy;
+
+ /* this spawns and controls the greeter session */
+ GdmLaunchEnvironment *launch_environment;
+
+ guint is_local : 1;
+ guint is_initial : 1;
+ guint allow_timed_login : 1;
+ guint have_existing_user_accounts : 1;
+ guint doing_initial_setup : 1;
+ guint session_registered : 1;
+
+ GStrv supported_session_types;
+} GdmDisplayPrivate;
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_STATUS,
+ PROP_SEAT_ID,
+ PROP_SESSION_ID,
+ PROP_SESSION_CLASS,
+ PROP_SESSION_TYPE,
+ PROP_REMOTE_HOSTNAME,
+ PROP_X11_DISPLAY_NUMBER,
+ PROP_X11_DISPLAY_NAME,
+ PROP_X11_COOKIE,
+ PROP_X11_AUTHORITY_FILE,
+ PROP_IS_CONNECTED,
+ PROP_IS_LOCAL,
+ PROP_LAUNCH_ENVIRONMENT,
+ PROP_IS_INITIAL,
+ PROP_ALLOW_TIMED_LOGIN,
+ PROP_HAVE_EXISTING_USER_ACCOUNTS,
+ PROP_DOING_INITIAL_SETUP,
+ PROP_SESSION_REGISTERED,
+ PROP_SUPPORTED_SESSION_TYPES,
+};
+
+static void gdm_display_class_init (GdmDisplayClass *klass);
+static void gdm_display_init (GdmDisplay *self);
+static void gdm_display_finalize (GObject *object);
+static void queue_finish (GdmDisplay *self);
+static void _gdm_display_set_status (GdmDisplay *self,
+ int status);
+static gboolean wants_initial_setup (GdmDisplay *self);
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdmDisplay, gdm_display, G_TYPE_OBJECT)
+
+GQuark
+gdm_display_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_display_error");
+ }
+
+ return ret;
+}
+
+time_t
+gdm_display_get_creation_time (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), 0);
+
+ priv = gdm_display_get_instance_private (self);
+ return priv->creation_time;
+}
+
+int
+gdm_display_get_status (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), 0);
+
+ priv = gdm_display_get_instance_private (self);
+ return priv->status;
+}
+
+const char *
+gdm_display_get_session_id (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ return priv->session_id;
+}
+
+static GdmDisplayAccessFile *
+_create_access_file_for_user (GdmDisplay *self,
+ const char *username,
+ GError **error)
+{
+ GdmDisplayAccessFile *access_file;
+ GError *file_error;
+
+ access_file = gdm_display_access_file_new (username);
+
+ file_error = NULL;
+ if (!gdm_display_access_file_open (access_file, &file_error)) {
+ g_propagate_error (error, file_error);
+ return NULL;
+ }
+
+ return access_file;
+}
+
+gboolean
+gdm_display_create_authority (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ GdmDisplayAccessFile *access_file;
+ GError *error;
+ gboolean res;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ g_return_val_if_fail (priv->access_file == NULL, FALSE);
+
+ error = NULL;
+ access_file = _create_access_file_for_user (self, GDM_USERNAME, &error);
+
+ if (access_file == NULL) {
+ g_critical ("could not create display access file: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ g_free (priv->x11_cookie);
+ priv->x11_cookie = NULL;
+ res = gdm_display_access_file_add_display (access_file,
+ self,
+ &priv->x11_cookie,
+ &priv->x11_cookie_size,
+ &error);
+
+ if (! res) {
+
+ g_critical ("could not add display to access file: %s", error->message);
+ g_error_free (error);
+ gdm_display_access_file_close (access_file);
+ g_object_unref (access_file);
+ return FALSE;
+ }
+
+ priv->access_file = access_file;
+
+ return TRUE;
+}
+
+static void
+setup_xhost_auth (XHostAddress *host_entries)
+{
+ host_entries[0].family = FamilyServerInterpreted;
+ host_entries[0].address = "localuser\0root";
+ host_entries[0].length = sizeof ("localuser\0root");
+ host_entries[1].family = FamilyServerInterpreted;
+ host_entries[1].address = "localuser\0" GDM_USERNAME;
+ host_entries[1].length = sizeof ("localuser\0" GDM_USERNAME);
+ host_entries[2].family = FamilyServerInterpreted;
+ host_entries[2].address = "localuser\0gnome-initial-setup";
+ host_entries[2].length = sizeof ("localuser\0gnome-initial-setup");
+}
+
+gboolean
+gdm_display_add_user_authorization (GdmDisplay *self,
+ const char *username,
+ char **filename,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+ GdmDisplayAccessFile *access_file;
+ GError *access_file_error;
+ gboolean res;
+
+ int i;
+ XHostAddress host_entries[3];
+ xcb_void_cookie_t cookies[3];
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+
+ g_debug ("GdmDisplay: Adding authorization for user:%s on display %s", username, priv->x11_display_name);
+
+ if (priv->user_access_file != NULL) {
+ g_set_error (error,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "user access already assigned");
+ return FALSE;
+ }
+
+ g_debug ("GdmDisplay: Adding user authorization for %s", username);
+
+ access_file_error = NULL;
+ access_file = _create_access_file_for_user (self,
+ username,
+ &access_file_error);
+
+ if (access_file == NULL) {
+ g_propagate_error (error, access_file_error);
+ return FALSE;
+ }
+
+ res = gdm_display_access_file_add_display_with_cookie (access_file,
+ self,
+ priv->x11_cookie,
+ priv->x11_cookie_size,
+ &access_file_error);
+ if (! res) {
+ g_debug ("GdmDisplay: Unable to add user authorization for %s: %s",
+ username,
+ access_file_error->message);
+ g_propagate_error (error, access_file_error);
+ gdm_display_access_file_close (access_file);
+ g_object_unref (access_file);
+ return FALSE;
+ }
+
+ *filename = gdm_display_access_file_get_path (access_file);
+ priv->user_access_file = access_file;
+
+ g_debug ("GdmDisplay: Added user authorization for %s: %s", username, *filename);
+ /* Remove access for the programs run by greeter now that the
+ * user session is starting.
+ */
+ setup_xhost_auth (host_entries);
+
+ for (i = 0; i < G_N_ELEMENTS (host_entries); i++) {
+ cookies[i] = xcb_change_hosts_checked (priv->xcb_connection,
+ XCB_HOST_MODE_DELETE,
+ host_entries[i].family,
+ host_entries[i].length,
+ (uint8_t *) host_entries[i].address);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (cookies); i++) {
+ xcb_generic_error_t *xcb_error;
+
+ xcb_error = xcb_request_check (priv->xcb_connection, cookies[i]);
+
+ if (xcb_error != NULL) {
+ g_warning ("Failed to remove greeter program access to the display. Trying to proceed.");
+ free (xcb_error);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_remove_user_authorization (GdmDisplay *self,
+ const char *username,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+
+ g_debug ("GdmDisplay: Removing authorization for user:%s on display %s", username, priv->x11_display_name);
+
+ gdm_display_access_file_close (priv->user_access_file);
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_x11_cookie (GdmDisplay *self,
+ const char **x11_cookie,
+ gsize *x11_cookie_size,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+
+ if (x11_cookie != NULL) {
+ *x11_cookie = priv->x11_cookie;
+ }
+
+ if (x11_cookie_size != NULL) {
+ *x11_cookie_size = priv->x11_cookie_size;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_x11_authority_file (GdmDisplay *self,
+ char **filename,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (priv->access_file != NULL) {
+ *filename = gdm_display_access_file_get_path (priv->access_file);
+ } else {
+ *filename = NULL;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_remote_hostname (GdmDisplay *self,
+ char **hostname,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (hostname != NULL) {
+ *hostname = g_strdup (priv->remote_hostname);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_x11_display_number (GdmDisplay *self,
+ int *number,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (number != NULL) {
+ *number = priv->x11_display_number;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_seat_id (GdmDisplay *self,
+ char **seat_id,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (seat_id != NULL) {
+ *seat_id = g_strdup (priv->seat_id);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_is_initial (GdmDisplay *self,
+ gboolean *is_initial,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (is_initial != NULL) {
+ *is_initial = priv->is_initial;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+finish_idle (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ priv->finish_idle_id = 0;
+ /* finish may end up finalizing object */
+ gdm_display_finish (self);
+ return FALSE;
+}
+
+static void
+queue_finish (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ if (priv->finish_idle_id == 0) {
+ priv->finish_idle_id = g_idle_add ((GSourceFunc)finish_idle, self);
+ }
+}
+
+static void
+_gdm_display_set_status (GdmDisplay *self,
+ int status)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ if (status != priv->status) {
+ priv->status = status;
+ g_object_notify (G_OBJECT (self), "status");
+ }
+}
+
+static gboolean
+gdm_display_real_prepare (GdmDisplay *self)
+{
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ g_debug ("GdmDisplay: prepare display");
+
+ _gdm_display_set_status (self, GDM_DISPLAY_PREPARED);
+
+ return TRUE;
+}
+
+static gboolean
+look_for_existing_users_sync (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GVariant) call_result = NULL;
+ g_autoptr(GVariant) user_list = NULL;
+
+ priv = gdm_display_get_instance_private (self);
+ priv->accountsservice_proxy = g_dbus_proxy_new_sync (priv->connection,
+ 0, NULL,
+ "org.freedesktop.Accounts",
+ "/org/freedesktop/Accounts",
+ "org.freedesktop.Accounts",
+ NULL,
+ &error);
+
+ if (!priv->accountsservice_proxy) {
+ g_critical ("Failed to contact accountsservice: %s", error->message);
+ return FALSE;
+ }
+
+ call_result = g_dbus_proxy_call_sync (priv->accountsservice_proxy,
+ "ListCachedUsers",
+ NULL,
+ 0,
+ -1,
+ NULL,
+ &error);
+
+ if (!call_result) {
+ g_critical ("Failed to list cached users: %s", error->message);
+ return FALSE;
+ }
+
+ g_variant_get (call_result, "(@ao)", &user_list);
+ priv->have_existing_user_accounts = g_variant_n_children (user_list) > 0;
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_prepare (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ gboolean ret;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+
+ g_debug ("GdmDisplay: Preparing display: %s", priv->id);
+
+ /* FIXME: we should probably do this in a more global place,
+ * asynchronously
+ */
+ if (!look_for_existing_users_sync (self)) {
+ exit (EXIT_FAILURE);
+ }
+
+ priv->doing_initial_setup = wants_initial_setup (self);
+
+ g_object_ref (self);
+ ret = GDM_DISPLAY_GET_CLASS (self)->prepare (self);
+ g_object_unref (self);
+
+ return ret;
+}
+
+gboolean
+gdm_display_manage (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ gboolean res;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+
+ g_debug ("GdmDisplay: Managing display: %s", priv->id);
+
+ /* If not explicitly prepared, do it now */
+ if (priv->status == GDM_DISPLAY_UNMANAGED) {
+ res = gdm_display_prepare (self);
+ if (! res) {
+ return FALSE;
+ }
+ }
+
+ if (g_strcmp0 (priv->session_class, "greeter") == 0) {
+ if (GDM_DISPLAY_GET_CLASS (self)->manage != NULL) {
+ GDM_DISPLAY_GET_CLASS (self)->manage (self);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_finish (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (priv->finish_idle_id != 0) {
+ g_source_remove (priv->finish_idle_id);
+ priv->finish_idle_id = 0;
+ }
+
+ _gdm_display_set_status (self, GDM_DISPLAY_FINISHED);
+
+ g_debug ("GdmDisplay: finish display");
+
+ return TRUE;
+}
+
+static void
+gdm_display_disconnect (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ /* These 3 bits are reserved/unused by the X protocol */
+ guint32 unused_bits = 0b11100000000000000000000000000000;
+ XID highest_client, client;
+ guint32 client_increment;
+ const xcb_setup_t *setup;
+
+ priv = gdm_display_get_instance_private (self);
+
+ if (priv->xcb_connection == NULL) {
+ return;
+ }
+
+ setup = xcb_get_setup (priv->xcb_connection);
+
+ /* resource_id_mask is the bits given to each client for
+ * addressing resources */
+ highest_client = (XID) ~unused_bits & ~setup->resource_id_mask;
+ client_increment = setup->resource_id_mask + 1;
+
+ /* Kill every client but ourselves, then close our own connection
+ */
+ for (client = 0;
+ client <= highest_client;
+ client += client_increment) {
+
+ if (client != setup->resource_id_base)
+ xcb_kill_client (priv->xcb_connection, client);
+ }
+
+ xcb_flush (priv->xcb_connection);
+
+ g_clear_pointer (&priv->xcb_connection, xcb_disconnect);
+}
+
+gboolean
+gdm_display_unmanage (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+
+ gdm_display_disconnect (self);
+
+ if (priv->user_access_file != NULL) {
+ gdm_display_access_file_close (priv->user_access_file);
+ g_object_unref (priv->user_access_file);
+ priv->user_access_file = NULL;
+ }
+
+ if (priv->access_file != NULL) {
+ gdm_display_access_file_close (priv->access_file);
+ g_object_unref (priv->access_file);
+ priv->access_file = NULL;
+ }
+
+ if (!priv->session_registered) {
+ g_warning ("GdmDisplay: Session never registered, failing");
+ _gdm_display_set_status (self, GDM_DISPLAY_FAILED);
+ } else {
+ _gdm_display_set_status (self, GDM_DISPLAY_UNMANAGED);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_id (GdmDisplay *self,
+ char **id,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (id != NULL) {
+ *id = g_strdup (priv->id);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_get_x11_display_name (GdmDisplay *self,
+ char **x11_display,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (x11_display != NULL) {
+ *x11_display = g_strdup (priv->x11_display_name);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gdm_display_is_local (GdmDisplay *self,
+ gboolean *local,
+ GError **error)
+{
+ GdmDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
+
+ priv = gdm_display_get_instance_private (self);
+ if (local != NULL) {
+ *local = priv->is_local;
+ }
+
+ return TRUE;
+}
+
+static void
+_gdm_display_set_id (GdmDisplay *self,
+ const char *id)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: id: %s", id);
+ g_free (priv->id);
+ priv->id = g_strdup (id);
+}
+
+static void
+_gdm_display_set_seat_id (GdmDisplay *self,
+ const char *seat_id)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: seat id: %s", seat_id);
+ g_free (priv->seat_id);
+ priv->seat_id = g_strdup (seat_id);
+}
+
+static void
+_gdm_display_set_session_id (GdmDisplay *self,
+ const char *session_id)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: session id: %s", session_id);
+ g_free (priv->session_id);
+ priv->session_id = g_strdup (session_id);
+}
+
+static void
+_gdm_display_set_session_class (GdmDisplay *self,
+ const char *session_class)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: session class: %s", session_class);
+ g_free (priv->session_class);
+ priv->session_class = g_strdup (session_class);
+}
+
+static void
+_gdm_display_set_session_type (GdmDisplay *self,
+ const char *session_type)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: session type: %s", session_type);
+ g_free (priv->session_type);
+ priv->session_type = g_strdup (session_type);
+}
+
+static void
+_gdm_display_set_remote_hostname (GdmDisplay *self,
+ const char *hostname)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_free (priv->remote_hostname);
+ priv->remote_hostname = g_strdup (hostname);
+}
+
+static void
+_gdm_display_set_x11_display_number (GdmDisplay *self,
+ int num)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ priv->x11_display_number = num;
+}
+
+static void
+_gdm_display_set_x11_display_name (GdmDisplay *self,
+ const char *x11_display)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_free (priv->x11_display_name);
+ priv->x11_display_name = g_strdup (x11_display);
+}
+
+static void
+_gdm_display_set_x11_cookie (GdmDisplay *self,
+ const char *x11_cookie)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_free (priv->x11_cookie);
+ priv->x11_cookie = g_strdup (x11_cookie);
+}
+
+static void
+_gdm_display_set_is_local (GdmDisplay *self,
+ gboolean is_local)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: local: %s", is_local? "yes" : "no");
+ priv->is_local = is_local;
+}
+
+static void
+_gdm_display_set_session_registered (GdmDisplay *self,
+ gboolean registered)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: session registered: %s", registered? "yes" : "no");
+ priv->session_registered = registered;
+}
+
+static void
+_gdm_display_set_launch_environment (GdmDisplay *self,
+ GdmLaunchEnvironment *launch_environment)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+
+ g_clear_object (&priv->launch_environment);
+
+ priv->launch_environment = g_object_ref (launch_environment);
+}
+
+static void
+_gdm_display_set_is_initial (GdmDisplay *self,
+ gboolean initial)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: initial: %s", initial? "yes" : "no");
+ priv->is_initial = initial;
+}
+
+static void
+_gdm_display_set_allow_timed_login (GdmDisplay *self,
+ gboolean allow_timed_login)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: allow timed login: %s", allow_timed_login? "yes" : "no");
+ priv->allow_timed_login = allow_timed_login;
+}
+
+static void
+_gdm_display_set_supported_session_types (GdmDisplay *self,
+ const char * const *supported_session_types)
+
+{
+ GdmDisplayPrivate *priv;
+ g_autofree char *supported_session_types_string = NULL;
+
+ if (supported_session_types != NULL)
+ supported_session_types_string = g_strjoinv (":", (GStrv) supported_session_types);
+
+ priv = gdm_display_get_instance_private (self);
+ g_debug ("GdmDisplay: supported session types: %s", supported_session_types_string);
+ g_strfreev (priv->supported_session_types);
+ priv->supported_session_types = g_strdupv ((GStrv) supported_session_types);
+}
+
+static void
+gdm_display_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmDisplay *self;
+
+ self = GDM_DISPLAY (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ _gdm_display_set_id (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS:
+ _gdm_display_set_status (self, g_value_get_int (value));
+ break;
+ case PROP_SEAT_ID:
+ _gdm_display_set_seat_id (self, g_value_get_string (value));
+ break;
+ case PROP_SESSION_ID:
+ _gdm_display_set_session_id (self, g_value_get_string (value));
+ break;
+ case PROP_SESSION_CLASS:
+ _gdm_display_set_session_class (self, g_value_get_string (value));
+ break;
+ case PROP_SESSION_TYPE:
+ _gdm_display_set_session_type (self, g_value_get_string (value));
+ break;
+ case PROP_REMOTE_HOSTNAME:
+ _gdm_display_set_remote_hostname (self, g_value_get_string (value));
+ break;
+ case PROP_X11_DISPLAY_NUMBER:
+ _gdm_display_set_x11_display_number (self, g_value_get_int (value));
+ break;
+ case PROP_X11_DISPLAY_NAME:
+ _gdm_display_set_x11_display_name (self, g_value_get_string (value));
+ break;
+ case PROP_X11_COOKIE:
+ _gdm_display_set_x11_cookie (self, g_value_get_string (value));
+ break;
+ case PROP_IS_LOCAL:
+ _gdm_display_set_is_local (self, g_value_get_boolean (value));
+ break;
+ case PROP_ALLOW_TIMED_LOGIN:
+ _gdm_display_set_allow_timed_login (self, g_value_get_boolean (value));
+ break;
+ case PROP_LAUNCH_ENVIRONMENT:
+ _gdm_display_set_launch_environment (self, g_value_get_object (value));
+ break;
+ case PROP_IS_INITIAL:
+ _gdm_display_set_is_initial (self, g_value_get_boolean (value));
+ break;
+ case PROP_SESSION_REGISTERED:
+ _gdm_display_set_session_registered (self, g_value_get_boolean (value));
+ break;
+ case PROP_SUPPORTED_SESSION_TYPES:
+ _gdm_display_set_supported_session_types (self, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_display_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmDisplay *self;
+ GdmDisplayPrivate *priv;
+
+ self = GDM_DISPLAY (object);
+ priv = gdm_display_get_instance_private (self);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_string (value, priv->id);
+ break;
+ case PROP_STATUS:
+ g_value_set_int (value, priv->status);
+ break;
+ case PROP_SEAT_ID:
+ g_value_set_string (value, priv->seat_id);
+ break;
+ case PROP_SESSION_ID:
+ g_value_set_string (value, priv->session_id);
+ break;
+ case PROP_SESSION_CLASS:
+ g_value_set_string (value, priv->session_class);
+ break;
+ case PROP_SESSION_TYPE:
+ g_value_set_string (value, priv->session_type);
+ break;
+ case PROP_REMOTE_HOSTNAME:
+ g_value_set_string (value, priv->remote_hostname);
+ break;
+ case PROP_X11_DISPLAY_NUMBER:
+ g_value_set_int (value, priv->x11_display_number);
+ break;
+ case PROP_X11_DISPLAY_NAME:
+ g_value_set_string (value, priv->x11_display_name);
+ break;
+ case PROP_X11_COOKIE:
+ g_value_set_string (value, priv->x11_cookie);
+ break;
+ case PROP_X11_AUTHORITY_FILE:
+ g_value_take_string (value,
+ priv->access_file?
+ gdm_display_access_file_get_path (priv->access_file) : NULL);
+ break;
+ case PROP_IS_LOCAL:
+ g_value_set_boolean (value, priv->is_local);
+ break;
+ case PROP_IS_CONNECTED:
+ g_value_set_boolean (value, priv->xcb_connection != NULL);
+ break;
+ case PROP_LAUNCH_ENVIRONMENT:
+ g_value_set_object (value, priv->launch_environment);
+ break;
+ case PROP_IS_INITIAL:
+ g_value_set_boolean (value, priv->is_initial);
+ break;
+ case PROP_HAVE_EXISTING_USER_ACCOUNTS:
+ g_value_set_boolean (value, priv->have_existing_user_accounts);
+ break;
+ case PROP_DOING_INITIAL_SETUP:
+ g_value_set_boolean (value, priv->doing_initial_setup);
+ break;
+ case PROP_SESSION_REGISTERED:
+ g_value_set_boolean (value, priv->session_registered);
+ break;
+ case PROP_ALLOW_TIMED_LOGIN:
+ g_value_set_boolean (value, priv->allow_timed_login);
+ break;
+ case PROP_SUPPORTED_SESSION_TYPES:
+ g_value_set_boxed (value, priv->supported_session_types);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+handle_get_id (GdmDBusDisplay *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmDisplay *self)
+{
+ char *id;
+
+ gdm_display_get_id (self, &id, NULL);
+
+ gdm_dbus_display_complete_get_id (skeleton, invocation, id);
+
+ g_free (id);
+ return TRUE;
+}
+
+static gboolean
+handle_get_remote_hostname (GdmDBusDisplay *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmDisplay *self)
+{
+ char *hostname;
+
+ gdm_display_get_remote_hostname (self, &hostname, NULL);
+
+ gdm_dbus_display_complete_get_remote_hostname (skeleton,
+ invocation,
+ hostname ? hostname : "");
+
+ g_free (hostname);
+ return TRUE;
+}
+
+static gboolean
+handle_get_seat_id (GdmDBusDisplay *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmDisplay *self)
+{
+ char *seat_id;
+
+ seat_id = NULL;
+ gdm_display_get_seat_id (self, &seat_id, NULL);
+
+ if (seat_id == NULL) {
+ seat_id = g_strdup ("");
+ }
+ gdm_dbus_display_complete_get_seat_id (skeleton, invocation, seat_id);
+
+ g_free (seat_id);
+ return TRUE;
+}
+
+static gboolean
+handle_get_x11_display_name (GdmDBusDisplay *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmDisplay *self)
+{
+ char *name;
+
+ gdm_display_get_x11_display_name (self, &name, NULL);
+
+ gdm_dbus_display_complete_get_x11_display_name (skeleton, invocation, name);
+
+ g_free (name);
+ return TRUE;
+}
+
+static gboolean
+handle_is_local (GdmDBusDisplay *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmDisplay *self)
+{
+ gboolean is_local;
+
+ gdm_display_is_local (self, &is_local, NULL);
+
+ gdm_dbus_display_complete_is_local (skeleton, invocation, is_local);
+
+ return TRUE;
+}
+
+static gboolean
+handle_is_initial (GdmDBusDisplay *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmDisplay *self)
+{
+ gboolean is_initial = FALSE;
+
+ gdm_display_is_initial (self, &is_initial, NULL);
+
+ gdm_dbus_display_complete_is_initial (skeleton, invocation, is_initial);
+
+ return TRUE;
+}
+
+static gboolean
+register_display (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ GError *error = NULL;
+
+ priv = gdm_display_get_instance_private (self);
+
+ error = NULL;
+ priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (priv->connection == NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ priv->object_skeleton = g_dbus_object_skeleton_new (priv->id);
+ priv->display_skeleton = GDM_DBUS_DISPLAY (gdm_dbus_display_skeleton_new ());
+
+ g_signal_connect_object (priv->display_skeleton, "handle-get-id",
+ G_CALLBACK (handle_get_id), self, 0);
+ g_signal_connect_object (priv->display_skeleton, "handle-get-remote-hostname",
+ G_CALLBACK (handle_get_remote_hostname), self, 0);
+ g_signal_connect_object (priv->display_skeleton, "handle-get-seat-id",
+ G_CALLBACK (handle_get_seat_id), self, 0);
+ g_signal_connect_object (priv->display_skeleton, "handle-get-x11-display-name",
+ G_CALLBACK (handle_get_x11_display_name), self, 0);
+ g_signal_connect_object (priv->display_skeleton, "handle-is-local",
+ G_CALLBACK (handle_is_local), self, 0);
+ g_signal_connect_object (priv->display_skeleton, "handle-is-initial",
+ G_CALLBACK (handle_is_initial), self, 0);
+
+ g_dbus_object_skeleton_add_interface (priv->object_skeleton,
+ G_DBUS_INTERFACE_SKELETON (priv->display_skeleton));
+
+ return TRUE;
+}
+
+/*
+ dbus-send --system --print-reply --dest=org.gnome.DisplayManager /org/gnome/DisplayManager/Displays/1 org.freedesktop.DBus.Introspectable.Introspect
+*/
+
+static GObject *
+gdm_display_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmDisplay *self;
+ GdmDisplayPrivate *priv;
+ gboolean res;
+
+ self = GDM_DISPLAY (G_OBJECT_CLASS (gdm_display_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ priv = gdm_display_get_instance_private (self);
+
+ g_free (priv->id);
+ priv->id = g_strdup_printf ("/org/gnome/DisplayManager/Displays/%lu",
+ (gulong) self);
+
+ res = register_display (self);
+ if (! res) {
+ g_warning ("Unable to register display with system bus");
+ }
+
+ return G_OBJECT (self);
+}
+
+static void
+gdm_display_dispose (GObject *object)
+{
+ GdmDisplay *self;
+ GdmDisplayPrivate *priv;
+
+ self = GDM_DISPLAY (object);
+ priv = gdm_display_get_instance_private (self);
+
+ g_debug ("GdmDisplay: Disposing display");
+
+ if (priv->finish_idle_id != 0) {
+ g_source_remove (priv->finish_idle_id);
+ priv->finish_idle_id = 0;
+ }
+ g_clear_object (&priv->launch_environment);
+ g_clear_pointer (&priv->supported_session_types, g_strfreev);
+
+ g_warn_if_fail (priv->status != GDM_DISPLAY_MANAGED);
+ g_warn_if_fail (priv->user_access_file == NULL);
+ g_warn_if_fail (priv->access_file == NULL);
+
+ G_OBJECT_CLASS (gdm_display_parent_class)->dispose (object);
+}
+
+static void
+gdm_display_class_init (GdmDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_display_get_property;
+ object_class->set_property = gdm_display_set_property;
+ object_class->constructor = gdm_display_constructor;
+ object_class->dispose = gdm_display_dispose;
+ object_class->finalize = gdm_display_finalize;
+
+ klass->prepare = gdm_display_real_prepare;
+
+ g_object_class_install_property (object_class,
+ PROP_ID,
+ g_param_spec_string ("id",
+ "id",
+ "id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_REMOTE_HOSTNAME,
+ g_param_spec_string ("remote-hostname",
+ "remote-hostname",
+ "remote-hostname",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_NUMBER,
+ g_param_spec_int ("x11-display-number",
+ "x11 display number",
+ "x11 display number",
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_NAME,
+ g_param_spec_string ("x11-display-name",
+ "x11-display-name",
+ "x11-display-name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SEAT_ID,
+ g_param_spec_string ("seat-id",
+ "seat id",
+ "seat id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SESSION_ID,
+ g_param_spec_string ("session-id",
+ "session id",
+ "session id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SESSION_CLASS,
+ g_param_spec_string ("session-class",
+ NULL,
+ NULL,
+ "greeter",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SESSION_TYPE,
+ g_param_spec_string ("session-type",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_IS_INITIAL,
+ g_param_spec_boolean ("is-initial",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_ALLOW_TIMED_LOGIN,
+ g_param_spec_boolean ("allow-timed-login",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_COOKIE,
+ g_param_spec_string ("x11-cookie",
+ "cookie",
+ "cookie",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_AUTHORITY_FILE,
+ g_param_spec_string ("x11-authority-file",
+ "authority file",
+ "authority file",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_IS_LOCAL,
+ g_param_spec_boolean ("is-local",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_IS_CONNECTED,
+ g_param_spec_boolean ("is-connected",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_HAVE_EXISTING_USER_ACCOUNTS,
+ g_param_spec_boolean ("have-existing-user-accounts",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DOING_INITIAL_SETUP,
+ g_param_spec_boolean ("doing-initial-setup",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SESSION_REGISTERED,
+ g_param_spec_boolean ("session-registered",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_LAUNCH_ENVIRONMENT,
+ g_param_spec_object ("launch-environment",
+ NULL,
+ NULL,
+ GDM_TYPE_LAUNCH_ENVIRONMENT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_STATUS,
+ g_param_spec_int ("status",
+ "status",
+ "status",
+ -1,
+ G_MAXINT,
+ GDM_DISPLAY_UNMANAGED,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SUPPORTED_SESSION_TYPES,
+ g_param_spec_boxed ("supported-session-types",
+ "supported session types",
+ "supported session types",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdm_display_init (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+
+ priv->creation_time = time (NULL);
+}
+
+static void
+gdm_display_finalize (GObject *object)
+{
+ GdmDisplay *self;
+ GdmDisplayPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_DISPLAY (object));
+
+ self = GDM_DISPLAY (object);
+ priv = gdm_display_get_instance_private (self);
+
+ g_return_if_fail (priv != NULL);
+
+ g_debug ("GdmDisplay: Finalizing display: %s", priv->id);
+ g_free (priv->id);
+ g_free (priv->seat_id);
+ g_free (priv->session_class);
+ g_free (priv->remote_hostname);
+ g_free (priv->x11_display_name);
+ g_free (priv->x11_cookie);
+
+ g_clear_object (&priv->display_skeleton);
+ g_clear_object (&priv->object_skeleton);
+ g_clear_object (&priv->connection);
+ g_clear_object (&priv->accountsservice_proxy);
+
+ if (priv->access_file != NULL) {
+ g_object_unref (priv->access_file);
+ }
+
+ if (priv->user_access_file != NULL) {
+ g_object_unref (priv->user_access_file);
+ }
+
+ G_OBJECT_CLASS (gdm_display_parent_class)->finalize (object);
+}
+
+GDBusObjectSkeleton *
+gdm_display_get_object_skeleton (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+ return priv->object_skeleton;
+}
+
+static void
+on_launch_environment_session_opened (GdmLaunchEnvironment *launch_environment,
+ GdmDisplay *self)
+{
+ char *session_id;
+
+ g_debug ("GdmDisplay: Greeter session opened");
+ session_id = gdm_launch_environment_get_session_id (launch_environment);
+ _gdm_display_set_session_id (self, session_id);
+ g_free (session_id);
+}
+
+static void
+on_launch_environment_session_started (GdmLaunchEnvironment *launch_environment,
+ GdmDisplay *self)
+{
+ g_debug ("GdmDisplay: Greeter started");
+}
+
+static void
+self_destruct (GdmDisplay *self)
+{
+ g_object_ref (self);
+
+ g_debug ("GdmDisplay: initiating display self-destruct");
+ gdm_display_unmanage (self);
+
+ if (gdm_display_get_status (self) != GDM_DISPLAY_FINISHED) {
+ queue_finish (self);
+ }
+ g_object_unref (self);
+}
+
+static void
+on_launch_environment_session_stopped (GdmLaunchEnvironment *launch_environment,
+ GdmDisplay *self)
+{
+ g_debug ("GdmDisplay: Greeter stopped");
+ self_destruct (self);
+}
+
+static void
+on_launch_environment_session_exited (GdmLaunchEnvironment *launch_environment,
+ int code,
+ GdmDisplay *self)
+{
+ g_debug ("GdmDisplay: Greeter exited: %d", code);
+ self_destruct (self);
+}
+
+static void
+on_launch_environment_session_died (GdmLaunchEnvironment *launch_environment,
+ int signal,
+ GdmDisplay *self)
+{
+ g_debug ("GdmDisplay: Greeter died: %d", signal);
+ self_destruct (self);
+}
+
+static gboolean
+can_create_environment (const char *session_id)
+{
+ char *path;
+ gboolean session_exists;
+
+ path = g_strdup_printf (GNOME_SESSION_SESSIONS_PATH "/%s.session", session_id);
+ session_exists = g_file_test (path, G_FILE_TEST_EXISTS);
+
+ g_free (path);
+
+ return session_exists;
+}
+
+#define ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT GDM_RUN_DIR "/gdm.ran-initial-setup"
+
+static gboolean
+already_done_initial_setup_on_this_boot (void)
+{
+ if (g_file_test (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT, G_FILE_TEST_EXISTS))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+kernel_cmdline_initial_setup_argument (const gchar *contents,
+ gchar **initial_setup_argument,
+ GError **error)
+{
+ GRegex *regex = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *match_group = NULL;
+
+ g_return_val_if_fail (initial_setup_argument != NULL, FALSE);
+
+ regex = g_regex_new ("\\bgnome.initial-setup=([^\\s]*)\\b", 0, 0, error);
+
+ if (!regex)
+ return FALSE;
+
+ if (!g_regex_match (regex, contents, 0, &match_info)) {
+ g_free (match_info);
+ g_free (regex);
+
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Could not match gnome.initial-setup= in kernel cmdline");
+
+ return FALSE;
+ }
+
+ match_group = g_match_info_fetch (match_info, 1);
+
+ if (!match_group) {
+ g_free (match_info);
+ g_free (regex);
+
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Could not match gnome.initial-setup= in kernel cmdline");
+
+ return FALSE;
+ }
+
+ *initial_setup_argument = match_group;
+
+ g_free (match_info);
+ g_free (regex);
+
+ return TRUE;
+}
+
+/* Function returns true if we had a force state in the kernel
+ * cmdline */
+static gboolean
+kernel_cmdline_initial_setup_force_state (gboolean *force_state)
+{
+ GError *error = NULL;
+ gchar *contents = NULL;
+ gchar *setup_argument = NULL;
+
+ g_return_val_if_fail (force_state != NULL, FALSE);
+
+ if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, &error)) {
+ g_debug ("GdmDisplay: Could not check kernel parameters, not forcing initial setup: %s",
+ error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ g_debug ("GdmDisplay: Checking kernel command buffer %s", contents);
+
+ if (!kernel_cmdline_initial_setup_argument (contents, &setup_argument, &error)) {
+ g_debug ("GdmDisplay: Failed to read kernel commandline: %s", error->message);
+ g_clear_pointer (&contents, g_free);
+ return FALSE;
+ }
+
+ g_clear_pointer (&contents, g_free);
+
+ /* Poor-man's check for truthy or falsey values */
+ *force_state = setup_argument[0] == '1';
+
+ g_free (setup_argument);
+ return TRUE;
+}
+
+static gboolean
+wants_initial_setup (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ gboolean enabled = FALSE;
+ gboolean forced = FALSE;
+
+ priv = gdm_display_get_instance_private (self);
+
+ if (already_done_initial_setup_on_this_boot ()) {
+ return FALSE;
+ }
+
+ if (kernel_cmdline_initial_setup_force_state (&forced)) {
+ if (forced) {
+ g_debug ("GdmDisplay: Forcing gnome-initial-setup");
+ return TRUE;
+ }
+
+ g_debug ("GdmDisplay: Forcing no gnome-initial-setup");
+ return FALSE;
+ }
+
+ /* don't run initial-setup on remote displays
+ */
+ if (!priv->is_local) {
+ return FALSE;
+ }
+
+ /* don't run if the system has existing users */
+ if (priv->have_existing_user_accounts) {
+ return FALSE;
+ }
+
+ /* don't run if initial-setup is unavailable */
+ if (!can_create_environment ("gnome-initial-setup")) {
+ return FALSE;
+ }
+
+ if (!gdm_settings_direct_get_boolean (GDM_KEY_INITIAL_SETUP_ENABLE, &enabled)) {
+ return FALSE;
+ }
+
+ return enabled;
+}
+
+void
+gdm_display_start_greeter_session (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ GdmSession *session;
+ char *display_name;
+ char *seat_id;
+ char *hostname;
+ char *auth_file = NULL;
+
+ priv = gdm_display_get_instance_private (self);
+ g_return_if_fail (g_strcmp0 (priv->session_class, "greeter") == 0);
+
+ g_debug ("GdmDisplay: Running greeter");
+
+ display_name = NULL;
+ seat_id = NULL;
+ hostname = NULL;
+
+ g_object_get (self,
+ "x11-display-name", &display_name,
+ "seat-id", &seat_id,
+ "remote-hostname", &hostname,
+ NULL);
+ if (priv->access_file != NULL) {
+ auth_file = gdm_display_access_file_get_path (priv->access_file);
+ }
+
+ g_debug ("GdmDisplay: Creating greeter for %s %s", display_name, hostname);
+
+ g_signal_connect_object (priv->launch_environment,
+ "opened",
+ G_CALLBACK (on_launch_environment_session_opened),
+ self, 0);
+ g_signal_connect_object (priv->launch_environment,
+ "started",
+ G_CALLBACK (on_launch_environment_session_started),
+ self, 0);
+ g_signal_connect_object (priv->launch_environment,
+ "stopped",
+ G_CALLBACK (on_launch_environment_session_stopped),
+ self, 0);
+ g_signal_connect_object (priv->launch_environment,
+ "exited",
+ G_CALLBACK (on_launch_environment_session_exited),
+ self, 0);
+ g_signal_connect_object (priv->launch_environment,
+ "died",
+ G_CALLBACK (on_launch_environment_session_died),
+ self, 0);
+
+ if (auth_file != NULL) {
+ g_object_set (priv->launch_environment,
+ "x11-authority-file", auth_file,
+ NULL);
+ }
+
+ gdm_launch_environment_start (priv->launch_environment);
+
+ session = gdm_launch_environment_get_session (priv->launch_environment);
+ g_object_set (G_OBJECT (session),
+ "display-is-initial", priv->is_initial,
+ "supported-session-types", priv->supported_session_types,
+ NULL);
+
+ g_free (display_name);
+ g_free (seat_id);
+ g_free (hostname);
+ g_free (auth_file);
+}
+
+void
+gdm_display_stop_greeter_session (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+
+ priv = gdm_display_get_instance_private (self);
+
+ if (priv->launch_environment != NULL) {
+
+ g_signal_handlers_disconnect_by_func (priv->launch_environment,
+ G_CALLBACK (on_launch_environment_session_opened),
+ self);
+ g_signal_handlers_disconnect_by_func (priv->launch_environment,
+ G_CALLBACK (on_launch_environment_session_started),
+ self);
+ g_signal_handlers_disconnect_by_func (priv->launch_environment,
+ G_CALLBACK (on_launch_environment_session_stopped),
+ self);
+ g_signal_handlers_disconnect_by_func (priv->launch_environment,
+ G_CALLBACK (on_launch_environment_session_exited),
+ self);
+ g_signal_handlers_disconnect_by_func (priv->launch_environment,
+ G_CALLBACK (on_launch_environment_session_died),
+ self);
+ gdm_launch_environment_stop (priv->launch_environment);
+ g_clear_object (&priv->launch_environment);
+ }
+}
+
+static xcb_window_t
+get_root_window (xcb_connection_t *connection,
+ int screen_number)
+{
+ xcb_screen_t *screen = NULL;
+ xcb_screen_iterator_t iter;
+
+ iter = xcb_setup_roots_iterator (xcb_get_setup (connection));
+ while (iter.rem) {
+ if (screen_number == 0)
+ screen = iter.data;
+ screen_number--;
+ xcb_screen_next (&iter);
+ }
+
+ if (screen != NULL) {
+ return screen->root;
+ }
+
+ return XCB_WINDOW_NONE;
+}
+
+static void
+gdm_display_set_windowpath (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ /* setting WINDOWPATH for clients */
+ xcb_intern_atom_cookie_t atom_cookie;
+ xcb_intern_atom_reply_t *atom_reply = NULL;
+ xcb_get_property_cookie_t get_property_cookie;
+ xcb_get_property_reply_t *get_property_reply = NULL;
+ xcb_window_t root_window = XCB_WINDOW_NONE;
+ const char *windowpath;
+ char *newwindowpath;
+ uint32_t num;
+ char nums[10];
+ int numn;
+
+ priv = gdm_display_get_instance_private (self);
+
+ atom_cookie = xcb_intern_atom (priv->xcb_connection, 0, strlen("XFree86_VT"), "XFree86_VT");
+ atom_reply = xcb_intern_atom_reply (priv->xcb_connection, atom_cookie, NULL);
+
+ if (atom_reply == NULL) {
+ g_debug ("no XFree86_VT atom\n");
+ goto out;
+ }
+
+ root_window = get_root_window (priv->xcb_connection,
+ priv->xcb_screen_number);
+
+ if (root_window == XCB_WINDOW_NONE) {
+ g_debug ("couldn't find root window\n");
+ goto out;
+ }
+
+ get_property_cookie = xcb_get_property (priv->xcb_connection,
+ FALSE,
+ root_window,
+ atom_reply->atom,
+ XCB_ATOM_INTEGER,
+ 0,
+ 1);
+
+ get_property_reply = xcb_get_property_reply (priv->xcb_connection, get_property_cookie, NULL);
+
+ if (get_property_reply == NULL) {
+ g_debug ("no XFree86_VT property\n");
+ goto out;
+ }
+
+ num = ((uint32_t *) xcb_get_property_value (get_property_reply))[0];
+
+ windowpath = getenv ("WINDOWPATH");
+ numn = snprintf (nums, sizeof (nums), "%u", num);
+ if (!windowpath) {
+ newwindowpath = malloc (numn + 1);
+ sprintf (newwindowpath, "%s", nums);
+ } else {
+ newwindowpath = malloc (strlen (windowpath) + 1 + numn + 1);
+ sprintf (newwindowpath, "%s:%s", windowpath, nums);
+ }
+
+ g_setenv ("WINDOWPATH", newwindowpath, TRUE);
+out:
+ g_clear_pointer (&atom_reply, free);
+ g_clear_pointer (&get_property_reply, free);
+}
+
+gboolean
+gdm_display_connect (GdmDisplay *self)
+{
+ GdmDisplayPrivate *priv;
+ xcb_auth_info_t *auth_info = NULL;
+ gboolean ret;
+
+ priv = gdm_display_get_instance_private (self);
+ ret = FALSE;
+
+ g_debug ("GdmDisplay: Server is ready - opening display %s", priv->x11_display_name);
+
+ /* Get access to the display independent of current hostname */
+ if (priv->x11_cookie != NULL) {
+ auth_info = g_alloca (sizeof (xcb_auth_info_t));
+
+ auth_info->namelen = strlen ("MIT-MAGIC-COOKIE-1");
+ auth_info->name = "MIT-MAGIC-COOKIE-1";
+ auth_info->datalen = priv->x11_cookie_size;
+ auth_info->data = priv->x11_cookie;
+
+ }
+
+ priv->xcb_connection = xcb_connect_to_display_with_auth_info (priv->x11_display_name,
+ auth_info,
+ &priv->xcb_screen_number);
+
+ if (xcb_connection_has_error (priv->xcb_connection)) {
+ g_clear_pointer (&priv->xcb_connection, xcb_disconnect);
+ g_warning ("Unable to connect to display %s", priv->x11_display_name);
+ ret = FALSE;
+ } else if (priv->is_local) {
+ XHostAddress host_entries[3];
+ xcb_void_cookie_t cookies[3];
+ int i;
+
+ g_debug ("GdmDisplay: Connected to display %s", priv->x11_display_name);
+ ret = TRUE;
+
+ /* Give programs access to the display independent of current hostname
+ */
+ setup_xhost_auth (host_entries);
+
+ for (i = 0; i < G_N_ELEMENTS (host_entries); i++) {
+ cookies[i] = xcb_change_hosts_checked (priv->xcb_connection,
+ XCB_HOST_MODE_INSERT,
+ host_entries[i].family,
+ host_entries[i].length,
+ (uint8_t *) host_entries[i].address);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (cookies); i++) {
+ xcb_generic_error_t *xcb_error;
+
+ xcb_error = xcb_request_check (priv->xcb_connection, cookies[i]);
+
+ if (xcb_error != NULL) {
+ g_debug ("Failed to give system user '%s' access to the display. Trying to proceed.", host_entries[i].address + sizeof ("localuser"));
+ free (xcb_error);
+ } else {
+ g_debug ("Gave system user '%s' access to the display.", host_entries[i].address + sizeof ("localuser"));
+ }
+ }
+
+ gdm_display_set_windowpath (self);
+ } else {
+ g_debug ("GdmDisplay: Connected to display %s", priv->x11_display_name);
+ ret = TRUE;
+ }
+
+ if (ret == TRUE) {
+ g_object_notify (G_OBJECT (self), "is-connected");
+ }
+
+ return ret;
+}
+
diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h
new file mode 100644
index 0000000..1575faa
--- /dev/null
+++ b/daemon/gdm-display.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_DISPLAY_H
+#define __GDM_DISPLAY_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_DISPLAY (gdm_display_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GdmDisplay, gdm_display, GDM, DISPLAY, GObject)
+
+typedef enum {
+ GDM_DISPLAY_UNMANAGED = 0,
+ GDM_DISPLAY_PREPARED,
+ GDM_DISPLAY_MANAGED,
+ GDM_DISPLAY_WAITING_TO_FINISH,
+ GDM_DISPLAY_FINISHED,
+ GDM_DISPLAY_FAILED,
+} GdmDisplayStatus;
+
+struct _GdmDisplayClass
+{
+ GObjectClass parent_class;
+
+ /* methods */
+ gboolean (*prepare) (GdmDisplay *display);
+ void (*manage) (GdmDisplay *self);
+};
+
+typedef enum
+{
+ GDM_DISPLAY_ERROR_GENERAL,
+ GDM_DISPLAY_ERROR_GETTING_USER_INFO,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+} GdmDisplayError;
+
+#define GDM_DISPLAY_ERROR gdm_display_error_quark ()
+
+GQuark gdm_display_error_quark (void);
+
+int gdm_display_get_status (GdmDisplay *display);
+time_t gdm_display_get_creation_time (GdmDisplay *display);
+const char * gdm_display_get_session_id (GdmDisplay *display);
+gboolean gdm_display_create_authority (GdmDisplay *display);
+gboolean gdm_display_prepare (GdmDisplay *display);
+gboolean gdm_display_manage (GdmDisplay *display);
+gboolean gdm_display_finish (GdmDisplay *display);
+gboolean gdm_display_unmanage (GdmDisplay *display);
+
+GDBusObjectSkeleton *gdm_display_get_object_skeleton (GdmDisplay *display);
+
+/* exported to bus */
+gboolean gdm_display_get_id (GdmDisplay *display,
+ char **id,
+ GError **error);
+gboolean gdm_display_get_remote_hostname (GdmDisplay *display,
+ char **hostname,
+ GError **error);
+gboolean gdm_display_get_x11_display_number (GdmDisplay *display,
+ int *number,
+ GError **error);
+gboolean gdm_display_get_x11_display_name (GdmDisplay *display,
+ char **x11_display,
+ GError **error);
+gboolean gdm_display_get_seat_id (GdmDisplay *display,
+ char **seat_id,
+ GError **error);
+gboolean gdm_display_is_local (GdmDisplay *display,
+ gboolean *local,
+ GError **error);
+gboolean gdm_display_is_initial (GdmDisplay *display,
+ gboolean *initial,
+ GError **error);
+
+gboolean gdm_display_get_x11_cookie (GdmDisplay *display,
+ const char **x11_cookie,
+ gsize *x11_cookie_size,
+ GError **error);
+gboolean gdm_display_get_x11_authority_file (GdmDisplay *display,
+ char **filename,
+ GError **error);
+gboolean gdm_display_add_user_authorization (GdmDisplay *display,
+ const char *username,
+ char **filename,
+ GError **error);
+gboolean gdm_display_remove_user_authorization (GdmDisplay *display,
+ const char *username,
+ GError **error);
+void gdm_display_start_greeter_session (GdmDisplay *display);
+void gdm_display_stop_greeter_session (GdmDisplay *display);
+
+gboolean gdm_display_connect (GdmDisplay *self);
+
+G_END_DECLS
+
+#endif /* __GDM_DISPLAY_H */
diff --git a/daemon/gdm-display.xml b/daemon/gdm-display.xml
new file mode 100644
index 0000000..3e9b5d8
--- /dev/null
+++ b/daemon/gdm-display.xml
@@ -0,0 +1,23 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.gnome.DisplayManager.Display">
+ <method name="GetId">
+ <arg name="id" direction="out" type="o"/>
+ </method>
+ <method name="GetX11DisplayName">
+ <arg name="name" direction="out" type="s"/>
+ </method>
+ <method name="GetSeatId">
+ <arg name="filename" direction="out" type="s"/>
+ </method>
+ <method name="IsInitial">
+ <arg name="initial" direction="out" type="b"/>
+ </method>
+ <method name="GetRemoteHostname">
+ <arg name="hostname" direction="out" type="s"/>
+ </method>
+ <method name="IsLocal">
+ <arg name="local" direction="out" type="b"/>
+ </method>
+ </interface>
+</node>
diff --git a/daemon/gdm-launch-environment.c b/daemon/gdm-launch-environment.c
new file mode 100644
index 0000000..932c3e8
--- /dev/null
+++ b/daemon/gdm-launch-environment.c
@@ -0,0 +1,1047 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <gio/gio.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 <signal.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include "gdm-common.h"
+
+#include "gdm-session-enum-types.h"
+#include "gdm-launch-environment.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+#define INITIAL_SETUP_USERNAME "gnome-initial-setup"
+#define GDM_SESSION_MODE "gdm"
+#define INITIAL_SETUP_SESSION_MODE "initial-setup"
+
+extern char **environ;
+
+struct GdmLaunchEnvironmentPrivate
+{
+ GdmSession *session;
+ char *command;
+ GPid pid;
+
+ GdmSessionVerificationMode verification_mode;
+
+ char *user_name;
+ char *runtime_dir;
+
+ char *session_id;
+ char *session_type;
+ char *session_mode;
+ char *x11_display_name;
+ char *x11_display_seat_id;
+ char *x11_display_device;
+ char *x11_display_hostname;
+ char *x11_authority_file;
+ gboolean x11_display_is_local;
+};
+
+enum {
+ PROP_0,
+ PROP_VERIFICATION_MODE,
+ PROP_SESSION_TYPE,
+ PROP_SESSION_MODE,
+ PROP_X11_DISPLAY_NAME,
+ PROP_X11_DISPLAY_SEAT_ID,
+ PROP_X11_DISPLAY_DEVICE,
+ PROP_X11_DISPLAY_HOSTNAME,
+ PROP_X11_AUTHORITY_FILE,
+ PROP_X11_DISPLAY_IS_LOCAL,
+ PROP_USER_NAME,
+ PROP_RUNTIME_DIR,
+ PROP_COMMAND,
+};
+
+enum {
+ OPENED,
+ STARTED,
+ STOPPED,
+ EXITED,
+ DIED,
+ HOSTNAME_SELECTED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_launch_environment_class_init (GdmLaunchEnvironmentClass *klass);
+static void gdm_launch_environment_init (GdmLaunchEnvironment *launch_environment);
+static void gdm_launch_environment_finalize (GObject *object);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdmLaunchEnvironment, gdm_launch_environment, G_TYPE_OBJECT)
+
+static char *
+get_var_cb (const char *var,
+ gpointer user_data)
+{
+ const char *value = g_hash_table_lookup (user_data, var);
+ return g_strdup (value);
+}
+
+static void
+load_env_func (const char *var,
+ const char *value,
+ gpointer user_data)
+{
+ GHashTable *environment = user_data;
+ g_hash_table_replace (environment, g_strdup (var), g_strdup (value));
+}
+
+static GHashTable *
+build_launch_environment (GdmLaunchEnvironment *launch_environment,
+ gboolean start_session)
+{
+ GHashTable *hash;
+ struct passwd *pwent;
+ static const char *const optional_environment[] = {
+ "GI_TYPELIB_PATH",
+ "LANG",
+ "LANGUAGE",
+ "LC_ADDRESS",
+ "LC_ALL",
+ "LC_COLLATE",
+ "LC_CTYPE",
+ "LC_IDENTIFICATION",
+ "LC_MEASUREMENT",
+ "LC_MESSAGES",
+ "LC_MONETARY",
+ "LC_NAME",
+ "LC_NUMERIC",
+ "LC_PAPER",
+ "LC_TELEPHONE",
+ "LC_TIME",
+ "LD_LIBRARY_PATH",
+ "PATH",
+ "WINDOWPATH",
+ "XCURSOR_PATH",
+ "XDG_CONFIG_DIRS",
+ NULL
+ };
+ char *system_data_dirs;
+ g_auto (GStrv) supported_session_types = NULL;
+ int i;
+
+ /* 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);
+
+ for (i = 0; optional_environment[i] != NULL; i++) {
+ if (g_getenv (optional_environment[i]) == NULL) {
+ continue;
+ }
+
+ g_hash_table_insert (hash,
+ g_strdup (optional_environment[i]),
+ g_strdup (g_getenv (optional_environment[i])));
+ }
+
+ if (launch_environment->priv->x11_authority_file != NULL)
+ g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (launch_environment->priv->x11_authority_file));
+
+ if (launch_environment->priv->session_mode != NULL) {
+ g_hash_table_insert (hash, g_strdup ("GNOME_SHELL_SESSION_MODE"), g_strdup (launch_environment->priv->session_mode));
+
+ if (strcmp (launch_environment->priv->session_mode, INITIAL_SETUP_SESSION_MODE) != 0) {
+ /* gvfs is needed for fetching remote avatars in the initial setup. Disable it otherwise. */
+ g_hash_table_insert (hash, g_strdup ("GVFS_DISABLE_FUSE"), g_strdup ("1"));
+ g_hash_table_insert (hash, g_strdup ("GIO_USE_VFS"), g_strdup ("local"));
+ g_hash_table_insert (hash, g_strdup ("GVFS_REMOTE_VOLUME_MONITOR_IGNORE"), g_strdup ("1"));
+
+ /* The locked down dconf profile should not be used for the initial setup session.
+ * This allows overridden values from the user profile to take effect.
+ */
+ g_hash_table_insert (hash, g_strdup ("DCONF_PROFILE"), g_strdup ("gdm"));
+ }
+ }
+
+ g_hash_table_insert (hash, g_strdup ("LOGNAME"), g_strdup (launch_environment->priv->user_name));
+ g_hash_table_insert (hash, g_strdup ("USER"), g_strdup (launch_environment->priv->user_name));
+ g_hash_table_insert (hash, g_strdup ("USERNAME"), g_strdup (launch_environment->priv->user_name));
+
+ g_hash_table_insert (hash, g_strdup ("GDM_VERSION"), g_strdup (VERSION));
+ g_hash_table_remove (hash, "MAIL");
+
+ g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/"));
+ g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup ("/"));
+ g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup ("/bin/sh"));
+
+ gdm_get_pwent_for_name (launch_environment->priv->user_name, &pwent);
+ if (pwent != NULL) {
+ if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') {
+ g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir));
+ g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup (pwent->pw_dir));
+ }
+
+ g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell));
+ }
+
+ if (start_session && launch_environment->priv->x11_display_seat_id != NULL) {
+ char *seat_id;
+
+ seat_id = launch_environment->priv->x11_display_seat_id;
+
+ g_hash_table_insert (hash, g_strdup ("GDM_SEAT_ID"), g_strdup (seat_id));
+ }
+
+ g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true"));
+
+ /* Now populate XDG_DATA_DIRS from env.d if we're running initial setup; this allows
+ * e.g. Flatpak apps to be recognized by gnome-shell.
+ */
+ if (g_strcmp0 (launch_environment->priv->session_mode, INITIAL_SETUP_SESSION_MODE) == 0)
+ gdm_load_env_d (load_env_func, get_var_cb, hash);
+
+ /* Prepend our own XDG_DATA_DIRS value */
+ system_data_dirs = g_strdup (g_hash_table_lookup (hash, "XDG_DATA_DIRS"));
+ if (!system_data_dirs)
+ system_data_dirs = g_strjoinv (":", (char **) g_get_system_data_dirs ());
+
+ g_hash_table_insert (hash,
+ g_strdup ("XDG_DATA_DIRS"),
+ g_strdup_printf ("%s:%s:%s",
+ DATADIR "/gdm/greeter",
+ DATADIR,
+ system_data_dirs));
+ g_free (system_data_dirs);
+
+ g_object_get (launch_environment->priv->session,
+ "supported-session-types",
+ &supported_session_types,
+ NULL);
+ g_hash_table_insert (hash,
+ g_strdup ("GDM_SUPPORTED_SESSION_TYPES"),
+ g_strjoinv (":", supported_session_types));
+
+ return hash;
+}
+
+static void
+on_session_setup_complete (GdmSession *session,
+ const char *service_name,
+ GdmLaunchEnvironment *launch_environment)
+{
+ GHashTable *hash;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ hash = build_launch_environment (launch_environment, TRUE);
+
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ gdm_session_set_environment_variable (launch_environment->priv->session, key, value);
+ }
+ g_hash_table_destroy (hash);
+}
+
+static void
+on_session_opened (GdmSession *session,
+ const char *service_name,
+ const char *session_id,
+ GdmLaunchEnvironment *launch_environment)
+{
+ launch_environment->priv->session_id = g_strdup (session_id);
+
+ g_signal_emit (G_OBJECT (launch_environment), signals [OPENED], 0);
+ gdm_session_start_session (launch_environment->priv->session, service_name);
+}
+
+static void
+on_session_started (GdmSession *session,
+ const char *service_name,
+ int pid,
+ GdmLaunchEnvironment *launch_environment)
+{
+ launch_environment->priv->pid = pid;
+ g_signal_emit (G_OBJECT (launch_environment), signals [STARTED], 0);
+}
+
+static void
+on_session_exited (GdmSession *session,
+ int exit_code,
+ GdmLaunchEnvironment *launch_environment)
+{
+ gdm_session_stop_conversation (launch_environment->priv->session, "gdm-launch-environment");
+
+ g_signal_emit (G_OBJECT (launch_environment), signals [EXITED], 0, exit_code);
+}
+
+static void
+on_session_died (GdmSession *session,
+ int signal_number,
+ GdmLaunchEnvironment *launch_environment)
+{
+ gdm_session_stop_conversation (launch_environment->priv->session, "gdm-launch-environment");
+
+ g_signal_emit (G_OBJECT (launch_environment), signals [DIED], 0, signal_number);
+}
+
+static void
+on_hostname_selected (GdmSession *session,
+ const char *hostname,
+ GdmLaunchEnvironment *launch_environment)
+{
+ g_debug ("GdmSession: hostname selected: %s", hostname);
+ g_signal_emit (launch_environment, signals [HOSTNAME_SELECTED], 0, hostname);
+}
+
+static void
+on_conversation_started (GdmSession *session,
+ const char *service_name,
+ GdmLaunchEnvironment *launch_environment)
+{
+ char *log_path;
+ char *log_file;
+
+ if (launch_environment->priv->x11_display_name != NULL)
+ log_file = g_strdup_printf ("%s-greeter.log", launch_environment->priv->x11_display_name);
+ else
+ log_file = g_strdup ("greeter.log");
+
+ log_path = g_build_filename (LOGDIR, log_file, NULL);
+ g_free (log_file);
+
+ gdm_session_setup_for_program (launch_environment->priv->session,
+ "gdm-launch-environment",
+ launch_environment->priv->user_name,
+ log_path);
+ g_free (log_path);
+}
+
+static void
+on_conversation_stopped (GdmSession *session,
+ const char *service_name,
+ GdmLaunchEnvironment *launch_environment)
+{
+ GdmSession *conversation_session;
+
+ conversation_session = launch_environment->priv->session;
+ launch_environment->priv->session = NULL;
+
+ g_debug ("GdmLaunchEnvironment: conversation stopped");
+
+ if (launch_environment->priv->pid > 1) {
+ gdm_signal_pid (-launch_environment->priv->pid, SIGTERM);
+ g_signal_emit (G_OBJECT (launch_environment), signals [STOPPED], 0);
+ }
+
+ if (conversation_session != NULL) {
+ gdm_session_close (conversation_session);
+ g_object_unref (conversation_session);
+ }
+}
+
+static gboolean
+ensure_directory_with_uid_gid (const char *path,
+ uid_t uid,
+ gid_t gid,
+ GError **error)
+{
+ if (mkdir (path, 0700) == -1 && errno != EEXIST) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to create directory %s: %s", path,
+ g_strerror (errno));
+ return FALSE;
+ }
+ if (chown (path, uid, gid) == -1) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to set owner of %s: %s", path,
+ g_strerror (errno));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * gdm_launch_environment_start:
+ * @disp: Pointer to a GdmDisplay structure
+ *
+ * Starts a local X launch_environment. Handles retries and fatal errors properly.
+ */
+gboolean
+gdm_launch_environment_start (GdmLaunchEnvironment *launch_environment)
+{
+ gboolean res = FALSE;
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ struct passwd *passwd_entry;
+ uid_t uid;
+ gid_t gid;
+
+ g_debug ("GdmLaunchEnvironment: Starting...");
+
+ if (!gdm_get_pwent_for_name (launch_environment->priv->user_name, &passwd_entry)) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Unknown user %s",
+ launch_environment->priv->user_name);
+ goto out;
+ }
+
+ uid = passwd_entry->pw_uid;
+ gid = passwd_entry->pw_gid;
+
+ g_debug ("GdmLaunchEnvironment: Setting up run time dir %s",
+ launch_environment->priv->runtime_dir);
+ if (!ensure_directory_with_uid_gid (launch_environment->priv->runtime_dir, uid, gid, error)) {
+ goto out;
+ }
+
+ /* Create the home directory too */
+ if (!ensure_directory_with_uid_gid (passwd_entry->pw_dir, uid, gid, error))
+ goto out;
+
+ launch_environment->priv->session = gdm_session_new (launch_environment->priv->verification_mode,
+ uid,
+ launch_environment->priv->x11_display_name,
+ launch_environment->priv->x11_display_hostname,
+ launch_environment->priv->x11_display_device,
+ launch_environment->priv->x11_display_seat_id,
+ launch_environment->priv->x11_authority_file,
+ launch_environment->priv->x11_display_is_local,
+ NULL);
+
+ g_signal_connect_object (launch_environment->priv->session,
+ "conversation-started",
+ G_CALLBACK (on_conversation_started),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "conversation-stopped",
+ G_CALLBACK (on_conversation_stopped),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "setup-complete",
+ G_CALLBACK (on_session_setup_complete),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "session-opened",
+ G_CALLBACK (on_session_opened),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "session-started",
+ G_CALLBACK (on_session_started),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "session-exited",
+ G_CALLBACK (on_session_exited),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "session-died",
+ G_CALLBACK (on_session_died),
+ launch_environment,
+ 0);
+ g_signal_connect_object (launch_environment->priv->session,
+ "hostname-selected",
+ G_CALLBACK (on_hostname_selected),
+ launch_environment,
+ 0);
+
+ gdm_session_start_conversation (launch_environment->priv->session, "gdm-launch-environment");
+ gdm_session_select_program (launch_environment->priv->session, launch_environment->priv->command);
+
+ if (launch_environment->priv->session_type != NULL) {
+ g_object_set (G_OBJECT (launch_environment->priv->session),
+ "session-type",
+ launch_environment->priv->session_type,
+ NULL);
+ }
+
+ res = TRUE;
+ out:
+ if (local_error) {
+ g_critical ("GdmLaunchEnvironment: %s", local_error->message);
+ g_clear_error (&local_error);
+ }
+ return res;
+}
+
+gboolean
+gdm_launch_environment_stop (GdmLaunchEnvironment *launch_environment)
+{
+ if (launch_environment->priv->pid > 1) {
+ gdm_signal_pid (-launch_environment->priv->pid, SIGTERM);
+ }
+
+ if (launch_environment->priv->session != NULL) {
+ gdm_session_close (launch_environment->priv->session);
+
+ g_clear_object (&launch_environment->priv->session);
+ }
+
+ g_signal_emit (G_OBJECT (launch_environment), signals [STOPPED], 0);
+
+ return TRUE;
+}
+
+GdmSession *
+gdm_launch_environment_get_session (GdmLaunchEnvironment *launch_environment)
+{
+ return launch_environment->priv->session;
+}
+
+char *
+gdm_launch_environment_get_session_id (GdmLaunchEnvironment *launch_environment)
+{
+ return g_strdup (launch_environment->priv->session_id);
+}
+
+static void
+_gdm_launch_environment_set_verification_mode (GdmLaunchEnvironment *launch_environment,
+ GdmSessionVerificationMode verification_mode)
+{
+ launch_environment->priv->verification_mode = verification_mode;
+}
+
+static void
+_gdm_launch_environment_set_session_type (GdmLaunchEnvironment *launch_environment,
+ const char *session_type)
+{
+ g_free (launch_environment->priv->session_type);
+ launch_environment->priv->session_type = g_strdup (session_type);
+}
+
+static void
+_gdm_launch_environment_set_session_mode (GdmLaunchEnvironment *launch_environment,
+ const char *session_mode)
+{
+ g_free (launch_environment->priv->session_mode);
+ launch_environment->priv->session_mode = g_strdup (session_mode);
+}
+
+static void
+_gdm_launch_environment_set_x11_display_name (GdmLaunchEnvironment *launch_environment,
+ const char *name)
+{
+ g_free (launch_environment->priv->x11_display_name);
+ launch_environment->priv->x11_display_name = g_strdup (name);
+}
+
+static void
+_gdm_launch_environment_set_x11_display_seat_id (GdmLaunchEnvironment *launch_environment,
+ const char *sid)
+{
+ g_free (launch_environment->priv->x11_display_seat_id);
+ launch_environment->priv->x11_display_seat_id = g_strdup (sid);
+}
+
+static void
+_gdm_launch_environment_set_x11_display_hostname (GdmLaunchEnvironment *launch_environment,
+ const char *name)
+{
+ g_free (launch_environment->priv->x11_display_hostname);
+ launch_environment->priv->x11_display_hostname = g_strdup (name);
+}
+
+static void
+_gdm_launch_environment_set_x11_display_device (GdmLaunchEnvironment *launch_environment,
+ const char *name)
+{
+ g_free (launch_environment->priv->x11_display_device);
+ launch_environment->priv->x11_display_device = g_strdup (name);
+}
+
+static void
+_gdm_launch_environment_set_x11_display_is_local (GdmLaunchEnvironment *launch_environment,
+ gboolean is_local)
+{
+ launch_environment->priv->x11_display_is_local = is_local;
+}
+
+static void
+_gdm_launch_environment_set_x11_authority_file (GdmLaunchEnvironment *launch_environment,
+ const char *file)
+{
+ g_free (launch_environment->priv->x11_authority_file);
+ launch_environment->priv->x11_authority_file = g_strdup (file);
+}
+
+static void
+_gdm_launch_environment_set_user_name (GdmLaunchEnvironment *launch_environment,
+ const char *name)
+{
+ g_free (launch_environment->priv->user_name);
+ launch_environment->priv->user_name = g_strdup (name);
+}
+
+static void
+_gdm_launch_environment_set_runtime_dir (GdmLaunchEnvironment *launch_environment,
+ const char *dir)
+{
+ g_free (launch_environment->priv->runtime_dir);
+ launch_environment->priv->runtime_dir = g_strdup (dir);
+}
+
+static void
+_gdm_launch_environment_set_command (GdmLaunchEnvironment *launch_environment,
+ const char *name)
+{
+ g_free (launch_environment->priv->command);
+ launch_environment->priv->command = g_strdup (name);
+}
+
+static void
+gdm_launch_environment_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmLaunchEnvironment *self;
+
+ self = GDM_LAUNCH_ENVIRONMENT (object);
+
+ switch (prop_id) {
+ case PROP_VERIFICATION_MODE:
+ _gdm_launch_environment_set_verification_mode (self, g_value_get_enum (value));
+ break;
+ case PROP_SESSION_TYPE:
+ _gdm_launch_environment_set_session_type (self, g_value_get_string (value));
+ break;
+ case PROP_SESSION_MODE:
+ _gdm_launch_environment_set_session_mode (self, g_value_get_string (value));
+ break;
+ case PROP_X11_DISPLAY_NAME:
+ _gdm_launch_environment_set_x11_display_name (self, g_value_get_string (value));
+ break;
+ case PROP_X11_DISPLAY_SEAT_ID:
+ _gdm_launch_environment_set_x11_display_seat_id (self, g_value_get_string (value));
+ break;
+ case PROP_X11_DISPLAY_HOSTNAME:
+ _gdm_launch_environment_set_x11_display_hostname (self, g_value_get_string (value));
+ break;
+ case PROP_X11_DISPLAY_DEVICE:
+ _gdm_launch_environment_set_x11_display_device (self, g_value_get_string (value));
+ break;
+ case PROP_X11_DISPLAY_IS_LOCAL:
+ _gdm_launch_environment_set_x11_display_is_local (self, g_value_get_boolean (value));
+ break;
+ case PROP_X11_AUTHORITY_FILE:
+ _gdm_launch_environment_set_x11_authority_file (self, g_value_get_string (value));
+ break;
+ case PROP_USER_NAME:
+ _gdm_launch_environment_set_user_name (self, g_value_get_string (value));
+ break;
+ case PROP_RUNTIME_DIR:
+ _gdm_launch_environment_set_runtime_dir (self, g_value_get_string (value));
+ break;
+ case PROP_COMMAND:
+ _gdm_launch_environment_set_command (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_launch_environment_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmLaunchEnvironment *self;
+
+ self = GDM_LAUNCH_ENVIRONMENT (object);
+
+ switch (prop_id) {
+ case PROP_VERIFICATION_MODE:
+ g_value_set_enum (value, self->priv->verification_mode);
+ break;
+ case PROP_SESSION_TYPE:
+ g_value_set_string (value, self->priv->session_type);
+ break;
+ case PROP_SESSION_MODE:
+ g_value_set_string (value, self->priv->session_mode);
+ break;
+ case PROP_X11_DISPLAY_NAME:
+ g_value_set_string (value, self->priv->x11_display_name);
+ break;
+ case PROP_X11_DISPLAY_SEAT_ID:
+ g_value_set_string (value, self->priv->x11_display_seat_id);
+ break;
+ case PROP_X11_DISPLAY_HOSTNAME:
+ g_value_set_string (value, self->priv->x11_display_hostname);
+ break;
+ case PROP_X11_DISPLAY_DEVICE:
+ g_value_set_string (value, self->priv->x11_display_device);
+ break;
+ case PROP_X11_DISPLAY_IS_LOCAL:
+ g_value_set_boolean (value, self->priv->x11_display_is_local);
+ break;
+ case PROP_X11_AUTHORITY_FILE:
+ g_value_set_string (value, self->priv->x11_authority_file);
+ break;
+ case PROP_USER_NAME:
+ g_value_set_string (value, self->priv->user_name);
+ break;
+ case PROP_RUNTIME_DIR:
+ g_value_set_string (value, self->priv->runtime_dir);
+ break;
+ case PROP_COMMAND:
+ g_value_set_string (value, self->priv->command);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_launch_environment_class_init (GdmLaunchEnvironmentClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_launch_environment_get_property;
+ object_class->set_property = gdm_launch_environment_set_property;
+ object_class->finalize = gdm_launch_environment_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_VERIFICATION_MODE,
+ g_param_spec_enum ("verification-mode",
+ "verification mode",
+ "verification mode",
+ GDM_TYPE_SESSION_VERIFICATION_MODE,
+ GDM_SESSION_VERIFICATION_MODE_LOGIN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SESSION_TYPE,
+ g_param_spec_string ("session-type",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_SESSION_MODE,
+ g_param_spec_string ("session-mode",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_NAME,
+ g_param_spec_string ("x11-display-name",
+ "name",
+ "name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_SEAT_ID,
+ g_param_spec_string ("x11-display-seat-id",
+ "seat id",
+ "seat id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_HOSTNAME,
+ g_param_spec_string ("x11-display-hostname",
+ "hostname",
+ "hostname",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_DEVICE,
+ g_param_spec_string ("x11-display-device",
+ "device",
+ "device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_DISPLAY_IS_LOCAL,
+ g_param_spec_boolean ("x11-display-is-local",
+ "is local",
+ "is local",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_X11_AUTHORITY_FILE,
+ g_param_spec_string ("x11-authority-file",
+ "authority file",
+ "authority file",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_USER_NAME,
+ g_param_spec_string ("user-name",
+ "user name",
+ "user name",
+ GDM_USERNAME,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_RUNTIME_DIR,
+ g_param_spec_string ("runtime-dir",
+ "runtime dir",
+ "runtime dir",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_COMMAND,
+ g_param_spec_string ("command",
+ "command",
+ "command",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ signals [OPENED] =
+ g_signal_new ("opened",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmLaunchEnvironmentClass, opened),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [STARTED] =
+ g_signal_new ("started",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmLaunchEnvironmentClass, 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 (GdmLaunchEnvironmentClass, 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 (GdmLaunchEnvironmentClass, 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 (GdmLaunchEnvironmentClass, died),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ signals [HOSTNAME_SELECTED] =
+ g_signal_new ("hostname-selected",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmLaunchEnvironmentClass, hostname_selected),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+}
+
+static void
+gdm_launch_environment_init (GdmLaunchEnvironment *launch_environment)
+{
+
+ launch_environment->priv = gdm_launch_environment_get_instance_private (launch_environment);
+
+ launch_environment->priv->command = NULL;
+ launch_environment->priv->session = NULL;
+}
+
+static void
+gdm_launch_environment_finalize (GObject *object)
+{
+ GdmLaunchEnvironment *launch_environment;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_LAUNCH_ENVIRONMENT (object));
+
+ launch_environment = GDM_LAUNCH_ENVIRONMENT (object);
+
+ g_return_if_fail (launch_environment->priv != NULL);
+
+ gdm_launch_environment_stop (launch_environment);
+
+ if (launch_environment->priv->session) {
+ g_object_unref (launch_environment->priv->session);
+ }
+
+ g_free (launch_environment->priv->command);
+ g_free (launch_environment->priv->user_name);
+ g_free (launch_environment->priv->runtime_dir);
+ g_free (launch_environment->priv->x11_display_name);
+ g_free (launch_environment->priv->x11_display_seat_id);
+ g_free (launch_environment->priv->x11_display_device);
+ g_free (launch_environment->priv->x11_display_hostname);
+ g_free (launch_environment->priv->x11_authority_file);
+ g_free (launch_environment->priv->session_id);
+ g_free (launch_environment->priv->session_type);
+
+ G_OBJECT_CLASS (gdm_launch_environment_parent_class)->finalize (object);
+}
+
+static GdmLaunchEnvironment *
+create_gnome_session_environment (const char *session_id,
+ const char *user_name,
+ const char *display_name,
+ const char *seat_id,
+ const char *session_type,
+ const char *session_mode,
+ const char *display_hostname,
+ gboolean display_is_local)
+{
+ gboolean debug = FALSE;
+ char *command;
+ GdmLaunchEnvironment *launch_environment;
+ char **argv;
+ GPtrArray *args;
+
+ gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
+
+ args = g_ptr_array_new ();
+ g_ptr_array_add (args, "gnome-session");
+
+ g_ptr_array_add (args, "--autostart");
+ g_ptr_array_add (args, DATADIR "/gdm/greeter/autostart");
+
+ if (debug) {
+ g_ptr_array_add (args, "--debug");
+ }
+
+ if (session_id != NULL) {
+ g_ptr_array_add (args, " --session");
+ g_ptr_array_add (args, (char *) session_id);
+ }
+
+ g_ptr_array_add (args, NULL);
+
+ argv = (char **) g_ptr_array_free (args, FALSE);
+ command = g_strjoinv (" ", argv);
+ g_free (argv);
+
+ launch_environment = g_object_new (GDM_TYPE_LAUNCH_ENVIRONMENT,
+ "command", command,
+ "user-name", user_name,
+ "session-type", session_type,
+ "session-mode", session_mode,
+ "x11-display-name", display_name,
+ "x11-display-seat-id", seat_id,
+ "x11-display-hostname", display_hostname,
+ "x11-display-is-local", display_is_local,
+ "runtime-dir", GDM_SCREENSHOT_DIR,
+ NULL);
+
+ g_free (command);
+ return launch_environment;
+}
+
+GdmLaunchEnvironment *
+gdm_create_greeter_launch_environment (const char *display_name,
+ const char *seat_id,
+ const char *session_type,
+ const char *display_hostname,
+ gboolean display_is_local)
+{
+ const char *session_name = NULL;
+
+ return create_gnome_session_environment (session_name,
+ GDM_USERNAME,
+ display_name,
+ seat_id,
+ session_type,
+ GDM_SESSION_MODE,
+ display_hostname,
+ display_is_local);
+}
+
+GdmLaunchEnvironment *
+gdm_create_initial_setup_launch_environment (const char *display_name,
+ const char *seat_id,
+ const char *session_type,
+ const char *display_hostname,
+ gboolean display_is_local)
+{
+ return create_gnome_session_environment ("gnome-initial-setup",
+ INITIAL_SETUP_USERNAME,
+ display_name,
+ seat_id,
+ session_type,
+ INITIAL_SETUP_SESSION_MODE,
+ display_hostname,
+ display_is_local);
+}
+
+GdmLaunchEnvironment *
+gdm_create_chooser_launch_environment (const char *display_name,
+ const char *seat_id,
+ const char *display_hostname)
+
+{
+ GdmLaunchEnvironment *launch_environment;
+
+ launch_environment = g_object_new (GDM_TYPE_LAUNCH_ENVIRONMENT,
+ "command", LIBEXECDIR "/gdm-simple-chooser",
+ "verification-mode", GDM_SESSION_VERIFICATION_MODE_CHOOSER,
+ "user-name", GDM_USERNAME,
+ "x11-display-name", display_name,
+ "x11-display-seat-id", seat_id,
+ "x11-display-hostname", display_hostname,
+ "x11-display-is-local", FALSE,
+ "runtime-dir", GDM_SCREENSHOT_DIR,
+ NULL);
+
+ return launch_environment;
+}
+
diff --git a/daemon/gdm-launch-environment.h b/daemon/gdm-launch-environment.h
new file mode 100644
index 0000000..00ac2a0
--- /dev/null
+++ b/daemon/gdm-launch-environment.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_LAUNCH_ENVIRONMENT_H
+#define __GDM_LAUNCH_ENVIRONMENT_H
+
+#include <glib-object.h>
+#include "gdm-session.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_LAUNCH_ENVIRONMENT (gdm_launch_environment_get_type ())
+#define GDM_LAUNCH_ENVIRONMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_LAUNCH_ENVIRONMENT, GdmLaunchEnvironment))
+#define GDM_LAUNCH_ENVIRONMENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_LAUNCH_ENVIRONMENT, GdmLaunchEnvironmentClass))
+#define GDM_IS_LAUNCH_ENVIRONMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_LAUNCH_ENVIRONMENT))
+#define GDM_IS_LAUNCH_ENVIRONMENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_LAUNCH_ENVIRONMENT))
+#define GDM_LAUNCH_ENVIRONMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_LAUNCH_ENVIRONMENT, GdmLaunchEnvironmentClass))
+
+typedef struct GdmLaunchEnvironmentPrivate GdmLaunchEnvironmentPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmLaunchEnvironmentPrivate *priv;
+} GdmLaunchEnvironment;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ /* methods */
+ gboolean (*start) (GdmLaunchEnvironment *launch_environment);
+ gboolean (*stop) (GdmLaunchEnvironment *launch_environment);
+
+
+ /* signals */
+ void (* opened) (GdmLaunchEnvironment *launch_environment);
+ void (* started) (GdmLaunchEnvironment *launch_environment);
+ void (* stopped) (GdmLaunchEnvironment *launch_environment);
+ void (* exited) (GdmLaunchEnvironment *launch_environment,
+ int exit_code);
+ void (* died) (GdmLaunchEnvironment *launch_environment,
+ int signal_number);
+ void (* hostname_selected) (GdmLaunchEnvironment *launch_environment,
+ const char *hostname);
+} GdmLaunchEnvironmentClass;
+
+GType gdm_launch_environment_get_type (void);
+
+gboolean gdm_launch_environment_start (GdmLaunchEnvironment *launch_environment);
+gboolean gdm_launch_environment_stop (GdmLaunchEnvironment *launch_environment);
+GdmSession * gdm_launch_environment_get_session (GdmLaunchEnvironment *launch_environment);
+char * gdm_launch_environment_get_session_id (GdmLaunchEnvironment *launch_environment);
+
+GdmLaunchEnvironment *gdm_create_greeter_launch_environment (const char *display_name,
+ const char *seat_id,
+ const char *session_type,
+ const char *display_hostname,
+ gboolean display_is_local);
+GdmLaunchEnvironment *gdm_create_initial_setup_launch_environment (const char *display_name,
+ const char *seat_id,
+ const char *session_type,
+ const char *display_hostname,
+ gboolean display_is_local);
+GdmLaunchEnvironment *gdm_create_chooser_launch_environment (const char *display_name,
+ const char *seat_id,
+ const char *display_hostname);
+
+G_END_DECLS
+
+#endif /* __GDM_LAUNCH_ENVIRONMENT_H */
diff --git a/daemon/gdm-legacy-display.c b/daemon/gdm-legacy-display.c
new file mode 100644
index 0000000..e53d82b
--- /dev/null
+++ b/daemon/gdm-legacy-display.c
@@ -0,0 +1,303 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-common.h"
+#include "gdm-display.h"
+#include "gdm-launch-environment.h"
+#include "gdm-legacy-display.h"
+#include "gdm-local-display-glue.h"
+#include "gdm-server.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+struct _GdmLegacyDisplay
+{
+ GdmDisplay parent;
+
+ GdmDBusLocalDisplay *skeleton;
+
+ GdmServer *server;
+};
+
+static void gdm_legacy_display_class_init (GdmLegacyDisplayClass *klass);
+static void gdm_legacy_display_init (GdmLegacyDisplay *legacy_display);
+
+G_DEFINE_TYPE (GdmLegacyDisplay, gdm_legacy_display, GDM_TYPE_DISPLAY)
+
+static GObject *
+gdm_legacy_display_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmLegacyDisplay *display;
+
+ display = GDM_LEGACY_DISPLAY (G_OBJECT_CLASS (gdm_legacy_display_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ display->skeleton = GDM_DBUS_LOCAL_DISPLAY (gdm_dbus_local_display_skeleton_new ());
+
+ g_dbus_object_skeleton_add_interface (gdm_display_get_object_skeleton (GDM_DISPLAY (display)),
+ G_DBUS_INTERFACE_SKELETON (display->skeleton));
+
+ return G_OBJECT (display);
+}
+
+static void
+gdm_legacy_display_finalize (GObject *object)
+{
+ GdmLegacyDisplay *display = GDM_LEGACY_DISPLAY (object);
+
+ g_clear_object (&display->skeleton);
+ g_clear_object (&display->server);
+
+ G_OBJECT_CLASS (gdm_legacy_display_parent_class)->finalize (object);
+}
+
+static gboolean
+gdm_legacy_display_prepare (GdmDisplay *display)
+{
+ GdmLegacyDisplay *self = GDM_LEGACY_DISPLAY (display);
+ GdmLaunchEnvironment *launch_environment;
+ char *display_name;
+ char *seat_id;
+ gboolean doing_initial_setup = FALSE;
+
+ display_name = NULL;
+ seat_id = NULL;
+
+ g_object_get (self,
+ "x11-display-name", &display_name,
+ "seat-id", &seat_id,
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ if (!doing_initial_setup) {
+ launch_environment = gdm_create_greeter_launch_environment (display_name,
+ seat_id,
+ NULL,
+ NULL,
+ TRUE);
+ } else {
+ launch_environment = gdm_create_initial_setup_launch_environment (display_name,
+ seat_id,
+ NULL,
+ NULL,
+ TRUE);
+ }
+
+ g_object_set (self, "launch-environment", launch_environment, NULL);
+ g_object_unref (launch_environment);
+
+ if (!gdm_display_create_authority (display)) {
+ g_warning ("Unable to set up access control for display %s",
+ display_name);
+ return FALSE;
+ }
+
+ return GDM_DISPLAY_CLASS (gdm_legacy_display_parent_class)->prepare (display);
+}
+
+static void
+on_server_ready (GdmServer *server,
+ GdmLegacyDisplay *self)
+{
+ gboolean ret;
+
+ ret = gdm_display_connect (GDM_DISPLAY (self));
+
+ if (!ret) {
+ g_debug ("GdmDisplay: could not connect to display");
+ gdm_display_unmanage (GDM_DISPLAY (self));
+ } else {
+ GdmLaunchEnvironment *launch_environment;
+ char *display_device;
+
+ display_device = gdm_server_get_display_device (server);
+
+ g_object_get (G_OBJECT (self),
+ "launch-environment", &launch_environment,
+ NULL);
+ g_object_set (G_OBJECT (launch_environment),
+ "x11-display-device",
+ display_device,
+ NULL);
+ g_clear_pointer(&display_device, g_free);
+ g_clear_object (&launch_environment);
+
+ g_debug ("GdmDisplay: connected to display");
+ g_object_set (G_OBJECT (self), "status", GDM_DISPLAY_MANAGED, NULL);
+ }
+}
+
+static void
+on_server_exited (GdmServer *server,
+ int exit_code,
+ GdmDisplay *self)
+{
+ g_debug ("GdmDisplay: server exited with code %d\n", exit_code);
+
+ gdm_display_unmanage (GDM_DISPLAY (self));
+}
+
+static void
+on_server_died (GdmServer *server,
+ int signal_number,
+ GdmDisplay *self)
+{
+ g_debug ("GdmDisplay: server died with signal %d, (%s)",
+ signal_number,
+ g_strsignal (signal_number));
+
+ gdm_display_unmanage (GDM_DISPLAY (self));
+}
+
+static void
+gdm_legacy_display_manage (GdmDisplay *display)
+{
+ GdmLegacyDisplay *self = GDM_LEGACY_DISPLAY (display);
+ char *display_name;
+ char *auth_file;
+ char *seat_id;
+ gboolean is_initial;
+ gboolean res;
+ gboolean disable_tcp;
+
+ g_object_get (G_OBJECT (self),
+ "x11-display-name", &display_name,
+ "x11-authority-file", &auth_file,
+ "seat-id", &seat_id,
+ "is-initial", &is_initial,
+ NULL);
+
+ self->server = gdm_server_new (display_name, seat_id, auth_file, is_initial);
+
+ g_free (display_name);
+ g_free (auth_file);
+ g_free (seat_id);
+
+ disable_tcp = TRUE;
+ if (gdm_settings_direct_get_boolean (GDM_KEY_DISALLOW_TCP, &disable_tcp)) {
+ g_object_set (self->server,
+ "disable-tcp", disable_tcp,
+ NULL);
+ }
+
+ g_signal_connect (self->server,
+ "exited",
+ G_CALLBACK (on_server_exited),
+ self);
+ g_signal_connect (self->server,
+ "died",
+ G_CALLBACK (on_server_died),
+ self);
+ g_signal_connect (self->server,
+ "ready",
+ G_CALLBACK (on_server_ready),
+ self);
+
+ res = gdm_server_start (self->server);
+ if (! res) {
+ g_warning (_("Could not start the X "
+ "server (your graphical environment) "
+ "due to an internal error. "
+ "Please contact your system administrator "
+ "or check your syslog to diagnose. "
+ "In the meantime this display will be "
+ "disabled. Please restart GDM when "
+ "the problem is corrected."));
+ gdm_display_unmanage (GDM_DISPLAY (self));
+ }
+
+ g_debug ("GdmDisplay: Started X server");
+
+}
+
+static void
+gdm_legacy_display_class_init (GdmLegacyDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass);
+
+ object_class->constructor = gdm_legacy_display_constructor;
+ object_class->finalize = gdm_legacy_display_finalize;
+
+ display_class->prepare = gdm_legacy_display_prepare;
+ display_class->manage = gdm_legacy_display_manage;
+}
+
+static void
+on_display_status_changed (GdmLegacyDisplay *self)
+{
+ int status;
+
+ status = gdm_display_get_status (GDM_DISPLAY (self));
+
+ switch (status) {
+ case GDM_DISPLAY_UNMANAGED:
+ if (self->server != NULL)
+ gdm_server_stop (self->server);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+gdm_legacy_display_init (GdmLegacyDisplay *legacy_display)
+{
+ g_signal_connect (legacy_display, "notify::status",
+ G_CALLBACK (on_display_status_changed),
+ NULL);
+}
+
+GdmDisplay *
+gdm_legacy_display_new (int display_number)
+{
+ GObject *object;
+ char *x11_display;
+
+ x11_display = g_strdup_printf (":%d", display_number);
+ object = g_object_new (GDM_TYPE_LEGACY_DISPLAY,
+ "x11-display-number", display_number,
+ "x11-display-name", x11_display,
+ NULL);
+ g_free (x11_display);
+
+ return GDM_DISPLAY (object);
+}
diff --git a/daemon/gdm-legacy-display.h b/daemon/gdm-legacy-display.h
new file mode 100644
index 0000000..9f98c83
--- /dev/null
+++ b/daemon/gdm-legacy-display.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_LEGACY_DISPLAY_H
+#define __GDM_LEGACY_DISPLAY_H
+
+#include <glib-object.h>
+#include "gdm-display.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_LEGACY_DISPLAY (gdm_legacy_display_get_type ())
+G_DECLARE_FINAL_TYPE (GdmLegacyDisplay, gdm_legacy_display, GDM, LEGACY_DISPLAY, GdmDisplay)
+
+GdmDisplay * gdm_legacy_display_new (int display_number);
+
+
+G_END_DECLS
+
+#endif /* __GDM_LEGACY_DISPLAY_H */
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
new file mode 100644
index 0000000..8c912cc
--- /dev/null
+++ b/daemon/gdm-local-display-factory.c
@@ -0,0 +1,1631 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#ifdef HAVE_UDEV
+#include <gudev/gudev.h>
+#endif
+
+#include <systemd/sd-login.h>
+
+#include "gdm-common.h"
+#include "gdm-manager.h"
+#include "gdm-display-factory.h"
+#include "gdm-local-display-factory.h"
+#include "gdm-local-display-factory-glue.h"
+
+#include "gdm-settings-keys.h"
+#include "gdm-settings-direct.h"
+#include "gdm-display-store.h"
+#include "gdm-local-display.h"
+#include "gdm-legacy-display.h"
+
+#define GDM_DBUS_PATH "/org/gnome/DisplayManager"
+#define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory"
+#define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.LocalDisplayFactory"
+
+#define MAX_DISPLAY_FAILURES 5
+#define WAIT_TO_FINISH_TIMEOUT 10 /* seconds */
+#define SEAT0_GRAPHICS_CHECK_TIMEOUT 10 /* seconds */
+
+struct _GdmLocalDisplayFactory
+{
+ GdmDisplayFactory parent;
+#ifdef HAVE_UDEV
+ GUdevClient *gudev_client;
+#endif
+
+ GdmDBusLocalDisplayFactory *skeleton;
+ GDBusConnection *connection;
+ GHashTable *used_display_numbers;
+
+ /* FIXME: this needs to be per seat? */
+ guint num_failures;
+
+ guint seat_new_id;
+ guint seat_removed_id;
+ guint seat_properties_changed_id;
+
+ gboolean seat0_has_platform_graphics;
+ gboolean seat0_has_boot_up_graphics;
+
+ gboolean seat0_graphics_check_timed_out;
+ guint seat0_graphics_check_timeout_id;
+
+ gulong uevent_handler_id;
+
+#if defined(ENABLE_USER_DISPLAY_SERVER)
+ unsigned int active_vt;
+ guint active_vt_watch_id;
+ guint wait_to_finish_timeout_id;
+#endif
+
+ gboolean is_started;
+};
+
+enum {
+ PROP_0,
+};
+
+static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass);
+static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory);
+static void gdm_local_display_factory_finalize (GObject *object);
+
+static void ensure_display_for_seat (GdmLocalDisplayFactory *factory,
+ const char *seat_id);
+
+static void on_display_status_changed (GdmDisplay *display,
+ GParamSpec *arg1,
+ GdmLocalDisplayFactory *factory);
+
+static gboolean gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory);
+static gpointer local_display_factory_object = NULL;
+static gboolean lookup_by_session_id (const char *id,
+ GdmDisplay *display,
+ gpointer user_data);
+
+G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY)
+
+GQuark
+gdm_local_display_factory_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_local_display_factory_error");
+ }
+
+ return ret;
+}
+
+static void
+listify_hash (gpointer key,
+ GdmDisplay *display,
+ GList **list)
+{
+ *list = g_list_prepend (*list, key);
+}
+
+static int
+sort_nums (gpointer a,
+ gpointer b)
+{
+ guint32 num_a;
+ guint32 num_b;
+
+ num_a = GPOINTER_TO_UINT (a);
+ num_b = GPOINTER_TO_UINT (b);
+
+ if (num_a > num_b) {
+ return 1;
+ } else if (num_a < num_b) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static guint32
+take_next_display_number (GdmLocalDisplayFactory *factory)
+{
+ GList *list;
+ GList *l;
+ guint32 ret;
+
+ ret = 0;
+ list = NULL;
+
+ g_hash_table_foreach (factory->used_display_numbers, (GHFunc)listify_hash, &list);
+ if (list == NULL) {
+ goto out;
+ }
+
+ /* sort low to high */
+ list = g_list_sort (list, (GCompareFunc)sort_nums);
+
+ g_debug ("GdmLocalDisplayFactory: Found the following X displays:");
+ for (l = list; l != NULL; l = l->next) {
+ g_debug ("GdmLocalDisplayFactory: %u", GPOINTER_TO_UINT (l->data));
+ }
+
+ for (l = list; l != NULL; l = l->next) {
+ guint32 num;
+ num = GPOINTER_TO_UINT (l->data);
+
+ /* always fill zero */
+ if (l->prev == NULL && num != 0) {
+ ret = 0;
+ break;
+ }
+ /* now find the first hole */
+ if (l->next == NULL || GPOINTER_TO_UINT (l->next->data) != (num + 1)) {
+ ret = num + 1;
+ break;
+ }
+ }
+ out:
+
+ /* now reserve this number */
+ g_debug ("GdmLocalDisplayFactory: Reserving X display: %u", ret);
+ g_hash_table_insert (factory->used_display_numbers, GUINT_TO_POINTER (ret), NULL);
+
+ return ret;
+}
+
+static char *
+get_preferred_display_server (GdmLocalDisplayFactory *factory)
+{
+ g_autofree gchar *preferred_display_server = NULL;
+ gboolean wayland_enabled = FALSE, xorg_enabled = FALSE;
+
+ gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled);
+ gdm_settings_direct_get_boolean (GDM_KEY_XORG_ENABLE, &xorg_enabled);
+
+ if (wayland_enabled && !xorg_enabled) {
+ return g_strdup ("wayland");
+ }
+
+ if (!wayland_enabled && !xorg_enabled) {
+ return g_strdup ("none");
+ }
+
+ gdm_settings_direct_get_string (GDM_KEY_PREFERRED_DISPLAY_SERVER, &preferred_display_server);
+
+ if (g_strcmp0 (preferred_display_server, "wayland") == 0) {
+ if (wayland_enabled)
+ return g_strdup (preferred_display_server);
+ else
+ return g_strdup ("xorg");
+ }
+
+ if (g_strcmp0 (preferred_display_server, "xorg") == 0) {
+ if (xorg_enabled)
+ return g_strdup (preferred_display_server);
+ else
+ return g_strdup ("wayland");
+ }
+
+ if (g_strcmp0 (preferred_display_server, "legacy-xorg") == 0) {
+ if (xorg_enabled)
+ return g_strdup (preferred_display_server);
+ }
+
+ return g_strdup ("none");
+}
+
+struct GdmDisplayServerConfiguration {
+ const char *display_server;
+ const char *key;
+ const char *binary;
+ const char *session_type;
+} display_server_configuration[] = {
+#ifdef ENABLE_WAYLAND_SUPPORT
+ { "wayland", GDM_KEY_WAYLAND_ENABLE, "/usr/bin/Xwayland", "wayland" },
+#endif
+ { "xorg", GDM_KEY_XORG_ENABLE, "/usr/bin/Xorg", "x11" },
+ { NULL, NULL, NULL },
+};
+
+static gboolean
+display_server_enabled (GdmLocalDisplayFactory *factory,
+ const char *display_server)
+{
+ size_t i;
+
+ for (i = 0; display_server_configuration[i].display_server != NULL; i++) {
+ const char *key = display_server_configuration[i].key;
+ const char *binary = display_server_configuration[i].binary;
+ gboolean enabled = FALSE;
+
+ if (!g_str_equal (display_server_configuration[i].display_server,
+ display_server))
+ continue;
+
+ if (!gdm_settings_direct_get_boolean (key, &enabled) || !enabled)
+ return FALSE;
+
+ if (!g_file_test (binary, G_FILE_TEST_IS_EXECUTABLE))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static const char *
+get_session_type_for_display_server (GdmLocalDisplayFactory *factory,
+ const char *display_server)
+{
+ size_t i;
+
+ for (i = 0; display_server_configuration[i].display_server != NULL; i++) {
+ if (!g_str_equal (display_server_configuration[i].display_server,
+ display_server))
+ continue;
+
+ return display_server_configuration[i].session_type;
+ }
+
+ return NULL;
+}
+
+static char **
+gdm_local_display_factory_get_session_types (GdmLocalDisplayFactory *factory,
+ gboolean should_fall_back)
+{
+ g_autofree gchar *preferred_display_server = NULL;
+ const char *fallback_display_server = NULL;
+ gboolean wayland_preferred = FALSE;
+ gboolean xorg_preferred = FALSE;
+ g_autoptr (GPtrArray) session_types_array = NULL;
+ char **session_types;
+
+ session_types_array = g_ptr_array_new ();
+
+ preferred_display_server = get_preferred_display_server (factory);
+
+ g_debug ("GdmLocalDisplayFactory: Getting session type (prefers %s, falling back: %s)",
+ preferred_display_server, should_fall_back? "yes" : "no");
+
+ wayland_preferred = g_str_equal (preferred_display_server, "wayland");
+ xorg_preferred = g_str_equal (preferred_display_server, "xorg");
+
+ if (wayland_preferred)
+ fallback_display_server = "xorg";
+ else if (xorg_preferred)
+ fallback_display_server = "wayland";
+ else
+ return NULL;
+
+ if (!should_fall_back) {
+ if (display_server_enabled (factory, preferred_display_server))
+ g_ptr_array_add (session_types_array, (gpointer) get_session_type_for_display_server (factory, preferred_display_server));
+ }
+
+ if (display_server_enabled (factory, fallback_display_server))
+ g_ptr_array_add (session_types_array, (gpointer) get_session_type_for_display_server (factory, fallback_display_server));
+
+ if (session_types_array->len == 0)
+ return NULL;
+
+ g_ptr_array_add (session_types_array, NULL);
+
+ session_types = g_strdupv ((char **) session_types_array->pdata);
+
+ return session_types;
+}
+
+static void
+on_display_disposed (GdmLocalDisplayFactory *factory,
+ GdmDisplay *display)
+{
+ g_debug ("GdmLocalDisplayFactory: Display %p disposed", display);
+}
+
+static void
+store_display (GdmLocalDisplayFactory *factory,
+ GdmDisplay *display)
+{
+ GdmDisplayStore *store;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ gdm_display_store_add (store, display);
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.gnome.DisplayManager \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/gnome/DisplayManager/Manager \
+ org.gnome.DisplayManager.Manager.GetDisplays
+*/
+gboolean
+gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory,
+ char **id,
+ GError **error)
+{
+ gboolean ret;
+ GdmDisplay *display = NULL;
+ gboolean is_initial = FALSE;
+ const char *session_type;
+ g_autofree gchar *preferred_display_server = NULL;
+
+ g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
+
+ ret = FALSE;
+
+ g_debug ("GdmLocalDisplayFactory: Creating transient display");
+
+ preferred_display_server = get_preferred_display_server (factory);
+
+#ifdef ENABLE_USER_DISPLAY_SERVER
+ if (g_strcmp0 (preferred_display_server, "wayland") == 0 ||
+ g_strcmp0 (preferred_display_server, "xorg") == 0) {
+ g_auto(GStrv) session_types = NULL;
+
+ session_types = gdm_local_display_factory_get_session_types (factory, FALSE);
+
+ if (session_types == NULL) {
+ g_set_error_literal (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GENERAL,
+ "Both Wayland and Xorg are unavailable");
+ return FALSE;
+ }
+
+ display = gdm_local_display_new ();
+ g_object_set (G_OBJECT (display),
+ "session-type", session_types[0],
+ "supported-session-types", session_types,
+ NULL);
+ is_initial = TRUE;
+ }
+#endif
+ if (g_strcmp0 (preferred_display_server, "legacy-xorg") == 0) {
+ if (display == NULL) {
+ guint32 num;
+
+ num = take_next_display_number (factory);
+
+ display = gdm_legacy_display_new (num);
+ }
+ }
+
+ if (display == NULL) {
+ g_set_error_literal (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GENERAL,
+ "Invalid preferred display server configured");
+ return FALSE;
+ }
+
+ g_object_set (display,
+ "seat-id", "seat0",
+ "allow-timed-login", FALSE,
+ "is-initial", is_initial,
+ NULL);
+
+ store_display (factory, display);
+
+ if (! gdm_display_manage (display)) {
+ display = NULL;
+ goto out;
+ }
+
+ if (! gdm_display_get_id (display, id, NULL)) {
+ display = NULL;
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ /* ref either held by store or not at all */
+ g_object_unref (display);
+
+ return ret;
+}
+
+static void
+finish_display_on_seat_if_waiting (GdmDisplayStore *display_store,
+ GdmDisplay *display,
+ const char *seat_id)
+{
+ if (gdm_display_get_status (display) != GDM_DISPLAY_WAITING_TO_FINISH)
+ return;
+
+ g_debug ("GdmLocalDisplayFactory: finish background display\n");
+ gdm_display_stop_greeter_session (display);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+}
+
+static void
+finish_waiting_displays_on_seat (GdmLocalDisplayFactory *factory,
+ const char *seat_id)
+{
+ GdmDisplayStore *store;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ gdm_display_store_foreach (store,
+ (GdmDisplayStoreFunc) finish_display_on_seat_if_waiting,
+ (gpointer)
+ seat_id);
+}
+
+static gboolean
+on_finish_waiting_for_seat0_displays_timeout (GdmLocalDisplayFactory *factory)
+{
+ g_debug ("GdmLocalDisplayFactory: timeout following VT switch to registered session complete, looking for any background displays to kill");
+ finish_waiting_displays_on_seat (factory, "seat0");
+ return G_SOURCE_REMOVE;
+}
+
+static void
+on_session_registered_cb (GObject *gobject,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GdmDisplay *display = GDM_DISPLAY (gobject);
+ GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (user_data);
+ gboolean registered;
+
+ g_object_get (display, "session-registered", &registered, NULL);
+
+ if (!registered)
+ return;
+
+ g_debug ("GdmLocalDisplayFactory: session registered on display, looking for any background displays to kill");
+
+ finish_waiting_displays_on_seat (factory, "seat0");
+}
+
+static void
+on_display_status_changed (GdmDisplay *display,
+ GParamSpec *arg1,
+ GdmLocalDisplayFactory *factory)
+{
+ int status;
+ int num;
+ char *seat_id = NULL;
+ char *session_type = NULL;
+ char *session_class = NULL;
+ gboolean is_initial = TRUE;
+ gboolean is_local = TRUE;
+
+
+ if (!factory->is_started)
+ return;
+
+ num = -1;
+ gdm_display_get_x11_display_number (display, &num, NULL);
+
+ g_object_get (display,
+ "seat-id", &seat_id,
+ "is-initial", &is_initial,
+ "is-local", &is_local,
+ "session-type", &session_type,
+ "session-class", &session_class,
+ NULL);
+
+ status = gdm_display_get_status (display);
+
+ g_debug ("GdmLocalDisplayFactory: display status changed: %d", status);
+ switch (status) {
+ case GDM_DISPLAY_FINISHED:
+ /* remove the display number from factory->used_display_numbers
+ so that it may be reused */
+ if (num != -1) {
+ g_hash_table_remove (factory->used_display_numbers, GUINT_TO_POINTER (num));
+ }
+ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory));
+
+ /* if this is a local display, do a full resync. Only
+ * seats without displays will get created anyway. This
+ * ensures we get a new login screen when the user logs out,
+ * if there isn't one.
+ */
+ if (is_local &&
+ (g_strcmp0 (session_class, "greeter") != 0 || factory->active_vt == GDM_INITIAL_VT)) {
+ /* reset num failures */
+ factory->num_failures = 0;
+
+ gdm_local_display_factory_sync_seats (factory);
+ }
+ break;
+ case GDM_DISPLAY_FAILED:
+ /* leave the display number in factory->used_display_numbers
+ so that it doesn't get reused */
+ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory));
+
+ /* Create a new equivalent display if it was static */
+ if (is_local) {
+
+ factory->num_failures++;
+
+ /* oh shit */
+ if (factory->num_failures > MAX_DISPLAY_FAILURES)
+ g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors");
+ else
+ ensure_display_for_seat (factory, seat_id);
+ }
+ break;
+ case GDM_DISPLAY_UNMANAGED:
+ break;
+ case GDM_DISPLAY_PREPARED:
+ break;
+ case GDM_DISPLAY_MANAGED:
+#if defined(ENABLE_USER_DISPLAY_SERVER)
+ g_signal_connect_object (display,
+ "notify::session-registered",
+ G_CALLBACK (on_session_registered_cb),
+ factory,
+ 0);
+#endif
+ break;
+ case GDM_DISPLAY_WAITING_TO_FINISH:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_free (seat_id);
+ g_free (session_type);
+ g_free (session_class);
+}
+
+static gboolean
+lookup_by_seat_id (const char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ const char *looking_for = user_data;
+ char *current;
+ gboolean res;
+
+ g_object_get (G_OBJECT (display), "seat-id", &current, NULL);
+
+ res = g_strcmp0 (current, looking_for) == 0;
+
+ g_free(current);
+
+ return res;
+}
+
+static gboolean
+lookup_prepared_display_by_seat_id (const char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ int status;
+
+ status = gdm_display_get_status (display);
+
+ if (status != GDM_DISPLAY_PREPARED)
+ return FALSE;
+
+ return lookup_by_seat_id (id, display, user_data);
+}
+
+#ifdef HAVE_UDEV
+static gboolean
+udev_is_settled (GdmLocalDisplayFactory *factory)
+{
+ g_autoptr (GUdevEnumerator) enumerator = NULL;
+ GList *devices;
+ GList *node;
+
+ gboolean is_settled = FALSE;
+
+ if (factory->seat0_has_platform_graphics) {
+ g_debug ("GdmLocalDisplayFactory: udev settled, platform graphics enabled.");
+ return TRUE;
+ }
+
+ if (factory->seat0_has_boot_up_graphics) {
+ g_debug ("GdmLocalDisplayFactory: udev settled, boot up graphics available.");
+ return TRUE;
+ }
+
+ if (factory->seat0_graphics_check_timed_out) {
+ g_debug ("GdmLocalDisplayFactory: udev timed out, proceeding anyway.");
+ return TRUE;
+ }
+
+ g_debug ("GdmLocalDisplayFactory: Checking if udev has settled enough to support graphics.");
+
+ enumerator = g_udev_enumerator_new (factory->gudev_client);
+
+ g_udev_enumerator_add_match_name (enumerator, "card*");
+ g_udev_enumerator_add_match_tag (enumerator, "master-of-seat");
+ g_udev_enumerator_add_match_subsystem (enumerator, "drm");
+
+ devices = g_udev_enumerator_execute (enumerator);
+ if (!devices) {
+ g_debug ("GdmLocalDisplayFactory: udev has no candidate graphics devices available yet.");
+ return FALSE;
+ }
+
+ node = devices;
+ while (node != NULL) {
+ GUdevDevice *device = node->data;
+ GList *next_node = node->next;
+ g_autoptr (GUdevDevice) platform_device = NULL;
+ g_autoptr (GUdevDevice) pci_device = NULL;
+
+ platform_device = g_udev_device_get_parent_with_subsystem (device, "platform", NULL);
+
+ if (platform_device != NULL) {
+ g_debug ("GdmLocalDisplayFactory: Found embedded platform graphics, proceeding.");
+ factory->seat0_has_platform_graphics = TRUE;
+ is_settled = TRUE;
+ break;
+ }
+
+ pci_device = g_udev_device_get_parent_with_subsystem (device, "pci", NULL);
+
+ if (pci_device != NULL) {
+ gboolean boot_vga;
+
+ boot_vga = g_udev_device_get_sysfs_attr_as_int (pci_device, "boot_vga");
+
+ if (boot_vga == 1) {
+ g_debug ("GdmLocalDisplayFactory: Found primary PCI graphics adapter, proceeding.");
+ factory->seat0_has_boot_up_graphics = TRUE;
+ is_settled = TRUE;
+ break;
+ } else {
+ g_debug ("GdmLocalDisplayFactory: Found secondary PCI graphics adapter, not proceeding yet.");
+ }
+ }
+ node = next_node;
+ }
+
+ g_debug ("GdmLocalDisplayFactory: udev has %ssettled enough for graphics.", is_settled? "" : "not ");
+ g_list_free_full (devices, g_object_unref);
+
+ if (is_settled)
+ g_clear_signal_handler (&factory->uevent_handler_id, factory->gudev_client);
+
+ return is_settled;
+}
+#endif
+
+static int
+on_seat0_graphics_check_timeout (gpointer user_data)
+{
+ GdmLocalDisplayFactory *factory = user_data;
+
+ factory->seat0_graphics_check_timeout_id = 0;
+
+ /* Simply try to re-add seat0. If it is there already (i.e. CanGraphical
+ * turned TRUE, then we'll find it and it will not be created again).
+ */
+ factory->seat0_graphics_check_timed_out = TRUE;
+ ensure_display_for_seat (factory, "seat0");
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ensure_display_for_seat (GdmLocalDisplayFactory *factory,
+ const char *seat_id)
+{
+ int ret;
+ gboolean seat_supports_graphics;
+ gboolean is_seat0;
+ g_auto (GStrv) session_types = NULL;
+ const char *legacy_session_types[] = { "x11", NULL };
+ GdmDisplayStore *store;
+ GdmDisplay *display = NULL;
+ g_autofree char *login_session_id = NULL;
+ gboolean wayland_enabled = FALSE, xorg_enabled = FALSE;
+ g_autofree gchar *preferred_display_server = NULL;
+ gboolean falling_back = FALSE;
+ gboolean waiting_on_udev = FALSE;
+
+ gdm_settings_direct_get_boolean (GDM_KEY_WAYLAND_ENABLE, &wayland_enabled);
+ gdm_settings_direct_get_boolean (GDM_KEY_XORG_ENABLE, &xorg_enabled);
+
+ preferred_display_server = get_preferred_display_server (factory);
+
+ if (g_strcmp0 (preferred_display_server, "none") == 0) {
+ g_debug ("GdmLocalDisplayFactory: Preferred display server is none, so not creating display");
+ return;
+ }
+
+#ifdef HAVE_UDEV
+ waiting_on_udev = !udev_is_settled (factory);
+#endif
+
+ if (!waiting_on_udev) {
+ ret = sd_seat_can_graphical (seat_id);
+
+ if (ret < 0) {
+ g_critical ("Failed to query CanGraphical information for seat %s", seat_id);
+ return;
+ }
+
+ if (ret == 0) {
+ g_debug ("GdmLocalDisplayFactory: System doesn't currently support graphics");
+ seat_supports_graphics = FALSE;
+ } else {
+ g_debug ("GdmLocalDisplayFactory: System supports graphics");
+ seat_supports_graphics = TRUE;
+ }
+ } else {
+ g_debug ("GdmLocalDisplayFactory: udev is still settling, so not creating display yet");
+ seat_supports_graphics = FALSE;
+ }
+
+ if (g_strcmp0 (seat_id, "seat0") == 0) {
+ is_seat0 = TRUE;
+
+ falling_back = factory->num_failures > 0;
+ session_types = gdm_local_display_factory_get_session_types (factory, falling_back);
+
+ if (session_types == NULL) {
+ g_debug ("GdmLocalDisplayFactory: Both Wayland and Xorg are unavailable");
+ seat_supports_graphics = FALSE;
+ } else {
+ g_debug ("GdmLocalDisplayFactory: New displays on seat0 will use %s%s",
+ session_types[0], falling_back? " fallback" : "");
+ }
+ } else {
+ is_seat0 = FALSE;
+
+ g_debug ("GdmLocalDisplayFactory: New displays on seat %s will use X11 fallback", seat_id);
+ /* Force legacy X11 for all auxiliary seats */
+ seat_supports_graphics = TRUE;
+ session_types = g_strdupv ((char **) legacy_session_types);
+ }
+
+ /* For seat0, we have a fallback logic to still try starting it after
+ * SEAT0_GRAPHICS_CHECK_TIMEOUT seconds. i.e. we simply continue even if
+ * CanGraphical is unset or udev otherwise never finds a suitable graphics card.
+ * This is ugly, but it means we'll come up eventually in some
+ * scenarios where no master device is present.
+ * Note that we'll force an X11 fallback even though there might be
+ * cases where an wayland capable device is present and simply not marked as
+ * master-of-seat. In these cases, this should likely be fixed in the
+ * udev rules.
+ *
+ * At the moment, systemd always sets CanGraphical for non-seat0 seats.
+ * This is because non-seat0 seats are defined by having master-of-seat
+ * set. This means we can avoid the fallback check for non-seat0 seats,
+ * which simplifies the code.
+ */
+ if (is_seat0) {
+ if (!seat_supports_graphics) {
+ if (!factory->seat0_graphics_check_timed_out) {
+ if (factory->seat0_graphics_check_timeout_id == 0) {
+ g_debug ("GdmLocalDisplayFactory: seat0 doesn't yet support graphics. Waiting %d seconds to try again.", SEAT0_GRAPHICS_CHECK_TIMEOUT);
+ factory->seat0_graphics_check_timeout_id = g_timeout_add_seconds (SEAT0_GRAPHICS_CHECK_TIMEOUT,
+ on_seat0_graphics_check_timeout,
+ factory);
+
+ } else {
+ /* It is not yet time to force X11 fallback. */
+ g_debug ("GdmLocalDisplayFactory: seat0 display requested when there is no graphics support before graphics check timeout.");
+ }
+
+ return;
+ }
+
+ g_debug ("GdmLocalDisplayFactory: Assuming we can use seat0 for X11 even though system says it doesn't support graphics!");
+ g_debug ("GdmLocalDisplayFactory: This might indicate an issue where the framebuffer device is not tagged as master-of-seat in udev.");
+ seat_supports_graphics = TRUE;
+ wayland_enabled = FALSE;
+ g_strfreev (session_types);
+ session_types = g_strdupv ((char **) legacy_session_types);
+ } else {
+ g_clear_handle_id (&factory->seat0_graphics_check_timeout_id, g_source_remove);
+ }
+ }
+
+ if (!seat_supports_graphics)
+ return;
+
+ if (session_types != NULL)
+ g_debug ("GdmLocalDisplayFactory: %s login display for seat %s requested",
+ session_types[0], seat_id);
+ else if (g_strcmp0 (preferred_display_server, "legacy-xorg") == 0)
+ g_debug ("GdmLocalDisplayFactory: Legacy Xorg login display for seat %s requested",
+ seat_id);
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ if (is_seat0)
+ display = gdm_display_store_find (store, lookup_prepared_display_by_seat_id, (gpointer) seat_id);
+ else
+ display = gdm_display_store_find (store, lookup_by_seat_id, (gpointer) seat_id);
+
+ /* Ensure we don't create the same display more than once */
+ if (display != NULL) {
+ g_debug ("GdmLocalDisplayFactory: display already created");
+ return;
+ }
+
+ /* If we already have a login window, switch to it */
+ if (gdm_get_login_window_session_id (seat_id, &login_session_id)) {
+ GdmDisplay *display;
+
+ display = gdm_display_store_find (store,
+ lookup_by_session_id,
+ (gpointer) login_session_id);
+ if (display != NULL &&
+ (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED ||
+ gdm_display_get_status (display) == GDM_DISPLAY_WAITING_TO_FINISH)) {
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL);
+ g_debug ("GdmLocalDisplayFactory: session %s found, activating.",
+ login_session_id);
+ gdm_activate_session_by_id (factory->connection, seat_id, login_session_id);
+ return;
+ }
+ }
+
+ g_debug ("GdmLocalDisplayFactory: Adding display on seat %s", seat_id);
+
+#ifdef ENABLE_USER_DISPLAY_SERVER
+ if (g_strcmp0 (preferred_display_server, "wayland") == 0 ||
+ g_strcmp0 (preferred_display_server, "xorg") == 0) {
+ if (is_seat0) {
+ display = gdm_local_display_new ();
+ g_object_set (G_OBJECT (display),
+ "session-type", session_types[0],
+ "supported-session-types", session_types,
+ NULL);
+ }
+ }
+#endif
+
+ if (display == NULL) {
+ guint32 num;
+
+ num = take_next_display_number (factory);
+
+ display = gdm_legacy_display_new (num);
+ g_object_set (G_OBJECT (display),
+ "session-type", legacy_session_types[0],
+ "supported-session-types", legacy_session_types,
+ NULL);
+ }
+
+ g_object_set (display, "seat-id", seat_id, NULL);
+ g_object_set (display, "is-initial", is_seat0, NULL);
+
+ store_display (factory, display);
+
+ /* let store own the ref */
+ g_object_unref (display);
+
+ if (! gdm_display_manage (display)) {
+ gdm_display_unmanage (display);
+ }
+
+ return;
+}
+
+static void
+delete_display (GdmLocalDisplayFactory *factory,
+ const char *seat_id) {
+
+ GdmDisplayStore *store;
+
+ g_debug ("GdmLocalDisplayFactory: Removing used_display_numbers on seat %s", seat_id);
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ gdm_display_store_foreach_remove (store, lookup_by_seat_id, (gpointer) seat_id);
+}
+
+static gboolean
+gdm_local_display_factory_sync_seats (GdmLocalDisplayFactory *factory)
+{
+ GError *error = NULL;
+ GVariant *result;
+ GVariant *array;
+ GVariantIter iter;
+ const char *seat;
+
+ g_debug ("GdmLocalDisplayFactory: enumerating seats from logind");
+ result = g_dbus_connection_call_sync (factory->connection,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListSeats",
+ NULL,
+ G_VARIANT_TYPE ("(a(so))"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL, &error);
+
+ if (!result) {
+ g_warning ("GdmLocalDisplayFactory: Failed to issue method call: %s", error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ array = g_variant_get_child_value (result, 0);
+ g_variant_iter_init (&iter, array);
+
+ while (g_variant_iter_loop (&iter, "(&so)", &seat, NULL)) {
+ ensure_display_for_seat (factory, seat);
+ }
+
+ g_variant_unref (result);
+ g_variant_unref (array);
+ return TRUE;
+}
+
+static void
+on_seat_new (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ const char *seat;
+
+ g_variant_get (parameters, "(&s&o)", &seat, NULL);
+ ensure_display_for_seat (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+}
+
+static void
+on_seat_removed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ const char *seat;
+
+ g_variant_get (parameters, "(&s&o)", &seat, NULL);
+ delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+}
+
+static void
+on_seat_properties_changed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ const gchar *seat = NULL;
+ g_autoptr(GVariant) changed_props = NULL;
+ g_autoptr(GVariant) changed_prop = NULL;
+ g_autofree const gchar **invalidated_props = NULL;
+ gboolean changed = FALSE;
+ int ret;
+
+ /* Extract seat id, i.e. the last element of the object path. */
+ seat = strrchr (object_path, '/');
+ if (seat == NULL)
+ return;
+ seat += 1;
+
+ /* Valid seat IDs must start with seat, i.e. ignore "auto" */
+ if (!g_str_has_prefix (seat, "seat"))
+ return;
+
+ g_variant_get (parameters, "(s@a{sv}^a&s)", NULL, &changed_props, &invalidated_props);
+
+ changed_prop = g_variant_lookup_value (changed_props, "CanGraphical", NULL);
+ if (changed_prop)
+ changed = TRUE;
+ if (!changed && g_strv_contains (invalidated_props, "CanGraphical"))
+ changed = TRUE;
+
+ if (!changed)
+ return;
+
+ ret = sd_seat_can_graphical (seat);
+ if (ret < 0)
+ return;
+
+ if (ret != 0) {
+ gdm_settings_direct_reload ();
+ ensure_display_for_seat (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+ } else {
+ delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
+ }
+}
+
+static gboolean
+lookup_by_session_id (const char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ const char *looking_for = user_data;
+ const char *current;
+
+ current = gdm_display_get_session_id (display);
+ return g_strcmp0 (current, looking_for) == 0;
+}
+
+static gboolean
+lookup_by_tty (const char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ const char *tty_to_find = user_data;
+ g_autofree char *tty_to_check = NULL;
+ const char *session_id;
+ int ret;
+
+ session_id = gdm_display_get_session_id (display);
+
+ if (!session_id)
+ return FALSE;
+
+ ret = sd_session_get_tty (session_id, &tty_to_check);
+
+ if (ret != 0)
+ return FALSE;
+
+ return g_strcmp0 (tty_to_check, tty_to_find) == 0;
+}
+
+#if defined(ENABLE_USER_DISPLAY_SERVER)
+static void
+maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory,
+ GdmDisplay *display)
+{
+ gboolean doing_initial_setup = FALSE;
+
+ if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) {
+ g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring");
+ return;
+ }
+
+ g_object_get (G_OBJECT (display),
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ /* we don't ever stop initial-setup implicitly */
+ if (doing_initial_setup) {
+ g_debug ("GdmLocalDisplayFactory: login window is performing initial-setup, so ignoring");
+ return;
+ }
+
+ g_debug ("GdmLocalDisplayFactory: killing login window once its unused");
+
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL);
+}
+
+static gboolean
+on_vt_changed (GIOChannel *source,
+ GIOCondition condition,
+ GdmLocalDisplayFactory *factory)
+{
+ GdmDisplayStore *store;
+ GIOStatus status;
+ g_autofree char *tty_of_active_vt = NULL;
+ g_autofree char *login_session_id = NULL;
+ g_autofree char *active_session_id = NULL;
+ unsigned int previous_vt, new_vt, login_window_vt = 0;
+ int ret, n_returned;
+
+ g_debug ("GdmLocalDisplayFactory: received VT change event");
+ g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL);
+
+ if (condition & G_IO_PRI) {
+ g_autoptr (GError) error = NULL;
+ status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error);
+
+ if (error != NULL) {
+ g_warning ("could not read active VT from kernel: %s", error->message);
+ }
+ switch (status) {
+ case G_IO_STATUS_ERROR:
+ return G_SOURCE_REMOVE;
+ case G_IO_STATUS_EOF:
+ return G_SOURCE_REMOVE;
+ case G_IO_STATUS_AGAIN:
+ return G_SOURCE_CONTINUE;
+ case G_IO_STATUS_NORMAL:
+ break;
+ }
+ }
+
+ if ((condition & G_IO_ERR) || (condition & G_IO_HUP)) {
+ g_debug ("GdmLocalDisplayFactory: kernel hung up active vt watch");
+ return G_SOURCE_REMOVE;
+ }
+
+ if (tty_of_active_vt == NULL) {
+ g_debug ("GdmLocalDisplayFactory: unable to read active VT from kernel");
+ return G_SOURCE_CONTINUE;
+ }
+
+ g_strchomp (tty_of_active_vt);
+
+ errno = 0;
+ n_returned = sscanf (tty_of_active_vt, "tty%u", &new_vt);
+
+ if (n_returned != 1 || errno != 0) {
+ g_critical ("GdmLocalDisplayFactory: Couldn't read active VT (got '%s')",
+ tty_of_active_vt);
+ return G_SOURCE_CONTINUE;
+ }
+
+ /* don't do anything if we're on the same VT we were before */
+ if (new_vt == factory->active_vt) {
+ g_debug ("GdmLocalDisplayFactory: VT changed to the same VT, ignoring");
+ return G_SOURCE_CONTINUE;
+ }
+
+ previous_vt = factory->active_vt;
+ factory->active_vt = new_vt;
+
+ /* don't do anything at start up */
+ if (previous_vt == 0) {
+ g_debug ("GdmLocalDisplayFactory: VT is %u at startup",
+ factory->active_vt);
+ return G_SOURCE_CONTINUE;
+ }
+
+ g_debug ("GdmLocalDisplayFactory: VT changed from %u to %u",
+ previous_vt, factory->active_vt);
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ /* if the old VT was running a wayland login screen kill it
+ */
+ if (gdm_get_login_window_session_id ("seat0", &login_session_id)) {
+ ret = sd_session_get_vt (login_session_id, &login_window_vt);
+ if (ret == 0 && login_window_vt != 0) {
+ g_debug ("GdmLocalDisplayFactory: VT of login window is %u", login_window_vt);
+ if (login_window_vt == previous_vt) {
+ GdmDisplay *display;
+
+ g_debug ("GdmLocalDisplayFactory: VT switched from login window");
+
+ display = gdm_display_store_find (store,
+ lookup_by_session_id,
+ (gpointer) login_session_id);
+ if (display != NULL)
+ maybe_stop_greeter_in_background (factory, display);
+ } else {
+ g_debug ("GdmLocalDisplayFactory: VT not switched from login window");
+ }
+ }
+ }
+
+ /* If we jumped to a registered user session, we can kill
+ * the login screen (after a suitable timeout to avoid flicker)
+ */
+ if (factory->active_vt != login_window_vt) {
+ GdmDisplay *display;
+
+ g_clear_handle_id (&factory->seat0_graphics_check_timeout_id, g_source_remove);
+
+ display = gdm_display_store_find (store,
+ lookup_by_tty,
+ (gpointer) tty_of_active_vt);
+
+ if (display != NULL) {
+ gboolean registered;
+
+ g_object_get (display, "session-registered", &registered, NULL);
+
+ if (registered) {
+ g_debug ("GdmLocalDisplayFactory: switched to registered user session, so reaping login screen in %d seconds",
+ WAIT_TO_FINISH_TIMEOUT);
+ if (factory->wait_to_finish_timeout_id != 0) {
+ g_debug ("GdmLocalDisplayFactory: deferring previous login screen clean up operation");
+ g_source_remove (factory->wait_to_finish_timeout_id);
+ }
+
+ factory->wait_to_finish_timeout_id = g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT,
+ (GSourceFunc)
+ on_finish_waiting_for_seat0_displays_timeout,
+ factory);
+ }
+ }
+ }
+
+ /* if user jumped back to initial vt and it's empty put a login screen
+ * on it (unless a login screen is already running elsewhere, then
+ * jump to that login screen)
+ */
+ if (factory->active_vt != GDM_INITIAL_VT) {
+ g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring");
+ return G_SOURCE_CONTINUE;
+ }
+
+ g_debug ("GdmLocalDisplayFactory: creating new display on seat0 because of VT change");
+
+ ensure_display_for_seat (factory, "seat0");
+
+ return G_SOURCE_CONTINUE;
+}
+#endif
+
+#ifdef HAVE_UDEV
+static void
+on_uevent (GUdevClient *client,
+ const char *action,
+ GUdevDevice *device,
+ GdmLocalDisplayFactory *factory)
+{
+ if (!g_udev_device_get_device_file (device))
+ return;
+
+ if (g_strcmp0 (action, "add") != 0 &&
+ g_strcmp0 (action, "change") != 0)
+ return;
+
+ if (!udev_is_settled (factory))
+ return;
+
+ gdm_settings_direct_reload ();
+ ensure_display_for_seat (factory, "seat0");
+}
+#endif
+
+static void
+gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
+{
+ g_autoptr (GIOChannel) io_channel = NULL;
+ const char *subsystems[] = { "drm", NULL };
+
+ factory->seat_new_id = g_dbus_connection_signal_subscribe (factory->connection,
+ "org.freedesktop.login1",
+ "org.freedesktop.login1.Manager",
+ "SeatNew",
+ "/org/freedesktop/login1",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_seat_new,
+ g_object_ref (factory),
+ g_object_unref);
+ factory->seat_removed_id = g_dbus_connection_signal_subscribe (factory->connection,
+ "org.freedesktop.login1",
+ "org.freedesktop.login1.Manager",
+ "SeatRemoved",
+ "/org/freedesktop/login1",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_seat_removed,
+ g_object_ref (factory),
+ g_object_unref);
+ factory->seat_properties_changed_id = g_dbus_connection_signal_subscribe (factory->connection,
+ "org.freedesktop.login1",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ NULL,
+ "org.freedesktop.login1.Seat",
+ G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
+ on_seat_properties_changed,
+ g_object_ref (factory),
+ g_object_unref);
+#ifdef HAVE_UDEV
+ factory->gudev_client = g_udev_client_new (subsystems);
+ factory->uevent_handler_id = g_signal_connect (factory->gudev_client,
+ "uevent",
+ G_CALLBACK (on_uevent),
+ factory);
+#endif
+
+#if defined(ENABLE_USER_DISPLAY_SERVER)
+ io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL);
+
+ if (io_channel != NULL) {
+ factory->active_vt_watch_id =
+ g_io_add_watch (io_channel,
+ G_IO_PRI,
+ (GIOFunc)
+ on_vt_changed,
+ factory);
+ }
+#endif
+}
+
+static void
+gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
+{
+ if (factory->uevent_handler_id) {
+ g_signal_handler_disconnect (factory->gudev_client, factory->uevent_handler_id);
+ factory->uevent_handler_id = 0;
+ }
+ g_clear_object (&factory->gudev_client);
+
+ if (factory->seat_new_id) {
+ g_dbus_connection_signal_unsubscribe (factory->connection,
+ factory->seat_new_id);
+ factory->seat_new_id = 0;
+ }
+ if (factory->seat_removed_id) {
+ g_dbus_connection_signal_unsubscribe (factory->connection,
+ factory->seat_removed_id);
+ factory->seat_removed_id = 0;
+ }
+ if (factory->seat_properties_changed_id) {
+ g_dbus_connection_signal_unsubscribe (factory->connection,
+ factory->seat_properties_changed_id);
+ factory->seat_properties_changed_id = 0;
+ }
+#if defined(ENABLE_USER_DISPLAY_SERVER)
+ if (factory->active_vt_watch_id) {
+ g_source_remove (factory->active_vt_watch_id);
+ factory->active_vt_watch_id = 0;
+ }
+ if (factory->wait_to_finish_timeout_id != 0) {
+ g_source_remove (factory->wait_to_finish_timeout_id);
+ factory->wait_to_finish_timeout_id = 0;
+ }
+#endif
+}
+
+static void
+on_display_added (GdmDisplayStore *display_store,
+ const char *id,
+ GdmLocalDisplayFactory *factory)
+{
+ GdmDisplay *display;
+
+ display = gdm_display_store_lookup (display_store, id);
+
+ if (display != NULL) {
+ g_signal_connect_object (display, "notify::status",
+ G_CALLBACK (on_display_status_changed),
+ factory,
+ 0);
+
+ g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory);
+ }
+}
+
+static void
+on_display_removed (GdmDisplayStore *display_store,
+ GdmDisplay *display,
+ GdmLocalDisplayFactory *factory)
+{
+ g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), factory);
+ g_object_weak_unref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory);
+}
+
+static gboolean
+gdm_local_display_factory_start (GdmDisplayFactory *base_factory)
+{
+ GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (base_factory);
+ GdmDisplayStore *store;
+
+ g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
+
+ factory->is_started = TRUE;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ g_signal_connect_object (G_OBJECT (store),
+ "display-added",
+ G_CALLBACK (on_display_added),
+ factory,
+ 0);
+
+ g_signal_connect_object (G_OBJECT (store),
+ "display-removed",
+ G_CALLBACK (on_display_removed),
+ factory,
+ 0);
+
+ gdm_local_display_factory_start_monitor (factory);
+ return gdm_local_display_factory_sync_seats (factory);
+}
+
+static gboolean
+gdm_local_display_factory_stop (GdmDisplayFactory *base_factory)
+{
+ GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (base_factory);
+ GdmDisplayStore *store;
+
+ g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE);
+
+ gdm_local_display_factory_stop_monitor (factory);
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (store),
+ G_CALLBACK (on_display_added),
+ factory);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (store),
+ G_CALLBACK (on_display_removed),
+ factory);
+ g_clear_handle_id (&factory->seat0_graphics_check_timeout_id, g_source_remove);
+
+ factory->is_started = FALSE;
+
+ return TRUE;
+}
+
+static void
+gdm_local_display_factory_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_local_display_factory_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+handle_create_transient_display (GdmDBusLocalDisplayFactory *skeleton,
+ GDBusMethodInvocation *invocation,
+ GdmLocalDisplayFactory *factory)
+{
+ GError *error = NULL;
+ gboolean created;
+ char *id = NULL;
+
+ created = gdm_local_display_factory_create_transient_display (factory,
+ &id,
+ &error);
+ if (!created) {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ } else {
+ gdm_dbus_local_display_factory_complete_create_transient_display (skeleton, invocation, id);
+ }
+
+ g_free (id);
+ return TRUE;
+}
+
+static gboolean
+register_factory (GdmLocalDisplayFactory *factory)
+{
+ GError *error = NULL;
+
+ error = NULL;
+ factory->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (factory->connection == NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ factory->skeleton = GDM_DBUS_LOCAL_DISPLAY_FACTORY (gdm_dbus_local_display_factory_skeleton_new ());
+
+ g_signal_connect (factory->skeleton,
+ "handle-create-transient-display",
+ G_CALLBACK (handle_create_transient_display),
+ factory);
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (factory->skeleton),
+ factory->connection,
+ GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH,
+ &error)) {
+ g_critical ("error exporting LocalDisplayFactory object: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ return TRUE;
+}
+
+static GObject *
+gdm_local_display_factory_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmLocalDisplayFactory *factory;
+ gboolean res;
+
+ factory = GDM_LOCAL_DISPLAY_FACTORY (G_OBJECT_CLASS (gdm_local_display_factory_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ res = register_factory (factory);
+ if (! res) {
+ g_warning ("Unable to register local display factory with system bus");
+ }
+
+ return G_OBJECT (factory);
+}
+
+static void
+gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdmDisplayFactoryClass *factory_class = GDM_DISPLAY_FACTORY_CLASS (klass);
+
+ object_class->get_property = gdm_local_display_factory_get_property;
+ object_class->set_property = gdm_local_display_factory_set_property;
+ object_class->finalize = gdm_local_display_factory_finalize;
+ object_class->constructor = gdm_local_display_factory_constructor;
+
+ factory_class->start = gdm_local_display_factory_start;
+ factory_class->stop = gdm_local_display_factory_stop;
+}
+
+static void
+gdm_local_display_factory_init (GdmLocalDisplayFactory *factory)
+{
+ factory->used_display_numbers = g_hash_table_new (NULL, NULL);
+}
+
+static void
+gdm_local_display_factory_finalize (GObject *object)
+{
+ GdmLocalDisplayFactory *factory;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (object));
+
+ factory = GDM_LOCAL_DISPLAY_FACTORY (object);
+
+ g_return_if_fail (factory != NULL);
+
+ g_clear_object (&factory->connection);
+ g_clear_object (&factory->skeleton);
+
+ g_hash_table_destroy (factory->used_display_numbers);
+
+ gdm_local_display_factory_stop_monitor (factory);
+
+ G_OBJECT_CLASS (gdm_local_display_factory_parent_class)->finalize (object);
+}
+
+GdmLocalDisplayFactory *
+gdm_local_display_factory_new (GdmDisplayStore *store)
+{
+ if (local_display_factory_object != NULL) {
+ g_object_ref (local_display_factory_object);
+ } else {
+ local_display_factory_object = g_object_new (GDM_TYPE_LOCAL_DISPLAY_FACTORY,
+ "display-store", store,
+ NULL);
+ g_object_add_weak_pointer (local_display_factory_object,
+ (gpointer *) &local_display_factory_object);
+ }
+
+ return GDM_LOCAL_DISPLAY_FACTORY (local_display_factory_object);
+}
diff --git a/daemon/gdm-local-display-factory.h b/daemon/gdm-local-display-factory.h
new file mode 100644
index 0000000..49c7140
--- /dev/null
+++ b/daemon/gdm-local-display-factory.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_LOCAL_DISPLAY_FACTORY_H
+#define __GDM_LOCAL_DISPLAY_FACTORY_H
+
+#include <glib-object.h>
+
+#include "gdm-display-factory.h"
+#include "gdm-display-store.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_LOCAL_DISPLAY_FACTORY (gdm_local_display_factory_get_type ())
+G_DECLARE_FINAL_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM, LOCAL_DISPLAY_FACTORY, GdmDisplayFactory)
+
+typedef enum
+{
+ GDM_LOCAL_DISPLAY_FACTORY_ERROR_GENERAL
+} GdmLocalDisplayFactoryError;
+
+#define GDM_LOCAL_DISPLAY_FACTORY_ERROR gdm_local_display_factory_error_quark ()
+
+GQuark gdm_local_display_factory_error_quark (void);
+
+GdmLocalDisplayFactory * gdm_local_display_factory_new (GdmDisplayStore *display_store);
+
+gboolean gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory,
+ char **id,
+ GError **error);
+G_END_DECLS
+
+#endif /* __GDM_LOCAL_DISPLAY_FACTORY_H */
diff --git a/daemon/gdm-local-display-factory.xml b/daemon/gdm-local-display-factory.xml
new file mode 100644
index 0000000..515d991
--- /dev/null
+++ b/daemon/gdm-local-display-factory.xml
@@ -0,0 +1,8 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/gnome/DisplayManager/LocalDisplayFactory">
+ <interface name="org.gnome.DisplayManager.LocalDisplayFactory">
+ <method name="CreateTransientDisplay">
+ <arg name="id" direction="out" type="o"/>
+ </method>
+ </interface>
+</node>
diff --git a/daemon/gdm-local-display.c b/daemon/gdm-local-display.c
new file mode 100644
index 0000000..69945d9
--- /dev/null
+++ b/daemon/gdm-local-display.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-common.h"
+#include "gdm-display.h"
+#include "gdm-launch-environment.h"
+#include "gdm-local-display.h"
+#include "gdm-local-display-glue.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+struct _GdmLocalDisplay
+{
+ GdmDisplay parent;
+ GdmDBusLocalDisplay *skeleton;
+};
+
+static void gdm_local_display_class_init (GdmLocalDisplayClass *klass);
+static void gdm_local_display_init (GdmLocalDisplay *local_display);
+
+G_DEFINE_TYPE (GdmLocalDisplay, gdm_local_display, GDM_TYPE_DISPLAY)
+
+static GObject *
+gdm_local_display_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmLocalDisplay *display;
+
+ display = GDM_LOCAL_DISPLAY (G_OBJECT_CLASS (gdm_local_display_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ display->skeleton = GDM_DBUS_LOCAL_DISPLAY (gdm_dbus_local_display_skeleton_new ());
+
+ g_dbus_object_skeleton_add_interface (gdm_display_get_object_skeleton (GDM_DISPLAY (display)),
+ G_DBUS_INTERFACE_SKELETON (display->skeleton));
+
+ return G_OBJECT (display);
+}
+
+static void
+gdm_local_display_finalize (GObject *object)
+{
+ GdmLocalDisplay *display = GDM_LOCAL_DISPLAY (object);
+
+ g_clear_object (&display->skeleton);
+
+ G_OBJECT_CLASS (gdm_local_display_parent_class)->finalize (object);
+}
+
+static gboolean
+gdm_local_display_prepare (GdmDisplay *display)
+{
+ GdmLocalDisplay *self = GDM_LOCAL_DISPLAY (display);
+ GdmLaunchEnvironment *launch_environment;
+ char *seat_id;
+ char *session_class;
+ char *session_type;
+ gboolean doing_initial_setup = FALSE;
+ gboolean failed = FALSE;
+
+ seat_id = NULL;
+
+ g_object_get (self,
+ "seat-id", &seat_id,
+ "doing-initial-setup", &doing_initial_setup,
+ "session-class", &session_class,
+ "session-type", &session_type,
+ NULL);
+
+ if (g_strcmp0 (session_class, "greeter") != 0) {
+ goto out;
+ }
+
+ g_debug ("doing initial setup? %s", doing_initial_setup? "yes" : "no");
+
+ if (!doing_initial_setup) {
+ launch_environment = gdm_create_greeter_launch_environment (NULL,
+ seat_id,
+ session_type,
+ NULL,
+ TRUE);
+ } else {
+ launch_environment = gdm_create_initial_setup_launch_environment (NULL,
+ seat_id,
+ session_type,
+ NULL,
+ TRUE);
+ }
+
+ g_object_set (self, "launch-environment", launch_environment, NULL);
+ g_object_unref (launch_environment);
+
+out:
+ g_free (seat_id);
+ g_free (session_class);
+ g_free (session_type);
+
+ if (failed) {
+ return FALSE;
+ }
+ return GDM_DISPLAY_CLASS (gdm_local_display_parent_class)->prepare (display);
+}
+
+static void
+gdm_local_display_class_init (GdmLocalDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass);
+
+ object_class->constructor = gdm_local_display_constructor;
+ object_class->finalize = gdm_local_display_finalize;
+
+ display_class->prepare = gdm_local_display_prepare;
+}
+
+static void
+gdm_local_display_init (GdmLocalDisplay *local_display)
+{
+}
+
+GdmDisplay *
+gdm_local_display_new (void)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_LOCAL_DISPLAY, NULL);
+
+ return GDM_DISPLAY (object);
+}
diff --git a/daemon/gdm-local-display.h b/daemon/gdm-local-display.h
new file mode 100644
index 0000000..5bd7f92
--- /dev/null
+++ b/daemon/gdm-local-display.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_LOCAL_DISPLAY_H
+#define __GDM_LOCAL_DISPLAY_H
+
+#include <glib-object.h>
+#include "gdm-display.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_LOCAL_DISPLAY (gdm_local_display_get_type ())
+G_DECLARE_FINAL_TYPE (GdmLocalDisplay, gdm_local_display, GDM, LOCAL_DISPLAY, GdmDisplay)
+
+GdmDisplay * gdm_local_display_new (void);
+
+
+G_END_DECLS
+
+#endif /* __GDM_LOCAL_DISPLAY_H */
diff --git a/daemon/gdm-local-display.xml b/daemon/gdm-local-display.xml
new file mode 100644
index 0000000..3d52dd2
--- /dev/null
+++ b/daemon/gdm-local-display.xml
@@ -0,0 +1,5 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.gnome.DisplayManager.LocalDisplay">
+ </interface>
+</node>
diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c
new file mode 100644
index 0000000..cc61efc
--- /dev/null
+++ b/daemon/gdm-manager.c
@@ -0,0 +1,2838 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include <act/act-user-manager.h>
+
+#include <systemd/sd-login.h>
+
+#include "gdm-common.h"
+
+#include "gdm-dbus-util.h"
+#include "gdm-manager.h"
+#include "gdm-manager-glue.h"
+#include "gdm-display-store.h"
+#include "gdm-display-factory.h"
+#include "gdm-launch-environment.h"
+#include "gdm-local-display.h"
+#include "gdm-local-display-factory.h"
+#include "gdm-session.h"
+#include "gdm-session-record.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+#include "gdm-xdmcp-display-factory.h"
+#include "gdm-xdmcp-chooser-display.h"
+
+#define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate))
+
+#define GDM_DBUS_PATH "/org/gnome/DisplayManager"
+#define GDM_MANAGER_PATH GDM_DBUS_PATH "/Manager"
+#define GDM_MANAGER_DISPLAYS_PATH GDM_DBUS_PATH "/Displays"
+
+#define INITIAL_SETUP_USERNAME "gnome-initial-setup"
+#define ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT GDM_RUN_DIR "/gdm.ran-initial-setup"
+
+typedef struct
+{
+ GdmManager *manager;
+ GdmSession *session;
+ char *service_name;
+ guint idle_id;
+} StartUserSessionOperation;
+
+struct GdmManagerPrivate
+{
+ GdmDisplayStore *display_store;
+ GdmLocalDisplayFactory *local_factory;
+#ifdef HAVE_LIBXDMCP
+ GdmXdmcpDisplayFactory *xdmcp_factory;
+#endif
+ GdmDisplay *automatic_login_display;
+ GList *user_sessions;
+ GHashTable *transient_sessions;
+ GHashTable *open_reauthentication_requests;
+ gboolean xdmcp_enabled;
+
+ gboolean started;
+ gboolean show_local_greeter;
+
+ GDBusConnection *connection;
+ GDBusObjectManagerServer *object_manager;
+
+#ifdef WITH_PLYMOUTH
+ guint plymouth_is_running : 1;
+#endif
+ guint did_automatic_login : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_XDMCP_ENABLED,
+ PROP_SHOW_LOCAL_GREETER
+};
+
+enum {
+ DISPLAY_ADDED,
+ DISPLAY_REMOVED,
+ LAST_SIGNAL
+};
+
+typedef enum {
+ SESSION_RECORD_LOGIN,
+ SESSION_RECORD_LOGOUT,
+ SESSION_RECORD_FAILED,
+} SessionRecord;
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_manager_class_init (GdmManagerClass *klass);
+static void gdm_manager_init (GdmManager *manager);
+static void gdm_manager_dispose (GObject *object);
+
+static void create_user_session_for_display (GdmManager *manager,
+ GdmDisplay *display,
+ uid_t allowed_user);
+static void start_user_session (GdmManager *manager,
+ StartUserSessionOperation *operation);
+static void clean_user_session (GdmSession *session);
+
+static gpointer manager_object = NULL;
+
+static void manager_interface_init (GdmDBusManagerIface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmManager,
+ gdm_manager,
+ GDM_DBUS_TYPE_MANAGER_SKELETON,
+ G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_MANAGER,
+ manager_interface_init)
+ G_ADD_PRIVATE (GdmManager));
+
+#ifdef WITH_PLYMOUTH
+static gboolean
+plymouth_is_running (void)
+{
+ int status;
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_sync ("plymouth --ping",
+ NULL, NULL, &status, &error);
+ if (! res) {
+ g_debug ("Could not ping plymouth: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
+}
+
+static void
+plymouth_prepare_for_transition (void)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_sync ("plymouth deactivate",
+ NULL, NULL, NULL, &error);
+ if (! res) {
+ g_warning ("Could not deactivate plymouth: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static gboolean
+plymouth_quit_with_transition (void)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_async ("plymouth quit --retain-splash", &error);
+ if (! res) {
+ g_warning ("Could not quit plymouth: %s", error->message);
+ g_error_free (error);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+plymouth_quit_without_transition (void)
+{
+ gboolean res;
+ GError *error;
+
+ error = NULL;
+ res = g_spawn_command_line_async ("plymouth quit", &error);
+ if (! res) {
+ g_warning ("Could not quit plymouth: %s", error->message);
+ g_error_free (error);
+ }
+}
+#endif
+
+static char *
+get_session_id_for_pid (pid_t pid,
+ GError **error)
+{
+ char *session, *gsession;
+ int ret;
+
+ session = NULL;
+ ret = sd_pid_get_session (pid, &session);
+ if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting session id from systemd: %s",
+ g_strerror (-ret));
+ return NULL;
+ }
+
+ if (session != NULL) {
+ gsession = g_strdup (session);
+ free (session);
+
+ return gsession;
+ } else {
+ return NULL;
+ }
+}
+
+static gboolean
+get_uid_for_session_id (const char *session_id,
+ uid_t *uid,
+ GError **error)
+{
+ int ret;
+
+ ret = sd_session_get_uid (session_id, uid);
+ if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting uid for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+lookup_by_session_id (const char *id,
+ GdmDisplay *display,
+ gpointer user_data)
+{
+ const char *looking_for = user_data;
+ const char *current;
+
+ current = gdm_display_get_session_id (display);
+ return g_strcmp0 (current, looking_for) == 0;
+}
+
+static gboolean
+is_login_session (GdmManager *self,
+ const char *session_id,
+ GError **error)
+{
+ char *session_class = NULL;
+ int ret;
+
+ ret = sd_session_get_class (session_id, &session_class);
+
+ if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting class for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ return FALSE;
+ }
+
+ if (g_strcmp0 (session_class, "greeter") != 0) {
+ g_free (session_class);
+ return FALSE;
+ }
+
+ g_free (session_class);
+ return TRUE;
+}
+
+static gboolean
+session_unlock (GdmManager *manager,
+ const char *ssid)
+{
+ GError *error = NULL;
+ GVariant *reply;
+
+ g_debug ("Unlocking session %s", ssid);
+
+ reply = g_dbus_connection_call_sync (manager->priv->connection,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "UnlockSession",
+ g_variant_new ("(s)", ssid),
+ NULL, /* expected reply */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (reply == NULL) {
+ g_debug ("GdmManager: logind 'UnlockSession' %s raised:\n %s\n\n",
+ g_dbus_error_get_remote_error (error), error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ g_variant_unref (reply);
+
+ return TRUE;
+}
+
+static GdmSession *
+find_session_for_user_on_seat (GdmManager *manager,
+ const char *username,
+ const char *seat_id,
+ GdmSession *dont_count_session)
+{
+ GList *node;
+
+ for (node = manager->priv->user_sessions; node != NULL; node = node->next) {
+ GdmSession *candidate_session = node->data;
+ const char *candidate_username, *candidate_seat_id, *candidate_session_id;
+
+ candidate_session_id = gdm_session_get_session_id (candidate_session);
+
+ if (candidate_session == dont_count_session) {
+ g_debug ("GdmSession: Ignoring session %s as requested",
+ candidate_session_id);
+ continue;
+ }
+
+ if (!gdm_session_is_running (candidate_session)) {
+ g_debug ("GdmSession: Ignoring session %s as it isn't running",
+ candidate_session_id);
+ continue;
+ }
+
+ candidate_username = gdm_session_get_username (candidate_session);
+ candidate_seat_id = gdm_session_get_display_seat_id (candidate_session);
+
+ g_debug ("GdmManager: Considering session %s on seat %s belonging to user %s",
+ candidate_session_id,
+ candidate_seat_id,
+ candidate_username);
+
+ if (g_strcmp0 (candidate_username, username) == 0 &&
+ g_strcmp0 (candidate_seat_id, seat_id) == 0) {
+ g_debug ("GdmManager: yes, found session %s", candidate_session_id);
+ return candidate_session;
+ }
+
+ g_debug ("GdmManager: no, will not use session %s", candidate_session_id);
+ }
+
+ g_debug ("GdmManager: no matching sessions found");
+ return NULL;
+}
+
+static gboolean
+is_remote_session (GdmManager *self,
+ const char *session_id,
+ GError **error)
+{
+ char *seat;
+ int ret;
+ gboolean is_remote;
+
+ /* FIXME: The next release of logind is going to have explicit api for
+ * checking remoteness.
+ */
+ seat = NULL;
+ ret = sd_session_get_seat (session_id, &seat);
+
+ if (ret < 0 && ret != -ENXIO) {
+ g_debug ("GdmManager: Error while retrieving seat for session %s: %s",
+ session_id, strerror (-ret));
+ }
+
+ if (seat != NULL) {
+ is_remote = FALSE;
+ free (seat);
+ } else {
+ is_remote = TRUE;
+ }
+
+ return is_remote;
+}
+
+static char *
+get_seat_id_for_session_id (const char *session_id,
+ GError **error)
+{
+ int ret;
+ char *seat, *out_seat;
+
+ seat = NULL;
+ ret = sd_session_get_seat (session_id, &seat);
+
+ if (ret == -ENXIO) {
+ out_seat = NULL;
+ } else if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting uid for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ out_seat = NULL;
+ } else {
+ out_seat = g_strdup (seat);
+ free (seat);
+ }
+
+ return out_seat;
+}
+
+static char *
+get_tty_for_session_id (const char *session_id,
+ GError **error)
+{
+ int ret;
+ char *tty, *out_tty;
+
+ ret = sd_session_get_tty (session_id, &tty);
+
+ if (ret == -ENXIO) {
+ out_tty = NULL;
+ } else if (ret < 0) {
+ g_set_error (error,
+ GDM_DISPLAY_ERROR,
+ GDM_DISPLAY_ERROR_GETTING_SESSION_INFO,
+ "Error getting tty for session id %s from systemd: %s",
+ session_id,
+ g_strerror (-ret));
+ out_tty = NULL;
+ } else {
+ out_tty = g_strdup (tty);
+ free (tty);
+ }
+
+ return out_tty;
+}
+
+static void
+get_display_and_details_for_bus_sender (GdmManager *self,
+ GDBusConnection *connection,
+ const char *sender,
+ GdmDisplay **out_display,
+ char **out_seat_id,
+ char **out_session_id,
+ char **out_tty,
+ GPid *out_pid,
+ uid_t *out_uid,
+ gboolean *out_is_login_screen,
+ gboolean *out_is_remote)
+{
+ GdmDisplay *display = NULL;
+ char *session_id = NULL;
+ GError *error = NULL;
+ int ret;
+ GPid pid;
+ uid_t caller_uid, session_uid;
+
+ ret = gdm_dbus_get_pid_for_name (sender, &pid, &error);
+
+ if (!ret) {
+ g_debug ("GdmManager: Error while retrieving pid for sender: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (out_pid != NULL) {
+ *out_pid = pid;
+ }
+
+ ret = gdm_dbus_get_uid_for_name (sender, &caller_uid, &error);
+
+ if (!ret) {
+ g_debug ("GdmManager: Error while retrieving uid for sender: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ ret = gdm_find_display_session (pid, caller_uid, &session_id, &error);
+
+ if (!ret) {
+ g_debug ("GdmManager: Unable to find display session for uid %d: %s",
+ (int) caller_uid,
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (out_session_id != NULL) {
+ *out_session_id = g_strdup (session_id);
+ }
+
+ if (out_is_login_screen != NULL) {
+ *out_is_login_screen = is_login_session (self, session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while checking if sender is login screen: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+ }
+
+ if (!get_uid_for_session_id (session_id, &session_uid, &error)) {
+ g_debug ("GdmManager: Error while retrieving uid for session: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (out_uid != NULL) {
+ *out_uid = caller_uid;
+ }
+
+ if (caller_uid != session_uid) {
+ g_debug ("GdmManager: uid for sender and uid for session don't match");
+ goto out;
+ }
+
+ if (out_seat_id != NULL) {
+ *out_seat_id = get_seat_id_for_session_id (session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while retrieving seat id for session: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (out_is_remote != NULL) {
+ *out_is_remote = is_remote_session (self, session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while retrieving remoteness for session: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (out_tty != NULL) {
+ *out_tty = get_tty_for_session_id (session_id, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmManager: Error while retrieving tty for session: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ display = gdm_display_store_find (self->priv->display_store,
+ lookup_by_session_id,
+ (gpointer) session_id);
+
+out:
+ if (out_display != NULL) {
+ *out_display = display;
+ }
+
+ g_free (session_id);
+}
+
+static gboolean
+switch_to_compatible_user_session (GdmManager *manager,
+ GdmSession *session,
+ gboolean fail_if_already_switched)
+{
+ gboolean res;
+ gboolean ret;
+ const char *username;
+ const char *seat_id;
+ const char *ssid_to_activate;
+ GdmSession *existing_session;
+
+ ret = FALSE;
+
+ username = gdm_session_get_username (session);
+ seat_id = gdm_session_get_display_seat_id (session);
+
+ if (!fail_if_already_switched) {
+ session = NULL;
+ }
+
+ existing_session = find_session_for_user_on_seat (manager, username, seat_id, session);
+
+ if (existing_session != NULL) {
+ ssid_to_activate = gdm_session_get_session_id (existing_session);
+ if (seat_id != NULL) {
+ res = gdm_activate_session_by_id (manager->priv->connection, seat_id, ssid_to_activate);
+ if (! res) {
+ g_debug ("GdmManager: unable to activate session: %s", ssid_to_activate);
+ goto out;
+ }
+ }
+
+ res = session_unlock (manager, ssid_to_activate);
+ if (!res) {
+ /* this isn't fatal */
+ g_debug ("GdmManager: unable to unlock session: %s", ssid_to_activate);
+ }
+ } else {
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static GdmDisplay *
+get_display_for_user_session (GdmSession *session)
+{
+ return g_object_get_data (G_OBJECT (session), "gdm-display");
+}
+
+static GdmSession *
+get_user_session_for_display (GdmDisplay *display)
+{
+ if (display == NULL) {
+ return NULL;
+ }
+
+ return g_object_get_data (G_OBJECT (display), "gdm-user-session");
+}
+
+static gboolean
+add_session_record (GdmManager *manager,
+ GdmSession *session,
+ GPid pid,
+ SessionRecord record)
+{
+ const char *username;
+ char *display_name, *hostname, *display_device;
+ gboolean recorded = FALSE;
+
+ display_name = NULL;
+ username = NULL;
+ hostname = NULL;
+ display_device = NULL;
+
+ username = gdm_session_get_username (session);
+
+ if (username == NULL) {
+ goto out;
+ }
+
+ g_object_get (G_OBJECT (session),
+ "display-name", &display_name,
+ "display-hostname", &hostname,
+ "display-device", &display_device,
+ NULL);
+
+ if (display_name == NULL && display_device == NULL) {
+ goto out;
+ }
+
+ switch (record) {
+ case SESSION_RECORD_LOGIN:
+ gdm_session_record_login (pid,
+ username,
+ hostname,
+ display_name,
+ display_device);
+ break;
+ case SESSION_RECORD_LOGOUT:
+ gdm_session_record_logout (pid,
+ username,
+ hostname,
+ display_name,
+ display_device);
+ break;
+ case SESSION_RECORD_FAILED:
+ gdm_session_record_failed (pid,
+ username,
+ hostname,
+ display_name,
+ display_device);
+ break;
+ }
+
+ recorded = TRUE;
+out:
+ g_free (display_name);
+ g_free (hostname);
+ g_free (display_device);
+
+ return recorded;
+}
+
+static GdmSession *
+find_user_session_for_display (GdmManager *self,
+ GdmDisplay *display)
+{
+
+ GList *node = self->priv->user_sessions;
+
+ while (node != NULL) {
+ GdmSession *session = node->data;
+ GdmDisplay *candidate_display;
+ GList *next_node = node->next;
+
+ candidate_display = get_display_for_user_session (session);
+
+ if (candidate_display == display)
+ return session;
+
+ node = next_node;
+ }
+
+ return NULL;
+}
+
+static gboolean
+gdm_manager_handle_register_display (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation,
+ GVariant *details)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ const char *sender;
+ GDBusConnection *connection;
+ GdmDisplay *display = NULL;
+ GdmSession *session;
+ GVariantIter iter;
+ char *key = NULL;
+ char *value = NULL;
+ char *x11_display_name = NULL;
+ char *tty = NULL;
+
+ g_debug ("GdmManager: trying to register new display");
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, &tty, NULL, NULL, NULL, NULL);
+
+ if (display == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("No display available"));
+
+ return TRUE;
+ }
+
+ g_variant_iter_init (&iter, details);
+ while (g_variant_iter_loop (&iter, "{&s&s}", &key, &value)) {
+ if (g_strcmp0 (key, "x11-display-name") == 0) {
+ x11_display_name = g_strdup (value);
+ break;
+ }
+ }
+
+ session = find_user_session_for_display (self, display);
+
+ if (session != NULL) {
+ GPid pid;
+
+ if (x11_display_name != NULL) {
+ g_object_set (G_OBJECT (session), "display-name", x11_display_name, NULL);
+ g_object_set (G_OBJECT (display), "x11-display-name", x11_display_name, NULL);
+ }
+
+ /* FIXME: this should happen in gdm-session.c when the session is opened
+ */
+ if (tty != NULL)
+ g_object_set (G_OBJECT (session), "display-device", tty, NULL);
+
+ pid = gdm_session_get_pid (session);
+
+ if (pid > 0) {
+ add_session_record (self, session, pid, SESSION_RECORD_LOGIN);
+ }
+ }
+
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL);
+
+ gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager),
+ invocation);
+
+ g_clear_pointer (&x11_display_name, g_free);
+ g_clear_pointer (&tty, g_free);
+ return TRUE;
+}
+
+static gboolean
+gdm_manager_handle_register_session (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation,
+ GVariant *details)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ GdmDisplay *display = NULL;
+ const char *sender;
+ GDBusConnection *connection;
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+
+ get_display_and_details_for_bus_sender (self, connection, sender, &display,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ g_debug ("GdmManager: trying to register new session on display %p", display);
+
+ if (display != NULL)
+ g_object_set (G_OBJECT (display), "session-registered", TRUE, NULL);
+ else
+ g_debug ("GdmManager: No display, not registering");
+
+ gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager),
+ invocation);
+
+ return TRUE;
+}
+
+static gboolean
+gdm_manager_handle_open_session (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ const char *sender;
+ GDBusConnection *connection;
+ GdmDisplay *display = NULL;
+ GdmSession *session = NULL;
+ const char *address;
+ GPid pid = 0;
+ uid_t uid = (uid_t) -1;
+ uid_t allowed_user;
+
+ g_debug ("GdmManager: trying to open new session");
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, NULL, &pid, &uid, NULL, NULL);
+
+ if (display == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("No session available"));
+
+ return TRUE;
+ }
+
+#ifdef HAVE_LIBXDMCP
+ if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) {
+ GdmLaunchEnvironment *launch_environment;
+
+ g_object_get (display, "launch-environment", &launch_environment, NULL);
+
+ if (launch_environment != NULL) {
+ session = gdm_launch_environment_get_session (launch_environment);
+ }
+
+ if (session == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Chooser session unavailable"));
+ return TRUE;
+ }
+ }
+#endif
+ if (session == NULL) {
+ session = get_user_session_for_display (display);
+ g_debug ("GdmSession: Considering session %s for username %s",
+ gdm_session_get_session_id (session),
+ gdm_session_get_username (session));
+
+ if (gdm_session_is_running (session)) {
+ g_debug ("GdmSession: the session is running, and therefore can't be used");
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Can only be called before user is logged in"));
+ return TRUE;
+ }
+ }
+
+ allowed_user = gdm_session_get_allowed_user (session);
+
+ if (uid != allowed_user) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Caller not GDM"));
+ return TRUE;
+ }
+
+ address = gdm_session_get_server_address (session);
+
+ if (address == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("Unable to open private communication channel"));
+ return TRUE;
+ }
+
+ gdm_dbus_manager_complete_open_session (GDM_DBUS_MANAGER (manager),
+ invocation,
+ address);
+ return TRUE;
+}
+
+static void
+close_transient_session (GdmManager *self,
+ GdmSession *session)
+{
+ GPid pid;
+ pid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (session), "caller-pid"));
+ gdm_session_close (session);
+ g_hash_table_remove (self->priv->transient_sessions,
+ GUINT_TO_POINTER (pid));
+}
+
+static void
+on_reauthentication_client_connected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: client connected to reauthentication server");
+}
+
+static void
+on_reauthentication_client_disconnected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *self)
+{
+ g_debug ("GdmManger: client disconnected from reauthentication server");
+ close_transient_session (self, session);
+}
+
+static void
+on_reauthentication_client_rejected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *self)
+{
+ GPid pid;
+
+ g_debug ("GdmManger: client with pid %ld rejected from reauthentication server", (long) pid_of_client);
+
+ if (gdm_session_client_is_connected (session)) {
+ /* we already have a client connected, ignore this rejected one */
+ return;
+ }
+
+ pid = (GPid) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (session), "caller-pid"));
+
+ if (pid != pid_of_client) {
+ const char *session_id;
+ char *client_session_id;
+
+ /* rejected client isn't the process that started the
+ * transient reauthentication session. If it's not even from the
+ * same audit session, ignore it since it doesn't "own" the
+ * reauthentication session
+ */
+ client_session_id = get_session_id_for_pid (pid_of_client,
+ NULL);
+ session_id = g_object_get_data (G_OBJECT (session), "caller-session-id");
+
+ if (g_strcmp0 (session_id, client_session_id) != 0) {
+ return;
+ }
+ }
+
+ /* client was rejected, so clean up its session object
+ */
+ close_transient_session (self, session);
+}
+
+static void
+on_reauthentication_cancelled (GdmSession *session,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: client cancelled reauthentication request");
+ close_transient_session (self, session);
+}
+
+static void
+on_reauthentication_conversation_started (GdmSession *session,
+ const char *service_name,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: reauthentication service '%s' started",
+ service_name);
+}
+
+static void
+on_reauthentication_conversation_stopped (GdmSession *session,
+ const char *service_name,
+ GdmManager *self)
+{
+ g_debug ("GdmManager: reauthentication service '%s' stopped",
+ service_name);
+}
+
+static void
+on_reauthentication_verification_complete (GdmSession *session,
+ const char *service_name,
+ GdmManager *self)
+{
+ const char *session_id;
+ session_id = g_object_get_data (G_OBJECT (session), "caller-session-id");
+ g_debug ("GdmManager: reauthenticated user in unmanaged session '%s' with service '%s'",
+ session_id, service_name);
+ session_unlock (self, session_id);
+ close_transient_session (self, session);
+}
+
+static char *
+open_temporary_reauthentication_channel (GdmManager *self,
+ char *seat_id,
+ char *session_id,
+ GPid pid,
+ uid_t uid,
+ gboolean is_remote)
+{
+ GdmSession *session;
+ char **environment;
+ const char *display, *auth_file;
+ const char *address;
+
+ /* Note we're just using a minimal environment here rather than the
+ * session's environment because the caller is unprivileged and the
+ * associated worker will be privileged */
+ environment = g_get_environ ();
+ display = "";
+ auth_file = "/dev/null";
+
+ session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE,
+ uid,
+ display,
+ NULL,
+ NULL,
+ seat_id,
+ auth_file,
+ is_remote == FALSE,
+ (const char * const *)
+ environment);
+ g_strfreev (environment);
+
+ g_debug ("GdmSession: Created session for temporary reauthentication channel for user %d (seat %s)",
+ (int) uid,
+ seat_id);
+
+ g_object_set_data_full (G_OBJECT (session),
+ "caller-session-id",
+ g_strdup (session_id),
+ (GDestroyNotify)
+ g_free);
+ g_object_set_data (G_OBJECT (session),
+ "caller-pid",
+ GUINT_TO_POINTER (pid));
+ g_hash_table_insert (self->priv->transient_sessions,
+ GINT_TO_POINTER (pid),
+ session);
+
+ g_signal_connect (session,
+ "client-connected",
+ G_CALLBACK (on_reauthentication_client_connected),
+ self);
+ g_signal_connect (session,
+ "client-disconnected",
+ G_CALLBACK (on_reauthentication_client_disconnected),
+ self);
+ g_signal_connect (session,
+ "client-rejected",
+ G_CALLBACK (on_reauthentication_client_rejected),
+ self);
+ g_signal_connect (session,
+ "cancelled",
+ G_CALLBACK (on_reauthentication_cancelled),
+ self);
+ g_signal_connect (session,
+ "conversation-started",
+ G_CALLBACK (on_reauthentication_conversation_started),
+ self);
+ g_signal_connect (session,
+ "conversation-stopped",
+ G_CALLBACK (on_reauthentication_conversation_stopped),
+ self);
+ g_signal_connect (session,
+ "verification-complete",
+ G_CALLBACK (on_reauthentication_verification_complete),
+ self);
+
+ address = gdm_session_get_server_address (session);
+
+ return g_strdup (address);
+}
+
+static gboolean
+gdm_manager_handle_open_reauthentication_channel (GdmDBusManager *manager,
+ GDBusMethodInvocation *invocation,
+ const char *username)
+{
+ GdmManager *self = GDM_MANAGER (manager);
+ const char *sender;
+ GdmDisplay *display = NULL;
+ GdmSession *session;
+ GDBusConnection *connection;
+ char *seat_id = NULL;
+ char *session_id = NULL;
+ GPid pid = 0;
+ uid_t uid = (uid_t) -1;
+ gboolean is_login_screen = FALSE;
+ gboolean is_remote = FALSE;
+
+ g_debug ("GdmManager: trying to open reauthentication channel for user %s", username);
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ get_display_and_details_for_bus_sender (self, connection, sender, &display, &seat_id, &session_id, NULL, &pid, &uid, &is_login_screen, &is_remote);
+
+ if (session_id == NULL || pid == 0 || uid == (uid_t) -1) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ _("No session available"));
+
+ return TRUE;
+ }
+
+ if (is_login_screen) {
+ g_debug ("GdmManager: looking for login screen session for user %s on seat %s", username, seat_id);
+ session = find_session_for_user_on_seat (self,
+ username,
+ seat_id,
+ NULL);
+ } else {
+ g_debug ("GdmManager: looking for user session on display");
+ session = get_user_session_for_display (display);
+ }
+
+ if (session != NULL && gdm_session_is_running (session)) {
+ gdm_session_start_reauthentication (session, pid, uid);
+ g_hash_table_insert (self->priv->open_reauthentication_requests,
+ GINT_TO_POINTER (pid),
+ invocation);
+ } else if (is_login_screen) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Login screen only allowed to open reauthentication channels for running sessions");
+ return TRUE;
+ } else {
+ char *address;
+ address = open_temporary_reauthentication_channel (self,
+ seat_id,
+ session_id,
+ pid,
+ uid,
+ is_remote);
+ gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager),
+ invocation,
+ address);
+ g_free (address);
+ }
+
+ return TRUE;
+}
+
+static void
+manager_interface_init (GdmDBusManagerIface *interface)
+{
+ interface->handle_register_display = gdm_manager_handle_register_display;
+ interface->handle_register_session = gdm_manager_handle_register_session;
+ interface->handle_open_session = gdm_manager_handle_open_session;
+ interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel;
+}
+
+static gboolean
+display_is_on_seat0 (GdmDisplay *display)
+{
+ gboolean is_on_seat0 = TRUE;
+ char *seat_id = NULL;
+
+ g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL);
+
+ if (g_strcmp0 (seat_id, "seat0") != 0) {
+ is_on_seat0 = FALSE;
+ }
+
+ g_free (seat_id);
+
+ return is_on_seat0;
+}
+
+static gboolean
+get_timed_login_details (GdmManager *manager,
+ char **usernamep,
+ int *delayp)
+{
+ gboolean res;
+ gboolean enabled;
+
+ int delay;
+ char *username = NULL;
+
+ enabled = FALSE;
+ username = NULL;
+ delay = 0;
+
+ res = gdm_settings_direct_get_boolean (GDM_KEY_TIMED_LOGIN_ENABLE, &enabled);
+ if (res && ! enabled) {
+ goto out;
+ }
+
+ res = gdm_settings_direct_get_string (GDM_KEY_TIMED_LOGIN_USER, &username);
+ if (res && (username == NULL || username[0] == '\0')) {
+ g_clear_pointer (&username, g_free);
+ goto out;
+ }
+
+ delay = 0;
+ res = gdm_settings_direct_get_int (GDM_KEY_TIMED_LOGIN_DELAY, &delay);
+
+ if (res && delay <= 0) {
+ /* we don't allow the timed login to have a zero delay */
+ delay = 10;
+ }
+
+ out:
+ if (enabled) {
+ g_debug ("GdmDisplay: Got timed login details for display: %d %s %d",
+ enabled,
+ username,
+ delay);
+ } else {
+ g_debug ("GdmDisplay: Got timed login details for display: 0");
+ }
+
+ if (usernamep != NULL) {
+ *usernamep = username;
+ } else {
+ g_free (username);
+ }
+ if (delayp != NULL) {
+ *delayp = delay;
+ }
+
+ return enabled;
+}
+
+static gboolean
+get_automatic_login_details (GdmManager *manager,
+ char **usernamep)
+{
+ gboolean res;
+ gboolean enabled;
+ char *username = NULL;
+
+ enabled = FALSE;
+ username = NULL;
+
+ res = gdm_settings_direct_get_boolean (GDM_KEY_AUTO_LOGIN_ENABLE, &enabled);
+ if (res && enabled) {
+ res = gdm_settings_direct_get_string (GDM_KEY_AUTO_LOGIN_USER, &username);
+ }
+
+ if (enabled && res && username != NULL && username[0] != '\0') {
+ goto out;
+ }
+
+ g_free (username);
+ username = NULL;
+ enabled = FALSE;
+
+ out:
+ if (enabled) {
+ g_debug ("GdmDisplay: Got automatic login details for display: %d %s",
+ enabled,
+ username);
+ } else {
+ g_debug ("GdmDisplay: Got automatic login details for display: 0");
+ }
+
+ if (usernamep != NULL) {
+ *usernamep = username;
+ } else {
+ g_free (username);
+ }
+
+ return enabled;
+}
+
+static const char *
+get_username_for_greeter_display (GdmManager *manager,
+ GdmDisplay *display)
+{
+ gboolean doing_initial_setup = FALSE;
+
+ g_object_get (G_OBJECT (display),
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ if (doing_initial_setup) {
+ return INITIAL_SETUP_USERNAME;
+ } else {
+ return GDM_USERNAME;
+ }
+}
+
+static void
+set_up_automatic_login_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ GdmSession *session;
+ g_auto (GStrv) supported_session_types = NULL;
+
+ /* 0 is root user; since the daemon talks to the session object
+ * directly, itself, for automatic login
+ */
+ create_user_session_for_display (manager, display, 0);
+ session = get_user_session_for_display (display);
+
+ g_object_get (G_OBJECT (display),
+ "supported-session-types", &supported_session_types,
+ NULL);
+
+ g_object_set (G_OBJECT (session),
+ "display-is-initial", FALSE,
+ "supported-session-types", supported_session_types,
+ NULL);
+
+ g_debug ("GdmManager: Starting automatic login conversation");
+ gdm_session_start_conversation (session, "gdm-autologin");
+}
+
+static void
+set_up_chooser_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ const char *allowed_user;
+ struct passwd *passwd_entry;
+
+ allowed_user = get_username_for_greeter_display (manager, display);
+
+ if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) {
+ g_warning ("GdmManager: couldn't look up username %s",
+ allowed_user);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ return;
+ }
+
+ gdm_display_start_greeter_session (display);
+}
+
+static void
+set_up_greeter_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ const char *allowed_user;
+ struct passwd *passwd_entry;
+
+ allowed_user = get_username_for_greeter_display (manager, display);
+
+ if (!gdm_get_pwent_for_name (allowed_user, &passwd_entry)) {
+ g_warning ("GdmManager: couldn't look up username %s",
+ allowed_user);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ return;
+ }
+
+ create_user_session_for_display (manager, display, passwd_entry->pw_uid);
+ gdm_display_start_greeter_session (display);
+}
+
+static void
+set_up_automatic_login_session_if_user_exists (GdmManager *manager,
+ GdmDisplay *display,
+ ActUser *user)
+{
+ if (act_user_is_nonexistent (user))
+ set_up_greeter_session (manager, display);
+ else
+ set_up_automatic_login_session (manager, display);
+}
+
+typedef struct {
+ GdmManager *manager;
+ GdmDisplay *display;
+ char *username;
+} UsernameLookupOperation;
+
+static void
+destroy_username_lookup_operation (UsernameLookupOperation *operation)
+{
+ g_object_unref (operation->manager);
+ g_object_unref (operation->display);
+ g_free (operation->username);
+ g_free (operation);
+}
+
+static void
+on_user_is_loaded_changed (ActUser *user,
+ GParamSpec *pspec,
+ UsernameLookupOperation *operation)
+{
+ if (act_user_is_loaded (user)) {
+ set_up_automatic_login_session_if_user_exists (operation->manager, operation->display, user);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (user),
+ G_CALLBACK (on_user_is_loaded_changed),
+ operation);
+ destroy_username_lookup_operation (operation);
+ }
+}
+
+static void
+set_up_session (GdmManager *manager,
+ GdmDisplay *display)
+{
+ ActUserManager *user_manager;
+ ActUser *user;
+ gboolean loaded;
+ gboolean seat_can_autologin = FALSE, seat_did_autologin = FALSE;
+ gboolean autologin_enabled = FALSE;
+ g_autofree char *seat_id = NULL;
+ char *username = NULL;
+
+ g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL);
+
+ if (g_strcmp0 (seat_id, "seat0") == 0)
+ seat_can_autologin = TRUE;
+
+ if (manager->priv->did_automatic_login || manager->priv->automatic_login_display != NULL)
+ seat_did_autologin = TRUE;
+
+ if (seat_can_autologin && !seat_did_autologin)
+ autologin_enabled = get_automatic_login_details (manager, &username);
+
+ if (!autologin_enabled) {
+ g_free (username);
+
+#ifdef HAVE_LIBXDMCP
+ if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) {
+ set_up_chooser_session (manager, display);
+ return;
+ }
+#endif
+
+ set_up_greeter_session (manager, display);
+ return;
+ }
+
+ /* Check whether the user really exists before committing to autologin. */
+ user_manager = act_user_manager_get_default ();
+ user = act_user_manager_get_user (user_manager, username);
+ g_object_get (user_manager, "is-loaded", &loaded, NULL);
+
+ if (loaded) {
+ set_up_automatic_login_session_if_user_exists (manager, display, user);
+ } else {
+ UsernameLookupOperation *operation;
+
+ operation = g_new (UsernameLookupOperation, 1);
+ operation->manager = g_object_ref (manager);
+ operation->display = g_object_ref (display);
+ operation->username = username;
+
+ g_signal_connect (user,
+ "notify::is-loaded",
+ G_CALLBACK (on_user_is_loaded_changed),
+ operation);
+ }
+}
+
+static void
+on_display_status_changed (GdmDisplay *display,
+ GParamSpec *arg1,
+ GdmManager *manager)
+{
+ int status;
+ int display_number = -1;
+ char *session_type = NULL;
+ gboolean doing_initial_setup = FALSE;
+#ifdef WITH_PLYMOUTH
+ gboolean display_is_local = FALSE;
+ gboolean quit_plymouth = FALSE;
+
+ g_object_get (display,
+ "is-local", &display_is_local,
+ NULL);
+ quit_plymouth = display_is_local && manager->priv->plymouth_is_running;
+#endif
+
+ g_object_get (display,
+ "x11-display-number", &display_number,
+ "session-type", &session_type,
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ status = gdm_display_get_status (display);
+
+ switch (status) {
+ case GDM_DISPLAY_PREPARED:
+ case GDM_DISPLAY_MANAGED:
+ if ((display_number == -1 && status == GDM_DISPLAY_PREPARED) ||
+ (display_number != -1 && status == GDM_DISPLAY_MANAGED)) {
+ char *session_class;
+
+ g_object_get (display,
+ "session-class", &session_class,
+ NULL);
+ if (g_strcmp0 (session_class, "greeter") == 0)
+ set_up_session (manager, display);
+ g_free (session_class);
+ }
+ break;
+ case GDM_DISPLAY_FAILED:
+ case GDM_DISPLAY_UNMANAGED:
+ case GDM_DISPLAY_FINISHED:
+#ifdef WITH_PLYMOUTH
+ if (quit_plymouth) {
+ plymouth_quit_without_transition ();
+ manager->priv->plymouth_is_running = FALSE;
+ }
+#endif
+
+ g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL);
+
+ if (display == manager->priv->automatic_login_display) {
+ g_clear_weak_pointer (&manager->priv->automatic_login_display);
+
+ manager->priv->did_automatic_login = TRUE;
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ if (g_strcmp0 (session_type, "wayland") != 0 && status == GDM_DISPLAY_FAILED) {
+ /* we're going to fall back to X11, so try to autologin again
+ */
+ manager->priv->did_automatic_login = FALSE;
+ }
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+static void
+on_display_removed (GdmDisplayStore *display_store,
+ GdmDisplay *display,
+ GdmManager *manager)
+{
+ char *id;
+
+ gdm_display_get_id (display, &id, NULL);
+ g_dbus_object_manager_server_unexport (manager->priv->object_manager, id);
+ g_free (id);
+
+ g_signal_handlers_disconnect_by_func (display, G_CALLBACK (on_display_status_changed), manager);
+
+ g_signal_emit (manager, signals[DISPLAY_REMOVED], 0, display);
+}
+
+static void
+destroy_start_user_session_operation (StartUserSessionOperation *operation)
+{
+ g_object_set_data (G_OBJECT (operation->session),
+ "start-user-session-operation",
+ NULL);
+ g_object_unref (operation->session);
+ g_free (operation->service_name);
+ g_slice_free (StartUserSessionOperation, operation);
+}
+
+static void
+start_user_session (GdmManager *manager,
+ StartUserSessionOperation *operation)
+{
+ GdmDisplay *display;
+
+ display = get_display_for_user_session (operation->session);
+
+ if (display != NULL) {
+ char *auth_file;
+ const char *username;
+ gboolean is_connected = FALSE;
+
+ g_object_get (G_OBJECT (display), "is-connected", &is_connected, NULL);
+
+ if (is_connected) {
+ auth_file = NULL;
+ username = gdm_session_get_username (operation->session);
+ gdm_display_add_user_authorization (display,
+ username,
+ &auth_file,
+ NULL);
+
+ g_assert (auth_file != NULL);
+
+ g_object_set (operation->session,
+ "user-x11-authority-file", auth_file,
+ NULL);
+
+ g_free (auth_file);
+ }
+ }
+
+ gdm_session_start_session (operation->session,
+ operation->service_name);
+
+ destroy_start_user_session_operation (operation);
+}
+
+static void
+create_display_for_user_session (GdmManager *self,
+ GdmSession *session,
+ const char *session_id)
+{
+ GdmDisplay *display;
+ /* at the moment we only create GdmLocalDisplay objects on seat0 */
+ const char *seat_id = "seat0";
+
+ display = gdm_local_display_new ();
+
+ g_object_set (G_OBJECT (display),
+ "session-class", "user",
+ "seat-id", seat_id,
+ "session-id", session_id,
+ NULL);
+ gdm_display_store_add (self->priv->display_store,
+ display);
+ g_object_set_data (G_OBJECT (session), "gdm-display", display);
+ g_object_set_data_full (G_OBJECT (display),
+ "gdm-user-session",
+ g_object_ref (session),
+ (GDestroyNotify)
+ clean_user_session);
+}
+
+static gboolean
+chown_file (GFile *file,
+ uid_t uid,
+ gid_t gid,
+ GError **error)
+{
+ if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_UID, uid,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error)) {
+ return FALSE;
+ }
+ if (!g_file_set_attribute_uint32 (file, G_FILE_ATTRIBUTE_UNIX_GID, gid,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+chown_recursively (GFile *dir,
+ uid_t uid,
+ gid_t gid,
+ GError **error)
+{
+ GFile *file = NULL;
+ GFileInfo *info = NULL;
+ GFileEnumerator *enumerator = NULL;
+ gboolean retval = FALSE;
+
+ if (chown_file (dir, uid, gid, error) == FALSE) {
+ goto out;
+ }
+
+ enumerator = g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE","
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, error);
+ if (!enumerator) {
+ goto out;
+ }
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, error)) != NULL) {
+ file = g_file_get_child (dir, g_file_info_get_name (info));
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+ if (chown_recursively (file, uid, gid, error) == FALSE) {
+ goto out;
+ }
+ } else if (chown_file (file, uid, gid, error) == FALSE) {
+ goto out;
+ }
+
+ g_clear_object (&file);
+ g_clear_object (&info);
+ }
+
+ if (*error) {
+ goto out;
+ }
+
+ retval = TRUE;
+out:
+ g_clear_object (&file);
+ g_clear_object (&info);
+ g_clear_object (&enumerator);
+
+ return retval;
+}
+
+static void
+chown_initial_setup_home_dir (void)
+{
+ GFile *dir;
+ GError *error;
+ char *gis_dir_path;
+ char *gis_uid_path;
+ char *gis_uid_contents;
+ struct passwd *pwe;
+ uid_t uid;
+
+ if (!gdm_get_pwent_for_name (INITIAL_SETUP_USERNAME, &pwe)) {
+ g_warning ("Unknown user %s", INITIAL_SETUP_USERNAME);
+ return;
+ }
+
+ gis_dir_path = g_strdup (pwe->pw_dir);
+
+ gis_uid_path = g_build_filename (gis_dir_path,
+ "gnome-initial-setup-uid",
+ NULL);
+ if (!g_file_get_contents (gis_uid_path, &gis_uid_contents, NULL, NULL)) {
+ g_warning ("Unable to read %s", gis_uid_path);
+ goto out;
+ }
+
+ uid = (uid_t) atoi (gis_uid_contents);
+ pwe = getpwuid (uid);
+ if (uid == 0 || pwe == NULL) {
+ g_warning ("UID '%s' in %s is not valid", gis_uid_contents, gis_uid_path);
+ goto out;
+ }
+
+ error = NULL;
+ dir = g_file_new_for_path (gis_dir_path);
+ if (!chown_recursively (dir, pwe->pw_uid, pwe->pw_gid, &error)) {
+ g_warning ("Failed to change ownership for %s: %s", gis_dir_path, error->message);
+ g_error_free (error);
+ }
+ g_object_unref (dir);
+out:
+ g_free (gis_uid_contents);
+ g_free (gis_uid_path);
+ g_free (gis_dir_path);
+}
+
+static gboolean
+on_start_user_session (StartUserSessionOperation *operation)
+{
+ GdmManager *self = operation->manager;
+ gboolean migrated;
+ gboolean fail_if_already_switched = TRUE;
+ gboolean doing_initial_setup = FALSE;
+ GdmDisplay *display;
+ const char *session_id;
+
+ g_debug ("GdmManager: start or jump to session");
+
+ /* If there's already a session running, jump to it.
+ * If the only session running is the one we just opened,
+ * start a session on it.
+ */
+ migrated = switch_to_compatible_user_session (operation->manager, operation->session, fail_if_already_switched);
+
+ g_debug ("GdmManager: migrated: %d", migrated);
+ if (migrated) {
+ /* We don't stop the manager here because
+ when Xorg exits it switches to the VT it was
+ started from. That interferes with fast
+ user switching. */
+ gdm_session_reset (operation->session);
+ destroy_start_user_session_operation (operation);
+ goto out;
+ }
+
+ display = get_display_for_user_session (operation->session);
+
+ g_object_get (G_OBJECT (display),
+ "doing-initial-setup", &doing_initial_setup,
+ NULL);
+
+ if (doing_initial_setup)
+ chown_initial_setup_home_dir ();
+
+ session_id = gdm_session_get_conversation_session_id (operation->session,
+ operation->service_name);
+
+ if (gdm_session_get_display_mode (operation->session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
+ /* In this case, the greeter's display is morphing into
+ * the user session display. Kill the greeter on this session
+ * and let the user session follow the same display. */
+ gdm_display_stop_greeter_session (display);
+ g_object_set (G_OBJECT (display),
+ "session-class", "user",
+ "session-id", session_id,
+ NULL);
+ } else {
+ uid_t allowed_uid;
+
+ g_object_ref (display);
+ if (doing_initial_setup) {
+ g_autoptr(GError) error = NULL;
+
+ g_debug ("GdmManager: closing down initial setup display in background");
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL);
+
+ if (!g_file_set_contents (ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT,
+ "1",
+ 1,
+ &error)) {
+ g_warning ("GdmDisplay: Could not write initial-setup-done marker to %s: %s",
+ ALREADY_RAN_INITIAL_SETUP_ON_THIS_BOOT,
+ error->message);
+ g_clear_error (&error);
+ }
+ } else {
+ g_debug ("GdmManager: session has its display server, reusing our server for another login screen");
+ }
+
+ /* The user session is going to follow the session worker
+ * into the new display. Untie it from this display and
+ * create a new session for a future user login. */
+ allowed_uid = gdm_session_get_allowed_user (operation->session);
+ g_object_set_data (G_OBJECT (display), "gdm-user-session", NULL);
+ g_object_set_data (G_OBJECT (operation->session), "gdm-display", NULL);
+ create_user_session_for_display (operation->manager, display, allowed_uid);
+
+ /* Give the user session a new display object for bookkeeping purposes */
+ create_display_for_user_session (operation->manager,
+ operation->session,
+ session_id);
+
+
+ if (g_strcmp0 (operation->service_name, "gdm-autologin") == 0 &&
+ !gdm_session_client_is_connected (operation->session)) {
+ /* remove the unused prepared greeter display since we're not going
+ * to have a greeter */
+ gdm_display_store_remove (self->priv->display_store, display);
+ g_object_unref (display);
+
+ self->priv->automatic_login_display = g_object_get_data (G_OBJECT (operation->session), "gdm-display");
+ g_object_add_weak_pointer (G_OBJECT (self->priv->automatic_login_display), (gpointer *) &self->priv->automatic_login_display);
+ }
+ }
+
+ start_user_session (operation->manager, operation);
+
+ out:
+ return G_SOURCE_REMOVE;
+}
+
+static void
+queue_start_user_session (GdmManager *manager,
+ GdmSession *session,
+ const char *service_name)
+{
+ StartUserSessionOperation *operation;
+
+ operation = g_slice_new0 (StartUserSessionOperation);
+ operation->manager = manager;
+ operation->session = g_object_ref (session);
+ operation->service_name = g_strdup (service_name);
+
+ operation->idle_id = g_idle_add ((GSourceFunc) on_start_user_session, operation);
+ g_object_set_data (G_OBJECT (session), "start-user-session-operation", operation);
+}
+
+static void
+start_user_session_if_ready (GdmManager *manager,
+ GdmSession *session,
+ const char *service_name)
+{
+ gboolean start_when_ready;
+
+ start_when_ready = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session), "start-when-ready"));
+ if (start_when_ready) {
+ g_object_set_data (G_OBJECT (session), "waiting-to-start", GINT_TO_POINTER (FALSE));
+ queue_start_user_session (manager, session, service_name);
+ } else {
+ g_object_set_data (G_OBJECT (session), "waiting-to-start", GINT_TO_POINTER (TRUE));
+ }
+}
+
+static void
+on_session_authentication_failed (GdmSession *session,
+ const char *service_name,
+ GPid conversation_pid,
+ GdmManager *manager)
+{
+ add_session_record (manager, session, conversation_pid, SESSION_RECORD_FAILED);
+}
+
+static void
+on_user_session_opened (GdmSession *session,
+ const char *service_name,
+ const char *session_id,
+ GdmManager *manager)
+{
+ manager->priv->user_sessions = g_list_append (manager->priv->user_sessions,
+ g_object_ref (session));
+ if (g_strcmp0 (service_name, "gdm-autologin") == 0 &&
+ !gdm_session_client_is_connected (session)) {
+ /* If we're auto logging in then don't wait for the go-ahead from a greeter,
+ * (since there is no greeter) */
+ g_object_set_data (G_OBJECT (session), "start-when-ready", GINT_TO_POINTER (TRUE));
+ }
+
+ start_user_session_if_ready (manager, session, service_name);
+}
+
+static void
+on_user_session_started (GdmSession *session,
+ const char *service_name,
+ GPid pid,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session started %d", pid);
+ add_session_record (manager, session, pid, SESSION_RECORD_LOGIN);
+
+#ifdef WITH_PLYMOUTH
+ if (g_strcmp0 (service_name, "gdm-autologin") == 0) {
+ if (manager->priv->plymouth_is_running) {
+ g_timeout_add_seconds (20, (GSourceFunc) plymouth_quit_with_transition, NULL);
+ manager->priv->plymouth_is_running = FALSE;
+ }
+ }
+#endif
+}
+
+static void
+remove_user_session (GdmManager *manager,
+ GdmSession *session)
+{
+ GList *node;
+ GdmDisplay *display;
+
+ display = get_display_for_user_session (session);
+
+ if (display != NULL) {
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ }
+
+ node = g_list_find (manager->priv->user_sessions, session);
+
+ if (node != NULL) {
+ manager->priv->user_sessions = g_list_delete_link (manager->priv->user_sessions, node);
+ gdm_session_close (session);
+ g_object_unref (session);
+ }
+}
+
+static void
+on_session_start_failed (GdmSession *session,
+ const char *service_name,
+ const char *message,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session failed to start: %s", message);
+ remove_user_session (manager, session);
+}
+
+static void
+on_user_session_exited (GdmSession *session,
+ int code,
+ GdmManager *manager)
+{
+ GPid pid;
+
+ g_debug ("GdmManager: session exited with status %d", code);
+ pid = gdm_session_get_pid (session);
+
+ if (pid > 0) {
+ add_session_record (manager, session, pid, SESSION_RECORD_LOGOUT);
+ }
+
+ remove_user_session (manager, session);
+}
+
+static void
+on_user_session_died (GdmSession *session,
+ int signal_number,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session died with signal %s", strsignal (signal_number));
+ remove_user_session (manager, session);
+}
+
+static char *
+get_display_device (GdmManager *manager,
+ GdmDisplay *display)
+{
+ /* systemd finds the display device out on its own based on the display */
+ return NULL;
+}
+
+static void
+on_session_reauthenticated (GdmSession *session,
+ const char *service_name,
+ GdmManager *manager)
+{
+ gboolean fail_if_already_switched = FALSE;
+
+ if (gdm_session_get_display_mode (session) == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
+ const char *seat_id;
+ char *session_id;
+
+ seat_id = gdm_session_get_display_seat_id (session);
+ if (gdm_get_login_window_session_id (seat_id, &session_id)) {
+ GdmDisplay *display = gdm_display_store_find (manager->priv->display_store,
+ lookup_by_session_id,
+ (gpointer) session_id);
+
+ if (display != NULL) {
+ gdm_display_stop_greeter_session (display);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+ }
+ g_free (session_id);
+ }
+ }
+
+ /* There should already be a session running, so jump to its
+ * VT. In the event we're already on the right VT, (i.e. user
+ * used an unlock screen instead of a user switched login screen),
+ * then silently succeed and unlock the session.
+ */
+ switch_to_compatible_user_session (manager, session, fail_if_already_switched);
+}
+
+static void
+on_session_client_ready_for_session_to_start (GdmSession *session,
+ const char *service_name,
+ gboolean client_is_ready,
+ GdmManager *manager)
+{
+ gboolean waiting_to_start_user_session;
+
+ if (client_is_ready) {
+ g_debug ("GdmManager: Will start session when ready");
+ } else {
+ g_debug ("GdmManager: Will start session when ready and told");
+ }
+
+ waiting_to_start_user_session = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (session),
+ "waiting-to-start"));
+
+ g_object_set_data (G_OBJECT (session),
+ "start-when-ready",
+ GINT_TO_POINTER (client_is_ready));
+
+ if (client_is_ready && waiting_to_start_user_session) {
+ start_user_session_if_ready (manager, session, service_name);
+ }
+}
+
+static void
+on_session_client_connected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *manager)
+{
+ GdmDisplay *display;
+ char *username;
+ int delay;
+ gboolean enabled;
+ gboolean allow_timed_login = FALSE;
+
+ g_debug ("GdmManager: client with pid %d connected", (int) pid_of_client);
+
+ if (gdm_session_is_running (session)) {
+ const char *session_username;
+ session_username = gdm_session_get_username (session);
+ g_debug ("GdmManager: ignoring connection, since session already running (for user %s)",
+ session_username);
+ return;
+ }
+
+ display = get_display_for_user_session (session);
+
+ if (display == NULL) {
+ return;
+ }
+
+ if (!display_is_on_seat0 (display)) {
+ return;
+ }
+
+#ifdef WITH_PLYMOUTH
+ if (manager->priv->plymouth_is_running) {
+ plymouth_quit_with_transition ();
+ manager->priv->plymouth_is_running = FALSE;
+ }
+#endif
+
+ g_object_get (G_OBJECT (display), "allow-timed-login", &allow_timed_login, NULL);
+
+ if (!allow_timed_login) {
+ return;
+ }
+
+ enabled = get_timed_login_details (manager, &username, &delay);
+
+ if (! enabled) {
+ return;
+ }
+
+ gdm_session_set_timed_login_details (session, username, delay);
+
+ g_debug ("GdmManager: Starting automatic login conversation (for timed login)");
+ gdm_session_start_conversation (session, "gdm-autologin");
+
+ g_free (username);
+
+}
+
+static void
+on_session_client_disconnected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: client with pid %d disconnected", (int) pid_of_client);
+}
+
+typedef struct
+{
+ GdmManager *manager;
+ GdmSession *session;
+ guint idle_id;
+} ResetSessionOperation;
+
+static void
+destroy_reset_session_operation (ResetSessionOperation *operation)
+{
+ g_object_set_data (G_OBJECT (operation->session),
+ "reset-session-operation",
+ NULL);
+ g_object_unref (operation->session);
+ g_slice_free (ResetSessionOperation, operation);
+}
+
+static gboolean
+on_reset_session (ResetSessionOperation *operation)
+{
+ gdm_session_reset (operation->session);
+
+ destroy_reset_session_operation (operation);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+queue_session_reset (GdmManager *manager,
+ GdmSession *session)
+{
+ ResetSessionOperation *operation;
+
+ operation = g_object_get_data (G_OBJECT (session), "reset-session-operation");
+
+ if (operation != NULL) {
+ return;
+ }
+
+ operation = g_slice_new0 (ResetSessionOperation);
+ operation->manager = manager;
+ operation->session = g_object_ref (session);
+ operation->idle_id = g_idle_add ((GSourceFunc) on_reset_session, operation);
+
+ g_object_set_data (G_OBJECT (session), "reset-session-operation", operation);
+}
+
+static void
+on_session_cancelled (GdmSession *session,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: Session was cancelled");
+ queue_session_reset (manager, session);
+}
+
+static void
+on_session_conversation_started (GdmSession *session,
+ const char *service_name,
+ GdmManager *manager)
+{
+ GdmDisplay *display;
+ gboolean enabled;
+ char *username;
+
+ g_debug ("GdmManager: session conversation started for service %s on session", service_name);
+
+ if (g_strcmp0 (service_name, "gdm-autologin") != 0) {
+ g_debug ("GdmManager: ignoring session conversation since its not automatic login conversation");
+ return;
+ }
+
+ display = get_display_for_user_session (session);
+
+ if (display == NULL) {
+ g_debug ("GdmManager: conversation has no associated display");
+ return;
+ }
+
+ if (!display_is_on_seat0 (display)) {
+ return;
+ }
+
+ enabled = get_automatic_login_details (manager, &username);
+
+ if (! enabled) {
+ return;
+ }
+
+ g_debug ("GdmManager: begin auto login for user '%s'", username);
+
+ /* service_name will be "gdm-autologin"
+ */
+ gdm_session_setup_for_user (session, service_name, username);
+
+ g_free (username);
+}
+
+static void
+on_session_conversation_stopped (GdmSession *session,
+ const char *service_name,
+ GdmManager *manager)
+{
+ g_debug ("GdmManager: session conversation '%s' stopped", service_name);
+}
+
+static void
+on_session_reauthentication_started (GdmSession *session,
+ int pid_of_caller,
+ const char *address,
+ GdmManager *manager)
+{
+ GDBusMethodInvocation *invocation;
+ gpointer source_tag;
+
+ g_debug ("GdmManager: reauthentication started");
+
+ source_tag = GINT_TO_POINTER (pid_of_caller);
+
+ invocation = g_hash_table_lookup (manager->priv->open_reauthentication_requests,
+ source_tag);
+
+ if (invocation != NULL) {
+ g_hash_table_steal (manager->priv->open_reauthentication_requests,
+ source_tag);
+ gdm_dbus_manager_complete_open_reauthentication_channel (GDM_DBUS_MANAGER (manager),
+ invocation,
+ address);
+ }
+}
+
+static void
+clean_user_session (GdmSession *session)
+{
+ g_object_set_data (G_OBJECT (session), "gdm-display", NULL);
+ g_object_unref (session);
+}
+
+static void
+create_user_session_for_display (GdmManager *manager,
+ GdmDisplay *display,
+ uid_t allowed_user)
+{
+ GdmSession *session;
+ gboolean display_is_local = FALSE;
+ char *display_name = NULL;
+ char *display_device = NULL;
+ char *remote_hostname = NULL;
+ char *display_auth_file = NULL;
+ char *display_seat_id = NULL;
+ char *display_id = NULL;
+ g_auto (GStrv) supported_session_types = NULL;
+
+ g_object_get (G_OBJECT (display),
+ "id", &display_id,
+ "x11-display-name", &display_name,
+ "is-local", &display_is_local,
+ "remote-hostname", &remote_hostname,
+ "x11-authority-file", &display_auth_file,
+ "seat-id", &display_seat_id,
+ "supported-session-types", &supported_session_types,
+ NULL);
+ display_device = get_display_device (manager, display);
+
+ session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_LOGIN,
+ allowed_user,
+ display_name,
+ remote_hostname,
+ display_device,
+ display_seat_id,
+ display_auth_file,
+ display_is_local,
+ NULL);
+ g_object_set (G_OBJECT (session),
+ "supported-session-types", supported_session_types,
+ NULL);
+
+ g_debug ("GdmSession: Created user session for user %d on display %s (seat %s)",
+ (int) allowed_user,
+ display_id,
+ display_seat_id);
+
+ g_free (display_name);
+ g_free (remote_hostname);
+ g_free (display_auth_file);
+ g_free (display_seat_id);
+
+ g_signal_connect (session,
+ "reauthentication-started",
+ G_CALLBACK (on_session_reauthentication_started),
+ manager);
+ g_signal_connect (session,
+ "reauthenticated",
+ G_CALLBACK (on_session_reauthenticated),
+ manager);
+ g_signal_connect (session,
+ "client-ready-for-session-to-start",
+ G_CALLBACK (on_session_client_ready_for_session_to_start),
+ manager);
+ g_signal_connect (session,
+ "client-connected",
+ G_CALLBACK (on_session_client_connected),
+ manager);
+ g_signal_connect (session,
+ "client-disconnected",
+ G_CALLBACK (on_session_client_disconnected),
+ manager);
+ g_signal_connect (session,
+ "cancelled",
+ G_CALLBACK (on_session_cancelled),
+ manager);
+ g_signal_connect (session,
+ "conversation-started",
+ G_CALLBACK (on_session_conversation_started),
+ manager);
+ g_signal_connect (session,
+ "conversation-stopped",
+ G_CALLBACK (on_session_conversation_stopped),
+ manager);
+ g_signal_connect (session,
+ "authentication-failed",
+ G_CALLBACK (on_session_authentication_failed),
+ manager);
+ g_signal_connect (session,
+ "session-opened",
+ G_CALLBACK (on_user_session_opened),
+ manager);
+ g_signal_connect (session,
+ "session-started",
+ G_CALLBACK (on_user_session_started),
+ manager);
+ g_signal_connect (session,
+ "session-start-failed",
+ G_CALLBACK (on_session_start_failed),
+ manager);
+ g_signal_connect (session,
+ "session-exited",
+ G_CALLBACK (on_user_session_exited),
+ manager);
+ g_signal_connect (session,
+ "session-died",
+ G_CALLBACK (on_user_session_died),
+ manager);
+ g_object_set_data (G_OBJECT (session), "gdm-display", display);
+ g_object_set_data_full (G_OBJECT (display),
+ "gdm-user-session",
+ session,
+ (GDestroyNotify)
+ clean_user_session);
+}
+
+static void
+on_display_added (GdmDisplayStore *display_store,
+ const char *id,
+ GdmManager *manager)
+{
+ GdmDisplay *display;
+
+ display = gdm_display_store_lookup (display_store, id);
+
+ if (display != NULL) {
+ g_dbus_object_manager_server_export (manager->priv->object_manager,
+ gdm_display_get_object_skeleton (display));
+
+ g_signal_connect (display, "notify::status",
+ G_CALLBACK (on_display_status_changed),
+ manager);
+ g_signal_emit (manager, signals[DISPLAY_ADDED], 0, id);
+ }
+}
+
+GQuark
+gdm_manager_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_manager_error");
+ }
+
+ return ret;
+}
+
+static void
+listify_display_ids (const char *id,
+ GdmDisplay *display,
+ GPtrArray **array)
+{
+ g_ptr_array_add (*array, g_strdup (id));
+}
+
+/*
+ Example:
+ dbus-send --system --dest=org.gnome.DisplayManager \
+ --type=method_call --print-reply --reply-timeout=2000 \
+ /org/gnome/DisplayManager/Displays \
+ org.freedesktop.ObjectManager.GetAll
+*/
+gboolean
+gdm_manager_get_displays (GdmManager *manager,
+ GPtrArray **displays,
+ GError **error)
+{
+ g_return_val_if_fail (GDM_IS_MANAGER (manager), FALSE);
+
+ if (displays == NULL) {
+ return FALSE;
+ }
+
+ *displays = g_ptr_array_new ();
+ gdm_display_store_foreach (manager->priv->display_store,
+ (GdmDisplayStoreFunc)listify_display_ids,
+ displays);
+
+ return TRUE;
+}
+
+void
+gdm_manager_stop (GdmManager *manager)
+{
+ g_debug ("GdmManager: GDM stopping");
+
+ if (manager->priv->local_factory != NULL) {
+ gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->local_factory));
+ }
+
+#ifdef HAVE_LIBXDMCP
+ if (manager->priv->xdmcp_factory != NULL) {
+ gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+#endif
+
+ manager->priv->started = FALSE;
+}
+
+void
+gdm_manager_start (GdmManager *manager)
+{
+ g_debug ("GdmManager: GDM starting to manage displays");
+
+#ifdef WITH_PLYMOUTH
+ manager->priv->plymouth_is_running = plymouth_is_running ();
+
+ if (manager->priv->plymouth_is_running) {
+ plymouth_prepare_for_transition ();
+ }
+#endif
+ if (!manager->priv->xdmcp_enabled || manager->priv->show_local_greeter) {
+ gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->local_factory));
+ }
+
+#ifdef HAVE_LIBXDMCP
+ /* Accept remote connections */
+ if (manager->priv->xdmcp_enabled) {
+#ifdef WITH_PLYMOUTH
+ /* Quit plymouth if xdmcp is the only display */
+ if (!manager->priv->show_local_greeter && manager->priv->plymouth_is_running) {
+ plymouth_quit_without_transition ();
+ manager->priv->plymouth_is_running = FALSE;
+ }
+#endif
+ if (manager->priv->xdmcp_factory != NULL) {
+ g_debug ("GdmManager: Accepting XDMCP connections...");
+ gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+ }
+#endif
+
+ manager->priv->started = TRUE;
+}
+
+static gboolean
+register_manager (GdmManager *manager)
+{
+ GError *error = NULL;
+ GDBusObjectManagerServer *object_server;
+
+ error = NULL;
+ manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
+ NULL,
+ &error);
+ if (manager->priv->connection == NULL) {
+ g_critical ("error getting system bus: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ object_server = g_dbus_object_manager_server_new (GDM_MANAGER_DISPLAYS_PATH);
+ g_dbus_object_manager_server_set_connection (object_server, manager->priv->connection);
+ manager->priv->object_manager = object_server;
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager),
+ manager->priv->connection,
+ GDM_MANAGER_PATH,
+ &error)) {
+ g_critical ("error exporting interface to %s: %s",
+ GDM_MANAGER_PATH,
+ error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ return TRUE;
+}
+
+void
+gdm_manager_set_xdmcp_enabled (GdmManager *manager,
+ gboolean enabled)
+{
+ g_return_if_fail (GDM_IS_MANAGER (manager));
+
+ if (manager->priv->xdmcp_enabled != enabled) {
+ manager->priv->xdmcp_enabled = enabled;
+#ifdef HAVE_LIBXDMCP
+ if (manager->priv->xdmcp_enabled) {
+ manager->priv->xdmcp_factory = gdm_xdmcp_display_factory_new (manager->priv->display_store);
+ if (manager->priv->started) {
+ gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+ } else {
+ if (manager->priv->started) {
+ gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->priv->xdmcp_factory));
+ }
+
+ g_object_unref (manager->priv->xdmcp_factory);
+ manager->priv->xdmcp_factory = NULL;
+ }
+#endif
+ }
+
+}
+
+void
+gdm_manager_set_show_local_greeter (GdmManager *manager,
+ gboolean show_local_greeter)
+{
+ g_return_if_fail (GDM_IS_MANAGER (manager));
+
+ manager->priv->show_local_greeter = show_local_greeter;
+}
+
+static void
+gdm_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmManager *self;
+
+ self = GDM_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_XDMCP_ENABLED:
+ gdm_manager_set_xdmcp_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_LOCAL_GREETER:
+ gdm_manager_set_show_local_greeter (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmManager *self;
+
+ self = GDM_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_XDMCP_ENABLED:
+ g_value_set_boolean (value, self->priv->xdmcp_enabled);
+ break;
+ case PROP_SHOW_LOCAL_GREETER:
+ g_value_set_boolean (value, self->priv->show_local_greeter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+gdm_manager_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmManager *manager;
+
+ manager = GDM_MANAGER (G_OBJECT_CLASS (gdm_manager_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ gdm_dbus_manager_set_version (GDM_DBUS_MANAGER (manager), PACKAGE_VERSION);
+
+ manager->priv->local_factory = gdm_local_display_factory_new (manager->priv->display_store);
+
+#ifdef HAVE_LIBXDMCP
+ if (manager->priv->xdmcp_enabled) {
+ manager->priv->xdmcp_factory = gdm_xdmcp_display_factory_new (manager->priv->display_store);
+ }
+#endif
+
+ return G_OBJECT (manager);
+}
+
+static void
+gdm_manager_class_init (GdmManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_manager_get_property;
+ object_class->set_property = gdm_manager_set_property;
+ object_class->constructor = gdm_manager_constructor;
+ object_class->dispose = gdm_manager_dispose;
+
+ signals [DISPLAY_ADDED] =
+ g_signal_new ("display-added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmManagerClass, display_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [DISPLAY_REMOVED] =
+ g_signal_new ("display-removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmManagerClass, display_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ g_object_class_install_property (object_class,
+ PROP_XDMCP_ENABLED,
+ g_param_spec_boolean ("xdmcp-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdm_manager_init (GdmManager *manager)
+{
+
+ manager->priv = GDM_MANAGER_GET_PRIVATE (manager);
+
+ manager->priv->display_store = gdm_display_store_new ();
+ manager->priv->user_sessions = NULL;
+ manager->priv->open_reauthentication_requests = g_hash_table_new_full (NULL,
+ NULL,
+ (GDestroyNotify)
+ NULL,
+ (GDestroyNotify)
+ g_object_unref);
+ manager->priv->transient_sessions = g_hash_table_new_full (NULL,
+ NULL,
+ (GDestroyNotify)
+ NULL,
+ (GDestroyNotify)
+ g_object_unref);
+ g_signal_connect (G_OBJECT (manager->priv->display_store),
+ "display-added",
+ G_CALLBACK (on_display_added),
+ manager);
+
+ g_signal_connect (G_OBJECT (manager->priv->display_store),
+ "display-removed",
+ G_CALLBACK (on_display_removed),
+ manager);
+}
+
+static void
+unexport_display (const char *id,
+ GdmDisplay *display,
+ GdmManager *manager)
+{
+ if (!g_dbus_connection_is_closed (manager->priv->connection))
+ g_dbus_object_manager_server_unexport (manager->priv->object_manager, id);
+}
+
+static void
+finish_display (const char *id,
+ GdmDisplay *display,
+ GdmManager *manager)
+{
+ gdm_display_stop_greeter_session (display);
+ if (gdm_display_get_status (display) == GDM_DISPLAY_MANAGED)
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+}
+
+static void
+gdm_manager_dispose (GObject *object)
+{
+ GdmManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_MANAGER (object));
+
+ manager = GDM_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ gdm_manager_stop (manager);
+
+ g_clear_weak_pointer (&manager->priv->automatic_login_display);
+
+#ifdef HAVE_LIBXDMCP
+ g_clear_object (&manager->priv->xdmcp_factory);
+#endif
+ g_clear_object (&manager->priv->local_factory);
+ g_clear_pointer (&manager->priv->open_reauthentication_requests,
+ g_hash_table_unref);
+ g_clear_pointer (&manager->priv->transient_sessions,
+ g_hash_table_unref);
+
+ g_list_foreach (manager->priv->user_sessions,
+ (GFunc) gdm_session_close,
+ NULL);
+ g_list_free_full (manager->priv->user_sessions, (GDestroyNotify) g_object_unref);
+ manager->priv->user_sessions = NULL;
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store),
+ G_CALLBACK (on_display_added),
+ manager);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->display_store),
+ G_CALLBACK (on_display_removed),
+ manager);
+
+ if (!g_dbus_connection_is_closed (manager->priv->connection)) {
+ gdm_display_store_foreach (manager->priv->display_store,
+ (GdmDisplayStoreFunc)unexport_display,
+ manager);
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (manager));
+ }
+
+ gdm_display_store_foreach (manager->priv->display_store,
+ (GdmDisplayStoreFunc) finish_display,
+ manager);
+
+ gdm_display_store_clear (manager->priv->display_store);
+
+ g_dbus_object_manager_server_set_connection (manager->priv->object_manager, NULL);
+
+ g_clear_object (&manager->priv->connection);
+ g_clear_object (&manager->priv->object_manager);
+ g_clear_object (&manager->priv->display_store);
+
+ G_OBJECT_CLASS (gdm_manager_parent_class)->dispose (object);
+}
+
+GdmManager *
+gdm_manager_new (void)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ gboolean res;
+
+ manager_object = g_object_new (GDM_TYPE_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ res = register_manager (manager_object);
+ if (! res) {
+ g_object_unref (manager_object);
+ return NULL;
+ }
+ }
+
+ return GDM_MANAGER (manager_object);
+}
diff --git a/daemon/gdm-manager.h b/daemon/gdm-manager.h
new file mode 100644
index 0000000..c8fb3f2
--- /dev/null
+++ b/daemon/gdm-manager.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_MANAGER_H
+#define __GDM_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gdm-display.h"
+#include "gdm-manager-glue.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_MANAGER (gdm_manager_get_type ())
+#define GDM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_MANAGER, GdmManager))
+#define GDM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_MANAGER, GdmManagerClass))
+#define GDM_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_MANAGER))
+#define GDM_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_MANAGER))
+#define GDM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_MANAGER, GdmManagerClass))
+
+typedef struct GdmManagerPrivate GdmManagerPrivate;
+
+typedef struct
+{
+ GdmDBusManagerSkeleton parent;
+ GdmManagerPrivate *priv;
+} GdmManager;
+
+typedef struct
+{
+ GdmDBusManagerSkeletonClass parent_class;
+
+ void (* display_added) (GdmManager *manager,
+ const char *id);
+ void (* display_removed) (GdmManager *manager,
+ GdmDisplay *display);
+} GdmManagerClass;
+
+typedef enum
+{
+ GDM_MANAGER_ERROR_GENERAL
+} GdmManagerError;
+
+#define GDM_MANAGER_ERROR gdm_manager_error_quark ()
+
+GQuark gdm_manager_error_quark (void);
+GType gdm_manager_get_type (void);
+
+GdmManager * gdm_manager_new (void);
+void gdm_manager_start (GdmManager *manager);
+void gdm_manager_stop (GdmManager *manager);
+
+void gdm_manager_set_xdmcp_enabled (GdmManager *manager,
+ gboolean enabled);
+void gdm_manager_set_show_local_greeter (GdmManager *manager,
+ gboolean show_local_greeter);
+gboolean gdm_manager_get_displays (GdmManager *manager,
+ GPtrArray **displays,
+ GError **error);
+
+
+G_END_DECLS
+
+#endif /* __GDM_MANAGER_H */
diff --git a/daemon/gdm-manager.xml b/daemon/gdm-manager.xml
new file mode 100644
index 0000000..92ef1d0
--- /dev/null
+++ b/daemon/gdm-manager.xml
@@ -0,0 +1,19 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/gnome/DisplayManager/Manager">
+ <interface name="org.gnome.DisplayManager.Manager">
+ <method name="RegisterDisplay">
+ <arg name="details" direction="in" type="a{ss}"/>
+ </method>
+ <method name="RegisterSession">
+ <arg name="details" direction="in" type="a{sv}"/>
+ </method>
+ <method name="OpenSession">
+ <arg name="address" direction="out" type="s"/>
+ </method>
+ <method name="OpenReauthenticationChannel">
+ <arg name="username" direction="in" type="s"/>
+ <arg name="address" direction="out" type="s"/>
+ </method>
+ <property name="Version" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/daemon/gdm-server.c b/daemon/gdm-server.c
new file mode 100644
index 0000000..e5d2352
--- /dev/null
+++ b/daemon/gdm-server.c
@@ -0,0 +1,1089 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <signal.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#ifdef WITH_PLYMOUTH
+#include <linux/vt.h>
+#endif
+
+#include <systemd/sd-daemon.h>
+
+#ifdef ENABLE_SYSTEMD_JOURNAL
+#include <systemd/sd-journal.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include <X11/Xlib.h> /* for Display */
+
+#include "gdm-common.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+#include "gdm-server.h"
+
+extern char **environ;
+
+#define MAX_LOGS 5
+
+struct _GdmServer
+{
+ GObject parent;
+
+ char *command;
+ GPid pid;
+
+ gboolean disable_tcp;
+ int priority;
+ char *user_name;
+ char *session_args;
+
+ char *log_dir;
+ char *display_name;
+ char *display_device;
+ char *display_seat_id;
+ char *auth_file;
+
+ guint child_watch_id;
+
+ gboolean is_initial;
+};
+
+enum {
+ PROP_0,
+ PROP_DISPLAY_NAME,
+ PROP_DISPLAY_SEAT_ID,
+ PROP_DISPLAY_DEVICE,
+ PROP_AUTH_FILE,
+ PROP_USER_NAME,
+ PROP_DISABLE_TCP,
+ PROP_IS_INITIAL,
+};
+
+enum {
+ READY,
+ EXITED,
+ DIED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_server_class_init (GdmServerClass *klass);
+static void gdm_server_init (GdmServer *server);
+static void gdm_server_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmServer, gdm_server, G_TYPE_OBJECT)
+
+char *
+gdm_server_get_display_device (GdmServer *server)
+{
+ /* systemd finds the display device out on its own based on the display */
+ return NULL;
+}
+
+static void
+gdm_server_ready (GdmServer *server)
+{
+ g_debug ("GdmServer: Got USR1 from X server - emitting READY");
+
+ gdm_run_script (GDMCONFDIR "/Init", GDM_USERNAME,
+ server->display_name,
+ NULL, /* hostname */
+ server->auth_file);
+
+ g_signal_emit (server, signals[READY], 0);
+}
+
+static GSList *active_servers;
+static gboolean sigusr1_thread_running;
+static GCond sigusr1_thread_cond;
+static GMutex sigusr1_thread_mutex;
+
+static gboolean
+got_sigusr1 (gpointer user_data)
+{
+ GPid pid = GPOINTER_TO_UINT (user_data);
+ GSList *l;
+
+ g_debug ("GdmServer: got SIGUSR1 from PID %d", pid);
+
+ for (l = active_servers; l; l = l->next) {
+ GdmServer *server = l->data;
+
+ if (server->pid == pid)
+ gdm_server_ready (server);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static gpointer
+sigusr1_thread_main (gpointer user_data)
+{
+ sigset_t sigusr1_mask;
+
+ /* Handle only SIGUSR1 */
+ sigemptyset (&sigusr1_mask);
+ sigaddset (&sigusr1_mask, SIGUSR1);
+ sigprocmask (SIG_SETMASK, &sigusr1_mask, NULL);
+
+ g_mutex_lock (&sigusr1_thread_mutex);
+ sigusr1_thread_running = TRUE;
+ g_cond_signal (&sigusr1_thread_cond);
+ g_mutex_unlock (&sigusr1_thread_mutex);
+
+ /* Spin waiting for a SIGUSR1 */
+ while (TRUE) {
+ siginfo_t info;
+
+ if (sigwaitinfo (&sigusr1_mask, &info) == -1)
+ continue;
+
+ g_idle_add (got_sigusr1, GUINT_TO_POINTER (info.si_pid));
+ }
+
+ return NULL;
+}
+
+static void
+gdm_server_launch_sigusr1_thread_if_needed (void)
+{
+ static GThread *sigusr1_thread;
+
+ if (sigusr1_thread == NULL) {
+ sigusr1_thread = g_thread_new ("gdm SIGUSR1 catcher", sigusr1_thread_main, NULL);
+
+ g_mutex_lock (&sigusr1_thread_mutex);
+ while (!sigusr1_thread_running)
+ g_cond_wait (&sigusr1_thread_cond, &sigusr1_thread_mutex);
+ g_mutex_unlock (&sigusr1_thread_mutex);
+ }
+}
+
+static void
+gdm_server_init_command (GdmServer *server)
+{
+ gboolean debug = FALSE;
+ const char *debug_options;
+ const char *verbosity = "";
+
+ if (server->command != NULL) {
+ return;
+ }
+
+ gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
+ if (debug) {
+ debug_options = " -logverbose 7 -core ";
+ } else {
+ debug_options = "";
+ }
+
+#define X_SERVER_ARG_FORMAT " -background none -noreset -verbose %s%s"
+
+ /* This is a temporary hack to work around the fact that XOrg
+ * currently lacks support for multi-seat hotplugging for
+ * display devices. This bit should be removed as soon as XOrg
+ * gains native support for automatically enumerating usb
+ * based graphics adapters at start-up via udev. */
+
+ /* systemd ships an X server wrapper tool which simply invokes
+ * the usual X but ensures it only uses the display devices of
+ * the seat. */
+
+ /* We do not rely on this wrapper server if, a) the machine
+ * wasn't booted using systemd, or b) the wrapper tool is
+ * missing, or c) we are running for the main seat 'seat0'. */
+
+#ifdef ENABLE_SYSTEMD_JOURNAL
+ /* For systemd, we don't have a log file but instead log to stdout,
+ so set it to the xserver's built-in default verbosity */
+ if (debug)
+ verbosity = "7 -logfile /dev/null";
+ else
+ verbosity = "3 -logfile /dev/null";
+#endif
+
+ if (g_access (SYSTEMD_X_SERVER, X_OK) < 0) {
+ goto fallback;
+ }
+
+ if (server->display_seat_id == NULL ||
+ strcmp (server->display_seat_id, "seat0") == 0) {
+ goto fallback;
+ }
+
+ server->command = g_strdup_printf (SYSTEMD_X_SERVER X_SERVER_ARG_FORMAT, verbosity, debug_options);
+ return;
+
+fallback:
+ server->command = g_strdup_printf (X_SERVER X_SERVER_ARG_FORMAT, verbosity, debug_options);
+
+}
+
+static gboolean
+gdm_server_resolve_command_line (GdmServer *server,
+ const char *vtarg,
+ int *argcp,
+ char ***argvp)
+{
+ int argc;
+ char **argv;
+ int len;
+ int i;
+ gboolean gotvtarg = FALSE;
+ gboolean query_in_arglist = FALSE;
+
+ gdm_server_init_command (server);
+
+ g_shell_parse_argv (server->command, &argc, &argv, NULL);
+
+ for (len = 0; argv != NULL && argv[len] != NULL; len++) {
+ char *arg = argv[len];
+
+ /* HACK! Not to add vt argument to servers that already force
+ * allocation. Mostly for backwards compat only */
+ if (strncmp (arg, "vt", 2) == 0 &&
+ isdigit (arg[2]) &&
+ (arg[3] == '\0' ||
+ (isdigit (arg[3]) && arg[4] == '\0')))
+ gotvtarg = TRUE;
+ if (strcmp (arg, "-query") == 0 ||
+ strcmp (arg, "-indirect") == 0)
+ query_in_arglist = TRUE;
+ }
+
+ argv = g_renew (char *, argv, len + 12);
+ /* shift args down one */
+ for (i = len - 1; i >= 1; i--) {
+ argv[i+1] = argv[i];
+ }
+
+ /* server number is the FIRST argument, before any others */
+ argv[1] = g_strdup (server->display_name);
+ len++;
+
+ if (server->auth_file != NULL) {
+ argv[len++] = g_strdup ("-auth");
+ argv[len++] = g_strdup (server->auth_file);
+ }
+
+ if (server->display_seat_id != NULL) {
+ argv[len++] = g_strdup ("-seat");
+ argv[len++] = g_strdup (server->display_seat_id);
+ }
+
+ /* If we were compiled with Xserver >= 1.17 we need to specify
+ * '-listen tcp' as the X server dosen't listen on tcp sockets
+ * by default anymore. In older versions we need to pass
+ * -nolisten tcp to disable listening on tcp sockets.
+ */
+ if (!query_in_arglist) {
+ if (server->disable_tcp) {
+ argv[len++] = g_strdup ("-nolisten");
+ argv[len++] = g_strdup ("tcp");
+ }
+
+#ifdef HAVE_XSERVER_WITH_LISTEN
+ if (!server->disable_tcp) {
+ argv[len++] = g_strdup ("-listen");
+ argv[len++] = g_strdup ("tcp");
+ }
+#endif
+ }
+
+ if (vtarg != NULL && ! gotvtarg) {
+ argv[len++] = g_strdup (vtarg);
+ }
+
+ argv[len++] = NULL;
+
+ *argvp = argv;
+ *argcp = len;
+
+ return TRUE;
+}
+
+static void
+rotate_logs (const char *path,
+ guint n_copies)
+{
+ int i;
+
+ for (i = n_copies - 1; i > 0; i--) {
+ char *name_n;
+ char *name_n1;
+
+ name_n = g_strdup_printf ("%s.%d", path, i);
+ if (i > 1) {
+ name_n1 = g_strdup_printf ("%s.%d", path, i - 1);
+ } else {
+ name_n1 = g_strdup (path);
+ }
+
+ VE_IGNORE_EINTR (g_unlink (name_n));
+ VE_IGNORE_EINTR (g_rename (name_n1, name_n));
+
+ g_free (name_n1);
+ g_free (name_n);
+ }
+
+ VE_IGNORE_EINTR (g_unlink (path));
+}
+
+static void
+change_user (GdmServer *server)
+{
+ struct passwd *pwent;
+
+ if (server->user_name == NULL) {
+ return;
+ }
+
+ gdm_get_pwent_for_name (server->user_name, &pwent);
+ if (pwent == NULL) {
+ g_warning (_("Server was to be spawned by user %s but that user doesn’t exist"),
+ server->user_name);
+ _exit (EXIT_FAILURE);
+ }
+
+ g_debug ("GdmServer: Changing (uid:gid) for child process to (%d:%d)",
+ pwent->pw_uid,
+ pwent->pw_gid);
+
+ if (pwent->pw_uid != 0) {
+ if (setgid (pwent->pw_gid) < 0) {
+ g_warning (_("Couldn’t set groupid to %d"),
+ pwent->pw_gid);
+ _exit (EXIT_FAILURE);
+ }
+
+ if (initgroups (pwent->pw_name, pwent->pw_gid) < 0) {
+ g_warning (_("initgroups () failed for %s"),
+ pwent->pw_name);
+ _exit (EXIT_FAILURE);
+ }
+
+ if (setuid (pwent->pw_uid) < 0) {
+ g_warning (_("Couldn’t set userid to %d"),
+ (int)pwent->pw_uid);
+ _exit (EXIT_FAILURE);
+ }
+ } else {
+ gid_t groups[1] = { 0 };
+
+ if (setgid (0) < 0) {
+ g_warning (_("Couldn’t set groupid to %d"), 0);
+ /* Don't error out, it's not fatal, if it fails we'll
+ * just still be */
+ }
+
+ /* this will get rid of any suplementary groups etc... */
+ setgroups (1, groups);
+ }
+}
+
+static gboolean
+gdm_server_setup_journal_fds (GdmServer *server)
+{
+#ifdef ENABLE_SYSTEMD_JOURNAL
+ if (sd_booted () > 0) {
+ int out, err;
+ const char *prefix = "gdm-Xorg-";
+ char *identifier;
+ gsize size;
+
+ size = strlen (prefix) + strlen (server->display_name) + 1;
+ identifier = g_alloca (size);
+ strcpy (identifier, prefix);
+ strcat (identifier, server->display_name);
+ identifier[size - 1] = '\0';
+
+ out = sd_journal_stream_fd (identifier, LOG_INFO, FALSE);
+ if (out < 0)
+ return FALSE;
+
+ err = sd_journal_stream_fd (identifier, LOG_WARNING, FALSE);
+ if (err < 0) {
+ close (out);
+ return FALSE;
+ }
+
+ VE_IGNORE_EINTR (dup2 (out, 1));
+ VE_IGNORE_EINTR (dup2 (err, 2));
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+static void
+gdm_server_setup_logfile (GdmServer *server)
+{
+ int logfd;
+ char *log_file;
+ char *log_path;
+
+ log_file = g_strdup_printf ("%s.log", server->display_name);
+ log_path = g_build_filename (server->log_dir, log_file, NULL);
+ g_free (log_file);
+
+ /* Rotate the X server logs */
+ rotate_logs (log_path, MAX_LOGS);
+
+ g_debug ("GdmServer: Opening logfile for server %s", log_path);
+
+ VE_IGNORE_EINTR (g_unlink (log_path));
+ VE_IGNORE_EINTR (logfd = open (log_path, O_CREAT|O_APPEND|O_TRUNC|O_WRONLY|O_EXCL, 0644));
+
+ g_free (log_path);
+
+ if (logfd != -1) {
+ VE_IGNORE_EINTR (dup2 (logfd, 1));
+ VE_IGNORE_EINTR (dup2 (logfd, 2));
+ close (logfd);
+ } else {
+ g_warning (_("%s: Could not open log file for display %s!"),
+ "gdm_server_spawn",
+ server->display_name);
+ }
+}
+
+static void
+server_child_setup (GdmServer *server)
+{
+ struct sigaction ign_signal;
+ sigset_t mask;
+
+ if (!gdm_server_setup_journal_fds(server))
+ gdm_server_setup_logfile(server);
+
+ /* The X server expects USR1/TTIN/TTOU to be SIG_IGN */
+ ign_signal.sa_handler = SIG_IGN;
+ ign_signal.sa_flags = SA_RESTART;
+ sigemptyset (&ign_signal.sa_mask);
+
+ if (sigaction (SIGUSR1, &ign_signal, NULL) < 0) {
+ g_warning (_("%s: Error setting %s to %s"),
+ "gdm_server_spawn", "USR1", "SIG_IGN");
+ _exit (EXIT_FAILURE);
+ }
+
+ if (sigaction (SIGTTIN, &ign_signal, NULL) < 0) {
+ g_warning (_("%s: Error setting %s to %s"),
+ "gdm_server_spawn", "TTIN", "SIG_IGN");
+ _exit (EXIT_FAILURE);
+ }
+
+ if (sigaction (SIGTTOU, &ign_signal, NULL) < 0) {
+ g_warning (_("%s: Error setting %s to %s"),
+ "gdm_server_spawn", "TTOU", "SIG_IGN");
+ _exit (EXIT_FAILURE);
+ }
+
+ /* And HUP and TERM are at SIG_DFL from gdm_unset_signals,
+ we also have an empty mask and all that fun stuff */
+
+ /* unblock signals (especially HUP/TERM/USR1) so that we
+ * can control the X server */
+ sigemptyset (&mask);
+ sigprocmask (SIG_SETMASK, &mask, NULL);
+
+ /* Terminate the process when the parent dies */
+#ifdef HAVE_SYS_PRCTL_H
+ prctl (PR_SET_PDEATHSIG, SIGTERM);
+#endif
+
+ if (server->priority != 0) {
+ if (setpriority (PRIO_PROCESS, 0, server->priority)) {
+ g_warning (_("%s: Server priority couldn’t be set to %d: %s"),
+ "gdm_server_spawn",
+ server->priority,
+ g_strerror (errno));
+ }
+ }
+
+ setpgid (0, 0);
+
+ change_user (server);
+}
+
+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_server_environment (GdmServer *server)
+{
+ GPtrArray *env;
+ char **l;
+ 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);
+ for (l = environ; *l != NULL; l++) {
+ char **str;
+ str = g_strsplit (*l, "=", 2);
+ g_hash_table_insert (hash, str[0], str[1]);
+ g_free (str);
+ }
+
+ /* modify environment here */
+ g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (server->display_name));
+
+ if (server->user_name != NULL) {
+ struct passwd *pwent;
+
+ gdm_get_pwent_for_name (server->user_name, &pwent);
+
+ if (pwent->pw_dir != NULL
+ && g_file_test (pwent->pw_dir, G_FILE_TEST_EXISTS)) {
+ g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir));
+ } else {
+ /* Hack */
+ g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/"));
+ }
+ g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell));
+ g_hash_table_remove (hash, "MAIL");
+ }
+
+ g_hash_table_foreach (hash, (GHFunc)listify_hash, env);
+ g_hash_table_destroy (hash);
+
+ g_ptr_array_add (env, NULL);
+
+ return env;
+}
+
+static void
+server_add_xserver_args (GdmServer *server,
+ int *argc,
+ char ***argv)
+{
+ int count;
+ char **args;
+ int len;
+ int i;
+
+ len = *argc;
+ g_shell_parse_argv (server->session_args, &count, &args, NULL);
+ *argv = g_renew (char *, *argv, len + count + 1);
+
+ for (i=0; i < count;i++) {
+ *argv[len++] = g_strdup (args[i]);
+ }
+
+ *argc += count;
+
+ argv[len] = NULL;
+ g_strfreev (args);
+}
+
+static void
+server_child_watch (GPid pid,
+ int status,
+ GdmServer *server)
+{
+ g_debug ("GdmServer: child (pid:%d) done (%s:%d)",
+ (int) pid,
+ WIFEXITED (status) ? "status"
+ : WIFSIGNALED (status) ? "signal"
+ : "unknown",
+ WIFEXITED (status) ? WEXITSTATUS (status)
+ : WIFSIGNALED (status) ? WTERMSIG (status)
+ : -1);
+
+ g_object_ref (server);
+
+ if (WIFEXITED (status)) {
+ int code = WEXITSTATUS (status);
+ g_signal_emit (server, signals [EXITED], 0, code);
+ } else if (WIFSIGNALED (status)) {
+ int num = WTERMSIG (status);
+ g_signal_emit (server, signals [DIED], 0, num);
+ }
+
+ g_spawn_close_pid (server->pid);
+ server->pid = -1;
+
+ g_object_unref (server);
+}
+
+static void
+prune_active_servers_list (GdmServer *server)
+{
+ active_servers = g_slist_remove (active_servers, server);
+}
+
+static gboolean
+gdm_server_spawn (GdmServer *server,
+ const char *vtarg,
+ GError **error)
+{
+ int argc;
+ gchar **argv = NULL;
+ GPtrArray *env = NULL;
+ gboolean ret = FALSE;
+ char *freeme;
+
+ /* Figure out the server command */
+ argv = NULL;
+ argc = 0;
+ gdm_server_resolve_command_line (server,
+ vtarg,
+ &argc,
+ &argv);
+
+ if (server->session_args) {
+ server_add_xserver_args (server, &argc, &argv);
+ }
+
+ if (argv[0] == NULL) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("%s: Empty server command for display %s"),
+ "gdm_server_spawn",
+ server->display_name);
+ goto out;
+ }
+
+ env = get_server_environment (server);
+
+ freeme = g_strjoinv (" ", argv);
+ g_debug ("GdmServer: Starting X server process: %s", freeme);
+ g_free (freeme);
+
+ active_servers = g_slist_append (active_servers, server);
+
+ g_object_weak_ref (G_OBJECT (server),
+ (GWeakNotify)
+ prune_active_servers_list,
+ server);
+
+ gdm_server_launch_sigusr1_thread_if_needed ();
+
+ if (!g_spawn_async_with_pipes (NULL,
+ argv,
+ (char **)env->pdata,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ (GSpawnChildSetupFunc)server_child_setup,
+ server,
+ &server->pid,
+ NULL,
+ NULL,
+ NULL,
+ error))
+ goto out;
+
+ g_debug ("GdmServer: Started X server process %d - waiting for READY", (int)server->pid);
+
+ server->child_watch_id = g_child_watch_add (server->pid,
+ (GChildWatchFunc)server_child_watch,
+ server);
+
+ ret = TRUE;
+ out:
+ g_strfreev (argv);
+ if (env) {
+ g_ptr_array_foreach (env, (GFunc)g_free, NULL);
+ g_ptr_array_free (env, TRUE);
+ }
+ return ret;
+}
+
+/**
+ * gdm_server_start:
+ * @disp: Pointer to a GdmDisplay structure
+ *
+ * Starts a local X server. Handles retries and fatal errors properly.
+ */
+
+gboolean
+gdm_server_start (GdmServer *server)
+{
+ gboolean res = FALSE;
+ const char *vtarg = NULL;
+ GError *local_error = NULL;
+ GError **error = &local_error;
+
+ /* Hardcode the VT for the initial X server, but nothing else */
+ if (server->is_initial) {
+ vtarg = "vt" G_STRINGIFY (GDM_INITIAL_VT);
+ }
+
+ /* fork X server process */
+ if (!gdm_server_spawn (server, vtarg, error)) {
+ goto out;
+ }
+
+ res = TRUE;
+ out:
+ if (local_error) {
+ g_printerr ("%s\n", local_error->message);
+ g_clear_error (&local_error);
+ }
+ return res;
+}
+
+static void
+server_died (GdmServer *server)
+{
+ int exit_status;
+
+ g_debug ("GdmServer: Waiting on process %d", server->pid);
+ exit_status = gdm_wait_on_pid (server->pid);
+
+ if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) {
+ g_debug ("GdmServer: Wait on child process failed");
+ } else {
+ /* exited normally */
+ }
+
+ g_spawn_close_pid (server->pid);
+ server->pid = -1;
+
+ if (server->display_device != NULL) {
+ g_free (server->display_device);
+ server->display_device = NULL;
+ g_object_notify (G_OBJECT (server), "display-device");
+ }
+
+ g_debug ("GdmServer: Server died");
+}
+
+gboolean
+gdm_server_stop (GdmServer *server)
+{
+ int res;
+
+ if (server->pid <= 1) {
+ return TRUE;
+ }
+
+ /* remove watch source before we can wait on child */
+ if (server->child_watch_id > 0) {
+ g_source_remove (server->child_watch_id);
+ server->child_watch_id = 0;
+ }
+
+ g_debug ("GdmServer: Stopping server");
+
+ res = gdm_signal_pid (server->pid, SIGTERM);
+ if (res < 0) {
+ } else {
+ server_died (server);
+ }
+
+ return TRUE;
+}
+
+
+static void
+_gdm_server_set_display_name (GdmServer *server,
+ const char *name)
+{
+ g_free (server->display_name);
+ server->display_name = g_strdup (name);
+}
+
+static void
+_gdm_server_set_display_seat_id (GdmServer *server,
+ const char *name)
+{
+ g_free (server->display_seat_id);
+ server->display_seat_id = g_strdup (name);
+}
+
+static void
+_gdm_server_set_auth_file (GdmServer *server,
+ const char *auth_file)
+{
+ g_free (server->auth_file);
+ server->auth_file = g_strdup (auth_file);
+}
+
+static void
+_gdm_server_set_user_name (GdmServer *server,
+ const char *name)
+{
+ g_free (server->user_name);
+ server->user_name = g_strdup (name);
+}
+
+static void
+_gdm_server_set_disable_tcp (GdmServer *server,
+ gboolean disabled)
+{
+ server->disable_tcp = disabled;
+}
+
+static void
+_gdm_server_set_is_initial (GdmServer *server,
+ gboolean initial)
+{
+ server->is_initial = initial;
+}
+
+static void
+gdm_server_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmServer *self;
+
+ self = GDM_SERVER (object);
+
+ switch (prop_id) {
+ case PROP_DISPLAY_NAME:
+ _gdm_server_set_display_name (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_SEAT_ID:
+ _gdm_server_set_display_seat_id (self, g_value_get_string (value));
+ break;
+ case PROP_AUTH_FILE:
+ _gdm_server_set_auth_file (self, g_value_get_string (value));
+ break;
+ case PROP_USER_NAME:
+ _gdm_server_set_user_name (self, g_value_get_string (value));
+ break;
+ case PROP_DISABLE_TCP:
+ _gdm_server_set_disable_tcp (self, g_value_get_boolean (value));
+ break;
+ case PROP_IS_INITIAL:
+ _gdm_server_set_is_initial (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_server_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmServer *self;
+
+ self = GDM_SERVER (object);
+
+ switch (prop_id) {
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, self->display_name);
+ break;
+ case PROP_DISPLAY_SEAT_ID:
+ g_value_set_string (value, self->display_seat_id);
+ break;
+ case PROP_DISPLAY_DEVICE:
+ g_value_take_string (value,
+ gdm_server_get_display_device (self));
+ break;
+ case PROP_AUTH_FILE:
+ g_value_set_string (value, self->auth_file);
+ break;
+ case PROP_USER_NAME:
+ g_value_set_string (value, self->user_name);
+ break;
+ case PROP_DISABLE_TCP:
+ g_value_set_boolean (value, self->disable_tcp);
+ break;
+ case PROP_IS_INITIAL:
+ g_value_set_boolean (value, self->is_initial);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_server_class_init (GdmServerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = gdm_server_get_property;
+ object_class->set_property = gdm_server_set_property;
+ object_class->finalize = gdm_server_finalize;
+
+ signals [READY] =
+ g_signal_new ("ready",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ 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,
+ 0,
+ 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,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_NAME,
+ g_param_spec_string ("display-name",
+ "name",
+ "name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_SEAT_ID,
+ g_param_spec_string ("display-seat-id",
+ "Seat ID",
+ "ID of the seat this display is running on",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_DEVICE,
+ g_param_spec_string ("display-device",
+ "Display Device",
+ "Path to terminal display is running on",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_AUTH_FILE,
+ g_param_spec_string ("auth-file",
+ "Authorization File",
+ "Path to X authorization file",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_USER_NAME,
+ g_param_spec_string ("user-name",
+ "user name",
+ "user name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISABLE_TCP,
+ g_param_spec_boolean ("disable-tcp",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_IS_INITIAL,
+ g_param_spec_boolean ("is-initial",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdm_server_init (GdmServer *server)
+{
+ server->pid = -1;
+
+ server->log_dir = g_strdup (LOGDIR);
+}
+
+static void
+gdm_server_finalize (GObject *object)
+{
+ GdmServer *server;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_SERVER (object));
+
+ server = GDM_SERVER (object);
+
+ gdm_server_stop (server);
+
+ g_free (server->command);
+ g_free (server->user_name);
+ g_free (server->session_args);
+ g_free (server->log_dir);
+ g_free (server->display_name);
+ g_free (server->display_seat_id);
+ g_free (server->display_device);
+ g_free (server->auth_file);
+
+ G_OBJECT_CLASS (gdm_server_parent_class)->finalize (object);
+}
+
+GdmServer *
+gdm_server_new (const char *display_name,
+ const char *seat_id,
+ const char *auth_file,
+ gboolean initial)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_SERVER,
+ "display-name", display_name,
+ "display-seat-id", seat_id,
+ "auth-file", auth_file,
+ "is-initial", initial,
+ NULL);
+
+ return GDM_SERVER (object);
+}
diff --git a/daemon/gdm-server.h b/daemon/gdm-server.h
new file mode 100644
index 0000000..d175f9b
--- /dev/null
+++ b/daemon/gdm-server.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_SERVER_H
+#define __GDM_SERVER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SERVER (gdm_server_get_type ())
+G_DECLARE_FINAL_TYPE (GdmServer, gdm_server, GDM, SERVER, GObject);
+
+GdmServer * gdm_server_new (const char *display_id,
+ const char *seat_id,
+ const char *auth_file,
+ gboolean initial);
+gboolean gdm_server_start (GdmServer *server);
+gboolean gdm_server_stop (GdmServer *server);
+char * gdm_server_get_display_device (GdmServer *server);
+
+G_END_DECLS
+
+#endif /* __GDM_SERVER_H */
diff --git a/daemon/gdm-session-auditor.c b/daemon/gdm-session-auditor.c
new file mode 100644
index 0000000..5f569a9
--- /dev/null
+++ b/daemon/gdm-session-auditor.c
@@ -0,0 +1,322 @@
+/* gdm-session-auditor.c - Object for auditing session login/logout
+ *
+ * Copyright (C) 2004, 2008 Sun Microsystems, Inc.
+ * Copyright (C) 2005, 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Brian A. Cameron <Brian.Cameron@sun.com>
+ * Gary Winiger <Gary.Winiger@sun.com>
+ * Ray Strode <rstrode@redhat.com>
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+#include "config.h"
+#include "gdm-session-auditor.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+typedef struct _GdmSessionAuditorPrivate
+{
+ char *username;
+ char *hostname;
+ char *display_device;
+} GdmSessionAuditorPrivate;
+
+static void gdm_session_auditor_finalize (GObject *object);
+static void gdm_session_auditor_class_install_properties (GdmSessionAuditorClass *
+ auditor_class);
+
+static void gdm_session_auditor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gdm_session_auditor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+enum {
+ PROP_0 = 0,
+ PROP_USERNAME,
+ PROP_HOSTNAME,
+ PROP_DISPLAY_DEVICE
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdmSessionAuditor, gdm_session_auditor, G_TYPE_OBJECT)
+
+static void
+gdm_session_auditor_class_init (GdmSessionAuditorClass *auditor_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (auditor_class);
+
+ object_class->finalize = gdm_session_auditor_finalize;
+
+ gdm_session_auditor_class_install_properties (auditor_class);
+}
+
+static void
+gdm_session_auditor_class_install_properties (GdmSessionAuditorClass *auditor_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *param_spec;
+
+ object_class = G_OBJECT_CLASS (auditor_class);
+ object_class->set_property = gdm_session_auditor_set_property;
+ object_class->get_property = gdm_session_auditor_get_property;
+
+ param_spec = g_param_spec_string ("username", _("Username"),
+ _("The username"),
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_USERNAME, param_spec);
+
+ param_spec = g_param_spec_string ("hostname", _("Hostname"),
+ _("The hostname"),
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_HOSTNAME, param_spec);
+
+ param_spec = g_param_spec_string ("display-device", _("Display Device"),
+ _("The display device"),
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_DISPLAY_DEVICE, param_spec);
+}
+
+static void
+gdm_session_auditor_init (GdmSessionAuditor *auditor)
+{
+}
+
+static void
+gdm_session_auditor_finalize (GObject *object)
+{
+ GdmSessionAuditor *auditor;
+ GdmSessionAuditorPrivate *priv;
+ GObjectClass *parent_class;
+
+ auditor = GDM_SESSION_AUDITOR (object);
+ priv = gdm_session_auditor_get_instance_private (auditor);
+
+ g_free (priv->username);
+ g_free (priv->hostname);
+ g_free (priv->display_device);
+
+ parent_class = G_OBJECT_CLASS (gdm_session_auditor_parent_class);
+
+ if (parent_class->finalize != NULL) {
+ parent_class->finalize (object);
+ }
+}
+
+void
+gdm_session_auditor_set_username (GdmSessionAuditor *auditor,
+ const char *username)
+{
+ GdmSessionAuditorPrivate *priv;
+
+ g_return_if_fail (GDM_IS_SESSION_AUDITOR (auditor));
+
+ priv = gdm_session_auditor_get_instance_private (auditor);
+
+ if (username == priv->username) {
+ return;
+ }
+
+ if ((username == NULL || priv->username == NULL) ||
+ strcmp (username, priv->username) != 0) {
+ priv->username = g_strdup (username);
+ g_object_notify (G_OBJECT (auditor), "username");
+ }
+}
+
+static void
+gdm_session_auditor_set_hostname (GdmSessionAuditor *auditor,
+ const char *hostname)
+{
+ GdmSessionAuditorPrivate *priv;
+
+ g_return_if_fail (GDM_IS_SESSION_AUDITOR (auditor));
+
+ priv = gdm_session_auditor_get_instance_private (auditor);
+ priv->hostname = g_strdup (hostname);
+}
+
+static void
+gdm_session_auditor_set_display_device (GdmSessionAuditor *auditor,
+ const char *display_device)
+{
+ GdmSessionAuditorPrivate *priv;
+
+ g_return_if_fail (GDM_IS_SESSION_AUDITOR (auditor));
+
+ priv = gdm_session_auditor_get_instance_private (auditor);
+ priv->display_device = g_strdup (display_device);
+}
+
+static char *
+gdm_session_auditor_get_username (GdmSessionAuditor *auditor)
+{
+ GdmSessionAuditorPrivate *priv;
+
+ priv = gdm_session_auditor_get_instance_private (auditor);
+ return g_strdup (priv->username);
+}
+
+static char *
+gdm_session_auditor_get_hostname (GdmSessionAuditor *auditor)
+{
+ GdmSessionAuditorPrivate *priv;
+
+ priv = gdm_session_auditor_get_instance_private (auditor);
+ return g_strdup (priv->hostname);
+}
+
+static char *
+gdm_session_auditor_get_display_device (GdmSessionAuditor *auditor)
+{
+ GdmSessionAuditorPrivate *priv;
+
+ priv = gdm_session_auditor_get_instance_private (auditor);
+ return g_strdup (priv->display_device);
+}
+
+static void
+gdm_session_auditor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionAuditor *auditor;
+
+ auditor = GDM_SESSION_AUDITOR (object);
+
+ switch (prop_id) {
+ case PROP_USERNAME:
+ gdm_session_auditor_set_username (auditor, g_value_get_string (value));
+ break;
+
+ case PROP_HOSTNAME:
+ gdm_session_auditor_set_hostname (auditor, g_value_get_string (value));
+ break;
+
+ case PROP_DISPLAY_DEVICE:
+ gdm_session_auditor_set_display_device (auditor, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gdm_session_auditor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionAuditor *auditor;
+
+ auditor = GDM_SESSION_AUDITOR (object);
+
+ switch (prop_id) {
+ case PROP_USERNAME:
+ g_value_take_string (value, gdm_session_auditor_get_username (auditor));
+ break;
+
+ case PROP_HOSTNAME:
+ g_value_take_string (value, gdm_session_auditor_get_hostname (auditor));
+ break;
+
+ case PROP_DISPLAY_DEVICE:
+ g_value_take_string (value, gdm_session_auditor_get_display_device (auditor));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+GdmSessionAuditor *
+gdm_session_auditor_new (const char *hostname,
+ const char *display_device)
+{
+ GdmSessionAuditor *auditor;
+
+ auditor = g_object_new (GDM_TYPE_SESSION_AUDITOR,
+ "hostname", hostname,
+ "display-device", display_device,
+ NULL);
+
+ return auditor;
+}
+
+void
+gdm_session_auditor_report_password_changed (GdmSessionAuditor *auditor)
+{
+ if (GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_password_changed != NULL) {
+ GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_password_changed (auditor);
+ }
+}
+
+void
+gdm_session_auditor_report_password_change_failure (GdmSessionAuditor *auditor)
+{
+ if (GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_password_change_failure != NULL) {
+ GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_password_change_failure (auditor);
+ }
+}
+
+void
+gdm_session_auditor_report_user_accredited (GdmSessionAuditor *auditor)
+{
+ if (GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_user_accredited != NULL) {
+ GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_user_accredited (auditor);
+ }
+}
+
+void
+gdm_session_auditor_report_login (GdmSessionAuditor *auditor)
+{
+ if (GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_login != NULL) {
+ GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_login (auditor);
+ }
+}
+
+void
+gdm_session_auditor_report_login_failure (GdmSessionAuditor *auditor,
+ int error_code,
+ const char *error_message)
+{
+ if (GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_login_failure != NULL) {
+ GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_login_failure (auditor, error_code, error_message);
+ }
+}
+
+void
+gdm_session_auditor_report_logout (GdmSessionAuditor *auditor)
+{
+ if (GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_logout != NULL) {
+ GDM_SESSION_AUDITOR_GET_CLASS (auditor)->report_logout (auditor);
+ }
+}
diff --git a/daemon/gdm-session-auditor.h b/daemon/gdm-session-auditor.h
new file mode 100644
index 0000000..dde00f6
--- /dev/null
+++ b/daemon/gdm-session-auditor.h
@@ -0,0 +1,65 @@
+/* gdm-session-auditor.h - Object for auditing session login/logout
+ *
+ * Copyright (C) 2004, 2008 Sun Microsystems, Inc.
+ * Copyright (C) 2005, 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Brian A. Cameron <Brian.Cameron@sun.com>
+ * Gary Winiger <Gary.Winiger@sun.com>
+ * Ray Strode <rstrode@redhat.com>
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+#ifndef GDM_SESSION_AUDITOR_H
+#define GDM_SESSION_AUDITOR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SESSION_AUDITOR (gdm_session_auditor_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GdmSessionAuditor, gdm_session_auditor, GDM, SESSION_AUDITOR, GObject)
+
+struct _GdmSessionAuditorClass
+{
+ GObjectClass parent_class;
+
+ void (* report_password_changed) (GdmSessionAuditor *auditor);
+ void (* report_password_change_failure) (GdmSessionAuditor *auditor);
+ void (* report_user_accredited) (GdmSessionAuditor *auditor);
+ void (* report_login) (GdmSessionAuditor *auditor);
+ void (* report_login_failure) (GdmSessionAuditor *auditor,
+ int error_code,
+ const char *error_message);
+ void (* report_logout) (GdmSessionAuditor *auditor);
+};
+
+GdmSessionAuditor *gdm_session_auditor_new (const char *hostname,
+ const char *display_device);
+void gdm_session_auditor_set_username (GdmSessionAuditor *auditor,
+ const char *username);
+void gdm_session_auditor_report_password_changed (GdmSessionAuditor *auditor);
+void gdm_session_auditor_report_password_change_failure (GdmSessionAuditor *auditor);
+void gdm_session_auditor_report_user_accredited (GdmSessionAuditor *auditor);
+void gdm_session_auditor_report_login (GdmSessionAuditor *auditor);
+void gdm_session_auditor_report_login_failure (GdmSessionAuditor *auditor,
+ int error_code,
+ const char *error_message);
+void gdm_session_auditor_report_logout (GdmSessionAuditor *auditor);
+
+G_END_DECLS
+#endif /* GDM_SESSION_AUDITOR_H */
diff --git a/daemon/gdm-session-enum-types.c.in b/daemon/gdm-session-enum-types.c.in
new file mode 100644
index 0000000..c028690
--- /dev/null
+++ b/daemon/gdm-session-enum-types.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+#include "@filename@"
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+
+GType
+@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+
+ if (G_UNLIKELY(etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+
+ return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+ /**/
+/*** END file-tail ***/
diff --git a/daemon/gdm-session-enum-types.h.in b/daemon/gdm-session-enum-types.h.in
new file mode 100644
index 0000000..b6934c1
--- /dev/null
+++ b/daemon/gdm-session-enum-types.h.in
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef GDM_SESSION_ENUM_TYPES_H
+#define GDM_SESSION_ENUM_TYPES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* GDM_SESSION_ENUM_TYPES_H */
+/*** END file-tail ***/
diff --git a/daemon/gdm-session-linux-auditor.c b/daemon/gdm-session-linux-auditor.c
new file mode 100644
index 0000000..0390ade
--- /dev/null
+++ b/daemon/gdm-session-linux-auditor.c
@@ -0,0 +1,165 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004, 2008 Sun Microsystems, Inc.
+ * Copyright (C) 2005, 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Brian A. Cameron <Brian.Cameron@sun.com>
+ * Gary Winiger <Gary.Winiger@sun.com>
+ * Ray Strode <rstrode@redhat.com>
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+#include "config.h"
+#include "gdm-session-linux-auditor.h"
+
+#include <fcntl.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <libaudit.h>
+
+#include <glib.h>
+
+#include "gdm-common.h"
+
+struct _GdmSessionLinuxAuditor
+{
+ GdmSessionAuditor parent;
+ int audit_fd;
+};
+
+static void gdm_session_linux_auditor_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmSessionLinuxAuditor, gdm_session_linux_auditor, GDM_TYPE_SESSION_AUDITOR)
+
+static void
+log_user_message (GdmSessionAuditor *auditor,
+ gint type,
+ gint result)
+{
+ GdmSessionLinuxAuditor *linux_auditor;
+ char buf[512];
+ char *username;
+ char *hostname;
+ char *display_device;
+ struct passwd *pw;
+
+ linux_auditor = GDM_SESSION_LINUX_AUDITOR (auditor);
+
+ g_object_get (G_OBJECT (auditor), "username", &username, NULL);
+ g_object_get (G_OBJECT (auditor), "hostname", &hostname, NULL);
+ g_object_get (G_OBJECT (auditor), "display-device", &display_device, NULL);
+
+ if (username != NULL) {
+ gdm_get_pwent_for_name (username, &pw);
+ } else {
+ username = g_strdup ("unknown");
+ pw = NULL;
+ }
+
+ if (pw != NULL) {
+ g_snprintf (buf, sizeof (buf), "uid=%d", pw->pw_uid);
+ audit_log_user_message (linux_auditor->audit_fd, type,
+ buf, hostname, NULL, display_device,
+ result);
+ } else {
+ g_snprintf (buf, sizeof (buf), "acct=%s", username);
+ audit_log_user_message (linux_auditor->audit_fd, type,
+ buf, hostname, NULL, display_device,
+ result);
+ }
+
+ g_free (username);
+ g_free (hostname);
+ g_free (display_device);
+}
+
+static void
+gdm_session_linux_auditor_report_login (GdmSessionAuditor *auditor)
+{
+ log_user_message (auditor, AUDIT_USER_LOGIN, 1);
+}
+
+static void
+gdm_session_linux_auditor_report_login_failure (GdmSessionAuditor *auditor,
+ int pam_error_code,
+ const char *pam_error_string)
+{
+ log_user_message (auditor, AUDIT_USER_LOGIN, 0);
+}
+
+static void
+gdm_session_linux_auditor_report_logout (GdmSessionAuditor *auditor)
+{
+ log_user_message (auditor, AUDIT_USER_LOGOUT, 1);
+}
+
+static void
+gdm_session_linux_auditor_class_init (GdmSessionLinuxAuditorClass *klass)
+{
+ GObjectClass *object_class;
+ GdmSessionAuditorClass *auditor_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ auditor_class = GDM_SESSION_AUDITOR_CLASS (klass);
+
+ object_class->finalize = gdm_session_linux_auditor_finalize;
+
+ auditor_class->report_login = gdm_session_linux_auditor_report_login;
+ auditor_class->report_login_failure = gdm_session_linux_auditor_report_login_failure;
+ auditor_class->report_logout = gdm_session_linux_auditor_report_logout;
+}
+
+static void
+gdm_session_linux_auditor_init (GdmSessionLinuxAuditor *auditor)
+{
+ auditor->audit_fd = audit_open ();
+}
+
+static void
+gdm_session_linux_auditor_finalize (GObject *object)
+{
+ GdmSessionLinuxAuditor *linux_auditor;
+ GObjectClass *parent_class;
+
+ linux_auditor = GDM_SESSION_LINUX_AUDITOR (object);
+
+ close (linux_auditor->audit_fd);
+
+ parent_class = G_OBJECT_CLASS (gdm_session_linux_auditor_parent_class);
+ if (parent_class->finalize != NULL) {
+ parent_class->finalize (object);
+ }
+}
+
+
+GdmSessionAuditor *
+gdm_session_linux_auditor_new (const char *hostname,
+ const char *display_device)
+{
+ GObject *auditor;
+
+ auditor = g_object_new (GDM_TYPE_SESSION_LINUX_AUDITOR,
+ "hostname", hostname,
+ "display-device", display_device,
+ NULL);
+
+ return GDM_SESSION_AUDITOR (auditor);
+}
+
+
diff --git a/daemon/gdm-session-linux-auditor.h b/daemon/gdm-session-linux-auditor.h
new file mode 100644
index 0000000..1e55728
--- /dev/null
+++ b/daemon/gdm-session-linux-auditor.h
@@ -0,0 +1,43 @@
+/* gdm-linux-session-auditor.h - Object for linux auditing of session login/logout
+ *
+ * Copyright (C) 2004, 2008 Sun Microsystems, Inc.
+ * Copyright (C) 2005, 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Brian A. Cameron <Brian.Cameron@sun.com>
+ * Gary Winiger <Gary.Winiger@sun.com>
+ * Ray Strode <rstrode@redhat.com>
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+#ifndef GDM_SESSION_LINUX_AUDITOR_H
+#define GDM_SESSION_LINUX_AUDITOR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-session-auditor.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SESSION_LINUX_AUDITOR (gdm_session_linux_auditor_get_type ())
+G_DECLARE_FINAL_TYPE (GdmSessionLinuxAuditor, gdm_session_linux_auditor, GDM, SESSION_LINUX_AUDITOR, GdmSessionAuditor)
+
+GdmSessionAuditor *gdm_session_linux_auditor_new (const char *hostname,
+ const char *display_device);
+
+G_END_DECLS
+#endif /* GDM_SESSION_LINUX_AUDITOR_H */
diff --git a/daemon/gdm-session-record.c b/daemon/gdm-session-record.c
new file mode 100644
index 0000000..7719d0a
--- /dev/null
+++ b/daemon/gdm-session-record.c
@@ -0,0 +1,305 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if defined(HAVE_UTMPX_H)
+#include <utmpx.h>
+#endif
+
+#if defined(HAVE_UTMP_H)
+#include <utmp.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "gdm-session-record.h"
+
+#ifndef GDM_BAD_SESSION_RECORDS_FILE
+#define GDM_BAD_SESSION_RECORDS_FILE "/var/log/btmp"
+#endif
+
+#if !defined(GDM_NEW_SESSION_RECORDS_FILE)
+# if defined(WTMPX_FILE)
+# define GDM_NEW_SESSION_RECORDS_FILE WTMPX_FILE
+# elif defined(_PATH_WTMPX)
+# define GDM_NEW_SESSION_RECORDS_FILE _PATH_WTMPX
+# elif defined(WTMPX_FILENAME)
+# define GDM_NEW_SESSION_RECORDS_FILE WTMPX_FILENAME
+# elif defined(WTMP_FILE)
+# define GDM_NEW_SESSION_RECORDS_FILE WTMP_FILE
+# elif defined(_PATH_WTMP) /* BSD systems */
+# define GDM_NEW_SESSION_RECORDS_FILE _PATH_WTMP
+# else
+# define GDM_NEW_SESSION_RECORDS_FILE "/var/log/wtmp"
+# endif
+#endif
+
+static void
+record_set_username (UTMP *u,
+ const char *username)
+{
+#if defined(HAVE_UT_UT_USER)
+ memccpy (u->ut_user, username, '\0', sizeof (u->ut_user));
+ g_debug ("using ut_user %.*s",
+ (int) sizeof (u->ut_user),
+ u->ut_user);
+#elif defined(HAVE_UT_UT_NAME)
+ memccpy (u->ut_name, username, '\0', sizeof (u->ut_name));
+ g_debug ("using ut_name %.*s",
+ (int) sizeof (u->ut_name),
+ u->ut_name);
+#endif
+}
+
+static void
+record_set_timestamp (UTMP *u)
+{
+#if defined(HAVE_UT_UT_TV)
+ GTimeVal now = { 0 };
+
+ /* Set time in TV format */
+ g_get_current_time (&now);
+ u->ut_tv.tv_sec = now.tv_sec;
+ u->ut_tv.tv_usec = now.tv_usec;
+ g_debug ("using ut_tv time %ld",
+ (glong) u->ut_tv.tv_sec);
+#elif defined(HAVE_UT_UT_TIME)
+ /* Set time in time format */
+ time (&u->ut_time);
+ g_debug ("using ut_time %ld",
+ (glong) u->ut_time);
+#endif
+}
+
+static void
+record_set_pid (UTMP *u,
+ GPid pid)
+{
+#if defined(HAVE_UT_UT_PID)
+ /* Set pid */
+ if (pid != 0) {
+ u->ut_pid = pid;
+ }
+ g_debug ("using ut_pid %d", (int) u->ut_pid);
+#endif
+}
+
+static void
+record_set_host (UTMP *u,
+ const char *x11_display_name,
+ const char *host_name)
+{
+ char *hostname;
+
+#if defined(HAVE_UT_UT_HOST)
+ hostname = NULL;
+
+ /*
+ * Set ut_host to hostname:$DISPLAY if remote, otherwise set
+ * to $DISPLAY
+ */
+ if (host_name != NULL
+ && x11_display_name != NULL
+ && g_str_has_prefix (x11_display_name, ":")) {
+ hostname = g_strdup_printf ("%s%s", host_name, x11_display_name);
+ } else {
+ hostname = g_strdup (x11_display_name);
+ }
+
+ if (hostname != NULL) {
+ memccpy (u->ut_host, hostname, '\0', sizeof (u->ut_host));
+ g_debug ("using ut_host %.*s", (int) sizeof (u->ut_host), u->ut_host);
+#ifdef HAVE_UT_UT_SYSLEN
+ u->ut_syslen = MIN (strlen (hostname), sizeof (u->ut_host));
+#endif
+ g_free (hostname);
+ }
+#endif
+}
+
+static void
+record_set_line (UTMP *u,
+ const char *display_device,
+ const char *x11_display_name)
+{
+ /*
+ * Set ut_line to the device name associated with this display
+ * but remove the "/dev/" prefix. If no device, then use the
+ * $DISPLAY value.
+ */
+ if (display_device != NULL
+ && g_str_has_prefix (display_device, "/dev/")) {
+ memccpy (u->ut_line,
+ display_device + strlen ("/dev/"),
+ '\0',
+ sizeof (u->ut_line));
+ } else if (x11_display_name != NULL) {
+ memccpy (u->ut_line,
+ x11_display_name,
+ '\0',
+ sizeof (u->ut_line));
+ }
+
+ g_debug ("using ut_line %.*s", (int) sizeof (u->ut_line), u->ut_line);
+}
+
+void
+gdm_session_record_login (GPid session_pid,
+ const char *user_name,
+ const char *host_name,
+ const char *x11_display_name,
+ const char *display_device)
+{
+ UTMP session_record = { 0 };
+
+ if (x11_display_name == NULL)
+ x11_display_name = display_device;
+
+ record_set_username (&session_record, user_name);
+
+ g_debug ("Writing login record");
+
+#if defined(HAVE_UT_UT_TYPE)
+ session_record.ut_type = USER_PROCESS;
+ g_debug ("using ut_type USER_PROCESS");
+#endif
+
+ record_set_timestamp (&session_record);
+ record_set_pid (&session_record, session_pid);
+ record_set_host (&session_record, x11_display_name, host_name);
+ record_set_line (&session_record, display_device, x11_display_name);
+
+ /* Handle wtmp */
+ g_debug ("Writing wtmp session record to " GDM_NEW_SESSION_RECORDS_FILE);
+#if defined(HAVE_UPDWTMPX)
+ updwtmpx (GDM_NEW_SESSION_RECORDS_FILE, &session_record);
+#elif defined(HAVE_UPDWTMP)
+ updwtmp (GDM_NEW_SESSION_RECORDS_FILE, &session_record);
+#elif defined(HAVE_LOGWTMP) && defined(HAVE_UT_UT_HOST)
+#if defined(HAVE_UT_UT_USER)
+ logwtmp (session_record.ut_line, session_record.ut_user, session_record.ut_host);
+#elif defined(HAVE_UT_UT_NAME)
+ logwtmp (session_record.ut_line, session_record.ut_name, session_record.ut_host);
+#endif
+#endif
+
+ /* Handle utmp */
+#if defined(HAVE_GETUTXENT)
+ g_debug ("Adding or updating utmp record for login");
+ setutxent();
+ pututxline (&session_record);
+ endutxent();
+#elif defined(HAVE_LOGIN)
+ login (&session_record);
+#endif
+}
+
+void
+gdm_session_record_logout (GPid session_pid,
+ const char *user_name,
+ const char *host_name,
+ const char *x11_display_name,
+ const char *display_device)
+{
+ UTMP session_record = { 0 };
+
+ if (x11_display_name == NULL)
+ x11_display_name = display_device;
+
+ g_debug ("Writing logout record");
+
+#if defined(HAVE_UT_UT_TYPE)
+ session_record.ut_type = DEAD_PROCESS;
+ g_debug ("using ut_type DEAD_PROCESS");
+#endif
+
+ record_set_timestamp (&session_record);
+ record_set_pid (&session_record, session_pid);
+ record_set_host (&session_record, x11_display_name, host_name);
+ record_set_line (&session_record, display_device, x11_display_name);
+
+ /* Handle wtmp */
+ g_debug ("Writing wtmp logout record to " GDM_NEW_SESSION_RECORDS_FILE);
+#if defined(HAVE_UPDWTMPX)
+ updwtmpx (GDM_NEW_SESSION_RECORDS_FILE, &session_record);
+#elif defined (HAVE_UPDWTMP)
+ updwtmp (GDM_NEW_SESSION_RECORDS_FILE, &session_record);
+#elif defined(HAVE_LOGWTMP)
+ logwtmp (session_record.ut_line, "", "");
+#endif
+
+ /* Handle utmp */
+#if defined(HAVE_GETUTXENT)
+ g_debug ("Adding or updating utmp record for logout");
+ setutxent();
+ pututxline (&session_record);
+ endutxent();
+#elif defined(HAVE_LOGOUT)
+ logout (session_record.ut_line);
+#endif
+}
+
+void
+gdm_session_record_failed (GPid session_pid,
+ const char *user_name,
+ const char *host_name,
+ const char *x11_display_name,
+ const char *display_device)
+{
+ UTMP session_record = { 0 };
+
+ if (x11_display_name == NULL)
+ x11_display_name = display_device;
+
+ record_set_username (&session_record, user_name);
+
+ g_debug ("Writing failed session attempt record");
+
+#if defined(HAVE_UT_UT_TYPE)
+ session_record.ut_type = USER_PROCESS;
+ g_debug ("using ut_type USER_PROCESS");
+#endif
+
+ record_set_timestamp (&session_record);
+ record_set_pid (&session_record, session_pid);
+ record_set_host (&session_record, x11_display_name, host_name);
+ record_set_line (&session_record, display_device, x11_display_name);
+
+#if defined(HAVE_UPDWTMPX) || defined(HAVE_UPDWTMP)
+ /* Handle btmp */
+ g_debug ("Writing btmp failed session attempt record to "
+ GDM_BAD_SESSION_RECORDS_FILE);
+#endif
+
+#if defined(HAVE_UPDWTMPX)
+ updwtmpx (GDM_BAD_SESSION_RECORDS_FILE, &session_record);
+#elif defined(HAVE_UPDWTMP)
+ updwtmp(GDM_BAD_SESSION_RECORDS_FILE, &session_record);
+#endif
+
+}
diff --git a/daemon/gdm-session-record.h b/daemon/gdm-session-record.h
new file mode 100644
index 0000000..3c53268
--- /dev/null
+++ b/daemon/gdm-session-record.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __GDM_SESSION_RECORD_H
+#define __GDM_SESSION_RECORD_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void
+gdm_session_record_login (GPid session_pid,
+ const char *user_name,
+ const char *host_name,
+ const char *x11_display_name,
+ const char *display_device);
+void
+gdm_session_record_logout (GPid session_pid,
+ const char *user_name,
+ const char *host_name,
+ const char *x11_display_name,
+ const char *display_device);
+void
+gdm_session_record_failed (GPid session_pid,
+ const char *user_name,
+ const char *host_name,
+ const char *x11_display_name,
+ const char *display_device);
+
+
+G_END_DECLS
+
+#endif /* GDM_SESSION_RECORD_H */
diff --git a/daemon/gdm-session-settings.c b/daemon/gdm-session-settings.c
new file mode 100644
index 0000000..ef5d72e
--- /dev/null
+++ b/daemon/gdm-session-settings.c
@@ -0,0 +1,425 @@
+/* gdm-session-settings.c - Loads session and language from ~/.dmrc
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+#include "config.h"
+#include "gdm-session-settings.h"
+
+#include <errno.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <act/act-user-manager.h>
+
+struct _GdmSessionSettingsPrivate
+{
+ ActUserManager *user_manager;
+ ActUser *user;
+ char *session_name;
+ char *session_type;
+ char *language_name;
+};
+
+static void gdm_session_settings_finalize (GObject *object);
+static void gdm_session_settings_class_install_properties (GdmSessionSettingsClass *
+ settings_class);
+
+static void gdm_session_settings_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gdm_session_settings_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+enum {
+ PROP_0 = 0,
+ PROP_SESSION_NAME,
+ PROP_SESSION_TYPE,
+ PROP_LANGUAGE_NAME,
+ PROP_IS_LOADED
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdmSessionSettings,
+ gdm_session_settings,
+ G_TYPE_OBJECT)
+
+static void
+gdm_session_settings_class_init (GdmSessionSettingsClass *settings_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (settings_class);
+
+ object_class->finalize = gdm_session_settings_finalize;
+
+ gdm_session_settings_class_install_properties (settings_class);
+}
+
+static void
+gdm_session_settings_class_install_properties (GdmSessionSettingsClass *settings_class)
+{
+ GObjectClass *object_class;
+ GParamSpec *param_spec;
+
+ object_class = G_OBJECT_CLASS (settings_class);
+ object_class->set_property = gdm_session_settings_set_property;
+ object_class->get_property = gdm_session_settings_get_property;
+
+ param_spec = g_param_spec_string ("session-name", "Session Name",
+ "The name of the session",
+ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SESSION_NAME, param_spec);
+
+ param_spec = g_param_spec_string ("session-type", "Session Type",
+ "The type of the session",
+ NULL, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_SESSION_TYPE, param_spec);
+
+ param_spec = g_param_spec_string ("language-name", "Language Name",
+ "The name of the language",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_LANGUAGE_NAME, param_spec);
+
+ param_spec = g_param_spec_boolean ("is-loaded", NULL, NULL,
+ FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_IS_LOADED, param_spec);
+}
+
+static void
+gdm_session_settings_init (GdmSessionSettings *settings)
+{
+ settings->priv = G_TYPE_INSTANCE_GET_PRIVATE (settings,
+ GDM_TYPE_SESSION_SETTINGS,
+ GdmSessionSettingsPrivate);
+
+ settings->priv->user_manager = act_user_manager_get_default ();
+
+}
+
+static void
+gdm_session_settings_finalize (GObject *object)
+{
+ GdmSessionSettings *settings;
+ GObjectClass *parent_class;
+
+ settings = GDM_SESSION_SETTINGS (object);
+
+ if (settings->priv->user != NULL) {
+ g_object_unref (settings->priv->user);
+ }
+
+ g_free (settings->priv->session_name);
+ g_free (settings->priv->language_name);
+
+ parent_class = G_OBJECT_CLASS (gdm_session_settings_parent_class);
+
+ if (parent_class->finalize != NULL) {
+ parent_class->finalize (object);
+ }
+}
+
+void
+gdm_session_settings_set_language_name (GdmSessionSettings *settings,
+ const char *language_name)
+{
+ g_return_if_fail (GDM_IS_SESSION_SETTINGS (settings));
+
+ if (settings->priv->language_name == NULL ||
+ strcmp (settings->priv->language_name, language_name) != 0) {
+ settings->priv->language_name = g_strdup (language_name);
+ g_object_notify (G_OBJECT (settings), "language-name");
+ }
+}
+
+void
+gdm_session_settings_set_session_name (GdmSessionSettings *settings,
+ const char *session_name)
+{
+ g_return_if_fail (GDM_IS_SESSION_SETTINGS (settings));
+
+ if (settings->priv->session_name == NULL ||
+ strcmp (settings->priv->session_name, session_name) != 0) {
+ settings->priv->session_name = g_strdup (session_name);
+ g_object_notify (G_OBJECT (settings), "session-name");
+ }
+}
+
+void
+gdm_session_settings_set_session_type (GdmSessionSettings *settings,
+ const char *session_type)
+{
+ g_return_if_fail (GDM_IS_SESSION_SETTINGS (settings));
+
+ if (settings->priv->session_type == NULL ||
+ g_strcmp0 (settings->priv->session_type, session_type) != 0) {
+ settings->priv->session_type = g_strdup (session_type);
+ g_object_notify (G_OBJECT (settings), "session-type");
+ }
+}
+
+char *
+gdm_session_settings_get_language_name (GdmSessionSettings *settings)
+{
+ g_return_val_if_fail (GDM_IS_SESSION_SETTINGS (settings), NULL);
+ return g_strdup (settings->priv->language_name);
+}
+
+char *
+gdm_session_settings_get_session_name (GdmSessionSettings *settings)
+{
+ g_return_val_if_fail (GDM_IS_SESSION_SETTINGS (settings), NULL);
+ return g_strdup (settings->priv->session_name);
+}
+
+char *
+gdm_session_settings_get_session_type (GdmSessionSettings *settings)
+{
+ g_return_val_if_fail (GDM_IS_SESSION_SETTINGS (settings), NULL);
+ return g_strdup (settings->priv->session_type);
+}
+
+static void
+gdm_session_settings_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionSettings *settings;
+
+ settings = GDM_SESSION_SETTINGS (object);
+
+ switch (prop_id) {
+ case PROP_LANGUAGE_NAME:
+ gdm_session_settings_set_language_name (settings, g_value_get_string (value));
+ break;
+
+ case PROP_SESSION_NAME:
+ gdm_session_settings_set_session_name (settings, g_value_get_string (value));
+ break;
+
+ case PROP_SESSION_TYPE:
+ gdm_session_settings_set_session_type (settings, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gdm_session_settings_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSessionSettings *settings;
+
+ settings = GDM_SESSION_SETTINGS (object);
+
+ switch (prop_id) {
+ case PROP_SESSION_NAME:
+ g_value_set_string (value, settings->priv->session_name);
+ break;
+
+ case PROP_SESSION_TYPE:
+ g_value_set_string (value, settings->priv->session_type);
+ break;
+
+ case PROP_LANGUAGE_NAME:
+ g_value_set_string (value, settings->priv->language_name);
+ break;
+
+ case PROP_IS_LOADED:
+ g_value_set_boolean (value, gdm_session_settings_is_loaded (settings));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+GdmSessionSettings *
+gdm_session_settings_new (void)
+{
+ GdmSessionSettings *settings;
+
+ settings = g_object_new (GDM_TYPE_SESSION_SETTINGS,
+ NULL);
+
+ return settings;
+}
+
+gboolean
+gdm_session_settings_is_loaded (GdmSessionSettings *settings)
+{
+ if (settings->priv->user == NULL) {
+ return FALSE;
+ }
+
+ return act_user_is_loaded (settings->priv->user);
+}
+
+static void
+load_settings_from_user (GdmSessionSettings *settings)
+{
+ const char *session_name;
+ const char *session_type;
+ const char *language_name;
+
+ if (!act_user_is_loaded (settings->priv->user)) {
+ g_warning ("GdmSessionSettings: trying to load user settings from unloaded user");
+ return;
+ }
+
+ /* if the user doesn't have saved state, they don't have any settings worth reading */
+ if (!act_user_get_saved (settings->priv->user))
+ goto out;
+
+ session_type = act_user_get_session_type (settings->priv->user);
+ session_name = act_user_get_session (settings->priv->user);
+
+ g_debug ("GdmSessionSettings: saved session is %s (type %s)", session_name, session_type);
+
+ if (session_type != NULL && session_type[0] != '\0') {
+ gdm_session_settings_set_session_type (settings, session_type);
+ }
+
+ if (session_name != NULL && session_name[0] != '\0') {
+ gdm_session_settings_set_session_name (settings, session_name);
+ }
+
+ language_name = act_user_get_language (settings->priv->user);
+
+ g_debug ("GdmSessionSettings: saved language is %s", language_name);
+ if (language_name != NULL && language_name[0] != '\0') {
+ gdm_session_settings_set_language_name (settings, language_name);
+ }
+
+out:
+ g_object_notify (G_OBJECT (settings), "is-loaded");
+}
+
+static void
+on_user_is_loaded_changed (ActUser *user,
+ GParamSpec *pspec,
+ GdmSessionSettings *settings)
+{
+ if (act_user_is_loaded (settings->priv->user)) {
+ load_settings_from_user (settings);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (settings->priv->user),
+ G_CALLBACK (on_user_is_loaded_changed),
+ settings);
+ }
+}
+
+gboolean
+gdm_session_settings_load (GdmSessionSettings *settings,
+ const char *username)
+{
+ ActUser *old_user;
+
+ g_return_val_if_fail (settings != NULL, FALSE);
+ g_return_val_if_fail (username != NULL, FALSE);
+ g_return_val_if_fail (!gdm_session_settings_is_loaded (settings), FALSE);
+
+ if (settings->priv->user != NULL) {
+ old_user = settings->priv->user;
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (settings->priv->user),
+ G_CALLBACK (on_user_is_loaded_changed),
+ settings);
+ } else {
+ old_user = NULL;
+ }
+
+ settings->priv->user = act_user_manager_get_user (settings->priv->user_manager,
+ username);
+
+ g_clear_object (&old_user);
+
+ if (!act_user_is_loaded (settings->priv->user)) {
+ g_signal_connect (settings->priv->user,
+ "notify::is-loaded",
+ G_CALLBACK (on_user_is_loaded_changed),
+ settings);
+ return FALSE;
+ }
+
+ load_settings_from_user (settings);
+
+ return TRUE;
+}
+
+gboolean
+gdm_session_settings_save (GdmSessionSettings *settings,
+ const char *username)
+{
+ ActUser *user;
+
+ g_return_val_if_fail (GDM_IS_SESSION_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (username != NULL, FALSE);
+ g_return_val_if_fail (gdm_session_settings_is_loaded (settings), FALSE);
+
+ user = act_user_manager_get_user (settings->priv->user_manager,
+ username);
+
+
+ if (!act_user_is_loaded (user)) {
+ g_object_unref (user);
+ return FALSE;
+ }
+
+ if (settings->priv->session_name != NULL) {
+ act_user_set_session (user, settings->priv->session_name);
+ }
+
+ if (settings->priv->session_type != NULL) {
+ act_user_set_session_type (user, settings->priv->session_type);
+ }
+
+ if (settings->priv->language_name != NULL) {
+ act_user_set_language (user, settings->priv->language_name);
+ }
+
+ if (!act_user_is_local_account (user)) {
+ g_autoptr (GError) error = NULL;
+
+ act_user_manager_cache_user (settings->priv->user_manager, username, &error);
+
+ if (error != NULL) {
+ g_debug ("GdmSessionSettings: Could not locally cache remote user: %s", error->message);
+ g_object_unref (user);
+ return FALSE;
+ }
+
+ }
+ g_object_unref (user);
+
+ return TRUE;
+}
diff --git a/daemon/gdm-session-settings.h b/daemon/gdm-session-settings.h
new file mode 100644
index 0000000..db38ffc
--- /dev/null
+++ b/daemon/gdm-session-settings.h
@@ -0,0 +1,72 @@
+/* gdm-session-settings.h - Object for auditing session login/logout
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+#ifndef GDM_SESSION_SETTINGS_H
+#define GDM_SESSION_SETTINGS_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define GDM_TYPE_SESSION_SETTINGS (gdm_session_settings_get_type ())
+#define GDM_SESSION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SESSION_SETTINGS, GdmSessionSettings))
+#define GDM_SESSION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SESSION_SETTINGS, GdmSessionSettingsClass))
+#define GDM_IS_SESSION_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SESSION_SETTINGS))
+#define GDM_IS_SESSION_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SESSION_SETTINGS))
+#define GDM_SESSION_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SESSION_SETTINGS, GdmSessionSettingsClass))
+#define GDM_SESSION_SETTINGS_ERROR (gdm_session_settings_error_quark ())
+typedef struct _GdmSessionSettings GdmSessionSettings;
+typedef struct _GdmSessionSettingsClass GdmSessionSettingsClass;
+typedef struct _GdmSessionSettingsPrivate GdmSessionSettingsPrivate;
+
+struct _GdmSessionSettings
+{
+ GObject parent;
+
+ /*< private > */
+ GdmSessionSettingsPrivate *priv;
+};
+
+struct _GdmSessionSettingsClass
+{
+ GObjectClass parent_class;
+};
+
+GType gdm_session_settings_get_type (void);
+GdmSessionSettings *gdm_session_settings_new (void);
+
+gboolean gdm_session_settings_load (GdmSessionSettings *settings,
+ const char *username);
+gboolean gdm_session_settings_save (GdmSessionSettings *settings,
+ const char *username);
+gboolean gdm_session_settings_is_loaded (GdmSessionSettings *settings);
+char *gdm_session_settings_get_language_name (GdmSessionSettings *settings);
+char *gdm_session_settings_get_session_name (GdmSessionSettings *settings);
+char *gdm_session_settings_get_session_type (GdmSessionSettings *settings);
+void gdm_session_settings_set_language_name (GdmSessionSettings *settings,
+ const char *language_name);
+void gdm_session_settings_set_session_name (GdmSessionSettings *settings,
+ const char *session_name);
+void gdm_session_settings_set_session_type (GdmSessionSettings *settings,
+ const char *session_type);
+
+G_END_DECLS
+#endif /* GDM_SESSION_SETTINGS_H */
diff --git a/daemon/gdm-session-solaris-auditor.c b/daemon/gdm-session-solaris-auditor.c
new file mode 100644
index 0000000..f632804
--- /dev/null
+++ b/daemon/gdm-session-solaris-auditor.c
@@ -0,0 +1,387 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004, 2008 Sun Microsystems, Inc.
+ * Copyright (C) 2005, 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Brian A. Cameron <Brian.Cameron@sun.com>
+ * Gary Winiger <Gary.Winiger@sun.com>
+ * Ray Strode <rstrode@redhat.com>
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+#include "config.h"
+#include "gdm-session-solaris-auditor.h"
+
+#include <syslog.h>
+#include <security/pam_appl.h>
+#include <pwd.h>
+
+#include <fcntl.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+struct GdmSessionSolarisAuditor
+{
+ GdmSessionAuditor parent;
+
+ adt_session_data_t *audit_session_handle;
+
+ guint password_change_initiated : 1;
+ guint password_changed : 1;
+ guint user_accredited : 1;
+
+ /* cached values to prevent repeated calls
+ * to getpwnam
+ */
+ char *username;
+ uid_t uid;
+ gid_t gid;
+};
+
+static void gdm_session_solaris_auditor_finalize (GObject *object);
+
+G_DEFINE_TYPE (GdmSessionSolarisAuditor, gdm_session_solaris_auditor, GDM_TYPE_SESSION_AUDITOR)
+
+static void
+gdm_session_solaris_auditor_report_password_changed (GdmSessionAuditor *auditor)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (auditor);
+ solaris_auditor->password_change_initiated = TRUE;
+ solaris_auditor->password_changed = TRUE;
+}
+
+static void
+gdm_session_solaris_auditor_report_password_change_failure (GdmSessionAuditor *auditor)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (auditor);
+ solaris_auditor->password_change_initiated = TRUE;
+ solaris_auditor->password_changed = FALSE;
+}
+
+static void
+gdm_session_solaris_auditor_report_user_accredited (GdmSessionAuditor *auditor)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (auditor);
+ solaris_auditor->user_accredited = TRUE;
+}
+
+static void
+gdm_session_solaris_auditor_report_login (GdmSessionAuditor *auditor)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+ adt_session_data_t *adt_ah; /* Audit session handle */
+ adt_event_data_t *event; /* Event to generate */
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (auditor);
+
+ g_return_if_fail (solaris_auditor->username != NULL);
+
+ adt_ah = NULL;
+ if (adt_start_session (&adt_ah, NULL, ADT_USE_PROC_DATA) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_start_session (ADT_login): %m");
+ goto cleanup;
+ }
+
+ if (adt_set_user (adt_ah, solaris_auditor->uid,
+ solaris_auditor->gid, solaris_auditor->uid,
+ solaris_auditor->gid, NULL, ADT_USER) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_set_user (ADT_login, %s): %m",
+ solaris_auditor->username);
+ }
+
+ event = adt_alloc_event (adt_ah, ADT_login);
+ if (event == NULL) {
+ syslog (LOG_AUTH | LOG_ALERT, "adt_alloc_event (ADT_login): %m");
+ } else if (adt_put_event (event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_login, ADT_SUCCESS): %m");
+ }
+
+ if (solaris_auditor->password_changed) {
+
+ g_assert (solaris_auditor->password_change_initiated);
+
+ /* Also audit password change */
+ adt_free_event (event);
+ event = adt_alloc_event (adt_ah, ADT_passwd);
+ if (event == NULL) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_alloc_event (ADT_passwd): %m");
+ } else if (adt_put_event (event, ADT_SUCCESS,
+ ADT_SUCCESS) != 0) {
+
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_passwd, ADT_SUCCESS): %m");
+ }
+ }
+
+ adt_free_event (event);
+
+cleanup:
+ solaris_auditor->audit_session_handle = adt_ah;
+}
+
+static void
+gdm_session_solaris_auditor_report_login_failure (GdmSessionAuditor *auditor,
+ int pam_error_code,
+ const char *pam_error_string)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+ char *hostname;
+ char *display_device;
+ adt_session_data_t *ah; /* Audit session handle */
+ adt_event_data_t *event; /* Event to generate */
+ adt_termid_t *tid; /* Terminal ID for failures */
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (auditor);
+ g_object_get (G_OBJECT (auditor),
+ "hostname", &hostname,
+ "display-device", &display_device, NULL);
+
+ if (solaris_auditor->user_accredited) {
+ if (adt_start_session (&ah, NULL, ADT_USE_PROC_DATA) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_start_session (ADT_login, ADT_FAILURE): %m");
+ goto cleanup;
+ }
+ } else {
+ if (adt_start_session (&ah, NULL, 0) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_start_session (ADT_login, ADT_FAILURE): %m");
+ goto cleanup;
+ }
+
+ /* If display is on console or VT */
+ if (hostname != NULL && hostname[0] != '\0') {
+ /* Login from a remote host */
+ if (adt_load_hostname (hostname, &tid) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_loadhostname (%s): %m", hostname);
+ }
+ } else {
+ /* login from the local host */
+ if (adt_load_ttyname (display_device, &tid) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_loadhostname (localhost): %m");
+ }
+ }
+
+ if (adt_set_user (ah,
+ solaris_auditor->username != NULL ? solaris_auditor->uid : ADT_NO_ATTRIB,
+ solaris_auditor->username != NULL ? solaris_auditor->gid : ADT_NO_ATTRIB,
+ solaris_auditor->username != NULL ? solaris_auditor->uid : ADT_NO_ATTRIB,
+ solaris_auditor->username != NULL ? solaris_auditor->gid : ADT_NO_ATTRIB,
+ tid, ADT_NEW) != 0) {
+
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_set_user (%s): %m",
+ solaris_auditor->username != NULL ? solaris_auditor->username : "ADT_NO_ATTRIB");
+ }
+ }
+
+ event = adt_alloc_event (ah, ADT_login);
+
+ if (event == NULL) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_alloc_event (ADT_login, ADT_FAILURE): %m");
+ goto done;
+ } else if (adt_put_event (event, ADT_FAILURE,
+ ADT_FAIL_PAM + pam_error_code) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_login (ADT_FAIL, %s): %m",
+ pam_error_string);
+ }
+
+ if (solaris_auditor->password_change_initiated) {
+ /* Also audit password change */
+ adt_free_event (event);
+
+ event = adt_alloc_event (ah, ADT_passwd);
+ if (event == NULL) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_alloc_event (ADT_passwd): %m");
+ goto done;
+ }
+
+ if (solaris_auditor->password_changed) {
+ if (adt_put_event (event, ADT_SUCCESS,
+ ADT_SUCCESS) != 0) {
+
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_passwd, ADT_SUCCESS): "
+ "%m");
+ }
+ } else {
+ if (adt_put_event (event, ADT_FAILURE,
+ ADT_FAIL_PAM + pam_error_code) != 0) {
+
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_passwd, ADT_FAILURE): "
+ "%m");
+ }
+ }
+ }
+ adt_free_event (event);
+
+done:
+ /* Reset process audit state. this process is being reused.*/
+ if ((adt_set_user (ah, ADT_NO_AUDIT, ADT_NO_AUDIT, ADT_NO_AUDIT,
+ ADT_NO_AUDIT, NULL, ADT_NEW) != 0) ||
+ (adt_set_proc (ah) != 0)) {
+
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_login (ADT_FAILURE reset, %m)");
+ }
+ (void) adt_end_session (ah);
+
+cleanup:
+ g_free (hostname);
+ g_free (display_device);
+}
+
+static void
+gdm_session_solaris_auditor_report_logout (GdmSessionAuditor *auditor)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+ adt_session_data_t *adt_ah; /* Audit session handle */
+ adt_event_data_t *event; /* Event to generate */
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (auditor);
+
+ adt_ah = solaris_auditor->audit_session_handle;
+
+ event = adt_alloc_event (adt_ah, ADT_logout);
+ if (event == NULL) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_alloc_event (ADT_logout): %m");
+ } else if (adt_put_event (event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_put_event (ADT_logout, ADT_SUCCESS): %m");
+ }
+
+ adt_free_event (event);
+
+ /* Reset process audit state. this process is being reused. */
+ if ((adt_set_user (adt_ah, ADT_NO_AUDIT, ADT_NO_AUDIT, ADT_NO_AUDIT,
+ ADT_NO_AUDIT, NULL, ADT_NEW) != 0) ||
+ (adt_set_proc (adt_ah) != 0)) {
+ syslog (LOG_AUTH | LOG_ALERT,
+ "adt_set_proc (ADT_logout reset): %m");
+ }
+
+ (void) adt_end_session (adt_ah);
+ solaris_auditor->audit_session_handle = NULL;
+}
+
+static void
+gdm_session_solaris_auditor_class_init (GdmSessionSolarisAuditorClass *klass)
+{
+ GObjectClass *object_class;
+ GdmSessionAuditorClass *auditor_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ auditor_class = GDM_SESSION_AUDITOR_CLASS (klass);
+
+ object_class->finalize = gdm_session_solaris_auditor_finalize;
+
+ auditor_class->report_password_changed = gdm_session_solaris_auditor_report_password_changed;
+ auditor_class->report_password_change_failure = gdm_session_solaris_auditor_report_password_change_failure;
+ auditor_class->report_user_accredited = gdm_session_solaris_auditor_report_user_accredited;
+ auditor_class->report_login = gdm_session_solaris_auditor_report_login;
+ auditor_class->report_login_failure = gdm_session_solaris_auditor_report_login_failure;
+ auditor_class->report_logout = gdm_session_solaris_auditor_report_logout;
+}
+
+static void
+on_username_set (GdmSessionSolarisAuditor *auditor)
+{
+ char *username;
+ struct passwd *passwd_entry;
+
+ g_object_get (G_OBJECT (auditor), "username", &username, NULL);
+
+ gdm_get_pwent_for_name (username, &passwd_entry);
+
+ if (passwd_entry != NULL) {
+ auditor->uid = passwd_entry->pw_uid;
+ auditor->gid = passwd_entry->pw_gid;
+ auditor->username = g_strdup (passwd_entry->pw_name);
+ } else {
+ g_free (auditor->username);
+ auditor->username = NULL;
+ auditor->uid = (uid_t) -1;
+ auditor->gid = (gid_t) -1;
+ }
+
+ g_free (username);
+}
+
+static void
+gdm_session_solaris_auditor_init (GdmSessionSolarisAuditor *auditor)
+{
+ g_signal_connect (G_OBJECT (auditor), "notify::username",
+ G_CALLBACK (on_username_set), NULL);
+
+ auditor->uid = (uid_t) -1;
+ auditor->gid = (gid_t) -1;
+}
+
+static void
+gdm_session_solaris_auditor_finalize (GObject *object)
+{
+ GdmSessionSolarisAuditor *solaris_auditor;
+ GObjectClass *parent_class;
+
+ solaris_auditor = GDM_SESSION_SOLARIS_AUDITOR (object);
+
+ g_free (solaris_auditor->username);
+ solaris_auditor->username = NULL;
+
+ parent_class = G_OBJECT_CLASS (gdm_session_solaris_auditor_parent_class);
+
+ if (parent_class->finalize != NULL) {
+ parent_class->finalize (object);
+ }
+}
+
+GdmSessionAuditor *
+gdm_session_solaris_auditor_new (const char *hostname,
+ const char *display_device)
+{
+ GObject *auditor;
+
+ auditor = g_object_new (GDM_TYPE_SESSION_SOLARIS_AUDITOR,
+ "hostname", hostname,
+ "display-device", display_device,
+ NULL);
+
+ return GDM_SESSION_AUDITOR (auditor);
+}
+
+
diff --git a/daemon/gdm-session-solaris-auditor.h b/daemon/gdm-session-solaris-auditor.h
new file mode 100644
index 0000000..dd58532
--- /dev/null
+++ b/daemon/gdm-session-solaris-auditor.h
@@ -0,0 +1,42 @@
+/* gdm-solaris-session-auditor.h - Object for solaris auditing of session login/logout
+ *
+ * Copyright (C) 2004, 2008 Sun Microsystems, Inc.
+ * Copyright (C) 2005, 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Written by: Brian A. Cameron <Brian.Cameron@sun.com>
+ * Gary Winiger <Gary.Winiger@sun.com>
+ * Ray Strode <rstrode@redhat.com>
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+#ifndef GDM_SESSION_SOLARIS_AUDITOR_H
+#define GDM_SESSION_SOLARIS_AUDITOR_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-session-auditor.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SESSION_SOLARIS_AUDITOR (gdm_session_solaris_auditor_get_type ())
+G_DECLARE_FINAL_TYPE (GdmSessionSolarisAuditor, gdm_session_solaris_auditor, GDM, SESSION_SOLARIS_AUDITOR, GdmSessionAuditor)
+
+GdmSessionAuditor *gdm_session_solaris_auditor_new (const char *hostname,
+ const char *display_device);
+G_END_DECLS
+#endif /* GDM_SESSION_SOLARIS_AUDITOR_H */
diff --git a/daemon/gdm-session-worker-common.c b/daemon/gdm-session-worker-common.c
new file mode 100644
index 0000000..a1c90c9
--- /dev/null
+++ b/daemon/gdm-session-worker-common.c
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ * Copyright (C) 2012 Jasper St. Pierre <jstpierre@mecheye.net>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gdm-session-worker-common.h"
+
+static const GDBusErrorEntry gdm_session_worker_error_entries[] = {
+ { GDM_SESSION_WORKER_ERROR_GENERIC , "org.gnome.DisplayManager.SessionWorker.Error.Generic", },
+ { GDM_SESSION_WORKER_ERROR_WITH_SESSION_COMMAND , "org.gnome.DisplayManager.SessionWorker.Error.WithSessionCommand" },
+ { GDM_SESSION_WORKER_ERROR_FORKING , "org.gnome.DisplayManager.SessionWorker.Error.Forking" },
+ { GDM_SESSION_WORKER_ERROR_OPENING_MESSAGE_PIPE , "org.gnome.DisplayManager.SessionWorker.Error.OpeningMessagePipe" },
+ { GDM_SESSION_WORKER_ERROR_COMMUNICATING , "org.gnome.DisplayManager.SessionWorker.Error.Communicating" },
+ { GDM_SESSION_WORKER_ERROR_WORKER_DIED , "org.gnome.DisplayManager.SessionWorker.Error.WorkerDied" },
+ { GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE , "org.gnome.DisplayManager.SessionWorker.Error.ServiceUnavailable" },
+ { GDM_SESSION_WORKER_ERROR_AUTHENTICATING , "org.gnome.DisplayManager.SessionWorker.Error.Authenticating" },
+ { GDM_SESSION_WORKER_ERROR_AUTHORIZING , "org.gnome.DisplayManager.SessionWorker.Error.Authorizing" },
+ { GDM_SESSION_WORKER_ERROR_OPENING_LOG_FILE , "org.gnome.DisplayManager.SessionWorker.Error.OpeningLogFile" },
+ { GDM_SESSION_WORKER_ERROR_OPENING_SESSION , "org.gnome.DisplayManager.SessionWorker.Error.OpeningSession" },
+ { GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS , "org.gnome.DisplayManager.SessionWorker.Error.GivingCredentials" },
+ { GDM_SESSION_WORKER_ERROR_WRONG_STATE , "org.gnome.DisplayManager.SessionWorker.Error.WrongState" },
+ { GDM_SESSION_WORKER_ERROR_OUTSTANDING_REQUEST , "org.gnome.DisplayManager.SessionWorker.Error.OutstandingRequest" },
+ { GDM_SESSION_WORKER_ERROR_IN_REAUTH_SESSION , "org.gnome.DisplayManager.SessionWorker.Error.InReauthSession" }
+};
+
+GQuark
+gdm_session_worker_error_quark (void)
+{
+ static volatile gsize error_quark = 0;
+
+ g_dbus_error_register_error_domain ("gdm-session-worker-error-quark",
+ &error_quark,
+ gdm_session_worker_error_entries,
+ G_N_ELEMENTS (gdm_session_worker_error_entries));
+
+ return (GQuark) error_quark;
+}
diff --git a/daemon/gdm-session-worker-common.h b/daemon/gdm-session-worker-common.h
new file mode 100644
index 0000000..7dab7d4
--- /dev/null
+++ b/daemon/gdm-session-worker-common.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ * Copyright (C) 2012 Jasper St. Pierre <jstpierre@mecheye.net>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __GDM_SESSION_WORKER_COMMON_H
+#define __GDM_SESSION_WORKER_COMMON_H
+
+#include <glib-object.h>
+
+#define GDM_SESSION_WORKER_ERROR (gdm_session_worker_error_quark ())
+
+GQuark gdm_session_worker_error_quark (void);
+
+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_SERVICE_UNAVAILABLE,
+ GDM_SESSION_WORKER_ERROR_TOO_MANY_RETRIES,
+ 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,
+ GDM_SESSION_WORKER_ERROR_WRONG_STATE,
+ GDM_SESSION_WORKER_ERROR_OUTSTANDING_REQUEST,
+ GDM_SESSION_WORKER_ERROR_IN_REAUTH_SESSION,
+} GdmSessionWorkerError;
+
+#endif /* GDM_SESSION_WORKER_COMMON_H */
diff --git a/daemon/gdm-session-worker-enum-types.c.in b/daemon/gdm-session-worker-enum-types.c.in
new file mode 100644
index 0000000..c028690
--- /dev/null
+++ b/daemon/gdm-session-worker-enum-types.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+#include "@filename@"
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+
+GType
+@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+
+ if (G_UNLIKELY(etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+
+ return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+ /**/
+/*** END file-tail ***/
diff --git a/daemon/gdm-session-worker-enum-types.h.in b/daemon/gdm-session-worker-enum-types.h.in
new file mode 100644
index 0000000..64f4b4b
--- /dev/null
+++ b/daemon/gdm-session-worker-enum-types.h.in
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef GDM_SESSION_WORKER_ENUM_TYPES_H
+#define GDM_SESSION_WORKER_ENUM_TYPES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* GDM_SESSION_WORKER_ENUM_TYPES_H */
+/*** END file-tail ***/
diff --git a/daemon/gdm-session-worker-job.c b/daemon/gdm-session-worker-job.c
new file mode 100644
index 0000000..3ac018f
--- /dev/null
+++ b/daemon/gdm-session-worker-job.c
@@ -0,0 +1,590 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <signal.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#include <systemd/sd-daemon.h>
+
+#ifdef ENABLE_SYSTEMD_JOURNAL
+#include <systemd/sd-journal.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-common.h"
+
+#include "gdm-session-worker-job.h"
+
+extern char **environ;
+
+struct _GdmSessionWorkerJob
+{
+ GObject parent;
+
+ char *command;
+ GPid pid;
+ gboolean for_reauth;
+
+ guint child_watch_id;
+
+ char *server_address;
+ char **environment;
+};
+
+enum {
+ PROP_0,
+ PROP_SERVER_ADDRESS,
+ PROP_ENVIRONMENT,
+ PROP_FOR_REAUTH,
+};
+
+enum {
+ STARTED,
+ 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_setup_journal_fds (void)
+{
+#ifdef ENABLE_SYSTEMD_JOURNAL
+ if (sd_booted () > 0) {
+ const char *identifier = "gdm-session-worker";
+ int out, err;
+
+ out = sd_journal_stream_fd (identifier, LOG_INFO, FALSE);
+ if (out < 0)
+ return;
+
+ err = sd_journal_stream_fd (identifier, LOG_WARNING, FALSE);
+ if (err < 0) {
+ close (out);
+ return;
+ }
+
+ VE_IGNORE_EINTR (dup2 (out, 1));
+ VE_IGNORE_EINTR (dup2 (err, 2));
+ return;
+ }
+#endif
+ return;
+}
+
+static void
+session_worker_job_child_setup (GdmSessionWorkerJob *session_worker_job)
+{
+ sigset_t mask;
+ session_worker_job_setup_journal_fds ();
+
+ /* Terminate the process when the parent dies */
+#ifdef HAVE_SYS_PRCTL_H
+ prctl (PR_SET_PDEATHSIG, SIGTERM);
+#endif
+ /*
+ * Reset signal mask to default since it was altered by the
+ * manager process
+ */
+ sigemptyset (&mask);
+ sigprocmask (SIG_SETMASK, &mask, NULL);
+}
+
+static void
+session_worker_job_child_watch (GPid pid,
+ int status,
+ GdmSessionWorkerJob *job)
+{
+ g_debug ("GdmSessionWorkerJob: child (pid:%d) done (%s:%d)",
+ (int) pid,
+ WIFEXITED (status) ? "status"
+ : WIFSIGNALED (status) ? "signal"
+ : "unknown",
+ WIFEXITED (status) ? WEXITSTATUS (status)
+ : WIFSIGNALED (status) ? WTERMSIG (status)
+ : -1);
+
+ g_spawn_close_pid (job->pid);
+ job->pid = -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);
+ }
+}
+
+static void
+listify_hash (const char *key,
+ const char *value,
+ GPtrArray *env)
+{
+ char *str;
+
+ if (value == NULL)
+ value = "";
+
+ str = g_strdup_printf ("%s=%s", key, value);
+ g_ptr_array_add (env, str);
+}
+
+static void
+copy_environment_to_hash (GdmSessionWorkerJob *job,
+ GHashTable *hash)
+{
+ char **environment;
+ gint i;
+
+ if (job->environment != NULL) {
+ environment = g_strdupv (job->environment);
+ } else {
+ environment = g_get_environ ();
+ }
+ for (i = 0; environment[i]; i++) {
+ char **parts;
+
+ parts = g_strsplit (environment[i], "=", 2);
+
+ if (parts[0] != NULL && parts[1] != NULL) {
+ g_hash_table_insert (hash, g_strdup (parts[0]), g_strdup (parts[1]));
+ }
+
+ g_strfreev (parts);
+ }
+
+ g_strfreev (environment);
+}
+
+static GPtrArray *
+get_job_arguments (GdmSessionWorkerJob *job,
+ const char *name)
+{
+ GPtrArray *args;
+ GError *error;
+ char **argv;
+ int i;
+
+ args = NULL;
+ argv = NULL;
+ error = NULL;
+ if (! g_shell_parse_argv (job->command, NULL, &argv, &error)) {
+ g_warning ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ args = g_ptr_array_new ();
+ g_ptr_array_add (args, g_strdup (argv[0]));
+ g_ptr_array_add (args, g_strdup (name));
+ for (i = 1; argv[i] != NULL; i++) {
+ g_ptr_array_add (args, g_strdup (argv[i]));
+ }
+ g_strfreev (argv);
+
+ g_ptr_array_add (args, NULL);
+out:
+ return args;
+}
+
+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);
+ copy_environment_to_hash (job, hash);
+
+ g_hash_table_insert (hash, g_strdup ("GDM_SESSION_DBUS_ADDRESS"), g_strdup (job->server_address));
+
+ if (job->for_reauth) {
+ g_hash_table_insert (hash, g_strdup ("GDM_SESSION_FOR_REAUTH"), g_strdup ("1"));
+ }
+
+ 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,
+ const char *name)
+{
+ GError *error;
+ gboolean ret;
+ GPtrArray *args;
+ GPtrArray *env;
+
+ ret = FALSE;
+
+ g_debug ("GdmSessionWorkerJob: Running session_worker_job process: %s %s",
+ name != NULL? name : "", session_worker_job->command);
+
+ args = get_job_arguments (session_worker_job, name);
+
+ if (args == NULL) {
+ return FALSE;
+ }
+ env = get_job_environment (session_worker_job);
+
+ error = NULL;
+ ret = g_spawn_async_with_pipes (NULL,
+ (char **) args->pdata,
+ (char **)env->pdata,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_FILE_AND_ARGV_ZERO,
+ (GSpawnChildSetupFunc)session_worker_job_child_setup,
+ session_worker_job,
+ &session_worker_job->pid,
+ NULL,
+ NULL,
+ NULL,
+ &error);
+
+ g_ptr_array_foreach (args, (GFunc)g_free, NULL);
+ g_ptr_array_free (args, TRUE);
+
+ 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->command,
+ error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("GdmSessionWorkerJob: : SessionWorkerJob on pid %d", (int)session_worker_job->pid);
+ }
+
+ session_worker_job->child_watch_id = g_child_watch_add (session_worker_job->pid,
+ (GChildWatchFunc)session_worker_job_child_watch,
+ session_worker_job);
+
+ 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,
+ const char *name)
+{
+ gboolean res;
+
+ g_debug ("GdmSessionWorkerJob: Starting worker...");
+
+ res = gdm_session_worker_job_spawn (session_worker_job, name);
+
+ return res;
+}
+
+static void
+handle_session_worker_job_death (GdmSessionWorkerJob *session_worker_job)
+{
+ int exit_status;
+
+ g_debug ("GdmSessionWorkerJob: Waiting on process %d", session_worker_job->pid);
+ exit_status = gdm_wait_on_and_disown_pid (session_worker_job->pid, 5);
+
+ if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) {
+ g_debug ("GdmSessionWorkerJob: Wait on child process failed");
+ } else {
+ /* exited normally */
+ }
+
+ g_spawn_close_pid (session_worker_job->pid);
+ session_worker_job->pid = -1;
+
+ g_debug ("GdmSessionWorkerJob: SessionWorkerJob died");
+}
+
+void
+gdm_session_worker_job_stop_now (GdmSessionWorkerJob *session_worker_job)
+{
+ if (session_worker_job->pid <= 1) {
+ return;
+ }
+
+ /* remove watch source before we can wait on child */
+ if (session_worker_job->child_watch_id > 0) {
+ g_source_remove (session_worker_job->child_watch_id);
+ session_worker_job->child_watch_id = 0;
+ }
+
+ gdm_session_worker_job_stop (session_worker_job);
+ handle_session_worker_job_death (session_worker_job);
+}
+
+void
+gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job)
+{
+ int res;
+
+ if (session_worker_job->pid <= 1) {
+ return;
+ }
+
+ g_debug ("GdmSessionWorkerJob: Stopping job pid:%d", session_worker_job->pid);
+
+ res = gdm_signal_pid (session_worker_job->pid, SIGTERM);
+
+ if (res < 0) {
+ g_warning ("Unable to kill session worker process");
+ }
+}
+
+GPid
+gdm_session_worker_job_get_pid (GdmSessionWorkerJob *session_worker_job)
+{
+ g_return_val_if_fail (GDM_IS_SESSION_WORKER_JOB (session_worker_job), 0);
+ return session_worker_job->pid;
+}
+
+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->server_address);
+ session_worker_job->server_address = g_strdup (address);
+}
+
+void
+gdm_session_worker_job_set_for_reauth (GdmSessionWorkerJob *session_worker_job,
+ gboolean for_reauth)
+{
+ g_return_if_fail (GDM_IS_SESSION_WORKER_JOB (session_worker_job));
+
+ session_worker_job->for_reauth = for_reauth;
+}
+
+void
+gdm_session_worker_job_set_environment (GdmSessionWorkerJob *session_worker_job,
+ const char * const *environment)
+{
+ g_return_if_fail (GDM_IS_SESSION_WORKER_JOB (session_worker_job));
+
+ session_worker_job->environment = g_strdupv ((char **) environment);
+}
+
+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;
+ case PROP_FOR_REAUTH:
+ gdm_session_worker_job_set_for_reauth (self, g_value_get_boolean (value));
+ break;
+ case PROP_ENVIRONMENT:
+ gdm_session_worker_job_set_environment (self, g_value_get_pointer (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->server_address);
+ break;
+ case PROP_FOR_REAUTH:
+ g_value_set_boolean (value, self->for_reauth);
+ break;
+ case PROP_ENVIRONMENT:
+ g_value_set_pointer (value, self->environment);
+ 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;
+
+ 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_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 | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_FOR_REAUTH,
+ g_param_spec_boolean ("for-reauth",
+ "for reauth",
+ "for reauth",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_ENVIRONMENT,
+ g_param_spec_pointer ("environment",
+ "environment",
+ "environment",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ signals [STARTED] =
+ g_signal_new ("started",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ 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,
+ 0,
+ 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,
+ 0,
+ 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->pid = -1;
+
+ session_worker_job->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);
+
+ gdm_session_worker_job_stop (session_worker_job);
+
+ g_free (session_worker_job->command);
+ g_free (session_worker_job->server_address);
+
+ 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 0000000..e9b9c8d
--- /dev/null
+++ b/daemon/gdm-session-worker-job.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 ())
+G_DECLARE_FINAL_TYPE (GdmSessionWorkerJob, gdm_session_worker_job, GDM, SESSION_WORKER_JOB, GObject)
+
+GdmSessionWorkerJob * gdm_session_worker_job_new (void);
+void gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_job,
+ const char *server_address);
+void gdm_session_worker_job_set_for_reauth (GdmSessionWorkerJob *session_worker_job,
+ gboolean for_reauth);
+void gdm_session_worker_job_set_environment (GdmSessionWorkerJob *session_worker_job,
+ const char * const *environment);
+gboolean gdm_session_worker_job_start (GdmSessionWorkerJob *session_worker_job,
+ const char *name);
+void gdm_session_worker_job_stop (GdmSessionWorkerJob *session_worker_job);
+void gdm_session_worker_job_stop_now (GdmSessionWorkerJob *session_worker_job);
+
+GPid gdm_session_worker_job_get_pid (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 0000000..a264ea1
--- /dev/null
+++ b/daemon/gdm-session-worker.c
@@ -0,0 +1,3621 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <sys/ioctl.h>
+#include <sys/vt.h>
+#include <sys/kd.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+
+#include <security/pam_appl.h>
+
+#ifdef HAVE_LOGINCAP
+#include <login_cap.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <X11/Xauth.h>
+
+#include <systemd/sd-daemon.h>
+
+#ifdef ENABLE_SYSTEMD_JOURNAL
+#include <systemd/sd-journal.h>
+#endif
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif /* HAVE_SELINUX */
+
+#include "gdm-common.h"
+#include "gdm-log.h"
+
+#ifdef SUPPORTS_PAM_EXTENSIONS
+#include "gdm-pam-extensions.h"
+#endif
+
+#include "gdm-dbus-glue.h"
+#include "gdm-session-worker.h"
+#include "gdm-session-glue.h"
+#include "gdm-session.h"
+
+#if defined (HAVE_ADT)
+#include "gdm-session-solaris-auditor.h"
+#elif defined (HAVE_LIBAUDIT)
+#include "gdm-session-linux-auditor.h"
+#else
+#include "gdm-session-auditor.h"
+#endif
+
+#include "gdm-session-settings.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_NAME "org.gnome.DisplayManager.Session"
+#define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel"
+
+#define GDM_WORKER_DBUS_PATH "/org/gnome/DisplayManager/Worker"
+
+#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"
+#endif
+
+#ifndef GDM_SESSION_ROOT_UID
+#define GDM_SESSION_ROOT_UID 0
+#endif
+
+#ifndef GDM_SESSION_LOG_FILENAME
+#define GDM_SESSION_LOG_FILENAME "session.log"
+#endif
+
+#define MAX_FILE_SIZE 65536
+#define MAX_LOGS 5
+
+#define RELEASE_DISPLAY_SIGNAL (SIGRTMAX)
+#define ACQUIRE_DISPLAY_SIGNAL (SIGRTMAX - 1)
+
+typedef struct
+{
+ GdmSessionWorker *worker;
+ GdmSession *session;
+ GPid pid_of_caller;
+ uid_t uid_of_caller;
+
+} ReauthenticationRequest;
+
+struct GdmSessionWorkerPrivate
+{
+ GdmSessionWorkerState state;
+
+ int exit_code;
+
+ pam_handle_t *pam_handle;
+
+ GPid child_pid;
+ guint child_watch_id;
+
+ /* from Setup */
+ char *service;
+ char *x11_display_name;
+ char *x11_authority_file;
+ char *display_device;
+ char *display_seat_id;
+ char *hostname;
+ char *username;
+ char *log_file;
+ char *session_id;
+ uid_t uid;
+ gid_t gid;
+ gboolean password_is_required;
+ char **extensions;
+
+ int cred_flags;
+ int session_vt;
+ int session_tty_fd;
+
+ char **arguments;
+ guint32 cancelled : 1;
+ guint32 timed_out : 1;
+ guint32 is_program_session : 1;
+ guint32 is_reauth_session : 1;
+ guint32 display_is_local : 1;
+ guint32 display_is_initial : 1;
+ guint state_change_idle_id;
+ GdmSessionDisplayMode display_mode;
+
+ char *server_address;
+ GDBusConnection *connection;
+ GdmDBusWorkerManager *manager;
+
+ GHashTable *reauthentication_requests;
+
+ GdmSessionAuditor *auditor;
+ GdmSessionSettings *user_settings;
+
+ GDBusMethodInvocation *pending_invocation;
+};
+
+#ifdef SUPPORTS_PAM_EXTENSIONS
+static char gdm_pam_extension_environment_block[_POSIX_ARG_MAX];
+
+static const char * const
+gdm_supported_pam_extensions[] = {
+ GDM_PAM_EXTENSION_CHOICE_LIST,
+ NULL
+};
+#endif
+
+enum {
+ PROP_0,
+ PROP_SERVER_ADDRESS,
+ PROP_IS_REAUTH_SESSION,
+ PROP_STATE,
+};
+
+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);
+
+static void gdm_session_worker_set_environment_variable (GdmSessionWorker *worker,
+ const char *key,
+ const char *value);
+
+static void queue_state_change (GdmSessionWorker *worker);
+
+static void worker_interface_init (GdmDBusWorkerIface *iface);
+
+
+typedef int (* GdmSessionWorkerPamNewMessagesFunc) (int,
+ const struct pam_message **,
+ struct pam_response **,
+ gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (GdmSessionWorker,
+ gdm_session_worker,
+ GDM_DBUS_TYPE_WORKER_SKELETON,
+ G_IMPLEMENT_INTERFACE (GDM_DBUS_TYPE_WORKER,
+ worker_interface_init)
+ G_ADD_PRIVATE (GdmSessionWorker))
+
+/* 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;
+}
+
+/*
+ * This function is called with username set to NULL to update the
+ * auditor username value.
+ */
+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 != NULL) {
+ *username = g_strdup ((char *) item);
+ g_debug ("GdmSessionWorker: username is '%s'",
+ *username != NULL ? *username : "<unset>");
+ }
+
+ if (worker->priv->auditor != NULL) {
+ gdm_session_auditor_set_username (worker->priv->auditor, (char *)item);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+attempt_to_load_user_settings (GdmSessionWorker *worker,
+ const char *username)
+{
+ if (worker->priv->user_settings == NULL)
+ return;
+
+ if (gdm_session_settings_is_loaded (worker->priv->user_settings))
+ return;
+
+ g_debug ("GdmSessionWorker: attempting to load user settings");
+ gdm_session_settings_load (worker->priv->user_settings,
+ username);
+}
+
+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) {
+ g_debug ("GdmSessionWorker: old-username='%s' new-username='%s'",
+ worker->priv->username != NULL ? worker->priv->username : "<unset>",
+ username != NULL ? username : "<unset>");
+
+
+ gdm_session_auditor_set_username (worker->priv->auditor, worker->priv->username);
+
+ if ((worker->priv->username == username) ||
+ ((worker->priv->username != NULL) && (username != NULL) &&
+ (strcmp (worker->priv->username, username) == 0)))
+ goto out;
+
+ g_debug ("GdmSessionWorker: setting username to '%s'", username);
+
+ g_free (worker->priv->username);
+ worker->priv->username = username;
+ username = NULL;
+
+ gdm_dbus_worker_emit_username_changed (GDM_DBUS_WORKER (worker),
+ worker->priv->username);
+
+ /* We have a new username to try. If we haven't been able to
+ * read user settings up until now, then give it a go now
+ * (see the comment in do_setup for rationale on why it's useful
+ * to keep trying to read settings)
+ */
+ if (worker->priv->username != NULL &&
+ worker->priv->username[0] != '\0') {
+ attempt_to_load_user_settings (worker, worker->priv->username);
+ }
+ }
+
+ out:
+ g_free (username);
+}
+
+static gboolean
+gdm_session_worker_ask_question (GdmSessionWorker *worker,
+ const char *question,
+ char **answerp)
+{
+ return gdm_dbus_worker_manager_call_info_query_sync (worker->priv->manager,
+ worker->priv->service,
+ question,
+ answerp,
+ NULL,
+ NULL);
+}
+
+static gboolean
+gdm_session_worker_ask_for_secret (GdmSessionWorker *worker,
+ const char *question,
+ char **answerp)
+{
+ return gdm_dbus_worker_manager_call_secret_info_query_sync (worker->priv->manager,
+ worker->priv->service,
+ question,
+ answerp,
+ NULL,
+ NULL);
+}
+
+static gboolean
+gdm_session_worker_report_info (GdmSessionWorker *worker,
+ const char *info)
+{
+ return gdm_dbus_worker_manager_call_info_sync (worker->priv->manager,
+ worker->priv->service,
+ info,
+ NULL,
+ NULL);
+}
+
+static gboolean
+gdm_session_worker_report_problem (GdmSessionWorker *worker,
+ const char *problem)
+{
+ return gdm_dbus_worker_manager_call_problem_sync (worker->priv->manager,
+ worker->priv->service,
+ problem,
+ NULL,
+ NULL);
+}
+
+#ifdef SUPPORTS_PAM_EXTENSIONS
+static gboolean
+gdm_session_worker_ask_list_of_choices (GdmSessionWorker *worker,
+ const char *prompt_message,
+ GdmChoiceList *list,
+ char **answerp)
+{
+ GVariantBuilder builder;
+ GVariant *choices_as_variant;
+ GError *error = NULL;
+ gboolean res;
+ size_t i;
+
+ g_debug ("GdmSessionWorker: presenting user with list of choices:");
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+
+ for (i = 0; i < list->number_of_items; i++) {
+ if (list->items[i].key == NULL) {
+ g_warning ("choice list contains item with NULL key");
+ g_variant_builder_clear (&builder);
+ return FALSE;
+ }
+ g_debug ("GdmSessionWorker: choices['%s'] = \"%s\"", list->items[i].key, list->items[i].text);
+ g_variant_builder_add (&builder, "{ss}", list->items[i].key, list->items[i].text);
+ }
+ g_debug ("GdmSessionWorker: (and waiting for reply)");
+
+ choices_as_variant = g_variant_builder_end (&builder);
+
+ res = gdm_dbus_worker_manager_call_choice_list_query_sync (worker->priv->manager,
+ worker->priv->service,
+ prompt_message,
+ choices_as_variant,
+ answerp,
+ NULL,
+ &error);
+
+ if (! res) {
+ g_debug ("GdmSessionWorker: list request failed: %s", error->message);
+ g_clear_error (&error);
+ } else {
+ g_debug ("GdmSessionWorker: user selected '%s'", *answerp);
+ }
+
+ return res;
+}
+
+static gboolean
+gdm_session_worker_process_choice_list_request (GdmSessionWorker *worker,
+ GdmPamExtensionChoiceListRequest *request,
+ GdmPamExtensionChoiceListResponse *response)
+{
+ return gdm_session_worker_ask_list_of_choices (worker, request->prompt_message, &request->list, &response->key);
+}
+
+static gboolean
+gdm_session_worker_process_extended_pam_message (GdmSessionWorker *worker,
+ const struct pam_message *query,
+ char **response)
+{
+ GdmPamExtensionMessage *extended_message;
+ gboolean res;
+
+ extended_message = GDM_PAM_EXTENSION_MESSAGE_FROM_PAM_MESSAGE (query);
+
+ if (GDM_PAM_EXTENSION_MESSAGE_TRUNCATED (extended_message)) {
+ g_warning ("PAM service requested binary response for truncated query");
+ return FALSE;
+ }
+
+ if (GDM_PAM_EXTENSION_MESSAGE_INVALID_TYPE (extended_message)) {
+ g_warning ("PAM service requested binary response for unadvertised query type");
+ return FALSE;
+ }
+
+ if (GDM_PAM_EXTENSION_MESSAGE_MATCH (extended_message, worker->priv->extensions, GDM_PAM_EXTENSION_CHOICE_LIST)) {
+ GdmPamExtensionChoiceListRequest *list_request = (GdmPamExtensionChoiceListRequest *) extended_message;
+ GdmPamExtensionChoiceListResponse *list_response = malloc (GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_SIZE);
+
+ g_debug ("GdmSessionWorker: received extended pam message '%s'", GDM_PAM_EXTENSION_CHOICE_LIST);
+
+ GDM_PAM_EXTENSION_CHOICE_LIST_RESPONSE_INIT (list_response);
+
+ res = gdm_session_worker_process_choice_list_request (worker, list_request, list_response);
+
+ if (! res) {
+ g_free (list_response);
+ return FALSE;
+ }
+
+ *response = GDM_PAM_EXTENSION_MESSAGE_TO_PAM_REPLY (list_response);
+ return TRUE;
+ } else {
+ g_debug ("GdmSessionWorker: received extended pam message of unknown type %u", (unsigned int) extended_message->type);
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+#endif
+
+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)
+{
+ char *user_answer;
+ gboolean res;
+ char *utf8_msg;
+ char *msg;
+
+ if (response != NULL) {
+ *response = NULL;
+ }
+
+ gdm_session_worker_update_username (worker);
+
+#ifdef SUPPORTS_PAM_EXTENSIONS
+ if (query->msg_style == PAM_BINARY_PROMPT)
+ return gdm_session_worker_process_extended_pam_message (worker, query, response);
+#endif
+
+ g_debug ("GdmSessionWorker: received pam message of type %u with payload '%s'",
+ query->msg_style, query->msg);
+
+ utf8_msg = convert_to_utf8 (query->msg);
+
+ worker->priv->cancelled = FALSE;
+ worker->priv->timed_out = FALSE;
+
+ user_answer = NULL;
+ res = FALSE;
+ switch (query->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ res = gdm_session_worker_ask_question (worker, utf8_msg, &user_answer);
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ res = gdm_session_worker_ask_for_secret (worker, utf8_msg, &user_answer);
+ break;
+ case PAM_TEXT_INFO:
+ res = gdm_session_worker_report_info (worker, utf8_msg);
+ break;
+ case PAM_ERROR_MSG:
+ res = gdm_session_worker_report_problem (worker, utf8_msg);
+ break;
+#ifdef PAM_RADIO_TYPE
+ case PAM_RADIO_TYPE:
+ msg = g_strdup_printf ("%s (yes/no)", utf8_msg);
+ res = gdm_session_worker_ask_question (worker, msg, &user_answer);
+ g_free (msg);
+ break;
+#endif
+ default:
+ res = FALSE;
+ g_warning ("Unknown and unhandled message type %d\n",
+ query->msg_style);
+
+ break;
+ }
+
+ if (worker->priv->timed_out) {
+ gdm_dbus_worker_emit_cancel_pending_query (GDM_DBUS_WORKER (worker));
+ worker->priv->timed_out = FALSE;
+ }
+
+ if (user_answer != NULL) {
+ /* we strndup and g_free to make sure we return malloc'd
+ * instead of g_malloc'd memory. PAM_MAX_RESP_SIZE includes
+ * the '\0' terminating character, thus the "- 1".
+ */
+ if (res && response != NULL) {
+ *response = strndup (user_answer, PAM_MAX_RESP_SIZE - 1);
+ }
+
+ memset (user_answer, '\0', strlen (user_answer));
+ g_free (user_answer);
+
+ g_debug ("GdmSessionWorker: trying to get updated username");
+
+ res = TRUE;
+ }
+
+ g_free (utf8_msg);
+
+ return res;
+}
+
+static const char *
+get_max_retries_error_message (GdmSessionWorker *worker)
+{
+ if (g_strcmp0 (worker->priv->service, "gdm-password") == 0)
+ return _("You reached the maximum password authentication attempts, please try another method");
+
+ if (g_strcmp0 (worker->priv->service, "gdm-autologin") == 0)
+ return _("You reached the maximum auto login attempts, please try another authentication method");
+
+ if (g_strcmp0 (worker->priv->service, "gdm-fingerprint") == 0)
+ return _("You reached the maximum fingerprint authentication attempts, please try another method");
+
+ if (g_strcmp0 (worker->priv->service, "gdm-smartcard") == 0)
+ return _("You reached the maximum smart card authentication attempts, please try another method");
+
+ return _("You reached the maximum authentication attempts, please try another method");
+}
+
+static const char *
+get_generic_error_message (GdmSessionWorker *worker)
+{
+ if (g_strcmp0 (worker->priv->service, "gdm-password") == 0)
+ return _("Sorry, password authentication didn’t work. Please try again.");
+
+ if (g_strcmp0 (worker->priv->service, "gdm-autologin") == 0)
+ return _("Sorry, auto login didn’t work. Please try again.");
+
+ if (g_strcmp0 (worker->priv->service, "gdm-fingerprint") == 0)
+ return _("Sorry, fingerprint authentication didn’t work. Please try again.");
+
+ if (g_strcmp0 (worker->priv->service, "gdm-smartcard") == 0)
+ return _("Sorry, smart card authentication didn’t work. Please try again.");
+
+ return _("Sorry, that didn’t work. Please try again.");
+}
+
+static const char *
+get_friendly_error_message (GdmSessionWorker *worker,
+ int error_code)
+{
+ switch (error_code) {
+ case PAM_SUCCESS:
+ case PAM_IGNORE:
+ return "";
+ break;
+
+ case PAM_ACCT_EXPIRED:
+ case PAM_AUTHTOK_EXPIRED:
+ return _("Your account was given a time limit that’s now passed.");
+ break;
+
+ case PAM_MAXTRIES:
+ return get_max_retries_error_message (worker);
+
+ default:
+ break;
+ }
+
+ return get_generic_error_message (worker);
+}
+
+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 ("GdmSessionWorker: %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;
+
+ response = NULL;
+ got_response = gdm_session_worker_process_pam_message (worker,
+ messages[i],
+ &response);
+ if (!got_response) {
+ goto out;
+ }
+
+ replies[i].resp = response;
+ 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;
+ }
+
+ g_debug ("GdmSessionWorker: PAM conversation returning %d: %s",
+ return_value,
+ pam_strerror (worker->priv->pam_handle, return_value));
+
+ return return_value;
+}
+
+static void
+gdm_session_worker_start_auditor (GdmSessionWorker *worker)
+{
+ /* Use dummy auditor so program session doesn't pollute user audit logs
+ */
+ if (worker->priv->is_program_session) {
+ worker->priv->auditor = gdm_session_auditor_new (worker->priv->hostname,
+ worker->priv->display_device);
+ return;
+ }
+
+/* FIXME: it may make sense at some point to keep a list of
+ * auditors, instead of assuming they are mutually exclusive
+ */
+#if defined (HAVE_ADT)
+ worker->priv->auditor = gdm_session_solaris_auditor_new (worker->priv->hostname,
+ worker->priv->display_device);
+#elif defined (HAVE_LIBAUDIT)
+ worker->priv->auditor = gdm_session_linux_auditor_new (worker->priv->hostname,
+ worker->priv->display_device);
+#else
+ worker->priv->auditor = gdm_session_auditor_new (worker->priv->hostname,
+ worker->priv->display_device);
+#endif
+}
+
+static void
+gdm_session_worker_stop_auditor (GdmSessionWorker *worker)
+{
+ g_object_unref (worker->priv->auditor);
+ worker->priv->auditor = NULL;
+}
+
+static void
+on_release_display (int signal)
+{
+ int fd;
+
+ fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
+ ioctl(fd, VT_RELDISP, 1);
+ close(fd);
+}
+
+static void
+on_acquire_display (int signal)
+{
+ int fd;
+
+ fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
+ ioctl(fd, VT_RELDISP, VT_ACKACQ);
+ close(fd);
+}
+
+static gboolean
+handle_terminal_vt_switches (GdmSessionWorker *worker,
+ int tty_fd)
+{
+ struct vt_mode setmode_request = { 0 };
+ gboolean succeeded = TRUE;
+
+ setmode_request.mode = VT_PROCESS;
+ setmode_request.relsig = RELEASE_DISPLAY_SIGNAL;
+ setmode_request.acqsig = ACQUIRE_DISPLAY_SIGNAL;
+
+ if (ioctl (tty_fd, VT_SETMODE, &setmode_request) < 0) {
+ g_debug ("GdmSessionWorker: couldn't manage VTs manually: %m");
+ succeeded = FALSE;
+ }
+
+ signal (RELEASE_DISPLAY_SIGNAL, on_release_display);
+ signal (ACQUIRE_DISPLAY_SIGNAL, on_acquire_display);
+
+ return succeeded;
+}
+
+static void
+fix_terminal_vt_mode (GdmSessionWorker *worker,
+ int tty_fd)
+{
+ struct vt_mode getmode_reply = { 0 };
+ int kernel_display_mode = 0;
+ gboolean mode_fixed = FALSE;
+ gboolean succeeded = TRUE;
+
+ if (ioctl (tty_fd, VT_GETMODE, &getmode_reply) < 0) {
+ g_debug ("GdmSessionWorker: couldn't query VT mode: %m");
+ succeeded = FALSE;
+ }
+
+ if (getmode_reply.mode != VT_AUTO) {
+ goto out;
+ }
+
+ if (ioctl (tty_fd, KDGETMODE, &kernel_display_mode) < 0) {
+ g_debug ("GdmSessionWorker: couldn't query kernel display mode: %m");
+ succeeded = FALSE;
+ }
+
+ if (kernel_display_mode == KD_TEXT) {
+ goto out;
+ }
+
+ /* VT is in the anti-social state of VT_AUTO + KD_GRAPHICS,
+ * fix it.
+ */
+ succeeded = handle_terminal_vt_switches (worker, tty_fd);
+ mode_fixed = TRUE;
+out:
+ if (!succeeded) {
+ g_error ("GdmSessionWorker: couldn't set up terminal, aborting...");
+ return;
+ }
+
+ g_debug ("GdmSessionWorker: VT mode did %sneed to be fixed",
+ mode_fixed? "" : "not ");
+}
+
+static void
+jump_to_vt (GdmSessionWorker *worker,
+ int vt_number)
+{
+ int fd;
+ int active_vt_tty_fd;
+ int active_vt = -1;
+ struct vt_stat vt_state = { 0 };
+
+ g_debug ("GdmSessionWorker: jumping to VT %d", vt_number);
+ active_vt_tty_fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
+
+ if (worker->priv->session_tty_fd != -1) {
+ static const char *clear_screen_escape_sequence = "\33[H\33[2J";
+
+ /* let's make sure the new VT is clear */
+ write (worker->priv->session_tty_fd,
+ clear_screen_escape_sequence,
+ sizeof (clear_screen_escape_sequence));
+
+ fd = worker->priv->session_tty_fd;
+
+ g_debug ("GdmSessionWorker: first setting graphics mode to prevent flicker");
+ if (ioctl (fd, KDSETMODE, KD_GRAPHICS) < 0) {
+ g_debug ("GdmSessionWorker: couldn't set graphics mode: %m");
+ }
+
+ /* It's possible that the current VT was left in a broken
+ * combination of states (KD_GRAPHICS with VT_AUTO), that
+ * can't be switched away from. This call makes sure things
+ * are set in a way that VT_ACTIVATE should work and
+ * VT_WAITACTIVE shouldn't hang.
+ */
+ fix_terminal_vt_mode (worker, active_vt_tty_fd);
+ } else {
+ fd = active_vt_tty_fd;
+ }
+
+ handle_terminal_vt_switches (worker, fd);
+
+ if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
+ g_debug ("GdmSessionWorker: couldn't get current VT: %m");
+ } else {
+ active_vt = vt_state.v_active;
+ }
+
+ if (active_vt != vt_number) {
+ if (ioctl (fd, VT_ACTIVATE, vt_number) < 0) {
+ g_debug ("GdmSessionWorker: couldn't initiate jump to VT %d: %m",
+ vt_number);
+ } else if (ioctl (fd, VT_WAITACTIVE, vt_number) < 0) {
+ g_debug ("GdmSessionWorker: couldn't finalize jump to VT %d: %m",
+ vt_number);
+ }
+ }
+
+ close (active_vt_tty_fd);
+}
+
+static void
+gdm_session_worker_set_state (GdmSessionWorker *worker,
+ GdmSessionWorkerState state)
+{
+ if (worker->priv->state == state)
+ return;
+
+ worker->priv->state = state;
+ g_object_notify (G_OBJECT (worker), "state");
+}
+
+static void
+gdm_session_worker_uninitialize_pam (GdmSessionWorker *worker,
+ int status)
+{
+ g_debug ("GdmSessionWorker: uninitializing PAM");
+
+ if (worker->priv->pam_handle == NULL)
+ return;
+
+ gdm_session_worker_get_username (worker, NULL);
+
+ if (worker->priv->state >= GDM_SESSION_WORKER_STATE_SESSION_OPENED) {
+ pam_close_session (worker->priv->pam_handle, 0);
+ gdm_session_auditor_report_logout (worker->priv->auditor);
+ } else {
+ gdm_session_auditor_report_login_failure (worker->priv->auditor,
+ status,
+ pam_strerror (worker->priv->pam_handle, status));
+ }
+
+ if (worker->priv->state >= GDM_SESSION_WORKER_STATE_ACCREDITED) {
+ pam_setcred (worker->priv->pam_handle, PAM_DELETE_CRED);
+ }
+
+ pam_end (worker->priv->pam_handle, status);
+ worker->priv->pam_handle = NULL;
+
+ gdm_session_worker_stop_auditor (worker);
+
+ g_debug ("GdmSessionWorker: state NONE");
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_NONE);
+}
+
+static char *
+_get_tty_for_pam (const char *x11_display_name,
+ const char *display_device)
+{
+#ifdef __sun
+ return g_strdup (display_device);
+#else
+ return g_strdup (x11_display_name);
+#endif
+}
+
+#ifdef PAM_XAUTHDATA
+static struct pam_xauth_data *
+_get_xauth_for_pam (const char *x11_authority_file)
+{
+ FILE *fh;
+ Xauth *auth = NULL;
+ struct pam_xauth_data *retval = NULL;
+ gsize len = sizeof (*retval) + 1;
+
+ fh = fopen (x11_authority_file, "r");
+ if (fh) {
+ auth = XauReadAuth (fh);
+ fclose (fh);
+ }
+ if (auth) {
+ len += auth->name_length + auth->data_length;
+ retval = g_malloc0 (len);
+ }
+ if (retval) {
+ retval->namelen = auth->name_length;
+ retval->name = (char *) (retval + 1);
+ memcpy (retval->name, auth->name, auth->name_length);
+ retval->datalen = auth->data_length;
+ retval->data = retval->name + auth->name_length + 1;
+ memcpy (retval->data, auth->data, auth->data_length);
+ }
+ XauDisposeAuth (auth);
+ return retval;
+}
+#endif
+
+static gboolean
+gdm_session_worker_initialize_pam (GdmSessionWorker *worker,
+ const char *service,
+ const char * const *extensions,
+ const char *username,
+ const char *hostname,
+ gboolean display_is_local,
+ const char *x11_display_name,
+ const char *x11_authority_file,
+ const char *display_device,
+ const char *seat_id,
+ GError **error)
+{
+ struct pam_conv pam_conversation;
+ int error_code;
+ char tty_string[256];
+
+ g_assert (worker->priv->pam_handle == NULL);
+
+ g_debug ("GdmSessionWorker: initializing PAM; service=%s username=%s seat=%s",
+ service ? service : "(null)",
+ username ? username : "(null)",
+ seat_id ? seat_id : "(null)");
+
+#ifdef SUPPORTS_PAM_EXTENSIONS
+ if (extensions != NULL) {
+ GDM_PAM_EXTENSION_ADVERTISE_SUPPORTED_EXTENSIONS (gdm_pam_extension_environment_block, extensions);
+ }
+#endif
+
+ pam_conversation.conv = (GdmSessionWorkerPamNewMessagesFunc) gdm_session_worker_pam_new_messages_handler;
+ pam_conversation.appdata_ptr = worker;
+
+ gdm_session_worker_start_auditor (worker);
+ error_code = pam_start (service,
+ username,
+ &pam_conversation,
+ &worker->priv->pam_handle);
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("GdmSessionWorker: could not initialize PAM: (error code %d)", error_code);
+ /* 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_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
+ "");
+
+ goto out;
+ }
+
+ /* set USER PROMPT */
+ if (username == NULL) {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_USER_PROMPT, _("Username:"));
+
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("GdmSessionWorker: error informing authentication system of preferred username prompt: %s",
+ pam_strerror (worker->priv->pam_handle, error_code));
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ "");
+ goto out;
+ }
+ }
+
+ /* set RHOST */
+ if (hostname != NULL && hostname[0] != '\0') {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_RHOST, hostname);
+ g_debug ("error informing authentication system of user's hostname %s: %s",
+ hostname,
+ pam_strerror (worker->priv->pam_handle, error_code));
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ "");
+ goto out;
+ }
+ }
+
+ /* set seat ID */
+ if (seat_id != NULL && seat_id[0] != '\0') {
+ gdm_session_worker_set_environment_variable (worker, "XDG_SEAT", seat_id);
+ }
+
+ if (strcmp (service, "gdm-launch-environment") == 0) {
+ gdm_session_worker_set_environment_variable (worker, "XDG_SESSION_CLASS", "greeter");
+ }
+
+ g_debug ("GdmSessionWorker: state SETUP_COMPLETE");
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
+
+ /* Temporarily set PAM_TTY with the login VT,
+ PAM_TTY will be reset with the users VT right before the user session is opened */
+ g_snprintf (tty_string, 256, "/dev/tty%d", GDM_INITIAL_VT);
+ pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string);
+ if (!display_is_local)
+ worker->priv->password_is_required = TRUE;
+
+ 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 ("GdmSessionWorker: authenticating user %s", worker->priv->username);
+
+ 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_AUTHINFO_UNAVAIL) {
+ g_debug ("GdmSessionWorker: authentication service unavailable");
+
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE,
+ "");
+ goto out;
+ } else if (error_code == PAM_MAXTRIES) {
+ g_debug ("GdmSessionWorker: authentication service had too many retries");
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_TOO_MANY_RETRIES,
+ get_friendly_error_message (worker, error_code));
+ goto out;
+ } else if (error_code != PAM_SUCCESS) {
+ g_debug ("GdmSessionWorker: authentication returned %d: %s", error_code, pam_strerror (worker->priv->pam_handle, error_code));
+
+ /*
+ * Do not display a different message for user unknown versus
+ * a failed password for a valid user.
+ */
+ if (error_code == PAM_USER_UNKNOWN) {
+ error_code = PAM_AUTH_ERR;
+ }
+
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ get_friendly_error_message (worker, error_code));
+ goto out;
+ }
+
+ g_debug ("GdmSessionWorker: state AUTHENTICATED");
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_AUTHENTICATED);
+
+ 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 ("GdmSessionWorker: determining if authenticated user (password required:%d) is authorized to session",
+ password_is_required);
+
+ 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 && !worker->priv->is_program_session) {
+ g_debug ("GdmSessionWorker: authenticated user requires new auth token");
+ error_code = pam_chauthtok (worker->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
+
+ gdm_session_worker_get_username (worker, NULL);
+
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_auditor_report_password_change_failure (worker->priv->auditor);
+ } else {
+ gdm_session_auditor_report_password_changed (worker->priv->auditor);
+ }
+ }
+
+ /* If the user is reauthenticating, then authorization isn't required to
+ * proceed, the user is already logged in after all.
+ */
+ if (worker->priv->is_reauth_session) {
+ error_code = PAM_SUCCESS;
+ }
+
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("GdmSessionWorker: user is not authorized to log in: %s",
+ pam_strerror (worker->priv->pam_handle, error_code));
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHORIZING,
+ get_friendly_error_message (worker, error_code));
+ goto out;
+ }
+
+ g_debug ("GdmSessionWorker: state AUTHORIZED");
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_AUTHORIZED);
+
+ 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)
+{
+ int error_code;
+ char *environment_entry;
+
+ if (value != NULL) {
+ environment_entry = g_strdup_printf ("%s=%s", key, value);
+ } else {
+ /* empty value means "remove from environment" */
+ environment_entry = g_strdup (key);
+ }
+
+ error_code = pam_putenv (worker->priv->pam_handle,
+ environment_entry);
+
+ if (error_code != PAM_SUCCESS) {
+ g_warning ("cannot put %s in pam environment: %s\n",
+ environment_entry,
+ pam_strerror (worker->priv->pam_handle, error_code));
+ }
+ g_debug ("GdmSessionWorker: Set PAM environment variable: '%s'", environment_entry);
+ g_free (environment_entry);
+}
+
+static char *
+gdm_session_worker_get_environment_variable (GdmSessionWorker *worker,
+ const char *key)
+{
+ return g_strdup (pam_getenv (worker->priv->pam_handle, key));
+}
+
+static void
+gdm_session_worker_update_environment_from_passwd_info (GdmSessionWorker *worker,
+ uid_t uid,
+ gid_t gid,
+ const char *home,
+ const char *shell)
+{
+ 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", home);
+ gdm_session_worker_set_environment_variable (worker, "PWD", home);
+ gdm_session_worker_set_environment_variable (worker, "SHELL", shell);
+}
+
+static gboolean
+gdm_session_worker_environment_variable_is_set (GdmSessionWorker *worker,
+ const char *key)
+{
+ return pam_getenv (worker->priv->pam_handle, key) != NULL;
+}
+
+static gboolean
+_change_user (GdmSessionWorker *worker,
+ uid_t uid,
+ gid_t gid)
+{
+#ifdef THE_MAN_PAGE_ISNT_LYING
+ /* 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 (uid, GDM_SESSION_ROOT_UID) < 0) {
+ return FALSE;
+ }
+#endif
+ worker->priv->uid = uid;
+ worker->priv->gid = gid;
+
+ if (setgid (gid) < 0) {
+ return FALSE;
+ }
+
+ if (initgroups (worker->priv->username, gid) < 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_lookup_passwd_info (const char *username,
+ uid_t *uidp,
+ gid_t *gidp,
+ char **homep,
+ char **shellp)
+{
+ gboolean ret;
+ struct passwd *passwd_entry;
+ struct passwd passwd_buffer;
+ char *aux_buffer;
+ long required_aux_buffer_size;
+ gsize aux_buffer_size;
+
+ ret = FALSE;
+ aux_buffer = NULL;
+ aux_buffer_size = 0;
+
+ 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
+ */
+ again:
+ passwd_entry = NULL;
+#ifdef HAVE_POSIX_GETPWNAM_R
+ errno = getpwnam_r (username,
+ &passwd_buffer,
+ aux_buffer,
+ (size_t) aux_buffer_size,
+ &passwd_entry);
+#else
+ passwd_entry = getpwnam_r (username,
+ &passwd_buffer,
+ aux_buffer,
+ (size_t) aux_buffer_size);
+ errno = 0;
+#endif /* !HAVE_POSIX_GETPWNAM_R */
+ if (errno == EINTR) {
+ g_debug ("%s", g_strerror (errno));
+ goto again;
+ } else if (errno != 0) {
+ g_warning ("%s", g_strerror (errno));
+ goto out;
+ }
+
+ if (passwd_entry == NULL) {
+ goto out;
+ }
+
+ if (uidp != NULL) {
+ *uidp = passwd_entry->pw_uid;
+ }
+ if (gidp != NULL) {
+ *gidp = passwd_entry->pw_gid;
+ }
+ if (homep != NULL) {
+ if (passwd_entry->pw_dir != NULL && passwd_entry->pw_dir[0] != '\0') {
+ *homep = g_strdup (passwd_entry->pw_dir);
+ } else {
+ *homep = g_strdup ("/");
+ }
+ }
+ if (shellp != NULL) {
+ if (passwd_entry->pw_shell != NULL && passwd_entry->pw_shell[0] != '\0') {
+ *shellp = g_strdup (passwd_entry->pw_shell);
+ } else {
+ *shellp = g_strdup ("/bin/bash");
+ }
+ }
+ ret = TRUE;
+ out:
+ if (aux_buffer != NULL) {
+ g_assert (aux_buffer_size > 0);
+ g_slice_free1 (aux_buffer_size, aux_buffer);
+ }
+
+ return ret;
+}
+
+static char *
+get_var_cb (const char *key,
+ gpointer user_data)
+{
+ return gdm_session_worker_get_environment_variable (user_data, key);
+}
+
+static void
+load_env_func (const char *var,
+ const char *value,
+ gpointer user_data)
+{
+ GdmSessionWorker *worker = user_data;
+ gdm_session_worker_set_environment_variable (worker, var, value);
+}
+
+static gboolean
+gdm_session_worker_accredit_user (GdmSessionWorker *worker,
+ GError **error)
+{
+ gboolean ret;
+ gboolean res;
+ uid_t uid;
+ gid_t gid;
+ char *shell;
+ char *home;
+ int error_code;
+
+ ret = FALSE;
+
+ home = NULL;
+ shell = NULL;
+
+ if (worker->priv->username == NULL) {
+ g_debug ("GdmSessionWorker: Username not set");
+ 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;
+ }
+
+ uid = 0;
+ gid = 0;
+ res = _lookup_passwd_info (worker->priv->username,
+ &uid,
+ &gid,
+ &home,
+ &shell);
+ if (! res) {
+ g_debug ("GdmSessionWorker: Unable to lookup account info");
+ error_code = PAM_AUTHINFO_UNAVAIL;
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ _("no user account available"));
+ goto out;
+ }
+
+ gdm_session_worker_update_environment_from_passwd_info (worker,
+ uid,
+ gid,
+ home,
+ shell);
+
+ /* 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")) {
+ if (strcmp (BINDIR, "/usr/bin") == 0) {
+ gdm_session_worker_set_environment_variable (worker, "PATH",
+ GDM_SESSION_DEFAULT_PATH);
+ } else {
+ gdm_session_worker_set_environment_variable (worker, "PATH",
+ BINDIR ":" GDM_SESSION_DEFAULT_PATH);
+ }
+ }
+
+ if (! _change_user (worker, uid, gid)) {
+ g_debug ("GdmSessionWorker: Unable to change to user");
+ error_code = PAM_SYSTEM_ERR;
+ g_set_error_literal (error, GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ _("Unable to change to user"));
+ goto out;
+ }
+
+ error_code = pam_setcred (worker->priv->pam_handle, worker->priv->cred_flags);
+
+ /* If the user is reauthenticating and they've made it this far, then there
+ * is no reason we should lock them out of their session. They've already
+ * proved they are they same person who logged in, and that's all we care
+ * about.
+ */
+ if (worker->priv->is_reauth_session) {
+ error_code = PAM_SUCCESS;
+ }
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_GIVING_CREDENTIALS,
+ pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ g_free (home);
+ g_free (shell);
+ if (ret) {
+ g_debug ("GdmSessionWorker: state ACCREDITED");
+ ret = TRUE;
+
+ gdm_session_worker_get_username (worker, NULL);
+ gdm_session_auditor_report_user_accredited (worker->priv->auditor);
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_ACCREDITED);
+ } else {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ }
+
+ return ret;
+}
+
+static const char * const *
+gdm_session_worker_get_environment (GdmSessionWorker *worker)
+{
+ return (const char * const *) pam_getenvlist (worker->priv->pam_handle);
+}
+
+static gboolean
+run_script (GdmSessionWorker *worker,
+ const char *dir)
+{
+ /* scripts are for non-program sessions only */
+ if (worker->priv->is_program_session) {
+ return TRUE;
+ }
+
+ return gdm_run_script (dir,
+ worker->priv->username,
+ worker->priv->x11_display_name,
+ worker->priv->display_is_local? NULL : worker->priv->hostname,
+ worker->priv->x11_authority_file);
+}
+
+static void
+wait_until_dbus_signal_emission_to_manager_finishes (GdmSessionWorker *worker)
+{
+ g_autoptr (GdmDBusPeer) peer_proxy = NULL;
+ g_autoptr (GError) error = NULL;
+ gboolean pinged;
+
+ peer_proxy = gdm_dbus_peer_proxy_new_sync (worker->priv->connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ "/org/freedesktop/DBus",
+ NULL,
+ &error);
+
+ if (peer_proxy == NULL) {
+ g_debug ("GdmSessionWorker: could not create peer proxy to daemon: %s",
+ error->message);
+ return;
+ }
+
+ pinged = gdm_dbus_peer_call_ping_sync (peer_proxy, NULL, &error);
+
+ if (!pinged) {
+ g_debug ("GdmSessionWorker: could not ping daemon: %s",
+ error->message);
+ return;
+ }
+}
+
+static void
+jump_back_to_initial_vt (GdmSessionWorker *worker)
+{
+ if (worker->priv->session_vt == 0)
+ return;
+
+ if (worker->priv->session_vt == GDM_INITIAL_VT)
+ return;
+
+ if (g_strcmp0 (worker->priv->display_seat_id, "seat0") != 0)
+ return;
+
+#ifdef ENABLE_USER_DISPLAY_SERVER
+ jump_to_vt (worker, GDM_INITIAL_VT);
+ worker->priv->session_vt = 0;
+#endif
+}
+
+static void
+session_worker_child_watch (GPid pid,
+ int status,
+ GdmSessionWorker *worker)
+{
+ g_debug ("GdmSessionWorker: child (pid:%d) done (%s:%d)",
+ (int) pid,
+ WIFEXITED (status) ? "status"
+ : WIFSIGNALED (status) ? "signal"
+ : "unknown",
+ WIFEXITED (status) ? WEXITSTATUS (status)
+ : WIFSIGNALED (status) ? WTERMSIG (status)
+ : -1);
+
+ gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS);
+
+ worker->priv->child_pid = -1;
+ worker->priv->child_watch_id = 0;
+ run_script (worker, GDMCONFDIR "/PostSession");
+
+ gdm_dbus_worker_emit_session_exited (GDM_DBUS_WORKER (worker),
+ worker->priv->service,
+ status);
+
+ killpg (pid, SIGHUP);
+
+ /* FIXME: It's important to give the manager an opportunity to process the
+ * session-exited emission above before switching VTs.
+ *
+ * This is because switching VTs makes the manager try to put a login screen
+ * up on VT 1, but it may actually want to try to auto login again in response
+ * to session-exited.
+ *
+ * This function just does a manager roundtrip over the bus to make sure the
+ * signal has been dispatched before jumping.
+ *
+ * Ultimately, we may want to improve the manager<->worker interface.
+ *
+ * See:
+ *
+ * https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/123
+ *
+ * for some ideas and more discussion.
+ *
+ */
+ wait_until_dbus_signal_emission_to_manager_finishes (worker);
+
+ jump_back_to_initial_vt (worker);
+}
+
+static void
+gdm_session_worker_watch_child (GdmSessionWorker *worker)
+{
+ g_debug ("GdmSession worker: watching pid %d", worker->priv->child_pid);
+ worker->priv->child_watch_id = g_child_watch_add (worker->priv->child_pid,
+ (GChildWatchFunc)session_worker_child_watch,
+ worker);
+
+}
+
+static gboolean
+_is_loggable_file (const char* filename)
+{
+ struct stat file_info;
+
+ if (g_lstat (filename, &file_info) < 0) {
+ return FALSE;
+ }
+
+ return S_ISREG (file_info.st_mode) && g_access (filename, R_OK | W_OK) == 0;
+}
+
+static void
+rotate_logs (const char *path,
+ guint n_copies)
+{
+ int i;
+
+ for (i = n_copies - 1; i > 0; i--) {
+ char *name_n;
+ char *name_n1;
+
+ name_n = g_strdup_printf ("%s.%d", path, i);
+ if (i > 1) {
+ name_n1 = g_strdup_printf ("%s.%d", path, i - 1);
+ } else {
+ name_n1 = g_strdup (path);
+ }
+
+ g_unlink (name_n);
+ g_rename (name_n1, name_n);
+
+ g_free (name_n1);
+ g_free (name_n);
+ }
+
+ g_unlink (path);
+}
+
+static int
+_open_program_session_log (const char *filename)
+{
+ int fd;
+
+ rotate_logs (filename, MAX_LOGS);
+
+ fd = g_open (filename, O_WRONLY | O_APPEND | O_CREAT, 0600);
+
+ if (fd < 0) {
+ char *temp_name;
+
+ temp_name = g_strdup_printf ("%s.XXXXXXXX", filename);
+
+ fd = g_mkstemp (temp_name);
+
+ if (fd < 0) {
+ g_free (temp_name);
+ goto out;
+ }
+
+ g_warning ("session log '%s' is not appendable, logging session to '%s' instead.\n", filename,
+ temp_name);
+ g_free (temp_name);
+ } else {
+ if (ftruncate (fd, 0) < 0) {
+ close (fd);
+ fd = -1;
+ goto out;
+ }
+ }
+
+ if (fchmod (fd, 0644) < 0) {
+ close (fd);
+ fd = -1;
+ goto out;
+ }
+
+
+out:
+ if (fd < 0) {
+ g_warning ("unable to log program session");
+ fd = g_open ("/dev/null", O_RDWR);
+ }
+
+ return fd;
+}
+
+static int
+_open_user_session_log (const char *dir)
+{
+ int fd;
+ char *filename;
+
+ filename = g_build_filename (dir, GDM_SESSION_LOG_FILENAME, NULL);
+
+ if (g_access (dir, R_OK | W_OK | X_OK) == 0 && _is_loggable_file (filename)) {
+ char *filename_old;
+
+ filename_old = g_strdup_printf ("%s.old", filename);
+ g_rename (filename, filename_old);
+ g_free (filename_old);
+ }
+
+ fd = g_open (filename, O_RDWR | O_APPEND | O_CREAT, 0600);
+
+ if (fd < 0) {
+ char *temp_name;
+
+ temp_name = g_strdup_printf ("%s.XXXXXXXX", filename);
+
+ fd = g_mkstemp (temp_name);
+
+ if (fd < 0) {
+ g_free (temp_name);
+ goto out;
+ }
+
+ g_warning ("session log '%s' is not appendable, logging session to '%s' instead.\n", filename,
+ temp_name);
+ g_free (filename);
+ filename = temp_name;
+ } else {
+ if (ftruncate (fd, 0) < 0) {
+ close (fd);
+ fd = -1;
+ goto out;
+ }
+ }
+
+ if (fchmod (fd, 0600) < 0) {
+ close (fd);
+ fd = -1;
+ goto out;
+ }
+
+
+out:
+ g_free (filename);
+
+ if (fd < 0) {
+ g_warning ("unable to log session");
+ fd = g_open ("/dev/null", O_RDWR);
+ }
+
+ return fd;
+}
+
+static gboolean
+gdm_session_worker_start_session (GdmSessionWorker *worker,
+ GError **error)
+{
+ struct passwd *passwd_entry;
+ pid_t session_pid;
+ int error_code;
+
+ gdm_get_pwent_for_name (worker->priv->username, &passwd_entry);
+ if (worker->priv->is_program_session) {
+ g_debug ("GdmSessionWorker: opening session for program '%s'",
+ worker->priv->arguments[0]);
+ } else {
+ g_debug ("GdmSessionWorker: opening user session with program '%s'",
+ worker->priv->arguments[0]);
+ }
+
+ error_code = PAM_SUCCESS;
+
+ /* If we're in new vt mode, jump to the new vt now. There's no need to jump for
+ * the other two modes: in the logind case, the session will activate itself when
+ * ready, and in the reuse server case, we're already on the correct VT. */
+ if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
+ if (worker->priv->display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) {
+ jump_to_vt (worker, worker->priv->session_vt);
+ }
+ }
+
+ if (!worker->priv->is_program_session && !run_script (worker, GDMCONFDIR "/PostLogin")) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ "Failed to execute PostLogin script");
+ error_code = PAM_ABORT;
+ goto out;
+ }
+
+ if (!worker->priv->is_program_session && !run_script (worker, GDMCONFDIR "/PreSession")) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ "Failed to execute PreSession script");
+ error_code = PAM_ABORT;
+ goto out;
+ }
+
+ session_pid = fork ();
+
+ if (session_pid < 0) {
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ g_strerror (errno));
+ error_code = PAM_ABORT;
+ goto out;
+ }
+
+ if (session_pid == 0) {
+ const char * const * environment;
+ char *home_dir;
+ int stdin_fd = -1, stdout_fd = -1, stderr_fd = -1;
+ gboolean has_journald = FALSE, needs_controlling_terminal = FALSE;
+ /* Leak the TTY into the session as stdin so that it stays open
+ * without any races. */
+ if (worker->priv->session_tty_fd > 0) {
+ dup2 (worker->priv->session_tty_fd, STDIN_FILENO);
+ close (worker->priv->session_tty_fd);
+ worker->priv->session_tty_fd = -1;
+ needs_controlling_terminal = TRUE;
+ } else {
+ stdin_fd = open ("/dev/null", O_RDWR);
+ dup2 (stdin_fd, STDIN_FILENO);
+ close (stdin_fd);
+ }
+
+#ifdef ENABLE_SYSTEMD_JOURNAL
+ has_journald = sd_booted() > 0;
+#endif
+ if (!has_journald && worker->priv->is_program_session) {
+ stdout_fd = _open_program_session_log (worker->priv->log_file);
+ stderr_fd = dup (stdout_fd);
+ }
+
+ if (setsid () < 0) {
+ g_debug ("GdmSessionWorker: could not set pid '%u' as leader of new session and process group: %s",
+ (guint) getpid (), g_strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+
+ /* Take control of the tty
+ */
+ if (needs_controlling_terminal) {
+ if (ioctl (STDIN_FILENO, TIOCSCTTY, 0) < 0) {
+ g_debug ("GdmSessionWorker: could not take control of tty: %m");
+ }
+ }
+
+#ifdef HAVE_LOGINCAP
+ if (setusercontext (NULL, passwd_entry, passwd_entry->pw_uid, LOGIN_SETALL) < 0) {
+ g_debug ("GdmSessionWorker: setusercontext() failed for user %s: %s",
+ passwd_entry->pw_name, g_strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+#else
+ if (setuid (worker->priv->uid) < 0) {
+ g_debug ("GdmSessionWorker: could not reset uid: %s", g_strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+#endif
+
+ if (!worker->priv->is_program_session) {
+ gdm_load_env_d (load_env_func, get_var_cb, worker);
+ }
+
+ environment = gdm_session_worker_get_environment (worker);
+
+ g_assert (geteuid () == getuid ());
+
+ home_dir = gdm_session_worker_get_environment_variable (worker, "HOME");
+ if ((home_dir == NULL) || g_chdir (home_dir) < 0) {
+ g_chdir ("/");
+ }
+
+#ifdef ENABLE_SYSTEMD_JOURNAL
+ if (has_journald) {
+ stdout_fd = sd_journal_stream_fd (worker->priv->arguments[0], LOG_INFO, FALSE);
+ stderr_fd = sd_journal_stream_fd (worker->priv->arguments[0], LOG_WARNING, FALSE);
+
+ /* Unset the CLOEXEC flags, because sd_journal_stream_fd
+ * gives it to us by default.
+ */
+ gdm_clear_close_on_exec_flag (stdout_fd);
+ gdm_clear_close_on_exec_flag (stderr_fd);
+ }
+#endif
+ if (!has_journald && !worker->priv->is_program_session) {
+ if (home_dir != NULL && home_dir[0] != '\0') {
+ char *cache_dir;
+ char *log_dir;
+
+ cache_dir = gdm_session_worker_get_environment_variable (worker, "XDG_CACHE_HOME");
+ if (cache_dir == NULL || cache_dir[0] == '\0') {
+ cache_dir = g_build_filename (home_dir, ".cache", NULL);
+ }
+
+ log_dir = g_build_filename (cache_dir, "gdm", NULL);
+ g_free (cache_dir);
+
+ if (g_mkdir_with_parents (log_dir, S_IRWXU) == 0) {
+ stdout_fd = _open_user_session_log (log_dir);
+ stderr_fd = dup (stdout_fd);
+ } else {
+ stdout_fd = open ("/dev/null", O_RDWR);
+ stderr_fd = dup (stdout_fd);
+ }
+ g_free (log_dir);
+ } else {
+ stdout_fd = open ("/dev/null", O_RDWR);
+ stderr_fd = dup (stdout_fd);
+ }
+ }
+ g_free (home_dir);
+
+ if (stdout_fd != -1) {
+ dup2 (stdout_fd, STDOUT_FILENO);
+ close (stdout_fd);
+ }
+
+ if (stderr_fd != -1) {
+ dup2 (stderr_fd, STDERR_FILENO);
+ close (stderr_fd);
+ }
+
+ gdm_log_shutdown ();
+
+ /*
+ * Reset SIGPIPE to default so that any process in the user
+ * session get the default SIGPIPE behavior instead of ignoring
+ * SIGPIPE.
+ */
+ signal (SIGPIPE, SIG_DFL);
+
+ gdm_session_execute (worker->priv->arguments[0],
+ worker->priv->arguments,
+ (char **)
+ environment,
+ TRUE);
+
+ gdm_log_init ();
+ g_debug ("GdmSessionWorker: child '%s' could not be started: %s",
+ worker->priv->arguments[0],
+ g_strerror (errno));
+
+ _exit (EXIT_FAILURE);
+ }
+
+ if (worker->priv->session_tty_fd > 0) {
+ close (worker->priv->session_tty_fd);
+ worker->priv->session_tty_fd = -1;
+ }
+
+ /* If we end up execing again, make sure we don't use the executable context set up
+ * by pam_selinux durin pam_open_session
+ */
+#ifdef HAVE_SELINUX
+ setexeccon (NULL);
+#endif
+
+ worker->priv->child_pid = session_pid;
+
+ g_debug ("GdmSessionWorker: session opened creating reply...");
+ g_assert (sizeof (GPid) <= sizeof (int));
+
+ g_debug ("GdmSessionWorker: state SESSION_STARTED");
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SESSION_STARTED);
+
+ 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
+set_up_for_new_vt (GdmSessionWorker *worker)
+{
+ int initial_vt_fd;
+ char vt_string[256], tty_string[256];
+ int session_vt = 0;
+
+ /* open the initial vt. We need it for two scenarios:
+ *
+ * 1) display_is_initial is TRUE. We need it directly.
+ * 2) display_is_initial is FALSE. We need it to mark
+ * the initial VT as "in use" so it doesn't get returned
+ * by VT_OPENQRY
+ * */
+ g_snprintf (tty_string, sizeof (tty_string), "/dev/tty%d", GDM_INITIAL_VT);
+ initial_vt_fd = open (tty_string, O_RDWR | O_NOCTTY);
+
+ if (initial_vt_fd < 0) {
+ g_debug ("GdmSessionWorker: couldn't open console of initial fd: %m");
+ return FALSE;
+ }
+
+ if (worker->priv->display_is_initial) {
+ session_vt = GDM_INITIAL_VT;
+ } else {
+
+ /* Typically VT_OPENQRY is called on /dev/tty0, but we already
+ * have /dev/tty1 open above, so might as well use it.
+ */
+ if (ioctl (initial_vt_fd, VT_OPENQRY, &session_vt) < 0) {
+ g_debug ("GdmSessionWorker: couldn't open new VT: %m");
+ goto fail;
+ }
+ }
+
+ worker->priv->session_vt = session_vt;
+
+ g_assert (session_vt > 0);
+
+ g_snprintf (vt_string, sizeof (vt_string), "%d", session_vt);
+
+ /* Set the VTNR. This is used by logind to configure a session in
+ * the logind-managed case, but it doesn't hurt to set it always.
+ * When logind gains support for XDG_VTNR=auto, we can make the
+ * OPENQRY and this whole path only used by the new VT code. */
+ gdm_session_worker_set_environment_variable (worker,
+ "XDG_VTNR",
+ vt_string);
+
+ if (worker->priv->display_is_initial) {
+ worker->priv->session_tty_fd = initial_vt_fd;
+ } else {
+ g_snprintf (tty_string, sizeof (tty_string), "/dev/tty%d", session_vt);
+ worker->priv->session_tty_fd = open (tty_string, O_RDWR | O_NOCTTY);
+ close (initial_vt_fd);
+ }
+
+ pam_set_item (worker->priv->pam_handle, PAM_TTY, tty_string);
+
+ return TRUE;
+
+fail:
+ close (initial_vt_fd);
+ return FALSE;
+}
+
+static gboolean
+set_xdg_vtnr_to_current_vt (GdmSessionWorker *worker)
+{
+ int fd;
+ char vt_string[256];
+ struct vt_stat vt_state = { 0 };
+
+ fd = open ("/dev/tty0", O_RDWR | O_NOCTTY);
+
+ if (fd < 0) {
+ g_debug ("GdmSessionWorker: couldn't open VT master: %m");
+ return FALSE;
+ }
+
+ if (ioctl (fd, VT_GETSTATE, &vt_state) < 0) {
+ g_debug ("GdmSessionWorker: couldn't get current VT: %m");
+ goto fail;
+ }
+
+ close (fd);
+ fd = -1;
+
+ g_snprintf (vt_string, sizeof (vt_string), "%d", vt_state.v_active);
+
+ gdm_session_worker_set_environment_variable (worker,
+ "XDG_VTNR",
+ vt_string);
+
+ return TRUE;
+
+fail:
+ close (fd);
+ return FALSE;
+}
+
+static gboolean
+set_up_for_current_vt (GdmSessionWorker *worker,
+ GError **error)
+{
+#ifdef PAM_XAUTHDATA
+ struct pam_xauth_data *pam_xauth;
+#endif
+ int error_code = PAM_SUCCESS;
+ char *pam_tty;
+
+ /* set TTY */
+ pam_tty = _get_tty_for_pam (worker->priv->x11_display_name, worker->priv->display_device);
+ if (pam_tty != NULL && pam_tty[0] != '\0') {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_TTY, pam_tty);
+
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("error informing authentication system of user's console %s: %s",
+ pam_tty,
+ pam_strerror (worker->priv->pam_handle, error_code));
+ g_free (pam_tty);
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ "");
+ goto out;
+ }
+ }
+ g_free (pam_tty);
+
+#ifdef PAM_XDISPLAY
+ /* set XDISPLAY */
+ if (worker->priv->x11_display_name != NULL && worker->priv->x11_display_name[0] != '\0') {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_XDISPLAY, worker->priv->x11_display_name);
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("error informing authentication system of display string %s: %s",
+ worker->priv->x11_display_name,
+ pam_strerror (worker->priv->pam_handle, error_code));
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ "");
+ goto out;
+ }
+ }
+#endif
+#ifdef PAM_XAUTHDATA
+ /* set XAUTHDATA */
+ pam_xauth = _get_xauth_for_pam (worker->priv->x11_authority_file);
+ if (pam_xauth != NULL) {
+ error_code = pam_set_item (worker->priv->pam_handle, PAM_XAUTHDATA, pam_xauth);
+ if (error_code != PAM_SUCCESS) {
+ g_debug ("error informing authentication system of display string %s: %s",
+ worker->priv->x11_display_name,
+ pam_strerror (worker->priv->pam_handle, error_code));
+ g_free (pam_xauth);
+
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_AUTHENTICATING,
+ "");
+ goto out;
+ }
+ g_free (pam_xauth);
+ }
+#endif
+
+ if (g_strcmp0 (worker->priv->display_seat_id, "seat0") == 0) {
+ g_debug ("GdmSessionWorker: setting XDG_VTNR to current vt");
+ set_xdg_vtnr_to_current_vt (worker);
+ } else {
+ g_debug ("GdmSessionWorker: not setting XDG_VTNR since not seat0");
+ }
+
+ return TRUE;
+out:
+ return FALSE;
+}
+
+static gboolean
+gdm_session_worker_open_session (GdmSessionWorker *worker,
+ GError **error)
+{
+ int error_code;
+ int flags;
+ char *session_id = NULL;
+
+ g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED);
+ g_assert (geteuid () == 0);
+
+ switch (worker->priv->display_mode) {
+ case GDM_SESSION_DISPLAY_MODE_REUSE_VT:
+ if (!set_up_for_current_vt (worker, error)) {
+ return FALSE;
+ }
+ break;
+ case GDM_SESSION_DISPLAY_MODE_NEW_VT:
+ case GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED:
+ if (!set_up_for_new_vt (worker)) {
+ g_set_error (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ "Unable to open VT");
+ return FALSE;
+ }
+ break;
+ }
+
+ flags = 0;
+
+ if (worker->priv->is_program_session) {
+ flags |= PAM_SILENT;
+ }
+
+ error_code = pam_open_session (worker->priv->pam_handle, flags);
+
+ if (error_code != PAM_SUCCESS) {
+ g_set_error_literal (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OPENING_SESSION,
+ pam_strerror (worker->priv->pam_handle, error_code));
+ goto out;
+ }
+
+ g_debug ("GdmSessionWorker: state SESSION_OPENED");
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_SESSION_OPENED);
+
+ session_id = gdm_session_worker_get_environment_variable (worker, "XDG_SESSION_ID");
+
+ if (session_id != NULL) {
+ g_free (worker->priv->session_id);
+ worker->priv->session_id = session_id;
+ }
+
+ out:
+ if (error_code != PAM_SUCCESS) {
+ gdm_session_worker_uninitialize_pam (worker, error_code);
+ worker->priv->session_vt = 0;
+ return FALSE;
+ }
+
+ gdm_session_worker_get_username (worker, NULL);
+ gdm_session_auditor_report_login (worker->priv->auditor);
+
+ 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_is_reauth_session (GdmSessionWorker *worker,
+ gboolean is_reauth_session)
+{
+ worker->priv->is_reauth_session = is_reauth_session;
+}
+
+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;
+ case PROP_IS_REAUTH_SESSION:
+ gdm_session_worker_set_is_reauth_session (self, g_value_get_boolean (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;
+ case PROP_IS_REAUTH_SESSION:
+ g_value_set_boolean (value, self->priv->is_reauth_session);
+ break;
+ case PROP_STATE:
+ g_value_set_enum (value, self->priv->state);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gdm_session_worker_handle_set_environment_variable (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *key,
+ const char *value)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ gdm_session_worker_set_environment_variable (worker, key, value);
+ gdm_dbus_worker_complete_set_environment_variable (object, invocation);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_set_session_name (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *session_name)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ g_debug ("GdmSessionWorker: session name set to %s", session_name);
+ if (worker->priv->user_settings != NULL)
+ gdm_session_settings_set_session_name (worker->priv->user_settings,
+ session_name);
+ gdm_dbus_worker_complete_set_session_name (object, invocation);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_set_session_display_mode (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *str)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ g_debug ("GdmSessionWorker: session display mode set to %s", str);
+ worker->priv->display_mode = gdm_session_display_mode_from_string (str);
+ gdm_dbus_worker_complete_set_session_display_mode (object, invocation);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_set_language_name (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *language_name)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ g_debug ("GdmSessionWorker: language name set to %s", language_name);
+ if (worker->priv->user_settings != NULL)
+ gdm_session_settings_set_language_name (worker->priv->user_settings,
+ language_name);
+ gdm_dbus_worker_complete_set_language_name (object, invocation);
+ return TRUE;
+}
+
+static void
+on_saved_language_name_read (GdmSessionWorker *worker)
+{
+ char *language_name;
+
+ language_name = gdm_session_settings_get_language_name (worker->priv->user_settings);
+
+ g_debug ("GdmSessionWorker: Saved language is %s", language_name);
+ gdm_dbus_worker_emit_saved_language_name_read (GDM_DBUS_WORKER (worker),
+ language_name);
+ g_free (language_name);
+}
+
+static void
+on_saved_session_name_read (GdmSessionWorker *worker)
+{
+ char *session_name;
+
+ session_name = gdm_session_settings_get_session_name (worker->priv->user_settings);
+
+ g_debug ("GdmSessionWorker: Saved session is %s", session_name);
+ gdm_dbus_worker_emit_saved_session_name_read (GDM_DBUS_WORKER (worker),
+ session_name);
+ g_free (session_name);
+}
+
+static void
+on_saved_session_type_read (GdmSessionWorker *worker)
+{
+ char *session_type;
+
+ session_type = gdm_session_settings_get_session_type (worker->priv->user_settings);
+
+ g_debug ("GdmSessionWorker: Saved session type is %s", session_type);
+ gdm_dbus_worker_emit_saved_session_type_read (GDM_DBUS_WORKER (worker),
+ session_type);
+ g_free (session_type);
+}
+
+
+static void
+do_setup (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = gdm_session_worker_initialize_pam (worker,
+ worker->priv->service,
+ (const char **) worker->priv->extensions,
+ worker->priv->username,
+ worker->priv->hostname,
+ worker->priv->display_is_local,
+ worker->priv->x11_display_name,
+ worker->priv->x11_authority_file,
+ worker->priv->display_device,
+ worker->priv->display_seat_id,
+ &error);
+
+ if (res) {
+ g_dbus_method_invocation_return_value (worker->priv->pending_invocation, NULL);
+ } else {
+ g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
+ }
+ worker->priv->pending_invocation = NULL;
+}
+
+static void
+do_authenticate (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ /* find out who the user is and ensure they are who they say they are
+ */
+ error = NULL;
+ res = gdm_session_worker_authenticate_user (worker,
+ worker->priv->password_is_required,
+ &error);
+ if (res) {
+ /* we're authenticated. Let's make sure we've been given
+ * a valid username for the system
+ */
+ if (!worker->priv->is_program_session) {
+ g_debug ("GdmSessionWorker: trying to get updated username");
+ gdm_session_worker_update_username (worker);
+ }
+
+ gdm_dbus_worker_complete_authenticate (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
+ } else {
+ g_debug ("GdmSessionWorker: Unable to verify user");
+ g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
+ }
+ worker->priv->pending_invocation = NULL;
+}
+
+static void
+do_authorize (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ /* make sure the user is allowed to log in to this system
+ */
+ error = NULL;
+ res = gdm_session_worker_authorize_user (worker,
+ worker->priv->password_is_required,
+ &error);
+ if (res) {
+ gdm_dbus_worker_complete_authorize (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
+ } else {
+ g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
+ }
+ worker->priv->pending_invocation = NULL;
+}
+
+static void
+do_accredit (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ /* get kerberos tickets, setup group lists, etc
+ */
+ error = NULL;
+ res = gdm_session_worker_accredit_user (worker, &error);
+
+ if (res) {
+ gdm_dbus_worker_complete_establish_credentials (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation);
+ } else {
+ g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
+ }
+ worker->priv->pending_invocation = NULL;
+}
+
+static void
+save_account_details_now (GdmSessionWorker *worker)
+{
+ g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED);
+
+ g_debug ("GdmSessionWorker: saving account details for user %s", worker->priv->username);
+
+ gdm_session_worker_set_state (worker, GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED);
+ if (worker->priv->user_settings != NULL) {
+ if (!gdm_session_settings_save (worker->priv->user_settings,
+ worker->priv->username)) {
+ g_warning ("could not save session and language settings");
+ }
+ }
+ queue_state_change (worker);
+}
+
+static void
+on_settings_is_loaded_changed (GdmSessionSettings *user_settings,
+ GParamSpec *pspec,
+ GdmSessionWorker *worker)
+{
+ if (!gdm_session_settings_is_loaded (worker->priv->user_settings)) {
+ return;
+ }
+
+ /* These signal handlers should be disconnected after the loading,
+ * so that gdm_session_settings_set_* APIs don't cause the emitting
+ * of Saved*NameRead D-Bus signals any more.
+ */
+ g_signal_handlers_disconnect_by_func (worker->priv->user_settings,
+ G_CALLBACK (on_saved_session_name_read),
+ worker);
+
+ g_signal_handlers_disconnect_by_func (worker->priv->user_settings,
+ G_CALLBACK (on_saved_language_name_read),
+ worker);
+
+ if (worker->priv->state == GDM_SESSION_WORKER_STATE_NONE) {
+ g_debug ("GdmSessionWorker: queuing setup for user: %s %s",
+ worker->priv->username, worker->priv->display_device);
+ queue_state_change (worker);
+ } else if (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED) {
+ save_account_details_now (worker);
+ } else {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (worker->priv->user_settings),
+ G_CALLBACK (on_settings_is_loaded_changed),
+ worker);
+}
+
+static void
+do_save_account_details_when_ready (GdmSessionWorker *worker)
+{
+ g_assert (worker->priv->state == GDM_SESSION_WORKER_STATE_ACCREDITED);
+
+ if (worker->priv->user_settings != NULL && !gdm_session_settings_is_loaded (worker->priv->user_settings)) {
+ g_signal_connect (G_OBJECT (worker->priv->user_settings),
+ "notify::is-loaded",
+ G_CALLBACK (on_settings_is_loaded_changed),
+ worker);
+ g_debug ("GdmSessionWorker: user %s, not fully loaded yet, will save account details later",
+ worker->priv->username);
+ gdm_session_settings_load (worker->priv->user_settings,
+ worker->priv->username);
+ return;
+ }
+
+ save_account_details_now (worker);
+}
+
+static void
+do_open_session (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = gdm_session_worker_open_session (worker, &error);
+
+ if (res) {
+ char *session_id = worker->priv->session_id;
+ if (session_id == NULL) {
+ session_id = "";
+ }
+
+ gdm_dbus_worker_complete_open (GDM_DBUS_WORKER (worker), worker->priv->pending_invocation, session_id);
+ } else {
+ g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
+ }
+ worker->priv->pending_invocation = NULL;
+}
+
+static void
+do_start_session (GdmSessionWorker *worker)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+ res = gdm_session_worker_start_session (worker, &error);
+ if (res) {
+ gdm_dbus_worker_complete_start_program (GDM_DBUS_WORKER (worker),
+ worker->priv->pending_invocation,
+ worker->priv->child_pid);
+ } else {
+ g_dbus_method_invocation_take_error (worker->priv->pending_invocation, error);
+ }
+ worker->priv->pending_invocation = NULL;
+}
+
+static const char *
+get_state_name (int state)
+{
+ const char *name;
+
+ name = NULL;
+
+ switch (state) {
+ case GDM_SESSION_WORKER_STATE_NONE:
+ name = "NONE";
+ break;
+ case GDM_SESSION_WORKER_STATE_SETUP_COMPLETE:
+ name = "SETUP_COMPLETE";
+ break;
+ case GDM_SESSION_WORKER_STATE_AUTHENTICATED:
+ name = "AUTHENTICATED";
+ break;
+ case GDM_SESSION_WORKER_STATE_AUTHORIZED:
+ name = "AUTHORIZED";
+ break;
+ case GDM_SESSION_WORKER_STATE_ACCREDITED:
+ name = "ACCREDITED";
+ break;
+ case GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED:
+ name = "ACCOUNT_DETAILS_SAVED";
+ break;
+ case GDM_SESSION_WORKER_STATE_SESSION_OPENED:
+ name = "SESSION_OPENED";
+ break;
+ case GDM_SESSION_WORKER_STATE_SESSION_STARTED:
+ name = "SESSION_STARTED";
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return name;
+}
+
+static gboolean
+state_change_idle (GdmSessionWorker *worker)
+{
+ int new_state;
+
+ new_state = worker->priv->state + 1;
+ g_debug ("GdmSessionWorker: attempting to change state to %s",
+ get_state_name (new_state));
+
+ worker->priv->state_change_idle_id = 0;
+
+ switch (new_state) {
+ case GDM_SESSION_WORKER_STATE_SETUP_COMPLETE:
+ do_setup (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_AUTHENTICATED:
+ do_authenticate (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_AUTHORIZED:
+ do_authorize (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_ACCREDITED:
+ do_accredit (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED:
+ do_save_account_details_when_ready (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_SESSION_OPENED:
+ do_open_session (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_SESSION_STARTED:
+ do_start_session (worker);
+ break;
+ case GDM_SESSION_WORKER_STATE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+ return FALSE;
+}
+
+static void
+queue_state_change (GdmSessionWorker *worker)
+{
+ if (worker->priv->state_change_idle_id > 0) {
+ return;
+ }
+
+ worker->priv->state_change_idle_id = g_idle_add ((GSourceFunc)state_change_idle, worker);
+}
+
+static gboolean
+validate_state_change (GdmSessionWorker *worker,
+ GDBusMethodInvocation *invocation,
+ int new_state)
+{
+ if (worker->priv->pending_invocation != NULL) {
+ g_dbus_method_invocation_return_error (invocation,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_OUTSTANDING_REQUEST,
+ "Cannot process state change to %s, as there is already an outstanding request to move to state %s",
+ get_state_name (new_state),
+ get_state_name (worker->priv->state + 1));
+ return FALSE;
+ } else if (worker->priv->state != new_state - 1) {
+ g_dbus_method_invocation_return_error (invocation,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_WRONG_STATE,
+ "Cannot move to state %s, in state %s, not %s",
+ get_state_name (new_state),
+ get_state_name (worker->priv->state),
+ get_state_name (new_state - 1));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+validate_and_queue_state_change (GdmSessionWorker *worker,
+ GDBusMethodInvocation *invocation,
+ int new_state)
+{
+ if (validate_state_change (worker, invocation, new_state)) {
+ worker->priv->pending_invocation = invocation;
+ queue_state_change (worker);
+ }
+}
+
+static gboolean
+gdm_session_worker_handle_authenticate (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_AUTHENTICATED);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_authorize (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_AUTHORIZED);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_establish_credentials (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_ACCREDITED);
+
+ if (!worker->priv->is_reauth_session) {
+ worker->priv->cred_flags = PAM_ESTABLISH_CRED;
+ } else {
+ worker->priv->cred_flags = PAM_REINITIALIZE_CRED;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_open (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED);
+ return TRUE;
+}
+
+static char **
+filter_extensions (const char * const *extensions)
+{
+ size_t i, j;
+ GPtrArray *array = NULL;
+ char **filtered_extensions = NULL;
+
+ array = g_ptr_array_new ();
+
+ for (i = 0; extensions[i] != NULL; i++) {
+ for (j = 0; gdm_supported_pam_extensions[j] != NULL; j++) {
+ if (g_strcmp0 (extensions[i], gdm_supported_pam_extensions[j]) == 0) {
+ g_ptr_array_add (array, g_strdup (gdm_supported_pam_extensions[j]));
+ break;
+ }
+ }
+ }
+ g_ptr_array_add (array, NULL);
+
+ filtered_extensions = g_strdupv ((char **) array->pdata);
+
+ g_ptr_array_free (array, TRUE);
+
+ return filtered_extensions;
+}
+
+static gboolean
+gdm_session_worker_handle_initialize (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ GVariant *details)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ GVariantIter iter;
+ char *key;
+ GVariant *value;
+ gboolean wait_for_settings = FALSE;
+
+ if (!validate_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE))
+ return TRUE;
+
+ g_variant_iter_init (&iter, details);
+ while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
+ if (g_strcmp0 (key, "service") == 0) {
+ worker->priv->service = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "extensions") == 0) {
+ worker->priv->extensions = filter_extensions (g_variant_get_strv (value, NULL));
+ } else if (g_strcmp0 (key, "username") == 0) {
+ worker->priv->username = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "is-program-session") == 0) {
+ worker->priv->is_program_session = g_variant_get_boolean (value);
+ } else if (g_strcmp0 (key, "log-file") == 0) {
+ worker->priv->log_file = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "x11-display-name") == 0) {
+ worker->priv->x11_display_name = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "x11-authority-file") == 0) {
+ worker->priv->x11_authority_file = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "console") == 0) {
+ worker->priv->display_device = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "seat-id") == 0) {
+ worker->priv->display_seat_id = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "hostname") == 0) {
+ worker->priv->hostname = g_variant_dup_string (value, NULL);
+ } else if (g_strcmp0 (key, "display-is-local") == 0) {
+ worker->priv->display_is_local = g_variant_get_boolean (value);
+ } else if (g_strcmp0 (key, "display-is-initial") == 0) {
+ worker->priv->display_is_initial = g_variant_get_boolean (value);
+ }
+ }
+
+ worker->priv->pending_invocation = invocation;
+
+ if (!worker->priv->is_program_session) {
+ worker->priv->user_settings = gdm_session_settings_new ();
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::language-name",
+ G_CALLBACK (on_saved_language_name_read),
+ worker);
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::session-name",
+ G_CALLBACK (on_saved_session_name_read),
+ worker);
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::session-type",
+ G_CALLBACK (on_saved_session_type_read),
+ worker);
+
+ if (worker->priv->username) {
+ wait_for_settings = !gdm_session_settings_load (worker->priv->user_settings,
+ worker->priv->username);
+ }
+ }
+
+ if (wait_for_settings) {
+ /* Load settings from accounts daemon before continuing
+ */
+ g_signal_connect (G_OBJECT (worker->priv->user_settings),
+ "notify::is-loaded",
+ G_CALLBACK (on_settings_is_loaded_changed),
+ worker);
+ } else {
+ queue_state_change (worker);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_setup (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *service,
+ const char *x11_display_name,
+ const char *x11_authority_file,
+ const char *console,
+ const char *seat_id,
+ const char *hostname,
+ gboolean display_is_local,
+ gboolean display_is_initial)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
+
+ worker->priv->service = g_strdup (service);
+ worker->priv->x11_display_name = g_strdup (x11_display_name);
+ worker->priv->x11_authority_file = g_strdup (x11_authority_file);
+ worker->priv->display_device = g_strdup (console);
+ worker->priv->display_seat_id = g_strdup (seat_id);
+ worker->priv->hostname = g_strdup (hostname);
+ worker->priv->display_is_local = display_is_local;
+ worker->priv->display_is_initial = display_is_initial;
+ worker->priv->username = NULL;
+
+ worker->priv->user_settings = gdm_session_settings_new ();
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::language-name",
+ G_CALLBACK (on_saved_language_name_read),
+ worker);
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::session-name",
+ G_CALLBACK (on_saved_session_name_read),
+ worker);
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::session-type",
+ G_CALLBACK (on_saved_session_type_read),
+ worker);
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_setup_for_user (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *service,
+ const char *username,
+ const char *x11_display_name,
+ const char *x11_authority_file,
+ const char *console,
+ const char *seat_id,
+ const char *hostname,
+ gboolean display_is_local,
+ gboolean display_is_initial)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+
+ if (!validate_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE))
+ return TRUE;
+
+ worker->priv->service = g_strdup (service);
+ worker->priv->x11_display_name = g_strdup (x11_display_name);
+ worker->priv->x11_authority_file = g_strdup (x11_authority_file);
+ worker->priv->display_device = g_strdup (console);
+ worker->priv->display_seat_id = g_strdup (seat_id);
+ worker->priv->hostname = g_strdup (hostname);
+ worker->priv->display_is_local = display_is_local;
+ worker->priv->display_is_initial = display_is_initial;
+ worker->priv->username = g_strdup (username);
+
+ worker->priv->user_settings = gdm_session_settings_new ();
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::language-name",
+ G_CALLBACK (on_saved_language_name_read),
+ worker);
+
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::session-name",
+ G_CALLBACK (on_saved_session_name_read),
+ worker);
+ g_signal_connect_swapped (worker->priv->user_settings,
+ "notify::session-type",
+ G_CALLBACK (on_saved_session_type_read),
+ worker);
+
+ /* Load settings from accounts daemon before continuing
+ */
+ worker->priv->pending_invocation = invocation;
+ if (gdm_session_settings_load (worker->priv->user_settings, username)) {
+ queue_state_change (worker);
+ } else {
+ g_signal_connect (G_OBJECT (worker->priv->user_settings),
+ "notify::is-loaded",
+ G_CALLBACK (on_settings_is_loaded_changed),
+ worker);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_setup_for_program (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *service,
+ const char *username,
+ const char *x11_display_name,
+ const char *x11_authority_file,
+ const char *console,
+ const char *seat_id,
+ const char *hostname,
+ gboolean display_is_local,
+ gboolean display_is_initial,
+ const char *log_file)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ validate_and_queue_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SETUP_COMPLETE);
+
+ worker->priv->service = g_strdup (service);
+ worker->priv->x11_display_name = g_strdup (x11_display_name);
+ worker->priv->x11_authority_file = g_strdup (x11_authority_file);
+ worker->priv->display_device = g_strdup (console);
+ worker->priv->display_seat_id = g_strdup (seat_id);
+ worker->priv->hostname = g_strdup (hostname);
+ worker->priv->display_is_local = display_is_local;
+ worker->priv->display_is_initial = display_is_initial;
+ worker->priv->username = g_strdup (username);
+ worker->priv->log_file = g_strdup (log_file);
+ worker->priv->is_program_session = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_worker_handle_start_program (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ const char *text)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ GError *parse_error = NULL;
+ validate_state_change (worker, invocation, GDM_SESSION_WORKER_STATE_SESSION_STARTED);
+
+ if (worker->priv->is_reauth_session) {
+ g_dbus_method_invocation_return_error (invocation,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_IN_REAUTH_SESSION,
+ "Cannot start a program while in a reauth session");
+ return TRUE;
+ }
+
+ g_debug ("GdmSessionWorker: start program: %s", text);
+
+ g_clear_pointer (&worker->priv->arguments, g_strfreev);
+ if (! g_shell_parse_argv (text, NULL, &worker->priv->arguments, &parse_error)) {
+ g_dbus_method_invocation_take_error (invocation, parse_error);
+ return TRUE;
+ }
+
+ worker->priv->pending_invocation = invocation;
+ queue_state_change (worker);
+
+ return TRUE;
+}
+
+static void
+on_reauthentication_client_connected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ ReauthenticationRequest *request)
+{
+ g_debug ("GdmSessionWorker: client connected to reauthentication server");
+}
+
+static void
+on_reauthentication_client_disconnected (GdmSession *session,
+ GCredentials *credentials,
+ GPid pid_of_client,
+ ReauthenticationRequest *request)
+{
+ GdmSessionWorker *worker;
+
+ g_debug ("GdmSessionWorker: client disconnected from reauthentication server");
+
+ worker = request->worker;
+ g_hash_table_remove (worker->priv->reauthentication_requests,
+ GINT_TO_POINTER (pid_of_client));
+}
+
+static void
+on_reauthentication_cancelled (GdmSession *session,
+ ReauthenticationRequest *request)
+{
+ g_debug ("GdmSessionWorker: client cancelled reauthentication request");
+ gdm_session_reset (session);
+}
+
+static void
+on_reauthentication_conversation_started (GdmSession *session,
+ const char *service_name,
+ ReauthenticationRequest *request)
+{
+ g_debug ("GdmSessionWorker: reauthentication service '%s' started",
+ service_name);
+}
+
+static void
+on_reauthentication_conversation_stopped (GdmSession *session,
+ const char *service_name,
+ ReauthenticationRequest *request)
+{
+ g_debug ("GdmSessionWorker: reauthentication service '%s' stopped",
+ service_name);
+}
+
+static void
+on_reauthentication_verification_complete (GdmSession *session,
+ const char *service_name,
+ ReauthenticationRequest *request)
+{
+ GdmSessionWorker *worker;
+
+ worker = request->worker;
+
+ g_debug ("GdmSessionWorker: pid %d reauthenticated user %d with service '%s'",
+ (int) request->pid_of_caller,
+ (int) request->uid_of_caller,
+ service_name);
+ gdm_session_reset (session);
+
+ gdm_dbus_worker_emit_reauthenticated (GDM_DBUS_WORKER (worker), service_name);
+}
+
+static ReauthenticationRequest *
+reauthentication_request_new (GdmSessionWorker *worker,
+ GPid pid_of_caller,
+ uid_t uid_of_caller,
+ GDBusMethodInvocation *invocation)
+{
+ ReauthenticationRequest *request;
+ const char * const * environment;
+ const char *address;
+
+ environment = gdm_session_worker_get_environment (worker);
+
+ request = g_slice_new (ReauthenticationRequest);
+
+ request->worker = worker;
+ request->pid_of_caller = pid_of_caller;
+ request->uid_of_caller = uid_of_caller;
+ request->session = gdm_session_new (GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE,
+ uid_of_caller,
+ worker->priv->x11_display_name,
+ worker->priv->hostname,
+ worker->priv->display_device,
+ worker->priv->display_seat_id,
+ worker->priv->x11_authority_file,
+ worker->priv->display_is_local,
+ environment);
+
+ g_signal_connect (request->session,
+ "client-connected",
+ G_CALLBACK (on_reauthentication_client_connected),
+ request);
+ g_signal_connect (request->session,
+ "client-disconnected",
+ G_CALLBACK (on_reauthentication_client_disconnected),
+ request);
+ g_signal_connect (request->session,
+ "cancelled",
+ G_CALLBACK (on_reauthentication_cancelled),
+ request);
+ g_signal_connect (request->session,
+ "conversation-started",
+ G_CALLBACK (on_reauthentication_conversation_started),
+ request);
+ g_signal_connect (request->session,
+ "conversation-stopped",
+ G_CALLBACK (on_reauthentication_conversation_stopped),
+ request);
+ g_signal_connect (request->session,
+ "verification-complete",
+ G_CALLBACK (on_reauthentication_verification_complete),
+ request);
+
+ address = gdm_session_get_server_address (request->session);
+
+ gdm_dbus_worker_complete_start_reauthentication (GDM_DBUS_WORKER (worker),
+ invocation,
+ address);
+
+ return request;
+}
+
+static gboolean
+gdm_session_worker_handle_start_reauthentication (GdmDBusWorker *object,
+ GDBusMethodInvocation *invocation,
+ int pid_of_caller,
+ int uid_of_caller)
+{
+ GdmSessionWorker *worker = GDM_SESSION_WORKER (object);
+ ReauthenticationRequest *request;
+
+ if (worker->priv->state != GDM_SESSION_WORKER_STATE_SESSION_STARTED) {
+ g_dbus_method_invocation_return_error (invocation,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_WRONG_STATE,
+ "Cannot reauthenticate while in state %s",
+ get_state_name (worker->priv->state));
+ return TRUE;
+ }
+
+ g_debug ("GdmSessionWorker: start reauthentication");
+
+ request = reauthentication_request_new (worker, pid_of_caller, uid_of_caller, invocation);
+ g_hash_table_replace (worker->priv->reauthentication_requests,
+ GINT_TO_POINTER (pid_of_caller),
+ request);
+ return TRUE;
+}
+
+static GObject *
+gdm_session_worker_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmSessionWorker *worker;
+ GError *error;
+
+ worker = GDM_SESSION_WORKER (G_OBJECT_CLASS (gdm_session_worker_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ g_debug ("GdmSessionWorker: connecting to address: %s", worker->priv->server_address);
+
+ error = NULL;
+ worker->priv->connection = g_dbus_connection_new_for_address_sync (worker->priv->server_address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL,
+ NULL,
+ &error);
+ if (worker->priv->connection == NULL) {
+ g_warning ("error opening connection: %s", error->message);
+ g_clear_error (&error);
+
+ exit (EXIT_FAILURE);
+ }
+
+ worker->priv->manager = GDM_DBUS_WORKER_MANAGER (gdm_dbus_worker_manager_proxy_new_sync (worker->priv->connection,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL, /* dbus name */
+ GDM_SESSION_DBUS_PATH,
+ NULL,
+ &error));
+ if (worker->priv->manager == NULL) {
+ g_warning ("error creating session proxy: %s", error->message);
+ g_clear_error (&error);
+
+ exit (EXIT_FAILURE);
+ }
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker),
+ worker->priv->connection,
+ GDM_WORKER_DBUS_PATH,
+ &error)) {
+ g_warning ("Error while exporting object: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (worker->priv->manager), G_MAXINT);
+
+ /* Send an initial Hello message so that the session can associate
+ * the conversation we manage with our pid.
+ */
+ gdm_dbus_worker_manager_call_hello_sync (worker->priv->manager,
+ NULL,
+ NULL);
+
+ return G_OBJECT (worker);
+}
+
+static void
+worker_interface_init (GdmDBusWorkerIface *interface)
+{
+ interface->handle_initialize = gdm_session_worker_handle_initialize;
+ /* The next three are for backward compat only */
+ interface->handle_setup = gdm_session_worker_handle_setup;
+ interface->handle_setup_for_user = gdm_session_worker_handle_setup_for_user;
+ interface->handle_setup_for_program = gdm_session_worker_handle_setup_for_program;
+ interface->handle_authenticate = gdm_session_worker_handle_authenticate;
+ interface->handle_authorize = gdm_session_worker_handle_authorize;
+ interface->handle_establish_credentials = gdm_session_worker_handle_establish_credentials;
+ interface->handle_open = gdm_session_worker_handle_open;
+ interface->handle_set_language_name = gdm_session_worker_handle_set_language_name;
+ interface->handle_set_session_name = gdm_session_worker_handle_set_session_name;
+ interface->handle_set_session_display_mode = gdm_session_worker_handle_set_session_display_mode;
+ interface->handle_set_environment_variable = gdm_session_worker_handle_set_environment_variable;
+ interface->handle_start_program = gdm_session_worker_handle_start_program;
+ interface->handle_start_reauthentication = gdm_session_worker_handle_start_reauthentication;
+}
+
+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_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 | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_IS_REAUTH_SESSION,
+ g_param_spec_boolean ("is-reauth-session",
+ "is reauth session",
+ "is reauth session",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_STATE,
+ g_param_spec_enum ("state",
+ "state",
+ "state",
+ GDM_TYPE_SESSION_WORKER_STATE,
+ GDM_SESSION_WORKER_STATE_NONE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+reauthentication_request_free (ReauthenticationRequest *request)
+{
+
+ g_signal_handlers_disconnect_by_func (request->session,
+ G_CALLBACK (on_reauthentication_client_connected),
+ request);
+ g_signal_handlers_disconnect_by_func (request->session,
+ G_CALLBACK (on_reauthentication_client_disconnected),
+ request);
+ g_signal_handlers_disconnect_by_func (request->session,
+ G_CALLBACK (on_reauthentication_cancelled),
+ request);
+ g_signal_handlers_disconnect_by_func (request->session,
+ G_CALLBACK (on_reauthentication_conversation_started),
+ request);
+ g_signal_handlers_disconnect_by_func (request->session,
+ G_CALLBACK (on_reauthentication_conversation_stopped),
+ request);
+ g_signal_handlers_disconnect_by_func (request->session,
+ G_CALLBACK (on_reauthentication_verification_complete),
+ request);
+ g_clear_object (&request->session);
+ g_slice_free (ReauthenticationRequest, request);
+}
+
+static void
+gdm_session_worker_init (GdmSessionWorker *worker)
+{
+ worker->priv = GDM_SESSION_WORKER_GET_PRIVATE (worker);
+
+ worker->priv->reauthentication_requests = g_hash_table_new_full (NULL,
+ NULL,
+ NULL,
+ (GDestroyNotify)
+ reauthentication_request_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->child_pid > 0) {
+ gdm_signal_pid (worker->priv->child_pid, SIGTERM);
+ gdm_wait_on_pid (worker->priv->child_pid);
+ }
+
+ if (worker->priv->pam_handle != NULL) {
+ gdm_session_worker_uninitialize_pam (worker, PAM_SUCCESS);
+ }
+
+ jump_back_to_initial_vt (worker);
+
+ g_object_unref (worker->priv->user_settings);
+ g_free (worker->priv->service);
+ g_free (worker->priv->x11_display_name);
+ g_free (worker->priv->x11_authority_file);
+ g_free (worker->priv->display_device);
+ g_free (worker->priv->display_seat_id);
+ g_free (worker->priv->hostname);
+ g_free (worker->priv->username);
+ g_free (worker->priv->server_address);
+ g_strfreev (worker->priv->arguments);
+ g_strfreev (worker->priv->extensions);
+
+ g_hash_table_unref (worker->priv->reauthentication_requests);
+
+ G_OBJECT_CLASS (gdm_session_worker_parent_class)->finalize (object);
+}
+
+GdmSessionWorker *
+gdm_session_worker_new (const char *address,
+ gboolean is_reauth_session)
+{
+ GObject *object;
+
+ object = g_object_new (GDM_TYPE_SESSION_WORKER,
+ "server-address", address,
+ "is-reauth-session", is_reauth_session,
+ 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 0000000..2814eab
--- /dev/null
+++ b/daemon/gdm-session-worker.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __GDM_SESSION_WORKER_H
+#define __GDM_SESSION_WORKER_H
+
+#include <glib-object.h>
+
+#include "gdm-session-worker-glue.h"
+#include "gdm-session-worker-common.h"
+#include "gdm-session-worker-enum-types.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))
+
+typedef enum {
+ GDM_SESSION_WORKER_STATE_NONE = 0,
+ GDM_SESSION_WORKER_STATE_SETUP_COMPLETE,
+ GDM_SESSION_WORKER_STATE_AUTHENTICATED,
+ GDM_SESSION_WORKER_STATE_AUTHORIZED,
+ GDM_SESSION_WORKER_STATE_ACCREDITED,
+ GDM_SESSION_WORKER_STATE_ACCOUNT_DETAILS_SAVED,
+ GDM_SESSION_WORKER_STATE_SESSION_OPENED,
+ GDM_SESSION_WORKER_STATE_SESSION_STARTED
+} GdmSessionWorkerState;
+
+typedef struct GdmSessionWorkerPrivate GdmSessionWorkerPrivate;
+
+typedef struct
+{
+ GdmDBusWorkerSkeleton parent;
+ GdmSessionWorkerPrivate *priv;
+} GdmSessionWorker;
+
+typedef struct
+{
+ GdmDBusWorkerSkeletonClass parent_class;
+} GdmSessionWorkerClass;
+
+GType gdm_session_worker_get_type (void);
+
+GdmSessionWorker * gdm_session_worker_new (const char *server_address,
+ gboolean is_for_reauth) G_GNUC_MALLOC;
+G_END_DECLS
+#endif /* GDM_SESSION_WORKER_H */
diff --git a/daemon/gdm-session-worker.xml b/daemon/gdm-session-worker.xml
new file mode 100644
index 0000000..a215779
--- /dev/null
+++ b/daemon/gdm-session-worker.xml
@@ -0,0 +1,93 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/gnome/DisplayManager/Worker">
+ <interface name="org.gnome.DisplayManager.Worker">
+ <method name="Authenticate" />
+ <method name="Authorize" />
+ <method name="EstablishCredentials" />
+ <method name="Open">
+ <arg name="session_id" direction="out" type="s"/>
+ </method>
+ <method name="SetLanguageName">
+ <arg name="language" direction="in" type="s"/>
+ </method>
+ <method name="SetSessionName">
+ <arg name="session_name" direction="in" type="s" />
+ </method>
+ <method name="SetSessionDisplayMode">
+ <arg name="mode" direction="in" type="s"/>
+ </method>
+ <method name="SetEnvironmentVariable">
+ <arg name="name" direction="in" type="s"/>
+ <arg name="value" direction="in" type="s"/>
+ </method>
+ <method name="StartProgram">
+ <arg name="command" direction="in" type="s"/>
+ <arg name="child_pid" direction="out" type="i"/>
+ </method>
+ <method name="Initialize">
+ <arg name="details" direction="in" type="a{sv}"/>
+ </method>
+ <method name="Setup">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="x11_display_name" direction="in" type="s"/>
+ <arg name="x11_authority_file" direction="in" type="s"/>
+ <arg name="display_device" direction="in" type="s"/>
+ <arg name="display_seat" direction="in" type="s"/>
+ <arg name="hostname" direction="in" type="s"/>
+ <arg name="display_is_local" direction="in" type="b"/>
+ <arg name="display_is_initial" direction="in" type="b"/>
+ </method>
+ <method name="SetupForUser">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="user_name" direction="in" type="s"/>
+ <arg name="x11_display_name" direction="in" type="s"/>
+ <arg name="x11_authority_file" direction="in" type="s"/>
+ <arg name="display_device" direction="in" type="s"/>
+ <arg name="display_seat" direction="in" type="s"/>
+ <arg name="hostname" direction="in" type="s"/>
+ <arg name="display_is_local" direction="in" type="b"/>
+ <arg name="display_is_initial" direction="in" type="b"/>
+ </method>
+ <method name="SetupForProgram">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="user_name" direction="in" type="s"/>
+ <arg name="x11_display_name" direction="in" type="s"/>
+ <arg name="x11_authority_file" direction="in" type="s"/>
+ <arg name="display_device" direction="in" type="s"/>
+ <arg name="display_seat" direction="in" type="s"/>
+ <arg name="hostname" direction="in" type="s"/>
+ <arg name="display_is_local" direction="in" type="b"/>
+ <arg name="display_is_initial" direction="in" type="b"/>
+ <arg name="log_file" direction="in" type="s"/>
+ </method>
+ <method name="StartReauthentication">
+ <arg name="pid_of_caller" direction="in" type="i"/>
+ <arg name="uid_of_caller" direction="in" type="i"/>
+ <arg name="address" direction="out" type="s"/>
+ </method>
+
+ <signal name="SessionExited">
+ <arg name="service_name" type="s" />
+ <!-- This is a combination of exit code and exit
+ signal. Use macros in sys/wait.h to handle it. -->
+ <arg name="status" type="i" />
+ </signal>
+ <signal name="SavedLanguageNameRead">
+ <arg name="language_name" type="s"/>
+ </signal>
+ <signal name="SavedSessionNameRead">
+ <arg name="session_name" type="s"/>
+ </signal>
+ <signal name="SavedSessionTypeRead">
+ <arg name="session_type" type="s"/>
+ </signal>
+ <signal name="UsernameChanged">
+ <arg name="new_username" type="s"/>
+ </signal>
+ <signal name="Reauthenticated">
+ <arg name="service_name" type="s"/>
+ </signal>
+ <signal name="CancelPendingQuery">
+ </signal>
+ </interface>
+</node>
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
new file mode 100644
index 0000000..4b70973
--- /dev/null
+++ b/daemon/gdm-session.c
@@ -0,0 +1,4132 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gdm-session.h"
+#include "gdm-session-glue.h"
+#include "gdm-dbus-util.h"
+
+#include "gdm-session.h"
+#include "gdm-session-enum-types.h"
+#include "gdm-session-worker-common.h"
+#include "gdm-session-worker-job.h"
+#include "gdm-session-worker-glue.h"
+#include "gdm-common.h"
+
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+#define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel"
+#define GDM_SESSION_DBUS_OBJECT_PATH "/org/gnome/DisplayManager/Session"
+
+#define GDM_WORKER_DBUS_PATH "/org/gnome/DisplayManager/Worker"
+
+typedef struct
+{
+ GdmSession *session;
+ GdmSessionWorkerJob *job;
+ GPid worker_pid;
+ char *service_name;
+ GDBusMethodInvocation *starting_invocation;
+ char *starting_username;
+ GDBusMethodInvocation *pending_invocation;
+ GdmDBusWorkerManager *worker_manager_interface;
+ GdmDBusWorker *worker_proxy;
+ GCancellable *worker_cancellable;
+ char *session_id;
+ guint32 is_stopping : 1;
+
+ GPid reauth_pid_of_caller;
+} GdmSessionConversation;
+
+struct _GdmSession
+{
+ GObject parent;
+
+ /* per open scope */
+ char *selected_program;
+ char *selected_session;
+ char *saved_session;
+ char *saved_session_type;
+ char *saved_language;
+ char *selected_user;
+ char *user_x11_authority_file;
+
+ char *timed_login_username;
+ int timed_login_delay;
+ GList *pending_timed_login_invocations;
+
+ GHashTable *conversations;
+
+ GdmSessionConversation *session_conversation;
+
+ char **conversation_environment;
+
+ GdmDBusUserVerifier *user_verifier_interface;
+ GHashTable *user_verifier_extensions;
+ GdmDBusGreeter *greeter_interface;
+ GdmDBusRemoteGreeter *remote_greeter_interface;
+ GdmDBusChooser *chooser_interface;
+
+ GList *pending_worker_connections;
+ GList *outside_connections;
+
+ GPid session_pid;
+
+ /* object lifetime scope */
+ char *session_type;
+ char *display_name;
+ char *display_hostname;
+ char *display_device;
+ char *display_seat_id;
+ char *display_x11_authority_file;
+ gboolean display_is_local;
+
+ GdmSessionVerificationMode verification_mode;
+
+ uid_t allowed_user;
+
+ char *fallback_session_name;
+
+ GDBusServer *worker_server;
+ GDBusServer *outside_server;
+ GHashTable *environment;
+
+ GStrv supported_session_types;
+
+ guint32 is_program_session : 1;
+ guint32 display_is_initial : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_VERIFICATION_MODE,
+ PROP_ALLOWED_USER,
+ PROP_DISPLAY_NAME,
+ PROP_DISPLAY_HOSTNAME,
+ PROP_DISPLAY_IS_LOCAL,
+ PROP_DISPLAY_IS_INITIAL,
+ PROP_SESSION_TYPE,
+ PROP_DISPLAY_DEVICE,
+ PROP_DISPLAY_SEAT_ID,
+ PROP_DISPLAY_X11_AUTHORITY_FILE,
+ PROP_USER_X11_AUTHORITY_FILE,
+ PROP_CONVERSATION_ENVIRONMENT,
+ PROP_SUPPORTED_SESSION_TYPES,
+};
+
+enum {
+ CONVERSATION_STARTED = 0,
+ CONVERSATION_STOPPED,
+ SETUP_COMPLETE,
+ CANCELLED,
+ HOSTNAME_SELECTED,
+ CLIENT_REJECTED,
+ CLIENT_CONNECTED,
+ CLIENT_DISCONNECTED,
+ CLIENT_READY_FOR_SESSION_TO_START,
+ DISCONNECTED,
+ AUTHENTICATION_FAILED,
+ VERIFICATION_COMPLETE,
+ SESSION_OPENED,
+ SESSION_OPENED_FAILED,
+ SESSION_STARTED,
+ SESSION_START_FAILED,
+ SESSION_EXITED,
+ SESSION_DIED,
+ REAUTHENTICATION_STARTED,
+ REAUTHENTICATED,
+ LAST_SIGNAL
+};
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+static gboolean gdm_session_is_wayland_session (GdmSession *self);
+#endif
+static void update_session_type (GdmSession *self);
+static void set_session_type (GdmSession *self,
+ const char *session_type);
+static void close_conversation (GdmSessionConversation *conversation);
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GdmSession,
+ gdm_session,
+ G_TYPE_OBJECT);
+
+static GdmSessionConversation *
+find_conversation_by_name (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ conversation = g_hash_table_lookup (self->conversations, service_name);
+
+ if (conversation == NULL) {
+ g_warning ("Tried to look up non-existent conversation %s", service_name);
+ }
+
+ return conversation;
+}
+
+static void
+report_and_stop_conversation (GdmSession *self,
+ const char *service_name,
+ GError *error)
+{
+ g_dbus_error_strip_remote_error (error);
+
+ if (self->user_verifier_interface != NULL) {
+ if (g_error_matches (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE) ||
+ g_error_matches (error,
+ GDM_SESSION_WORKER_ERROR,
+ GDM_SESSION_WORKER_ERROR_TOO_MANY_RETRIES)) {
+ gdm_dbus_user_verifier_emit_service_unavailable (self->user_verifier_interface,
+ service_name,
+ error->message);
+ } else {
+ gdm_dbus_user_verifier_emit_problem (self->user_verifier_interface,
+ service_name,
+ error->message);
+ }
+ gdm_dbus_user_verifier_emit_verification_failed (self->user_verifier_interface,
+ service_name);
+ }
+
+ gdm_session_stop_conversation (self, service_name);
+}
+
+static void
+on_authenticate_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+
+ worked = gdm_dbus_worker_call_authenticate_finish (proxy, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ gdm_session_authorize (self, service_name);
+ } else {
+ g_signal_emit (self,
+ signals[AUTHENTICATION_FAILED],
+ 0,
+ service_name,
+ conversation->worker_pid);
+ report_and_stop_conversation (self, service_name, error);
+ }
+}
+
+static void
+on_authorize_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+
+ worked = gdm_dbus_worker_call_authorize_finish (proxy, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ gdm_session_accredit (self, service_name);
+ } else {
+ report_and_stop_conversation (self, service_name, error);
+ }
+}
+
+static void
+on_establish_credentials_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+
+ worked = gdm_dbus_worker_call_establish_credentials_finish (proxy, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = g_object_ref (conversation->session);
+ service_name = g_strdup (conversation->service_name);
+
+ if (worked) {
+ switch (self->verification_mode) {
+ case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+ case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
+ gdm_session_open_session (self, service_name);
+ break;
+ case GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE:
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
+ service_name);
+ g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ report_and_stop_conversation (self, service_name, error);
+ }
+
+ g_free (service_name);
+ g_object_unref (self);
+}
+
+static gboolean
+supports_session_type (GdmSession *self,
+ const char *session_type)
+{
+ if (session_type == NULL)
+ return TRUE;
+
+ return g_strv_contains ((const char * const *) self->supported_session_types,
+ session_type);
+}
+
+static char **
+get_system_session_dirs (GdmSession *self,
+ const char *type)
+{
+ GArray *search_array = NULL;
+ char **search_dirs;
+ int i, j;
+ const gchar * const *system_data_dirs = g_get_system_data_dirs ();
+
+ static const char *x_search_dirs[] = {
+ "/etc/X11/sessions/",
+ DMCONFDIR "/Sessions/",
+ DATADIR "/gdm/BuiltInSessions/",
+ DATADIR "/xsessions/",
+ };
+
+ static const char *wayland_search_dir = DATADIR "/wayland-sessions/";
+
+ search_array = g_array_new (TRUE, TRUE, sizeof (char *));
+
+ for (j = 0; self->supported_session_types[j] != NULL; j++) {
+ const char *supported_type = self->supported_session_types[j];
+
+ if (g_str_equal (supported_type, "x11") &&
+ (type == NULL || g_str_equal (type, supported_type))) {
+ for (i = 0; system_data_dirs[i]; i++) {
+ gchar *dir = g_build_filename (system_data_dirs[i], "xsessions", NULL);
+ g_array_append_val (search_array, dir);
+ }
+
+ g_array_append_vals (search_array, x_search_dirs, G_N_ELEMENTS (x_search_dirs));
+ }
+
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ if (g_str_equal (supported_type, "wayland") &&
+ (type == NULL || g_str_equal (type, supported_type))) {
+ for (i = 0; system_data_dirs[i]; i++) {
+ gchar *dir = g_build_filename (system_data_dirs[i], "wayland-sessions", NULL);
+ g_array_append_val (search_array, dir);
+ }
+
+ g_array_append_val (search_array, wayland_search_dir);
+ }
+#endif
+ }
+
+ search_dirs = g_strdupv ((char **) search_array->data);
+
+ g_array_free (search_array, TRUE);
+
+ return search_dirs;
+}
+
+static gboolean
+is_prog_in_path (const char *prog)
+{
+ char *f;
+ gboolean ret;
+
+ f = g_find_program_in_path (prog);
+ ret = (f != NULL);
+ g_free (f);
+ return ret;
+}
+
+static GKeyFile *
+load_key_file_for_file (GdmSession *self,
+ const char *file,
+ const char *type,
+ char **full_path)
+{
+ GKeyFile *key_file;
+ GError *error = NULL;
+ gboolean res;
+ char **search_dirs;
+
+ key_file = g_key_file_new ();
+
+ search_dirs = get_system_session_dirs (self, type);
+
+ error = NULL;
+ res = g_key_file_load_from_dirs (key_file,
+ file,
+ (const char **) search_dirs,
+ full_path,
+ G_KEY_FILE_NONE,
+ &error);
+ if (! res) {
+ g_debug ("GdmSession: File '%s' not found in search dirs", file);
+ if (error != NULL) {
+ g_debug ("GdmSession: %s", error->message);
+ g_error_free (error);
+ }
+ g_key_file_free (key_file);
+ key_file = NULL;
+ }
+
+ g_strfreev (search_dirs);
+
+ return key_file;
+}
+
+static gboolean
+get_session_command_for_file (GdmSession *self,
+ const char *file,
+ const char *type,
+ char **command)
+{
+ GKeyFile *key_file;
+ GError *error;
+ char *exec;
+ gboolean ret;
+ gboolean res;
+
+ exec = NULL;
+ ret = FALSE;
+ if (command != NULL) {
+ *command = NULL;
+ }
+
+ if (!supports_session_type (self, type)) {
+ g_debug ("GdmSession: ignoring %s session command request for file '%s'",
+ type, file);
+ goto out;
+ }
+
+ g_debug ("GdmSession: getting session command for file '%s'", file);
+ key_file = load_key_file_for_file (self, file, type, NULL);
+ if (key_file == NULL) {
+ goto out;
+ }
+
+ error = NULL;
+ res = g_key_file_get_boolean (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_HIDDEN,
+ &error);
+ if (error == NULL && res) {
+ g_debug ("GdmSession: Session %s is marked as hidden", file);
+ goto out;
+ }
+
+ exec = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
+ NULL);
+ if (exec != NULL) {
+ res = is_prog_in_path (exec);
+ g_free (exec);
+ exec = NULL;
+
+ if (! res) {
+ g_debug ("GdmSession: Command not found: %s",
+ G_KEY_FILE_DESKTOP_KEY_TRY_EXEC);
+ goto out;
+ }
+ }
+
+ error = NULL;
+ exec = g_key_file_get_string (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ &error);
+ if (error != NULL) {
+ g_debug ("GdmSession: %s key not found: %s",
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (command != NULL) {
+ *command = g_strdup (exec);
+ }
+ ret = TRUE;
+
+out:
+ g_free (exec);
+
+ return ret;
+}
+
+static gboolean
+get_session_command_for_name (GdmSession *self,
+ const char *name,
+ const char *type,
+ char **command)
+{
+ gboolean res;
+ char *filename;
+
+ filename = g_strdup_printf ("%s.desktop", name);
+ res = get_session_command_for_file (self, filename, type, command);
+ g_free (filename);
+
+ return res;
+}
+
+static const char *
+get_default_language_name (GdmSession *self)
+{
+ const char *default_language;
+
+ if (self->saved_language != NULL) {
+ return self->saved_language;
+ }
+
+ default_language = g_hash_table_lookup (self->environment,
+ "LANG");
+
+ if (default_language != NULL) {
+ return default_language;
+ }
+
+ return setlocale (LC_MESSAGES, NULL);
+}
+
+static const char *
+get_fallback_session_name (GdmSession *self)
+{
+ char **search_dirs;
+ int i;
+ char *name;
+ GSequence *sessions;
+ GSequenceIter *session;
+
+ if (self->fallback_session_name != NULL) {
+ /* verify that the cached version still exists */
+ if (get_session_command_for_name (self, self->fallback_session_name, NULL, NULL)) {
+ goto out;
+ }
+ }
+
+ name = g_strdup ("gnome");
+ if (get_session_command_for_name (self, name, NULL, NULL)) {
+ g_free (self->fallback_session_name);
+ self->fallback_session_name = name;
+ goto out;
+ }
+ g_free (name);
+
+ sessions = g_sequence_new (g_free);
+
+ search_dirs = get_system_session_dirs (self, NULL);
+ for (i = 0; search_dirs[i] != NULL; i++) {
+ GDir *dir;
+ const char *base_name;
+
+ dir = g_dir_open (search_dirs[i], 0, NULL);
+
+ if (dir == NULL) {
+ continue;
+ }
+
+ do {
+ base_name = g_dir_read_name (dir);
+
+ if (base_name == NULL) {
+ break;
+ }
+
+ if (!g_str_has_suffix (base_name, ".desktop")) {
+ continue;
+ }
+
+ if (get_session_command_for_file (self, base_name, NULL, NULL)) {
+ name = g_strndup (base_name, strlen (base_name) - strlen (".desktop"));
+ g_sequence_insert_sorted (sessions, name, (GCompareDataFunc) g_strcmp0, NULL);
+ }
+ } while (base_name != NULL);
+
+ g_dir_close (dir);
+ }
+ g_strfreev (search_dirs);
+
+ name = NULL;
+ session = g_sequence_get_begin_iter (sessions);
+
+ if (g_sequence_iter_is_end (session))
+ g_error ("GdmSession: no session desktop files installed, aborting...");
+
+ do {
+ name = g_sequence_get (session);
+ if (name) {
+ break;
+ }
+ session = g_sequence_iter_next (session);
+ } while (!g_sequence_iter_is_end (session));
+
+ g_free (self->fallback_session_name);
+ self->fallback_session_name = g_strdup (name);
+
+ g_sequence_free (sessions);
+
+ out:
+ return self->fallback_session_name;
+}
+
+static const char *
+get_default_session_name (GdmSession *self)
+{
+ if (self->saved_session != NULL) {
+ return self->saved_session;
+ }
+
+ return get_fallback_session_name (self);
+}
+
+static void
+gdm_session_defaults_changed (GdmSession *self)
+{
+
+ update_session_type (self);
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_default_language_name_changed (self->greeter_interface,
+ get_default_language_name (self));
+ gdm_dbus_greeter_emit_default_session_name_changed (self->greeter_interface,
+ get_default_session_name (self));
+ }
+}
+
+void
+gdm_session_select_user (GdmSession *self,
+ const char *text)
+{
+
+ g_debug ("GdmSession: selecting user '%s' for session '%s' (%p)",
+ text,
+ gdm_session_get_session_id (self),
+ self);
+
+ g_free (self->selected_user);
+ self->selected_user = g_strdup (text);
+
+ g_free (self->saved_session);
+ self->saved_session = NULL;
+
+ g_free (self->saved_session_type);
+ self->saved_session_type = NULL;
+
+ g_free (self->saved_language);
+ self->saved_language = NULL;
+}
+
+static void
+cancel_pending_query (GdmSessionConversation *conversation)
+{
+ if (conversation->pending_invocation == NULL) {
+ return;
+ }
+
+ g_debug ("GdmSession: Cancelling pending query");
+
+ g_dbus_method_invocation_return_dbus_error (conversation->pending_invocation,
+ GDM_SESSION_DBUS_ERROR_CANCEL,
+ "Operation cancelled");
+ conversation->pending_invocation = NULL;
+}
+
+static void
+answer_pending_query (GdmSessionConversation *conversation,
+ const char *answer)
+{
+ g_dbus_method_invocation_return_value (conversation->pending_invocation,
+ g_variant_new ("(s)", answer));
+ conversation->pending_invocation = NULL;
+}
+
+static void
+set_pending_query (GdmSessionConversation *conversation,
+ GDBusMethodInvocation *message)
+{
+ g_assert (conversation->pending_invocation == NULL);
+
+ conversation->pending_invocation = g_object_ref (message);
+}
+
+static gboolean
+gdm_session_handle_choice_list_query (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *prompt_message,
+ GVariant *query,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+ GdmDBusUserVerifierChoiceList *choice_list_interface = NULL;
+
+ g_debug ("GdmSession: choice query for service '%s'", service_name);
+
+ if (self->user_verifier_extensions != NULL)
+ choice_list_interface = g_hash_table_lookup (self->user_verifier_extensions,
+ gdm_dbus_user_verifier_choice_list_interface_info ()->name);
+
+ if (choice_list_interface == NULL) {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_NOT_SUPPORTED,
+ "ChoiceList interface not supported by client");
+ return TRUE;
+ }
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ set_pending_query (conversation, invocation);
+
+ g_debug ("GdmSession: emitting choice query '%s'", prompt_message);
+ gdm_dbus_user_verifier_choice_list_emit_choice_query (choice_list_interface,
+ service_name,
+ prompt_message,
+ query);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_info_query (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *query,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (self->user_verifier_interface != NULL, FALSE);
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ set_pending_query (conversation, invocation);
+
+ gdm_dbus_user_verifier_emit_info_query (self->user_verifier_interface,
+ service_name,
+ query);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_secret_info_query (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *query,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (self->user_verifier_interface != NULL, FALSE);
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ set_pending_query (conversation, invocation);
+
+ gdm_dbus_user_verifier_emit_secret_info_query (self->user_verifier_interface,
+ service_name,
+ query);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_info (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *info,
+ GdmSession *self)
+{
+ gdm_dbus_worker_manager_complete_info (worker_manager_interface,
+ invocation);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_info (self->user_verifier_interface,
+ service_name,
+ info);
+ }
+
+ return TRUE;
+}
+
+static void
+worker_on_cancel_pending_query (GdmDBusWorker *worker,
+ GdmSessionConversation *conversation)
+{
+ cancel_pending_query (conversation);
+}
+
+static gboolean
+gdm_session_handle_problem (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *problem,
+ GdmSession *self)
+{
+ gdm_dbus_worker_manager_complete_problem (worker_manager_interface,
+ invocation);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_problem (self->user_verifier_interface,
+ service_name,
+ problem);
+ }
+ return TRUE;
+}
+
+static void
+on_opened (GdmDBusWorker *worker,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+ char *session_id;
+
+ worked = gdm_dbus_worker_call_open_finish (worker,
+ &session_id,
+ res,
+ &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ g_clear_pointer (&conversation->session_id,
+ (GDestroyNotify) g_free);
+
+ conversation->session_id = g_strdup (session_id);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_verification_complete (self->user_verifier_interface,
+ service_name);
+ g_signal_emit (self, signals[VERIFICATION_COMPLETE], 0, service_name);
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_session_opened (self->greeter_interface,
+ service_name);
+ }
+
+ g_debug ("GdmSession: Emitting 'session-opened' signal");
+ g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
+ } else {
+ report_and_stop_conversation (self, service_name, error);
+
+ g_debug ("GdmSession: Emitting 'session-start-failed' signal");
+ g_signal_emit (self, signals[SESSION_OPENED_FAILED], 0, service_name, error->message);
+ }
+}
+
+static void
+worker_on_username_changed (GdmDBusWorker *worker,
+ const char *username,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_debug ("GdmSession: changing username from '%s' to '%s'",
+ self->selected_user != NULL ? self->selected_user : "<unset>",
+ (strlen (username)) ? username : "<unset>");
+
+ gdm_session_select_user (self, (strlen (username) > 0) ? g_strdup (username) : NULL);
+ gdm_session_defaults_changed (self);
+}
+
+static void
+worker_on_session_exited (GdmDBusWorker *worker,
+ const char *service_name,
+ int status,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ self->session_conversation = NULL;
+
+ if (WIFEXITED (status)) {
+ g_debug ("GdmSession: Emitting 'session-exited' signal with exit code '%d'",
+ WEXITSTATUS (status));
+ g_signal_emit (self, signals[SESSION_EXITED], 0, WEXITSTATUS (status));
+ } else if (WIFSIGNALED (status)) {
+ g_debug ("GdmSession: Emitting 'session-died' signal with signal number '%d'",
+ WTERMSIG (status));
+ g_signal_emit (self, signals[SESSION_DIED], 0, WTERMSIG (status));
+ }
+}
+
+static void
+on_reauthentication_started_cb (GdmDBusWorker *worker,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+
+ GError *error = NULL;
+ gboolean worked;
+ char *address;
+
+ worked = gdm_dbus_worker_call_start_reauthentication_finish (worker,
+ &address,
+ res,
+ &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+
+ if (worked) {
+ GPid pid_of_caller = conversation->reauth_pid_of_caller;
+ g_debug ("GdmSession: Emitting 'reauthentication-started' signal for caller pid '%d'", pid_of_caller);
+ g_signal_emit (self, signals[REAUTHENTICATION_STARTED], 0, pid_of_caller, address);
+ }
+
+ conversation->reauth_pid_of_caller = 0;
+}
+
+static void
+worker_on_reauthenticated (GdmDBusWorker *worker,
+ const char *service_name,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+ g_debug ("GdmSession: Emitting 'reauthenticated' signal ");
+ g_signal_emit (self, signals[REAUTHENTICATED], 0, service_name);
+}
+
+static void
+worker_on_saved_language_name_read (GdmDBusWorker *worker,
+ const char *language_name,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ if (strlen (language_name) > 0) {
+ g_free (self->saved_language);
+ self->saved_language = g_strdup (language_name);
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_default_language_name_changed (self->greeter_interface,
+ language_name);
+ }
+ }
+}
+
+static void
+worker_on_saved_session_name_read (GdmDBusWorker *worker,
+ const char *session_name,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ if (! get_session_command_for_name (self, session_name, self->saved_session_type, NULL)) {
+ /* ignore sessions that don't exist */
+ g_debug ("GdmSession: not using invalid .dmrc session: %s", session_name);
+ g_free (self->saved_session);
+ self->saved_session = NULL;
+ update_session_type (self);
+ } else {
+ if (strcmp (session_name,
+ get_default_session_name (self)) != 0) {
+ g_free (self->saved_session);
+ self->saved_session = g_strdup (session_name);
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_emit_default_session_name_changed (self->greeter_interface,
+ session_name);
+ }
+ }
+ if (self->saved_session_type != NULL)
+ set_session_type (self, self->saved_session_type);
+ else
+ update_session_type (self);
+ }
+
+}
+
+static void
+worker_on_saved_session_type_read (GdmDBusWorker *worker,
+ const char *session_type,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_free (self->saved_session_type);
+ self->saved_session_type = g_strdup (session_type);
+}
+
+static GdmSessionConversation *
+find_conversation_by_pid (GdmSession *self,
+ GPid pid)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, self->conversations);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmSessionConversation *conversation;
+
+ conversation = (GdmSessionConversation *) value;
+
+ if (conversation->worker_pid == pid) {
+ return conversation;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+allow_worker_function (GDBusAuthObserver *observer,
+ GIOStream *stream,
+ GCredentials *credentials,
+ GdmSession *self)
+{
+ uid_t connecting_user;
+
+ connecting_user = g_credentials_get_unix_user (credentials, NULL);
+
+ if (connecting_user == 0) {
+ return TRUE;
+ }
+
+ if (connecting_user == self->allowed_user) {
+ return TRUE;
+ }
+
+ g_debug ("GdmSession: User not allowed");
+
+ return FALSE;
+}
+
+static void
+on_worker_connection_closed (GDBusConnection *connection,
+ gboolean remote_peer_vanished,
+ GError *error,
+ GdmSession *self)
+{
+ self->pending_worker_connections =
+ g_list_remove (self->pending_worker_connections,
+ connection);
+ g_object_unref (connection);
+}
+
+static gboolean
+register_worker (GdmDBusWorkerManager *worker_manager_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+ GDBusConnection *connection;
+ GList *connection_node;
+ GCredentials *credentials;
+ GPid pid;
+
+ g_debug ("GdmSession: Authenticating new connection");
+
+ connection = g_dbus_method_invocation_get_connection (invocation);
+ connection_node = g_list_find (self->pending_worker_connections, connection);
+
+ if (connection_node == NULL) {
+ g_debug ("GdmSession: Ignoring connection that we aren't tracking");
+ return FALSE;
+ }
+
+ /* connection was ref'd when it was added to list, we're taking that
+ * reference over and removing it from the list
+ */
+ self->pending_worker_connections =
+ g_list_delete_link (self->pending_worker_connections,
+ connection_node);
+
+ g_signal_handlers_disconnect_by_func (connection,
+ G_CALLBACK (on_worker_connection_closed),
+ self);
+
+ credentials = g_dbus_connection_get_peer_credentials (connection);
+ pid = g_credentials_get_unix_pid (credentials, NULL);
+
+ conversation = find_conversation_by_pid (self, (GPid) pid);
+
+ if (conversation == NULL) {
+ g_warning ("GdmSession: New worker connection is from unknown source");
+
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Connection is not from a known conversation");
+ g_dbus_connection_close_sync (connection, NULL, NULL);
+ return TRUE;
+ }
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ conversation->worker_proxy = gdm_dbus_worker_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ GDM_WORKER_DBUS_PATH,
+ NULL, NULL);
+ /* drop the reference we stole from the pending connections list
+ * since the proxy owns the connection now */
+ g_object_unref (connection);
+
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (conversation->worker_proxy), G_MAXINT);
+
+ conversation->worker_cancellable = g_cancellable_new ();
+
+ g_signal_connect (conversation->worker_proxy,
+ "username-changed",
+ G_CALLBACK (worker_on_username_changed), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "session-exited",
+ G_CALLBACK (worker_on_session_exited), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "reauthenticated",
+ G_CALLBACK (worker_on_reauthenticated), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "saved-language-name-read",
+ G_CALLBACK (worker_on_saved_language_name_read), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "saved-session-name-read",
+ G_CALLBACK (worker_on_saved_session_name_read), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "saved-session-type-read",
+ G_CALLBACK (worker_on_saved_session_type_read), conversation);
+ g_signal_connect (conversation->worker_proxy,
+ "cancel-pending-query",
+ G_CALLBACK (worker_on_cancel_pending_query), conversation);
+
+ conversation->worker_manager_interface = g_object_ref (worker_manager_interface);
+ g_debug ("GdmSession: worker connection is %p", connection);
+
+ g_debug ("GdmSession: Emitting conversation-started signal");
+ g_signal_emit (self, signals[CONVERSATION_STARTED], 0, conversation->service_name);
+
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_conversation_started (self->user_verifier_interface,
+ conversation->service_name);
+ }
+
+ if (conversation->starting_invocation != NULL) {
+ if (conversation->starting_username != NULL) {
+ gdm_session_setup_for_user (self, conversation->service_name, conversation->starting_username);
+
+ g_clear_pointer (&conversation->starting_username,
+ (GDestroyNotify)
+ g_free);
+ } else {
+ gdm_session_setup (self, conversation->service_name);
+ }
+ }
+
+ g_debug ("GdmSession: Conversation started");
+
+ return TRUE;
+}
+
+static void
+export_worker_manager_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusWorkerManager *worker_manager_interface;
+
+ worker_manager_interface = GDM_DBUS_WORKER_MANAGER (gdm_dbus_worker_manager_skeleton_new ());
+ g_signal_connect (worker_manager_interface,
+ "handle-hello",
+ G_CALLBACK (register_worker),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-info-query",
+ G_CALLBACK (gdm_session_handle_info_query),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-secret-info-query",
+ G_CALLBACK (gdm_session_handle_secret_info_query),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-info",
+ G_CALLBACK (gdm_session_handle_info),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-problem",
+ G_CALLBACK (gdm_session_handle_problem),
+ self);
+ g_signal_connect (worker_manager_interface,
+ "handle-choice-list-query",
+ G_CALLBACK (gdm_session_handle_choice_list_query),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+}
+
+static void
+unexport_worker_manager_interface (GdmSession *self,
+ GdmDBusWorkerManager *worker_manager_interface)
+{
+
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (worker_manager_interface));
+
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (register_worker),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_info_query),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_secret_info_query),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_info),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_problem),
+ self);
+ g_signal_handlers_disconnect_by_func (worker_manager_interface,
+ G_CALLBACK (gdm_session_handle_choice_list_query),
+ self);
+}
+
+static gboolean
+handle_connection_from_worker (GDBusServer *server,
+ GDBusConnection *connection,
+ GdmSession *self)
+{
+
+ g_debug ("GdmSession: Handling new connection from worker");
+
+ /* add to the list of pending connections. We won't be able to
+ * associate it with a specific worker conversation until we have
+ * authenticated the connection (from the Hello handler).
+ */
+ self->pending_worker_connections =
+ g_list_prepend (self->pending_worker_connections,
+ g_object_ref (connection));
+
+ g_signal_connect_object (connection,
+ "closed",
+ G_CALLBACK (on_worker_connection_closed),
+ self,
+ 0);
+
+ export_worker_manager_interface (self, connection);
+
+ return TRUE;
+}
+
+static GdmSessionConversation *
+begin_verification_conversation (GdmSession *self,
+ GDBusMethodInvocation *invocation,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation = NULL;
+ gboolean conversation_started;
+
+ conversation_started = gdm_session_start_conversation (self, service_name);
+
+ if (conversation_started) {
+ conversation = find_conversation_by_name (self, service_name);
+ }
+
+ if (conversation == NULL) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_SPAWN_FAILED,
+ _("Could not create authentication helper process"));
+ }
+
+ return conversation;
+}
+
+static gboolean
+gdm_session_handle_client_select_choice (GdmDBusUserVerifierChoiceList *choice_list_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *answer,
+ GdmSession *self)
+{
+ g_debug ("GdmSession: user selected choice '%s'", answer);
+ gdm_dbus_user_verifier_choice_list_complete_select_choice (choice_list_interface, invocation);
+ gdm_session_answer_query (self, service_name, answer);
+ return TRUE;
+}
+
+static void
+export_user_verifier_choice_list_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusUserVerifierChoiceList *interface;
+
+ interface = GDM_DBUS_USER_VERIFIER_CHOICE_LIST (gdm_dbus_user_verifier_choice_list_skeleton_new ());
+
+ g_signal_connect (interface,
+ "handle-select-choice",
+ G_CALLBACK (gdm_session_handle_client_select_choice),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ g_hash_table_insert (self->user_verifier_extensions,
+ gdm_dbus_user_verifier_choice_list_interface_info ()->name,
+ interface);
+}
+
+static gboolean
+gdm_session_handle_client_enable_extensions (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char * const * extensions,
+ GDBusConnection *connection)
+{
+ GdmSession *self = g_object_get_data (G_OBJECT (connection), "gdm-session");
+ size_t i;
+
+ g_hash_table_remove_all (self->user_verifier_extensions);
+
+ for (i = 0; extensions[i] != NULL; i++) {
+ if (g_hash_table_lookup (self->user_verifier_extensions, extensions[i]) != NULL)
+ continue;
+
+ if (strcmp (extensions[i],
+ gdm_dbus_user_verifier_choice_list_interface_info ()->name) == 0)
+ export_user_verifier_choice_list_interface (self, connection);
+
+ }
+
+ gdm_dbus_user_verifier_complete_enable_extensions (user_verifier_interface, invocation);
+
+ return TRUE;
+}
+static gboolean
+gdm_session_handle_client_begin_verification (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ conversation = begin_verification_conversation (self, invocation, service_name);
+
+ if (conversation != NULL) {
+ conversation->starting_invocation = g_object_ref (invocation);
+ conversation->starting_username = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_begin_verification_for_user (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *username,
+ GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ conversation = begin_verification_conversation (self, invocation, service_name);
+
+ if (conversation != NULL) {
+ conversation->starting_invocation = g_object_ref (invocation);
+ conversation->starting_username = g_strdup (username);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_answer_query (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ const char *answer,
+ GdmSession *self)
+{
+ gdm_dbus_user_verifier_complete_answer_query (user_verifier_interface,
+ invocation);
+ gdm_session_answer_query (self, service_name, answer);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_cancel (GdmDBusUserVerifier *user_verifier_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ gdm_dbus_user_verifier_complete_cancel (user_verifier_interface,
+ invocation);
+ gdm_session_cancel (self);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_select_session (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *session,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *username;
+
+ username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to select session %s since it's already running (for user %s)",
+ session,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_select_session (greeter_interface,
+ invocation);
+ }
+ gdm_session_select_session (self, session);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_select_user (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *username,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *session_username;
+
+ session_username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to select user %s, since session (%p) already running (for user %s)",
+ username,
+ self,
+ session_username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ session_username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_select_user (greeter_interface,
+ invocation);
+ }
+ g_debug ("GdmSession: client selected user '%s' on session (%p)", username, self);
+ gdm_session_select_user (self, username);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_start_session_when_ready (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *service_name,
+ gboolean client_is_ready,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *username;
+
+ username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to start session (%p), since it's already running (for user %s)",
+ self,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_start_session_when_ready (greeter_interface,
+ invocation);
+ }
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_READY_FOR_SESSION_TO_START],
+ 0,
+ service_name,
+ client_is_ready);
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_get_timed_login_details (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ if (gdm_session_is_running (self)) {
+ const char *username;
+
+ username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing to give timed login details, session (%p) already running (for user %s)",
+ self,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already running for user %s",
+ username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_get_timed_login_details (greeter_interface,
+ invocation,
+ self->timed_login_username != NULL,
+ self->timed_login_username != NULL? self->timed_login_username : "",
+ self->timed_login_delay);
+ if (self->timed_login_username != NULL) {
+ gdm_dbus_greeter_emit_timed_login_requested (self->greeter_interface,
+ self->timed_login_username,
+ self->timed_login_delay);
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+gdm_session_handle_client_begin_auto_login (GdmDBusGreeter *greeter_interface,
+ GDBusMethodInvocation *invocation,
+ const char *username,
+ GdmSession *self)
+{
+ const char *session_username;
+
+ if (gdm_session_is_running (self)) {
+ session_username = gdm_session_get_username (self);
+ g_debug ("GdmSession: refusing auto login operation, session (%p) already running for user %s (%s requested)",
+ self,
+ session_username,
+ username);
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Session already owned by user %s",
+ session_username);
+ return TRUE;
+ }
+
+ if (self->greeter_interface != NULL) {
+ gdm_dbus_greeter_complete_begin_auto_login (greeter_interface,
+ invocation);
+ }
+
+ g_debug ("GdmSession: client requesting automatic login for user '%s' on session '%s' (%p)",
+ username,
+ gdm_session_get_session_id (self),
+ self);
+
+ gdm_session_setup_for_user (self, "gdm-autologin", username);
+
+ return TRUE;
+}
+
+static void
+export_user_verifier_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusUserVerifier *user_verifier_interface;
+ user_verifier_interface = GDM_DBUS_USER_VERIFIER (gdm_dbus_user_verifier_skeleton_new ());
+
+ g_object_set_data (G_OBJECT (connection), "gdm-session", self);
+
+ g_signal_connect (user_verifier_interface,
+ "handle-enable-extensions",
+ G_CALLBACK (gdm_session_handle_client_enable_extensions),
+ connection);
+ g_signal_connect (user_verifier_interface,
+ "handle-begin-verification",
+ G_CALLBACK (gdm_session_handle_client_begin_verification),
+ self);
+ g_signal_connect (user_verifier_interface,
+ "handle-begin-verification-for-user",
+ G_CALLBACK (gdm_session_handle_client_begin_verification_for_user),
+ self);
+ g_signal_connect (user_verifier_interface,
+ "handle-answer-query",
+ G_CALLBACK (gdm_session_handle_client_answer_query),
+ self);
+ g_signal_connect (user_verifier_interface,
+ "handle-cancel",
+ G_CALLBACK (gdm_session_handle_client_cancel),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (user_verifier_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->user_verifier_interface = user_verifier_interface;
+}
+
+static void
+export_greeter_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusGreeter *greeter_interface;
+
+ greeter_interface = GDM_DBUS_GREETER (gdm_dbus_greeter_skeleton_new ());
+
+ g_signal_connect (greeter_interface,
+ "handle-begin-auto-login",
+ G_CALLBACK (gdm_session_handle_client_begin_auto_login),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-select-session",
+ G_CALLBACK (gdm_session_handle_client_select_session),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-select-user",
+ G_CALLBACK (gdm_session_handle_client_select_user),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-start-session-when-ready",
+ G_CALLBACK (gdm_session_handle_client_start_session_when_ready),
+ self);
+ g_signal_connect (greeter_interface,
+ "handle-get-timed-login-details",
+ G_CALLBACK (gdm_session_handle_get_timed_login_details),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (greeter_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->greeter_interface = greeter_interface;
+
+}
+
+static gboolean
+gdm_session_handle_client_disconnect (GdmDBusChooser *chooser_interface,
+ GDBusMethodInvocation *invocation,
+ GdmSession *self)
+{
+ gdm_dbus_chooser_complete_disconnect (chooser_interface,
+ invocation);
+ g_signal_emit (self, signals[DISCONNECTED], 0);
+ return TRUE;
+}
+
+static void
+export_remote_greeter_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusRemoteGreeter *remote_greeter_interface;
+
+ remote_greeter_interface = GDM_DBUS_REMOTE_GREETER (gdm_dbus_remote_greeter_skeleton_new ());
+
+ g_signal_connect (remote_greeter_interface,
+ "handle-disconnect",
+ G_CALLBACK (gdm_session_handle_client_disconnect),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (remote_greeter_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->remote_greeter_interface = remote_greeter_interface;
+
+}
+
+static gboolean
+gdm_session_handle_client_select_hostname (GdmDBusChooser *chooser_interface,
+ GDBusMethodInvocation *invocation,
+ const char *hostname,
+ GdmSession *self)
+{
+
+ gdm_dbus_chooser_complete_select_hostname (chooser_interface,
+ invocation);
+ g_signal_emit (self, signals[HOSTNAME_SELECTED], 0, hostname);
+ return TRUE;
+}
+
+static void
+export_chooser_interface (GdmSession *self,
+ GDBusConnection *connection)
+{
+ GdmDBusChooser *chooser_interface;
+
+ chooser_interface = GDM_DBUS_CHOOSER (gdm_dbus_chooser_skeleton_new ());
+
+ g_signal_connect (chooser_interface,
+ "handle-select-hostname",
+ G_CALLBACK (gdm_session_handle_client_select_hostname),
+ self);
+
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (chooser_interface),
+ connection,
+ GDM_SESSION_DBUS_OBJECT_PATH,
+ NULL);
+
+ self->chooser_interface = chooser_interface;
+}
+
+static void
+on_outside_connection_closed (GDBusConnection *connection,
+ gboolean remote_peer_vanished,
+ GError *error,
+ GdmSession *self)
+{
+ GCredentials *credentials;
+ GPid pid_of_client;
+
+ g_debug ("GdmSession: external connection closed");
+
+ self->outside_connections = g_list_remove (self->outside_connections,
+ connection);
+
+ credentials = g_dbus_connection_get_peer_credentials (connection);
+ pid_of_client = g_credentials_get_unix_pid (credentials, NULL);
+
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_DISCONNECTED],
+ 0,
+ credentials,
+ (guint)
+ pid_of_client);
+
+ g_object_unref (connection);
+}
+
+static gboolean
+handle_connection_from_outside (GDBusServer *server,
+ GDBusConnection *connection,
+ GdmSession *self)
+{
+ GCredentials *credentials;
+ GPid pid_of_client;
+
+ g_debug ("GdmSession: Handling new connection from outside");
+
+ self->outside_connections = g_list_prepend (self->outside_connections,
+ g_object_ref (connection));
+
+ g_signal_connect_object (connection,
+ "closed",
+ G_CALLBACK (on_outside_connection_closed),
+ self,
+ 0);
+
+ export_user_verifier_interface (self, connection);
+
+ switch (self->verification_mode) {
+ case GDM_SESSION_VERIFICATION_MODE_LOGIN:
+ export_greeter_interface (self, connection);
+ break;
+
+ case GDM_SESSION_VERIFICATION_MODE_CHOOSER:
+ export_chooser_interface (self, connection);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!self->display_is_local) {
+ export_remote_greeter_interface (self, connection);
+ }
+
+ credentials = g_dbus_connection_get_peer_credentials (connection);
+ pid_of_client = g_credentials_get_unix_pid (credentials, NULL);
+
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_CONNECTED],
+ 0,
+ credentials,
+ (guint)
+ pid_of_client);
+
+ return TRUE;
+}
+
+static void
+setup_worker_server (GdmSession *self)
+{
+ GDBusAuthObserver *observer;
+ GDBusServer *server;
+ GError *error = NULL;
+
+ g_debug ("GdmSession: Creating D-Bus server for worker for session");
+
+ observer = g_dbus_auth_observer_new ();
+ g_signal_connect_object (observer,
+ "authorize-authenticated-peer",
+ G_CALLBACK (allow_worker_function),
+ self,
+ 0);
+
+ server = gdm_dbus_setup_private_server (observer, &error);
+ g_object_unref (observer);
+
+ if (server == NULL) {
+ g_warning ("Cannot create worker D-Bus server for the session: %s",
+ error->message);
+ return;
+ }
+
+ g_signal_connect_object (server,
+ "new-connection",
+ G_CALLBACK (handle_connection_from_worker),
+ self,
+ 0);
+ self->worker_server = server;
+
+ g_dbus_server_start (server);
+
+ g_debug ("GdmSession: D-Bus server for workers listening on %s",
+ g_dbus_server_get_client_address (self->worker_server));
+}
+
+static gboolean
+allow_user_function (GDBusAuthObserver *observer,
+ GIOStream *stream,
+ GCredentials *credentials,
+ GdmSession *self)
+{
+ uid_t client_uid;
+ GPid pid_of_client;
+
+ client_uid = g_credentials_get_unix_user (credentials, NULL);
+ if (client_uid == self->allowed_user) {
+ return TRUE;
+ }
+
+ g_debug ("GdmSession: User not allowed");
+
+ pid_of_client = g_credentials_get_unix_pid (credentials, NULL);
+ g_signal_emit (G_OBJECT (self),
+ signals [CLIENT_REJECTED],
+ 0,
+ credentials,
+ (guint)
+ pid_of_client);
+
+
+ return FALSE;
+}
+
+static void
+setup_outside_server (GdmSession *self)
+{
+ GDBusAuthObserver *observer;
+ GDBusServer *server;
+ GError *error = NULL;
+
+ g_debug ("GdmSession: Creating D-Bus server for greeters and such for session %s (%p)",
+ gdm_session_get_session_id (self),
+ self);
+
+ observer = g_dbus_auth_observer_new ();
+ g_signal_connect_object (observer,
+ "authorize-authenticated-peer",
+ G_CALLBACK (allow_user_function),
+ self,
+ 0);
+
+ server = gdm_dbus_setup_private_server (observer, &error);
+ g_object_unref (observer);
+
+ if (server == NULL) {
+ g_warning ("Cannot create greeter D-Bus server for the session: %s",
+ error->message);
+ return;
+ }
+
+ g_signal_connect_object (server,
+ "new-connection",
+ G_CALLBACK (handle_connection_from_outside),
+ self,
+ 0);
+ self->outside_server = server;
+
+ g_dbus_server_start (server);
+
+ g_debug ("GdmSession: D-Bus server for greeters listening on %s",
+ g_dbus_server_get_client_address (self->outside_server));
+}
+
+static void
+free_conversation (GdmSessionConversation *conversation)
+{
+ close_conversation (conversation);
+
+ if (conversation->job != NULL) {
+ g_warning ("Freeing conversation '%s' with active job", conversation->service_name);
+ }
+
+ g_free (conversation->service_name);
+ g_free (conversation->starting_username);
+ g_free (conversation->session_id);
+ g_clear_object (&conversation->worker_manager_interface);
+
+ g_cancellable_cancel (conversation->worker_cancellable);
+ g_clear_object (&conversation->worker_cancellable);
+
+ if (conversation->worker_proxy != NULL) {
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_username_changed),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_session_exited),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_reauthenticated),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_saved_language_name_read),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_saved_session_name_read),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_saved_session_type_read),
+ conversation);
+ g_signal_handlers_disconnect_by_func (conversation->worker_proxy,
+ G_CALLBACK (worker_on_cancel_pending_query),
+ conversation);
+ g_clear_object (&conversation->worker_proxy);
+ }
+ g_clear_object (&conversation->session);
+ g_free (conversation);
+}
+
+static void
+load_lang_config_file (GdmSession *self)
+{
+ static const char *config_file = LANG_CONFIG_FILE;
+ gchar *contents = NULL;
+ gchar *p;
+ gchar *key;
+ gchar *value;
+ gsize length;
+ GError *error;
+ GString *line;
+ GRegex *re;
+
+ if (!g_file_test (config_file, G_FILE_TEST_EXISTS)) {
+ g_debug ("Cannot access '%s'", config_file);
+ return;
+ }
+
+ error = NULL;
+ if (!g_file_get_contents (config_file, &contents, &length, &error)) {
+ g_debug ("Failed to parse '%s': %s",
+ LANG_CONFIG_FILE,
+ (error && error->message) ? error->message : "(null)");
+ g_error_free (error);
+ return;
+ }
+
+ if (!g_utf8_validate (contents, length, NULL)) {
+ g_warning ("Invalid UTF-8 in '%s'", config_file);
+ g_free (contents);
+ return;
+ }
+
+ re = g_regex_new ("(?P<key>(LANG|LANGUAGE|LC_CTYPE|LC_NUMERIC|LC_TIME|LC_COLLATE|LC_MONETARY|LC_MESSAGES|LC_PAPER|LC_NAME|LC_ADDRESS|LC_TELEPHONE|LC_MEASUREMENT|LC_IDENTIFICATION|LC_ALL))=(\")?(?P<value>[^\"]*)?(\")?", 0, 0, &error);
+ if (re == NULL) {
+ g_warning ("Failed to regex: %s",
+ (error && error->message) ? error->message : "(null)");
+ g_error_free (error);
+ g_free (contents);
+ return;
+ }
+
+ line = g_string_new ("");
+ for (p = contents; p && *p; p = g_utf8_find_next_char (p, NULL)) {
+ gunichar ch;
+ GMatchInfo *match_info = NULL;
+
+ ch = g_utf8_get_char (p);
+ if ((ch != '\n') && (ch != '\0')) {
+ g_string_append_unichar (line, ch);
+ continue;
+ }
+
+ if (line->str && g_utf8_get_char (line->str) == '#') {
+ goto next_line;
+ }
+
+ if (!g_regex_match (re, line->str, 0, &match_info)) {
+ goto next_line;
+ }
+
+ if (!g_match_info_matches (match_info)) {
+ goto next_line;
+ }
+
+ key = g_match_info_fetch_named (match_info, "key");
+ value = g_match_info_fetch_named (match_info, "value");
+
+ if (key && *key && value && *value) {
+ g_setenv (key, value, TRUE);
+ }
+
+ g_free (key);
+ g_free (value);
+next_line:
+ g_match_info_free (match_info);
+ g_string_set_size (line, 0);
+ }
+
+ g_string_free (line, TRUE);
+ g_regex_unref (re);
+ g_free (contents);
+}
+
+static void
+unexport_and_free_user_verifier_extension (GDBusInterfaceSkeleton *interface)
+{
+ g_dbus_interface_skeleton_unexport (interface);
+
+ g_object_run_dispose (G_OBJECT (interface));
+ g_object_unref (interface);
+}
+
+static void
+gdm_session_init (GdmSession *self)
+{
+ self->conversations = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify)
+ free_conversation);
+ self->environment = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+ self->user_verifier_extensions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)
+ unexport_and_free_user_verifier_extension);
+
+ load_lang_config_file (self);
+ setup_worker_server (self);
+ setup_outside_server (self);
+}
+
+static void
+worker_started (GdmSessionWorkerJob *job,
+ GdmSessionConversation *conversation)
+{
+ g_debug ("GdmSession: Worker job started");
+
+}
+
+static void
+worker_exited (GdmSessionWorkerJob *job,
+ int code,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_debug ("GdmSession: Worker job exited: %d", code);
+
+ g_hash_table_steal (self->conversations, conversation->service_name);
+
+ g_object_ref (conversation->job);
+ if (self->session_conversation == conversation) {
+ g_signal_emit (self, signals[SESSION_EXITED], 0, code);
+ self->session_conversation = NULL;
+ }
+
+ g_debug ("GdmSession: Emitting conversation-stopped signal");
+ g_signal_emit (self, signals[CONVERSATION_STOPPED], 0, conversation->service_name);
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_conversation_stopped (self->user_verifier_interface,
+ conversation->service_name);
+ }
+ g_object_unref (conversation->job);
+
+ if (conversation->is_stopping) {
+ g_object_unref (conversation->job);
+ conversation->job = NULL;
+ }
+
+ free_conversation (conversation);
+}
+
+static void
+worker_died (GdmSessionWorkerJob *job,
+ int signum,
+ GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ g_debug ("GdmSession: Worker job died: %d", signum);
+
+ g_hash_table_steal (self->conversations, conversation->service_name);
+
+ g_object_ref (conversation->job);
+ if (self->session_conversation == conversation) {
+ g_signal_emit (self, signals[SESSION_DIED], 0, signum);
+ self->session_conversation = NULL;
+ }
+
+ g_debug ("GdmSession: Emitting conversation-stopped signal");
+ g_signal_emit (self, signals[CONVERSATION_STOPPED], 0, conversation->service_name);
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_conversation_stopped (self->user_verifier_interface,
+ conversation->service_name);
+ }
+ g_object_unref (conversation->job);
+
+ if (conversation->is_stopping) {
+ g_object_unref (conversation->job);
+ conversation->job = NULL;
+ }
+
+ free_conversation (conversation);
+}
+
+static GdmSessionConversation *
+start_conversation (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+ char *job_name;
+
+ conversation = g_new0 (GdmSessionConversation, 1);
+ conversation->session = g_object_ref (self);
+ conversation->service_name = g_strdup (service_name);
+ conversation->worker_pid = -1;
+ conversation->job = gdm_session_worker_job_new ();
+ gdm_session_worker_job_set_server_address (conversation->job,
+ g_dbus_server_get_client_address (self->worker_server));
+ gdm_session_worker_job_set_for_reauth (conversation->job,
+ self->verification_mode == GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE);
+
+ if (self->conversation_environment != NULL) {
+ gdm_session_worker_job_set_environment (conversation->job,
+ (const char * const *)
+ self->conversation_environment);
+
+ }
+ g_signal_connect (conversation->job,
+ "started",
+ G_CALLBACK (worker_started),
+ conversation);
+ g_signal_connect (conversation->job,
+ "exited",
+ G_CALLBACK (worker_exited),
+ conversation);
+ g_signal_connect (conversation->job,
+ "died",
+ G_CALLBACK (worker_died),
+ conversation);
+
+ job_name = g_strdup_printf ("gdm-session-worker [pam/%s]", service_name);
+ if (!gdm_session_worker_job_start (conversation->job, job_name)) {
+ g_object_unref (conversation->job);
+ g_free (conversation->service_name);
+ g_free (conversation);
+ g_free (job_name);
+ return NULL;
+ }
+
+ g_free (job_name);
+
+ conversation->worker_pid = gdm_session_worker_job_get_pid (conversation->job);
+
+ return conversation;
+}
+
+static void
+close_conversation (GdmSessionConversation *conversation)
+{
+ GdmSession *self = conversation->session;
+
+ if (conversation->worker_manager_interface != NULL) {
+ unexport_worker_manager_interface (self, conversation->worker_manager_interface);
+ g_clear_object (&conversation->worker_manager_interface);
+ }
+
+ if (conversation->worker_proxy != NULL) {
+ GDBusConnection *connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (conversation->worker_proxy));
+ g_dbus_connection_close_sync (connection, NULL, NULL);
+ }
+}
+
+static void
+stop_conversation (GdmSessionConversation *conversation)
+{
+ close_conversation (conversation);
+
+ conversation->is_stopping = TRUE;
+ gdm_session_worker_job_stop (conversation->job);
+}
+
+static void
+stop_conversation_now (GdmSessionConversation *conversation)
+{
+ close_conversation (conversation);
+
+ gdm_session_worker_job_stop_now (conversation->job);
+ g_clear_object (&conversation->job);
+}
+
+void
+gdm_session_set_supported_session_types (GdmSession *self,
+ const char * const *supported_session_types)
+{
+ const char * const session_types[] = { "wayland", "x11", NULL };
+ g_strfreev (self->supported_session_types);
+
+ if (supported_session_types == NULL)
+ self->supported_session_types = g_strdupv ((GStrv) session_types);
+ else
+ self->supported_session_types = g_strdupv ((GStrv) supported_session_types);
+}
+
+gboolean
+gdm_session_start_conversation (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ conversation = g_hash_table_lookup (self->conversations,
+ service_name);
+
+ if (conversation != NULL) {
+ if (!conversation->is_stopping) {
+ g_warning ("GdmSession: conversation %s started more than once", service_name);
+ return FALSE;
+ }
+ g_debug ("GdmSession: stopping old conversation %s", service_name);
+ gdm_session_worker_job_stop_now (conversation->job);
+ g_object_unref (conversation->job);
+ conversation->job = NULL;
+ }
+
+ g_debug ("GdmSession: starting conversation %s for session (%p)", service_name, self);
+
+ conversation = start_conversation (self, service_name);
+
+ g_hash_table_insert (self->conversations,
+ g_strdup (service_name), conversation);
+ return TRUE;
+}
+
+void
+gdm_session_stop_conversation (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ g_debug ("GdmSession: stopping conversation %s", service_name);
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation != NULL) {
+ stop_conversation (conversation);
+ }
+}
+
+static void
+on_initialization_complete_cb (GdmDBusWorker *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ GVariant *ret;
+
+ ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (ret != NULL) {
+ if (conversation->starting_invocation) {
+ g_dbus_method_invocation_return_value (conversation->starting_invocation,
+ NULL);
+ }
+
+ g_signal_emit (G_OBJECT (self),
+ signals [SETUP_COMPLETE],
+ 0,
+ service_name);
+
+ gdm_session_authenticate (self, service_name);
+ g_variant_unref (ret);
+
+ } else {
+ g_dbus_method_invocation_return_gerror (conversation->starting_invocation, error);
+ report_and_stop_conversation (self, service_name, error);
+ g_error_free (error);
+ }
+
+ g_clear_object (&conversation->starting_invocation);
+}
+
+static void
+initialize (GdmSession *self,
+ const char *service_name,
+ const char *username,
+ const char *log_file)
+{
+ GVariantBuilder details;
+ const char **extensions;
+ GdmSessionConversation *conversation;
+
+ g_assert (service_name != NULL);
+
+ g_variant_builder_init (&details, G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add_parsed (&details, "{'service', <%s>}", service_name);
+ extensions = (const char **) g_hash_table_get_keys_as_array (self->user_verifier_extensions, NULL);
+
+ g_variant_builder_add_parsed (&details, "{'extensions', <%^as>}", extensions);
+
+ if (username != NULL)
+ g_variant_builder_add_parsed (&details, "{'username', <%s>}", username);
+
+ if (log_file != NULL)
+ g_variant_builder_add_parsed (&details, "{'log-file', <%s>}", log_file);
+
+ if (self->is_program_session)
+ g_variant_builder_add_parsed (&details, "{'is-program-session', <%b>}", self->is_program_session);
+
+ if (self->display_name != NULL)
+ g_variant_builder_add_parsed (&details, "{'x11-display-name', <%s>}", self->display_name);
+
+ if (self->display_hostname != NULL)
+ g_variant_builder_add_parsed (&details, "{'hostname', <%s>}", self->display_hostname);
+
+ if (self->display_is_local)
+ g_variant_builder_add_parsed (&details, "{'display-is-local', <%b>}", self->display_is_local);
+
+ if (self->display_is_initial)
+ g_variant_builder_add_parsed (&details, "{'display-is-initial', <%b>}", self->display_is_initial);
+
+ if (self->display_device != NULL)
+ g_variant_builder_add_parsed (&details, "{'console', <%s>}", self->display_device);
+
+ if (self->display_seat_id != NULL)
+ g_variant_builder_add_parsed (&details, "{'seat-id', <%s>}", self->display_seat_id);
+
+ if (self->display_x11_authority_file != NULL)
+ g_variant_builder_add_parsed (&details, "{'x11-authority-file', <%s>}", self->display_x11_authority_file);
+
+ g_debug ("GdmSession: Beginning initialization");
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_initialize (conversation->worker_proxy,
+ g_variant_builder_end (&details),
+
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_initialization_complete_cb,
+ conversation);
+ }
+
+ g_free (extensions);
+}
+
+void
+gdm_session_setup (GdmSession *self,
+ const char *service_name)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ update_session_type (self);
+
+ initialize (self, service_name, NULL, NULL);
+ gdm_session_defaults_changed (self);
+}
+
+
+void
+gdm_session_setup_for_user (GdmSession *self,
+ const char *service_name,
+ const char *username)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+ g_return_if_fail (username != NULL);
+
+ update_session_type (self);
+
+ g_debug ("GdmSession: Set up service %s for username %s on session (%p)",
+ service_name,
+ username,
+ self);
+ gdm_session_select_user (self, username);
+
+ self->is_program_session = FALSE;
+ initialize (self, service_name, self->selected_user, NULL);
+ gdm_session_defaults_changed (self);
+}
+
+void
+gdm_session_setup_for_program (GdmSession *self,
+ const char *service_name,
+ const char *username,
+ const char *log_file)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ self->is_program_session = TRUE;
+ initialize (self, service_name, username, log_file);
+}
+
+void
+gdm_session_authenticate (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_authenticate (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_authenticate_cb,
+ conversation);
+ }
+}
+
+void
+gdm_session_authorize (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_authorize (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_authorize_cb,
+ conversation);
+ }
+}
+
+void
+gdm_session_accredit (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ gdm_dbus_worker_call_establish_credentials (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_establish_credentials_cb,
+ conversation);
+ }
+
+}
+
+static void
+send_environment_variable (const char *key,
+ const char *value,
+ GdmSessionConversation *conversation)
+{
+ gdm_dbus_worker_call_set_environment_variable (conversation->worker_proxy,
+ key, value,
+ conversation->worker_cancellable,
+ NULL, NULL);
+}
+
+static void
+send_environment (GdmSession *self,
+ GdmSessionConversation *conversation)
+{
+
+ g_hash_table_foreach (self->environment,
+ (GHFunc) send_environment_variable,
+ conversation);
+}
+
+void
+gdm_session_send_environment (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+ if (conversation != NULL) {
+ send_environment (self, conversation);
+ }
+}
+
+static const char *
+get_session_name (GdmSession *self)
+{
+ /* FIXME: test the session names before we use them? */
+
+ if (self->selected_session != NULL) {
+ return self->selected_session;
+ }
+
+ return get_default_session_name (self);
+}
+
+static char *
+get_session_command (GdmSession *self)
+{
+ gboolean res;
+ char *command;
+ const char *session_name;
+
+ session_name = get_session_name (self);
+
+ command = NULL;
+ res = get_session_command_for_name (self, session_name, NULL, &command);
+ if (! res) {
+ g_critical ("Cannot find a command for specified session: %s", session_name);
+ exit (EXIT_FAILURE);
+ }
+
+ return command;
+}
+
+static gchar *
+get_session_desktop_names (GdmSession *self)
+{
+ gchar *filename;
+ GKeyFile *keyfile;
+ gchar *desktop_names = NULL;
+
+ if (self->selected_program != NULL) {
+ return g_strdup ("GNOME-Greeter:GNOME");
+ }
+
+ filename = g_strdup_printf ("%s.desktop", get_session_name (self));
+ g_debug ("GdmSession: getting desktop names for file '%s'", filename);
+ keyfile = load_key_file_for_file (self, filename, NULL, NULL);
+ if (keyfile != NULL) {
+ gchar **names;
+
+ names = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP,
+ "DesktopNames", NULL, NULL);
+ if (names != NULL) {
+ desktop_names = g_strjoinv (":", names);
+
+ g_strfreev (names);
+ }
+ }
+
+ g_key_file_free (keyfile);
+ g_free (filename);
+ return desktop_names;
+}
+
+void
+gdm_session_set_environment_variable (GdmSession *self,
+ const char *key,
+ const char *value)
+{
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (value != NULL);
+
+ g_hash_table_replace (self->environment,
+ g_strdup (key),
+ g_strdup (value));
+}
+
+static void
+set_up_session_language (GdmSession *self)
+{
+ char **environment;
+ int i;
+ const char *value;
+
+ environment = g_listenv ();
+ for (i = 0; environment[i] != NULL; i++) {
+ if (strcmp (environment[i], "LANG") != 0 &&
+ strcmp (environment[i], "LANGUAGE") != 0 &&
+ !g_str_has_prefix (environment[i], "LC_")) {
+ continue;
+ }
+
+ value = g_getenv (environment[i]);
+
+ gdm_session_set_environment_variable (self,
+ environment[i],
+ value);
+ }
+ g_strfreev (environment);
+}
+
+static void
+set_up_session_environment (GdmSession *self)
+{
+ GdmSessionDisplayMode display_mode;
+ gchar *desktop_names;
+ char *locale;
+
+ if (self->selected_program == NULL) {
+ gdm_session_set_environment_variable (self,
+ "GDMSESSION",
+ get_session_name (self));
+ gdm_session_set_environment_variable (self,
+ "DESKTOP_SESSION",
+ get_session_name (self));
+ gdm_session_set_environment_variable (self,
+ "XDG_SESSION_DESKTOP",
+ get_session_name (self));
+ }
+
+ desktop_names = get_session_desktop_names (self);
+ if (desktop_names != NULL) {
+ gdm_session_set_environment_variable (self, "XDG_CURRENT_DESKTOP", desktop_names);
+ }
+
+ set_up_session_language (self);
+
+ locale = g_strdup (get_default_language_name (self));
+
+ if (locale != NULL && locale[0] != '\0') {
+ gdm_session_set_environment_variable (self,
+ "LANG",
+ locale);
+ gdm_session_set_environment_variable (self,
+ "GDM_LANG",
+ locale);
+ }
+
+ g_free (locale);
+
+ display_mode = gdm_session_get_display_mode (self);
+ if (display_mode == GDM_SESSION_DISPLAY_MODE_REUSE_VT) {
+ gdm_session_set_environment_variable (self,
+ "DISPLAY",
+ self->display_name);
+
+ if (self->user_x11_authority_file != NULL) {
+ gdm_session_set_environment_variable (self,
+ "XAUTHORITY",
+ self->user_x11_authority_file);
+ }
+ }
+
+ if (g_getenv ("WINDOWPATH") != NULL) {
+ gdm_session_set_environment_variable (self,
+ "WINDOWPATH",
+ g_getenv ("WINDOWPATH"));
+ }
+
+ g_free (desktop_names);
+}
+
+static void
+send_display_mode (GdmSession *self,
+ GdmSessionConversation *conversation)
+{
+ GdmSessionDisplayMode mode;
+
+ mode = gdm_session_get_display_mode (self);
+ gdm_dbus_worker_call_set_session_display_mode (conversation->worker_proxy,
+ gdm_session_display_mode_to_string (mode),
+ conversation->worker_cancellable,
+ NULL, NULL);
+}
+
+static void
+send_session_type (GdmSession *self,
+ GdmSessionConversation *conversation)
+{
+ const char *session_type = "x11";
+
+ if (self->session_type != NULL) {
+ session_type = self->session_type;
+ }
+
+ gdm_dbus_worker_call_set_environment_variable (conversation->worker_proxy,
+ "XDG_SESSION_TYPE",
+ session_type,
+ conversation->worker_cancellable,
+ NULL, NULL);
+}
+
+void
+gdm_session_open_session (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation != NULL) {
+ send_display_mode (self, conversation);
+ send_session_type (self, conversation);
+
+ gdm_dbus_worker_call_open (conversation->worker_proxy,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_opened, conversation);
+ }
+}
+
+static void
+stop_all_other_conversations (GdmSession *self,
+ GdmSessionConversation *conversation_to_keep,
+ gboolean now)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (self->conversations == NULL) {
+ return;
+ }
+
+ if (conversation_to_keep == NULL) {
+ g_debug ("GdmSession: Stopping all conversations");
+ } else {
+ g_debug ("GdmSession: Stopping all conversations except for %s",
+ conversation_to_keep->service_name);
+ }
+
+ g_hash_table_iter_init (&iter, self->conversations);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmSessionConversation *conversation;
+
+ conversation = (GdmSessionConversation *) value;
+
+ if (conversation == conversation_to_keep) {
+ if (now) {
+ g_hash_table_iter_steal (&iter);
+ g_free (key);
+ }
+ } else {
+ if (now) {
+ stop_conversation_now (conversation);
+ } else {
+ stop_conversation (conversation);
+ }
+ }
+ }
+
+ if (now) {
+ g_hash_table_remove_all (self->conversations);
+
+ if (conversation_to_keep != NULL) {
+ g_hash_table_insert (self->conversations,
+ g_strdup (conversation_to_keep->service_name),
+ conversation_to_keep);
+ }
+
+ if (self->session_conversation != conversation_to_keep) {
+ self->session_conversation = NULL;
+ }
+ }
+
+}
+
+static void
+on_start_program_cb (GdmDBusWorker *worker,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GdmSessionConversation *conversation = user_data;
+ GdmSession *self;
+ char *service_name;
+
+ GError *error = NULL;
+ gboolean worked;
+ GPid pid;
+
+ worked = gdm_dbus_worker_call_start_program_finish (worker,
+ &pid,
+ res,
+ &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ self = conversation->session;
+ service_name = conversation->service_name;
+
+ if (worked) {
+ self->session_pid = pid;
+ self->session_conversation = conversation;
+
+ g_debug ("GdmSession: Emitting 'session-started' signal with pid '%d'", pid);
+ g_signal_emit (self, signals[SESSION_STARTED], 0, service_name, pid);
+ } else {
+ gdm_session_stop_conversation (self, service_name);
+
+ g_debug ("GdmSession: Emitting 'session-start-failed' signal");
+ g_signal_emit (self, signals[SESSION_START_FAILED], 0, service_name, error->message);
+ }
+}
+
+void
+gdm_session_start_session (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+ GdmSessionDisplayMode display_mode;
+ gboolean is_x11 = TRUE;
+ gboolean run_launcher = FALSE;
+ gboolean allow_remote_connections = FALSE;
+ char *command;
+ char *program;
+ gboolean register_session;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+ g_return_if_fail (self->session_conversation == NULL);
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation == NULL) {
+ g_warning ("GdmSession: Tried to start session of "
+ "nonexistent conversation %s", service_name);
+ return;
+ }
+
+ stop_all_other_conversations (self, conversation, FALSE);
+
+ display_mode = gdm_session_get_display_mode (self);
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ is_x11 = g_strcmp0 (self->session_type, "wayland") != 0;
+#endif
+
+ if (display_mode == GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED ||
+ display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) {
+ run_launcher = TRUE;
+ }
+
+ register_session = !gdm_session_session_registers (self);
+
+ if (self->selected_program == NULL) {
+ gboolean run_xsession_script;
+
+ command = get_session_command (self);
+
+ run_xsession_script = !gdm_session_bypasses_xsession (self);
+
+ if (self->display_is_local) {
+ gboolean disallow_tcp = TRUE;
+ gdm_settings_direct_get_boolean (GDM_KEY_DISALLOW_TCP, &disallow_tcp);
+ allow_remote_connections = !disallow_tcp;
+ } else {
+ allow_remote_connections = TRUE;
+ }
+
+ if (run_launcher) {
+ if (is_x11) {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s%s %s\"%s\"",
+ register_session ? "--register-session " : "",
+ run_xsession_script? "--run-script " : "",
+ allow_remote_connections? "--allow-remote-connections " : "",
+ command);
+ } else {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"%s\"",
+ register_session ? "--register-session " : "",
+ command);
+ }
+ } else if (run_xsession_script) {
+ program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command);
+ } else {
+ program = g_strdup (command);
+ }
+
+ g_free (command);
+ } else {
+ /* FIXME:
+ * Always use a separate DBus bus for each greeter session.
+ * Firstly, this means that if we run multiple greeter session
+ * (which we really should not do, but have to currently), then
+ * each one will get its own DBus session bus.
+ * But, we also explicitly do this for seat0, because that way
+ * it cannot make use of systemd to run the GNOME session. This
+ * prevents the session lookup logic from getting confused.
+ * This has a similar effect as passing --builtin to gnome-session.
+ *
+ * We really should not be doing this. But the fix is to use
+ * separate dynamically created users and that requires some
+ * major refactorings.
+ */
+ if (run_launcher) {
+ if (is_x11) {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s\"dbus-run-session -- %s\"",
+ register_session ? "--register-session " : "",
+ self->selected_program);
+ } else {
+ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"dbus-run-session -- %s\"",
+ register_session ? "--register-session " : "",
+ self->selected_program);
+ }
+ } else {
+ program = g_strdup_printf ("dbus-run-session -- %s",
+ self->selected_program);
+ }
+ }
+
+ set_up_session_environment (self);
+ send_environment (self, conversation);
+
+ gdm_dbus_worker_call_start_program (conversation->worker_proxy,
+ program,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_start_program_cb,
+ conversation);
+ g_free (program);
+}
+
+static void
+stop_all_conversations (GdmSession *self)
+{
+ stop_all_other_conversations (self, NULL, TRUE);
+}
+
+static void
+do_reset (GdmSession *self)
+{
+ stop_all_conversations (self);
+
+ g_list_free_full (self->pending_worker_connections, g_object_unref);
+ self->pending_worker_connections = NULL;
+
+ g_free (self->selected_user);
+ self->selected_user = NULL;
+
+ g_free (self->selected_session);
+ self->selected_session = NULL;
+
+ g_free (self->saved_session);
+ self->saved_session = NULL;
+
+ g_free (self->saved_language);
+ self->saved_language = NULL;
+
+ g_free (self->user_x11_authority_file);
+ self->user_x11_authority_file = NULL;
+
+ g_hash_table_remove_all (self->environment);
+
+ self->session_pid = -1;
+ self->session_conversation = NULL;
+}
+
+void
+gdm_session_close (GdmSession *self)
+{
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ g_debug ("GdmSession: Closing session");
+ do_reset (self);
+
+ g_list_free_full (self->outside_connections, g_object_unref);
+ self->outside_connections = NULL;
+}
+
+void
+gdm_session_answer_query (GdmSession *self,
+ const char *service_name,
+ const char *text)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation != NULL) {
+ answer_pending_query (conversation, text);
+ }
+}
+
+void
+gdm_session_cancel (GdmSession *self)
+{
+ g_return_if_fail (GDM_IS_SESSION (self));
+
+ g_signal_emit (G_OBJECT (self), signals [CANCELLED], 0);
+}
+
+void
+gdm_session_reset (GdmSession *self)
+{
+ if (self->user_verifier_interface != NULL) {
+ gdm_dbus_user_verifier_emit_reset (self->user_verifier_interface);
+ }
+
+ do_reset (self);
+}
+
+void
+gdm_session_set_timed_login_details (GdmSession *self,
+ const char *username,
+ int delay)
+{
+ g_debug ("GdmSession: timed login details %s %d", username, delay);
+ self->timed_login_username = g_strdup (username);
+ self->timed_login_delay = delay;
+}
+
+gboolean
+gdm_session_is_running (GdmSession *self)
+{
+ return self->session_pid > 0;
+}
+
+gboolean
+gdm_session_client_is_connected (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ return self->outside_connections != NULL;
+}
+
+uid_t
+gdm_session_get_allowed_user (GdmSession *self)
+{
+ return self->allowed_user;
+}
+
+void
+gdm_session_start_reauthentication (GdmSession *session,
+ GPid pid_of_caller,
+ uid_t uid_of_caller)
+{
+ GdmSessionConversation *conversation = session->session_conversation;
+
+ g_return_if_fail (conversation != NULL);
+
+ g_debug ("GdmSession: starting reauthentication for session %s for client with pid %d",
+ conversation->session_id,
+ (int) uid_of_caller);
+
+ conversation->reauth_pid_of_caller = pid_of_caller;
+
+ gdm_dbus_worker_call_start_reauthentication (conversation->worker_proxy,
+ (int) pid_of_caller,
+ (int) uid_of_caller,
+ conversation->worker_cancellable,
+ (GAsyncReadyCallback) on_reauthentication_started_cb,
+ conversation);
+}
+
+const char *
+gdm_session_get_server_address (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return g_dbus_server_get_client_address (self->outside_server);
+}
+
+const char *
+gdm_session_get_username (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return self->selected_user;
+}
+
+const char *
+gdm_session_get_display_device (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return self->display_device;
+}
+
+const char *
+gdm_session_get_display_seat_id (GdmSession *self)
+{
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ return g_strdup (self->display_seat_id);
+}
+
+const char *
+gdm_session_get_session_id (GdmSession *self)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ conversation = self->session_conversation;
+
+ if (conversation == NULL) {
+ return NULL;
+ }
+
+ return conversation->session_id;
+}
+
+const char *
+gdm_session_get_conversation_session_id (GdmSession *self,
+ const char *service_name)
+{
+ GdmSessionConversation *conversation;
+
+ g_return_val_if_fail (GDM_IS_SESSION (self), NULL);
+
+ conversation = find_conversation_by_name (self, service_name);
+
+ if (conversation == NULL) {
+ return NULL;
+ }
+
+ return conversation->session_id;
+}
+
+static char *
+get_session_filename (GdmSession *self)
+{
+ return g_strdup_printf ("%s.desktop", get_session_name (self));
+}
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+static gboolean
+gdm_session_is_wayland_session (GdmSession *self)
+{
+ GKeyFile *key_file;
+ gboolean is_wayland_session = FALSE;
+ char *filename;
+ g_autofree char *full_path = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, NULL, &full_path);
+
+ if (key_file == NULL) {
+ goto out;
+ }
+
+ if (full_path != NULL && strstr (full_path, "/wayland-sessions/") != NULL) {
+ is_wayland_session = TRUE;
+ }
+ g_debug ("GdmSession: checking if file '%s' is wayland session: %s", filename, is_wayland_session? "yes" : "no");
+
+out:
+ g_clear_pointer (&key_file, g_key_file_free);
+ g_free (filename);
+ return is_wayland_session;
+}
+#endif
+
+static void
+update_session_type (GdmSession *self)
+{
+#ifdef ENABLE_WAYLAND_SUPPORT
+ gboolean is_wayland_session = FALSE;
+
+ if (supports_session_type (self, "wayland"))
+ is_wayland_session = gdm_session_is_wayland_session (self);
+
+ if (is_wayland_session) {
+ set_session_type (self, "wayland");
+ } else {
+ set_session_type (self, NULL);
+ }
+#endif
+}
+
+gboolean
+gdm_session_session_registers (GdmSession *self)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GKeyFile) key_file = NULL;
+ gboolean session_registers = FALSE;
+ g_autofree char *filename = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, NULL, NULL);
+
+ session_registers = g_key_file_get_boolean (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ "X-GDM-SessionRegisters",
+ &error);
+ if (!session_registers &&
+ error != NULL &&
+ !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+ g_warning ("GdmSession: Couldn't read session file '%s'", filename);
+ return FALSE;
+ }
+
+ g_debug ("GdmSession: '%s' %s self", filename,
+ session_registers ? "registers" : "does not register");
+
+ return session_registers;
+}
+
+gboolean
+gdm_session_bypasses_xsession (GdmSession *self)
+{
+ GError *error;
+ GKeyFile *key_file;
+ gboolean res;
+ gboolean bypasses_xsession = FALSE;
+ char *filename = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ if (gdm_session_is_wayland_session (self)) {
+ bypasses_xsession = TRUE;
+ goto out;
+ }
+#endif
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, "x11", NULL);
+
+ error = NULL;
+ res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", NULL);
+ if (!res) {
+ goto out;
+ } else {
+ bypasses_xsession = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", &error);
+ if (error) {
+ bypasses_xsession = FALSE;
+ g_error_free (error);
+ goto out;
+ }
+ }
+
+out:
+ if (bypasses_xsession) {
+ g_debug ("GdmSession: Session %s bypasses Xsession wrapper script", filename);
+ }
+ g_free (filename);
+ return bypasses_xsession;
+}
+
+GdmSessionDisplayMode
+gdm_session_get_display_mode (GdmSession *self)
+{
+ g_debug ("GdmSession: type %s, program? %s, seat %s",
+ self->session_type,
+ self->is_program_session? "yes" : "no",
+ self->display_seat_id);
+
+ /* Non-seat0 sessions share their X server with their login screen
+ * for now.
+ */
+ if (g_strcmp0 (self->display_seat_id, "seat0") != 0) {
+ return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
+ }
+
+#ifdef ENABLE_USER_DISPLAY_SERVER
+ /* All other cases (wayland login screen, X login screen,
+ * wayland user session, X user session) use the NEW_VT
+ * display mode. That display mode means that GDM allocates
+ * a new VT and jumps to it before starting the session. The
+ * session is expected to use logind to gain access to the
+ * display and input devices.
+ *
+ * GDM also has a LOGIND_MANAGED display mode which we can't
+ * use yet. The difference between it and NEW_VT, is with it,
+ * GDM doesn't do any VT handling at all, expecting the session
+ * and logind to do everything. The problem is, for wayland
+ * sessions it will cause flicker until * this bug is fixed:
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=745141
+ *
+ * Likewise, for X sessions it's problematic because
+ * 1) X doesn't call TakeControl before switching VTs
+ * 2) X doesn't support getting started "in the background"
+ * right now. It will die with an error if logind devices
+ * are paused when handed out.
+ */
+ return GDM_SESSION_DISPLAY_MODE_NEW_VT;
+#else
+
+#ifdef ENABLE_WAYLAND_SUPPORT
+ /* Wayland sessions are for now assumed to run in a
+ * mutter-launch-like environment, so we allocate
+ * a new VT for them. */
+ if (g_strcmp0 (self->session_type, "wayland") == 0) {
+ return GDM_SESSION_DISPLAY_MODE_NEW_VT;
+ }
+#endif
+ return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
+#endif
+}
+
+void
+gdm_session_select_program (GdmSession *self,
+ const char *text)
+{
+
+ g_free (self->selected_program);
+
+ self->selected_program = g_strdup (text);
+}
+
+void
+gdm_session_select_session (GdmSession *self,
+ const char *text)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_debug ("GdmSession: selecting session '%s'", text);
+
+ g_free (self->selected_session);
+ self->selected_session = g_strdup (text);
+
+ update_session_type (self);
+
+ g_hash_table_iter_init (&iter, self->conversations);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GdmSessionConversation *conversation;
+
+ conversation = (GdmSessionConversation *) value;
+
+ gdm_dbus_worker_call_set_session_name (conversation->worker_proxy,
+ get_session_name (self),
+ conversation->worker_cancellable,
+ NULL, NULL);
+ }
+}
+
+static void
+set_display_name (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_name);
+ self->display_name = g_strdup (name);
+}
+
+static void
+set_display_hostname (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_hostname);
+ self->display_hostname = g_strdup (name);
+}
+
+static void
+set_display_device (GdmSession *self,
+ const char *name)
+{
+ g_debug ("GdmSession: Setting display device: %s", name);
+ g_free (self->display_device);
+ self->display_device = g_strdup (name);
+}
+
+static void
+set_display_seat_id (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_seat_id);
+ self->display_seat_id = g_strdup (name);
+}
+
+static void
+set_user_x11_authority_file (GdmSession *self,
+ const char *name)
+{
+ g_free (self->user_x11_authority_file);
+ self->user_x11_authority_file = g_strdup (name);
+}
+
+static void
+set_display_x11_authority_file (GdmSession *self,
+ const char *name)
+{
+ g_free (self->display_x11_authority_file);
+ self->display_x11_authority_file = g_strdup (name);
+}
+
+static void
+set_display_is_local (GdmSession *self,
+ gboolean is_local)
+{
+ self->display_is_local = is_local;
+}
+
+static void
+set_display_is_initial (GdmSession *self,
+ gboolean is_initial)
+{
+ self->display_is_initial = is_initial;
+}
+
+static void
+set_verification_mode (GdmSession *self,
+ GdmSessionVerificationMode verification_mode)
+{
+ self->verification_mode = verification_mode;
+}
+
+static void
+set_allowed_user (GdmSession *self,
+ uid_t allowed_user)
+{
+ self->allowed_user = allowed_user;
+}
+
+static void
+set_conversation_environment (GdmSession *self,
+ char **environment)
+{
+ g_strfreev (self->conversation_environment);
+ self->conversation_environment = g_strdupv (environment);
+}
+
+static void
+set_session_type (GdmSession *self,
+ const char *session_type)
+{
+
+ if (g_strcmp0 (self->session_type, session_type) != 0) {
+ g_debug ("GdmSession: setting session to type '%s'", session_type? session_type : "");
+ g_free (self->session_type);
+ self->session_type = g_strdup (session_type);
+ }
+}
+
+static void
+gdm_session_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_SESSION_TYPE:
+ set_session_type (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_NAME:
+ set_display_name (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_HOSTNAME:
+ set_display_hostname (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_DEVICE:
+ set_display_device (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_SEAT_ID:
+ set_display_seat_id (self, g_value_get_string (value));
+ break;
+ case PROP_USER_X11_AUTHORITY_FILE:
+ set_user_x11_authority_file (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_X11_AUTHORITY_FILE:
+ set_display_x11_authority_file (self, g_value_get_string (value));
+ break;
+ case PROP_DISPLAY_IS_LOCAL:
+ set_display_is_local (self, g_value_get_boolean (value));
+ break;
+ case PROP_DISPLAY_IS_INITIAL:
+ set_display_is_initial (self, g_value_get_boolean (value));
+ break;
+ case PROP_VERIFICATION_MODE:
+ set_verification_mode (self, g_value_get_enum (value));
+ break;
+ case PROP_ALLOWED_USER:
+ set_allowed_user (self, g_value_get_uint (value));
+ break;
+ case PROP_CONVERSATION_ENVIRONMENT:
+ set_conversation_environment (self, g_value_get_pointer (value));
+ break;
+ case PROP_SUPPORTED_SESSION_TYPES:
+ gdm_session_set_supported_session_types (self, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_session_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (object);
+
+ switch (prop_id) {
+ case PROP_SESSION_TYPE:
+ g_value_set_string (value, self->session_type);
+ break;
+ case PROP_DISPLAY_NAME:
+ g_value_set_string (value, self->display_name);
+ break;
+ case PROP_DISPLAY_HOSTNAME:
+ g_value_set_string (value, self->display_hostname);
+ break;
+ case PROP_DISPLAY_DEVICE:
+ g_value_set_string (value, self->display_device);
+ break;
+ case PROP_DISPLAY_SEAT_ID:
+ g_value_set_string (value, self->display_seat_id);
+ break;
+ case PROP_USER_X11_AUTHORITY_FILE:
+ g_value_set_string (value, self->user_x11_authority_file);
+ break;
+ case PROP_DISPLAY_X11_AUTHORITY_FILE:
+ g_value_set_string (value, self->display_x11_authority_file);
+ break;
+ case PROP_DISPLAY_IS_LOCAL:
+ g_value_set_boolean (value, self->display_is_local);
+ break;
+ case PROP_DISPLAY_IS_INITIAL:
+ g_value_set_boolean (value, self->display_is_initial);
+ break;
+ case PROP_VERIFICATION_MODE:
+ g_value_set_enum (value, self->verification_mode);
+ break;
+ case PROP_ALLOWED_USER:
+ g_value_set_uint (value, self->allowed_user);
+ break;
+ case PROP_CONVERSATION_ENVIRONMENT:
+ g_value_set_pointer (value, self->environment);
+ break;
+ case PROP_SUPPORTED_SESSION_TYPES:
+ g_value_set_boxed (value, self->supported_session_types);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_session_dispose (GObject *object)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (object);
+
+ g_debug ("GdmSession: Disposing session");
+
+ gdm_session_close (self);
+
+ g_clear_pointer (&self->supported_session_types,
+ g_strfreev);
+ g_clear_pointer (&self->conversations,
+ g_hash_table_unref);
+
+ g_clear_object (&self->user_verifier_interface);
+ g_clear_pointer (&self->user_verifier_extensions,
+ g_hash_table_unref);
+ g_clear_object (&self->greeter_interface);
+ g_clear_object (&self->remote_greeter_interface);
+ g_clear_object (&self->chooser_interface);
+
+ g_free (self->display_name);
+ self->display_name = NULL;
+
+ g_free (self->display_hostname);
+ self->display_hostname = NULL;
+
+ g_free (self->display_device);
+ self->display_device = NULL;
+
+ g_free (self->display_seat_id);
+ self->display_seat_id = NULL;
+
+ g_free (self->display_x11_authority_file);
+ self->display_x11_authority_file = NULL;
+
+ g_strfreev (self->conversation_environment);
+ self->conversation_environment = NULL;
+
+ if (self->worker_server != NULL) {
+ g_dbus_server_stop (self->worker_server);
+ g_clear_object (&self->worker_server);
+ }
+
+ if (self->outside_server != NULL) {
+ g_dbus_server_stop (self->outside_server);
+ g_clear_object (&self->outside_server);
+ }
+
+ if (self->environment != NULL) {
+ g_hash_table_destroy (self->environment);
+ self->environment = NULL;
+ }
+
+ G_OBJECT_CLASS (gdm_session_parent_class)->dispose (object);
+}
+
+static void
+gdm_session_finalize (GObject *object)
+{
+ GdmSession *self;
+ GObjectClass *parent_class;
+
+ self = GDM_SESSION (object);
+
+ g_free (self->selected_user);
+ g_free (self->selected_session);
+ g_free (self->saved_session);
+ g_free (self->saved_language);
+
+ g_free (self->fallback_session_name);
+
+ parent_class = G_OBJECT_CLASS (gdm_session_parent_class);
+
+ if (parent_class->finalize != NULL)
+ parent_class->finalize (object);
+}
+
+static GObject *
+gdm_session_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ GdmSession *self;
+
+ self = GDM_SESSION (G_OBJECT_CLASS (gdm_session_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+ return G_OBJECT (self);
+}
+
+static void
+gdm_session_class_init (GdmSessionClass *session_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (session_class);
+
+ object_class->get_property = gdm_session_get_property;
+ object_class->set_property = gdm_session_set_property;
+ object_class->constructor = gdm_session_constructor;
+ object_class->dispose = gdm_session_dispose;
+ object_class->finalize = gdm_session_finalize;
+
+ signals [CONVERSATION_STARTED] =
+ g_signal_new ("conversation-started",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [CONVERSATION_STOPPED] =
+ g_signal_new ("conversation-stopped",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+ signals [SETUP_COMPLETE] =
+ g_signal_new ("setup-complete",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+
+ signals [AUTHENTICATION_FAILED] =
+ g_signal_new ("authentication-failed",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+ signals [VERIFICATION_COMPLETE] =
+ g_signal_new ("verification-complete",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+ signals [SESSION_OPENED] =
+ g_signal_new ("session-opened",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ signals [SESSION_OPENED_FAILED] =
+ g_signal_new ("session-opened-failed",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING, G_TYPE_STRING);
+ signals [SESSION_STARTED] =
+ g_signal_new ("session-started",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+ signals [SESSION_START_FAILED] =
+ g_signal_new ("session-start-failed",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING, G_TYPE_STRING);
+ signals [SESSION_EXITED] =
+ g_signal_new ("session-exited",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+ signals [SESSION_DIED] =
+ g_signal_new ("session-died",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ signals [REAUTHENTICATION_STARTED] =
+ g_signal_new ("reauthentication-started",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_INT,
+ G_TYPE_STRING);
+ signals [REAUTHENTICATED] =
+ g_signal_new ("reauthenticated",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+ signals [CANCELLED] =
+ g_signal_new ("cancelled",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals [CLIENT_REJECTED] =
+ g_signal_new ("client-rejected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_CREDENTIALS,
+ G_TYPE_UINT);
+
+ signals [CLIENT_CONNECTED] =
+ g_signal_new ("client-connected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_CREDENTIALS,
+ G_TYPE_UINT);
+
+ signals [CLIENT_DISCONNECTED] =
+ g_signal_new ("client-disconnected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_CREDENTIALS,
+ G_TYPE_UINT);
+ signals [CLIENT_READY_FOR_SESSION_TO_START] =
+ g_signal_new ("client-ready-for-session-to-start",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+
+ signals [HOSTNAME_SELECTED] =
+ g_signal_new ("hostname-selected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+ signals [DISCONNECTED] =
+ g_signal_new ("disconnected",
+ GDM_TYPE_SESSION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_VERIFICATION_MODE,
+ g_param_spec_enum ("verification-mode",
+ "verification mode",
+ "verification mode",
+ GDM_TYPE_SESSION_VERIFICATION_MODE,
+ GDM_SESSION_VERIFICATION_MODE_LOGIN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_ALLOWED_USER,
+ g_param_spec_uint ("allowed-user",
+ "allowed user",
+ "allowed user ",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_CONVERSATION_ENVIRONMENT,
+ g_param_spec_pointer ("conversation-environment",
+ "conversation environment",
+ "conversation environment",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SESSION_TYPE,
+ g_param_spec_string ("session-type",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_NAME,
+ g_param_spec_string ("display-name",
+ "display name",
+ "display name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_HOSTNAME,
+ g_param_spec_string ("display-hostname",
+ "display hostname",
+ "display hostname",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_IS_LOCAL,
+ g_param_spec_boolean ("display-is-local",
+ "display is local",
+ "display is local",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_IS_INITIAL,
+ g_param_spec_boolean ("display-is-initial",
+ "display is initial",
+ "display is initial",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_X11_AUTHORITY_FILE,
+ g_param_spec_string ("display-x11-authority-file",
+ "display x11 authority file",
+ "display x11 authority file",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+ /* not construct only */
+ g_object_class_install_property (object_class,
+ PROP_USER_X11_AUTHORITY_FILE,
+ g_param_spec_string ("user-x11-authority-file",
+ "",
+ "",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_DEVICE,
+ g_param_spec_string ("display-device",
+ "display device",
+ "display device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_DISPLAY_SEAT_ID,
+ g_param_spec_string ("display-seat-id",
+ "display seat id",
+ "display seat id",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SUPPORTED_SESSION_TYPES,
+ g_param_spec_boxed ("supported-session-types",
+ "supported session types",
+ "supported session types",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ /* Ensure we can resolve errors */
+ gdm_dbus_error_ensure (GDM_SESSION_WORKER_ERROR);
+}
+
+GdmSession *
+gdm_session_new (GdmSessionVerificationMode verification_mode,
+ uid_t allowed_user,
+ const char *display_name,
+ const char *display_hostname,
+ const char *display_device,
+ const char *display_seat_id,
+ const char *display_x11_authority_file,
+ gboolean display_is_local,
+ const char * const *environment)
+{
+ GdmSession *self;
+
+ self = g_object_new (GDM_TYPE_SESSION,
+ "verification-mode", verification_mode,
+ "allowed-user", (guint) allowed_user,
+ "display-name", display_name,
+ "display-hostname", display_hostname,
+ "display-device", display_device,
+ "display-seat-id", display_seat_id,
+ "display-x11-authority-file", display_x11_authority_file,
+ "display-is-local", display_is_local,
+ "conversation-environment", environment,
+ NULL);
+
+ return self;
+}
+
+GdmSessionDisplayMode
+gdm_session_display_mode_from_string (const char *str)
+{
+ if (strcmp (str, "reuse-vt") == 0)
+ return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
+ if (strcmp (str, "new-vt") == 0)
+ return GDM_SESSION_DISPLAY_MODE_NEW_VT;
+ if (strcmp (str, "logind-managed") == 0)
+ return GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED;
+
+ g_warning ("Unknown GdmSessionDisplayMode %s", str);
+ return -1;
+}
+
+const char *
+gdm_session_display_mode_to_string (GdmSessionDisplayMode mode)
+{
+ switch (mode) {
+ case GDM_SESSION_DISPLAY_MODE_REUSE_VT:
+ return "reuse-vt";
+ case GDM_SESSION_DISPLAY_MODE_NEW_VT:
+ return "new-vt";
+ case GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED:
+ return "logind-managed";
+ default:
+ break;
+ }
+
+ g_warning ("Unknown GdmSessionDisplayMode %d", mode);
+ return "";
+}
+
+GPid
+gdm_session_get_pid (GdmSession *session)
+{
+ return session->session_pid;
+}
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
new file mode 100644
index 0000000..dc1eeef
--- /dev/null
+++ b/daemon/gdm-session.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __GDM_SESSION_H
+#define __GDM_SESSION_H
+
+#include <glib-object.h>
+#include <sys/types.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SESSION (gdm_session_get_type ())
+G_DECLARE_FINAL_TYPE (GdmSession, gdm_session, GDM, SESSION, GObject)
+
+typedef enum
+{
+ GDM_SESSION_VERIFICATION_MODE_LOGIN,
+ GDM_SESSION_VERIFICATION_MODE_CHOOSER,
+ GDM_SESSION_VERIFICATION_MODE_REAUTHENTICATE
+} GdmSessionVerificationMode;
+
+typedef enum {
+ /* We reuse the existing display server, e.g. X server
+ * in "classic" mode from the greeter for the first seat. */
+ GDM_SESSION_DISPLAY_MODE_REUSE_VT,
+
+ /* Doesn't know anything about VTs. Tries to set DRM
+ * master and will throw a tantrum if something bad
+ * happens. e.g. weston-launch or mutter-launch. */
+ GDM_SESSION_DISPLAY_MODE_NEW_VT,
+
+ /* Uses logind sessions to manage itself. We need to set an
+ * XDG_VTNR and it will switch to the correct VT on startup.
+ * e.g. mutter-wayland with logind integration, X server with
+ * logind integration. */
+ GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED,
+} GdmSessionDisplayMode;
+
+GdmSessionDisplayMode gdm_session_display_mode_from_string (const char *str);
+const char * gdm_session_display_mode_to_string (GdmSessionDisplayMode mode);
+
+GdmSession *gdm_session_new (GdmSessionVerificationMode verification_mode,
+ uid_t allowed_user,
+ const char *display_name,
+ const char *display_hostname,
+ const char *display_device,
+ const char *display_seat_id,
+ const char *display_x11_authority_file,
+ gboolean display_is_local,
+ const char * const *environment);
+uid_t gdm_session_get_allowed_user (GdmSession *session);
+void gdm_session_start_reauthentication (GdmSession *session,
+ GPid pid_of_caller,
+ uid_t uid_of_caller);
+
+const char *gdm_session_get_server_address (GdmSession *session);
+const char *gdm_session_get_username (GdmSession *session);
+const char *gdm_session_get_display_device (GdmSession *session);
+const char *gdm_session_get_display_seat_id (GdmSession *session);
+const char *gdm_session_get_session_id (GdmSession *session);
+gboolean gdm_session_bypasses_xsession (GdmSession *session);
+gboolean gdm_session_session_registers (GdmSession *session);
+GdmSessionDisplayMode gdm_session_get_display_mode (GdmSession *session);
+gboolean gdm_session_start_conversation (GdmSession *session,
+ const char *service_name);
+void gdm_session_stop_conversation (GdmSession *session,
+ const char *service_name);
+const char *gdm_session_get_conversation_session_id (GdmSession *session,
+ const char *service_name);
+void gdm_session_setup (GdmSession *session,
+ const char *service_name);
+void gdm_session_setup_for_user (GdmSession *session,
+ const char *service_name,
+ const char *username);
+void gdm_session_setup_for_program (GdmSession *session,
+ const char *service_name,
+ const char *username,
+ const char *log_file);
+void gdm_session_set_environment_variable (GdmSession *session,
+ const char *key,
+ const char *value);
+void gdm_session_send_environment (GdmSession *self,
+ const char *service_name);
+void gdm_session_reset (GdmSession *session);
+void gdm_session_cancel (GdmSession *session);
+void gdm_session_authenticate (GdmSession *session,
+ const char *service_name);
+void gdm_session_authorize (GdmSession *session,
+ const char *service_name);
+void gdm_session_accredit (GdmSession *session,
+ const char *service_name);
+void gdm_session_open_session (GdmSession *session,
+ const char *service_name);
+void gdm_session_start_session (GdmSession *session,
+ const char *service_name);
+void gdm_session_close (GdmSession *session);
+
+void gdm_session_answer_query (GdmSession *session,
+ const char *service_name,
+ const char *text);
+void gdm_session_select_program (GdmSession *session,
+ const char *command_line);
+void gdm_session_select_session (GdmSession *session,
+ const char *session_name);
+void gdm_session_select_user (GdmSession *session,
+ const char *username);
+void gdm_session_set_timed_login_details (GdmSession *session,
+ const char *username,
+ int delay);
+gboolean gdm_session_client_is_connected (GdmSession *session);
+gboolean gdm_session_is_running (GdmSession *session);
+GPid gdm_session_get_pid (GdmSession *session);
+
+G_END_DECLS
+
+#endif /* GDM_SESSION_H */
diff --git a/daemon/gdm-session.xml b/daemon/gdm-session.xml
new file mode 100644
index 0000000..137be5e
--- /dev/null
+++ b/daemon/gdm-session.xml
@@ -0,0 +1,146 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/gnome/DisplayManager/Session">
+ <!-- methods are called by the session worker,
+ signals are emitted by the main daemon -->
+ <interface name="org.gnome.DisplayManager.WorkerManager">
+ <method name="Hello" />
+ <method name="InfoQuery">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="query" direction="in" type="s"/>
+ <arg name="answer" direction="out" type="s"/>
+ </method>
+ <method name="SecretInfoQuery">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="query" direction="in" type="s"/>
+ <arg name="answer" direction="out" type="s"/>
+ </method>
+ <method name="Info">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="info" direction="in" type="s"/>
+ </method>
+ <method name="Problem">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="problem" direction="in" type="s"/>
+ </method>
+ <method name="ChoiceListQuery">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="prompt_message" direction="in" type="s"/>
+ <arg name="query" direction="in" type="a{ss}"/>
+ <arg name="answer" direction="out" type="s"/>
+ </method>
+ </interface>
+ <interface name="org.gnome.DisplayManager.UserVerifier">
+ <method name="EnableExtensions">
+ <arg name="extensions" direction="in" type="as"/>
+ </method>
+ <method name="BeginVerification">
+ <arg name="service_name" direction="in" type="s"/>
+ </method>
+ <method name="BeginVerificationForUser">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="username" direction="in" type="s"/>
+ </method>
+ <method name="AnswerQuery">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="answer" direction="in" type="s"/>
+ </method>
+ <method name="Cancel">
+ </method>
+ <signal name="ConversationStarted">
+ <arg name="service_name" type="s"/>
+ </signal>
+ <signal name="ConversationStopped">
+ <arg name="service_name" type="s"/>
+ </signal>
+ <signal name="ReauthenticationStarted">
+ <arg name="pid_of_caller" type="i"/>
+ </signal>
+ <signal name="Info">
+ <arg name="service_name" type="s"/>
+ <arg name="info" type="s"/>
+ </signal>
+ <signal name="Problem">
+ <arg name="service_name" type="s"/>
+ <arg name="problem" type="s"/>
+ </signal>
+ <signal name="InfoQuery">
+ <arg name="service_name" type="s"/>
+ <arg name="query" type="s"/>
+ </signal>
+ <signal name="SecretInfoQuery">
+ <arg name="service_name" type="s"/>
+ <arg name="query" type="s"/>
+ </signal>
+ <signal name="Reset">
+ </signal>
+ <signal name="ServiceUnavailable">
+ <arg name="service_name" type="s"/>
+ <arg name="message" type="s"/>
+ </signal>
+ <signal name="VerificationFailed">
+ <arg name="service_name" type="s"/>
+ </signal>
+ <signal name="VerificationComplete">
+ <arg name="service_name" type="s"/>
+ </signal>
+ </interface>
+ <interface name="org.gnome.DisplayManager.UserVerifier.ChoiceList">
+ <method name="SelectChoice">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="choice" direction="in" type="s"/>
+ </method>
+ <signal name="ChoiceQuery">
+ <arg name="service_name" type="s"/>
+ <arg name="prompt_message" type="s"/>
+ <arg name="list" type="a{ss}"/>
+ </signal>
+ </interface>
+ <interface name="org.gnome.DisplayManager.Greeter">
+ <method name="SelectSession">
+ <arg name="session" direction="in" type="s"/>
+ </method>
+ <method name="SelectUser">
+ <arg name="username" direction="in" type="s"/>
+ </method>
+ <method name="BeginAutoLogin">
+ <arg name="username" direction="in" type="s"/>
+ </method>
+ <method name="GetTimedLoginDetails">
+ <arg name="enabled" direction="out" type="b"/>
+ <arg name="username" direction="out" type="s"/>
+ <arg name="delay" direction="out" type="i"/>
+ </method>
+ <method name="StartSessionWhenReady">
+ <arg name="service_name" direction="in" type="s"/>
+ <arg name="should_start_session" direction="in" type="b"/>
+ </method>
+ <signal name="SelectedUserChanged">
+ <arg name="username" type="s"/>
+ </signal>
+ <signal name="DefaultLanguageNameChanged">
+ <arg name="language_name" type="s"/>
+ </signal>
+ <signal name="DefaultSessionNameChanged">
+ <arg name="session_name" type="s"/>
+ </signal>
+ <signal name="TimedLoginRequested">
+ <arg name="username" type="s"/>
+ <arg name="delay" type="i"/>
+ </signal>
+ <signal name="SessionOpened">
+ <arg name="service_name" type="s"/>
+ </signal>
+ <signal name="Reauthenticated">
+ <arg name="service_name" type="s"/>
+ </signal>
+ </interface>
+ <interface name="org.gnome.DisplayManager.RemoteGreeter">
+ <method name="Disconnect" />
+ </interface>
+ <interface name="org.gnome.DisplayManager.Chooser">
+ <method name="SelectHostname">
+ <arg name="hostname" direction="in" type="s"/>
+ </method>
+ <method name="Disconnect" />
+ </interface>
+</node>
diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c
new file mode 100644
index 0000000..d0404d2
--- /dev/null
+++ b/daemon/gdm-wayland-session.c
@@ -0,0 +1,620 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include "config.h"
+
+#include <locale.h>
+#include <sysexits.h>
+
+#include "gdm-common.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+#include "gdm-log.h"
+
+#include "gdm-manager-glue.h"
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-unix.h>
+#include <glib.h>
+
+#include <gio/gunixinputstream.h>
+
+#define BUS_ADDRESS_FILENO (STDERR_FILENO + 1)
+
+typedef struct
+{
+ GdmSettings *settings;
+ GCancellable *cancellable;
+
+ GSubprocess *bus_subprocess;
+ GDBusConnection *bus_connection;
+ GdmDBusManager *display_manager_proxy;
+ char *bus_address;
+
+ char **environment;
+
+ GSubprocess *session_subprocess;
+ char *session_command;
+ int session_exit_status;
+
+ guint register_session_id;
+
+ GMainLoop *main_loop;
+
+ guint32 debug_enabled : 1;
+} State;
+
+static void
+on_bus_finished (GSubprocess *subprocess,
+ GAsyncResult *result,
+ State *state)
+{
+ gboolean cancelled;
+
+ cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
+
+ if (cancelled) {
+ goto out;
+ }
+
+ if (g_subprocess_get_if_exited (subprocess)) {
+ int exit_status;
+
+ exit_status = g_subprocess_get_exit_status (subprocess);
+
+ g_debug ("message bus exited with status %d", exit_status);
+ } else {
+ int signal_number;
+
+ signal_number = g_subprocess_get_term_sig (subprocess);
+ g_debug ("message bus was killed with status %d", signal_number);
+ }
+
+ g_clear_object (&state->bus_subprocess);
+out:
+ g_main_loop_quit (state->main_loop);
+}
+
+static gboolean
+spawn_bus (State *state,
+ GCancellable *cancellable)
+{
+ GDBusConnection *bus_connection = NULL;
+ GPtrArray *arguments = NULL;
+ GSubprocessLauncher *launcher = NULL;
+ GSubprocess *subprocess = NULL;
+ GInputStream *input_stream = NULL;
+ GDataInputStream *data_stream = NULL;
+ GError *error = NULL;
+ char *bus_address_fd_string = NULL;
+ char *bus_address = NULL;
+ gsize bus_address_size;
+
+ gboolean is_running = FALSE;
+ int ret;
+ int pipe_fds[2];
+
+ g_debug ("Running session message bus");
+
+ bus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
+ cancellable,
+ NULL);
+
+ if (bus_connection != NULL) {
+ g_debug ("session message bus already running, not starting another one");
+ state->bus_connection = bus_connection;
+ return TRUE;
+ }
+
+ ret = g_unix_open_pipe (pipe_fds, FD_CLOEXEC, &error);
+
+ if (!ret) {
+ g_debug ("could not open pipe: %s", error->message);
+ goto out;
+ }
+
+ arguments = g_ptr_array_new ();
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+ g_subprocess_launcher_take_fd (launcher, pipe_fds[1], BUS_ADDRESS_FILENO);
+
+ bus_address_fd_string = g_strdup_printf ("%d", BUS_ADDRESS_FILENO);
+
+ g_ptr_array_add (arguments, "dbus-daemon");
+
+ g_ptr_array_add (arguments, "--print-address");
+ g_ptr_array_add (arguments, bus_address_fd_string);
+ g_ptr_array_add (arguments, "--session");
+ g_ptr_array_add (arguments, NULL);
+
+ subprocess = g_subprocess_launcher_spawnv (launcher,
+ (const char * const *) arguments->pdata,
+ &error);
+ g_free (bus_address_fd_string);
+ g_clear_object (&launcher);
+ g_ptr_array_free (arguments, TRUE);
+
+ if (subprocess == NULL) {
+ g_debug ("could not start dbus-daemon: %s", error->message);
+ goto out;
+ }
+
+ input_stream = g_unix_input_stream_new (pipe_fds[0], TRUE);
+ data_stream = g_data_input_stream_new (input_stream);
+ g_clear_object (&input_stream);
+
+ bus_address = g_data_input_stream_read_line (data_stream,
+ &bus_address_size,
+ cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_debug ("could not read address from session message bus: %s", error->message);
+ goto out;
+ }
+
+ if (bus_address == NULL) {
+ g_debug ("session message bus did not write address");
+ goto out;
+ }
+
+ state->bus_address = bus_address;
+
+ state->bus_subprocess = g_object_ref (subprocess);
+
+ g_subprocess_wait_async (state->bus_subprocess,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_bus_finished,
+ state);
+
+ bus_connection = g_dbus_connection_new_for_address_sync (state->bus_address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
+ NULL,
+ cancellable,
+ &error);
+
+ if (bus_connection == NULL) {
+ g_debug ("could not open connection to session bus: %s",
+ error->message);
+ goto out;
+ }
+
+ state->bus_connection = bus_connection;
+ is_running = TRUE;
+out:
+ g_clear_object (&data_stream);
+ g_clear_object (&subprocess);
+ g_clear_object (&launcher);
+ g_clear_error (&error);
+
+ return is_running;
+}
+
+static gboolean
+import_environment (State *state,
+ GCancellable *cancellable)
+{
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GVariant) environment_variant = NULL;
+ g_autoptr(GError) error = NULL;
+
+ reply = g_dbus_connection_call_sync (state->bus_connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.systemd1.Manager",
+ "Environment"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, cancellable, &error);
+
+ if (reply == NULL) {
+ g_debug ("could not fetch environment: %s", error->message);
+ return FALSE;
+ }
+
+ g_variant_get (reply, "(v)", &environment_variant);
+
+ state->environment = g_variant_dup_strv (environment_variant, NULL);
+
+ return TRUE;
+}
+
+static void
+on_session_finished (GSubprocess *subprocess,
+ GAsyncResult *result,
+ State *state)
+{
+ gboolean cancelled;
+
+ cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
+
+ if (cancelled) {
+ goto out;
+ }
+
+ if (g_subprocess_get_if_exited (subprocess)) {
+ int exit_status;
+
+ exit_status = g_subprocess_get_exit_status (subprocess);
+
+ g_debug ("session exited with status %d", exit_status);
+
+ state->session_exit_status = exit_status;
+ } else {
+ int signal_number;
+
+ signal_number = g_subprocess_get_term_sig (subprocess);
+ g_debug ("session was killed with status %d", signal_number);
+ }
+
+ g_clear_object (&state->session_subprocess);
+out:
+ g_main_loop_quit (state->main_loop);
+}
+
+static gboolean
+spawn_session (State *state,
+ GCancellable *cancellable)
+{
+ GSubprocessLauncher *launcher = NULL;
+ GSubprocess *subprocess = NULL;
+ GError *error = NULL;
+ gboolean is_running = FALSE;
+ int ret;
+ char **argv = NULL;
+ static const char *session_variables[] = { "DISPLAY",
+ "XAUTHORITY",
+ "WAYLAND_DISPLAY",
+ "WAYLAND_SOCKET",
+ "GNOME_SHELL_SESSION_MODE",
+ NULL };
+ /* The environment variables listed below are those we have set (or
+ * received from our own execution environment) only as a fallback to
+ * make things work, as opposed to a information directly pertaining to
+ * the session about to be started. Variables listed here will not
+ * overwrite the existing environment (possibly) imported from the
+ * systemd --user instance.
+ * As an example: We need a PATH for some of the launched subprocesses
+ * to work, but if the user (or the distributor) has customized the PATH
+ * via one of systemds user-environment-generators, that version should
+ * be preferred. */
+ static const char *fallback_variables[] = { "PATH", NULL };
+
+ g_debug ("Running wayland session");
+
+ ret = g_shell_parse_argv (state->session_command,
+ NULL,
+ &argv,
+ &error);
+
+ if (!ret) {
+ g_debug ("could not parse session arguments: %s", error->message);
+ goto out;
+ }
+
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+
+ if (state->environment != NULL) {
+ size_t i;
+
+ for (i = 0; state->environment[i] != NULL; i++) {
+ g_auto(GStrv) environment_entry = NULL;
+
+ if (state->environment[i][0] == '\0') {
+ continue;
+ }
+
+ environment_entry = g_strsplit (state->environment[i], "=", 2);
+
+ if (environment_entry[0] == NULL || environment_entry[1] == NULL) {
+ continue;
+ }
+
+ /* Merge the environment block imported from systemd --user with the
+ * environment we have set for ourselves (and thus pass on to the
+ * launcher process). Variables we have set have precedence, as to not
+ * import stale data from prior user sessions, with the exception of
+ * those listed in fallback_variables. See the comment there for more
+ * explanations. */
+ g_subprocess_launcher_setenv (launcher,
+ environment_entry[0],
+ environment_entry[1],
+ g_strv_contains (fallback_variables, environment_entry[0]));
+ }
+
+ /* Don't allow session specific environment variables from earlier sessions to
+ * leak through */
+ for (i = 0; session_variables[i] != NULL; i++) {
+ if (g_getenv (session_variables[i]) == NULL) {
+ g_subprocess_launcher_unsetenv (launcher, session_variables[i]);
+ }
+ }
+ }
+
+ if (state->bus_address != NULL) {
+ g_subprocess_launcher_setenv (launcher, "DBUS_SESSION_BUS_ADDRESS", state->bus_address, TRUE);
+ }
+
+ subprocess = g_subprocess_launcher_spawnv (launcher,
+ (const char * const *) argv,
+ &error);
+ g_strfreev (argv);
+
+ if (subprocess == NULL) {
+ g_debug ("could not start session: %s", error->message);
+ goto out;
+ }
+
+ state->session_subprocess = g_object_ref (subprocess);
+
+ g_subprocess_wait_async (state->session_subprocess,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_session_finished,
+ state);
+
+ is_running = TRUE;
+out:
+ g_clear_object (&subprocess);
+ return is_running;
+}
+
+static void
+signal_subprocesses (State *state)
+{
+ if (state->session_subprocess != NULL) {
+ g_subprocess_send_signal (state->session_subprocess, SIGTERM);
+ }
+
+ if (state->bus_subprocess != NULL) {
+ g_subprocess_send_signal (state->bus_subprocess, SIGTERM);
+ }
+}
+
+static void
+wait_on_subprocesses (State *state)
+{
+ if (state->bus_subprocess != NULL) {
+ g_subprocess_wait (state->bus_subprocess, NULL, NULL);
+ }
+
+ if (state->session_subprocess != NULL) {
+ g_subprocess_wait (state->session_subprocess, NULL, NULL);
+ }
+}
+
+static gboolean
+register_display (State *state,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ gboolean registered = FALSE;
+ GVariantBuilder details;
+
+ g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
+ g_variant_builder_add (&details, "{ss}", "session-type", "wayland");
+
+ registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy,
+ g_variant_builder_end (&details),
+ cancellable,
+ &error);
+ if (error != NULL) {
+ g_debug ("Could not register display: %s", error->message);
+ g_error_free (error);
+ }
+
+ return registered;
+}
+
+static void
+init_state (State **state)
+{
+ static State state_allocation;
+
+ *state = &state_allocation;
+}
+
+static void
+clear_state (State **out_state)
+{
+ State *state = *out_state;
+
+ g_clear_object (&state->cancellable);
+ g_clear_object (&state->bus_connection);
+ g_clear_object (&state->session_subprocess);
+ g_clear_pointer (&state->environment, g_strfreev);
+ g_clear_pointer (&state->main_loop, g_main_loop_unref);
+ g_clear_handle_id (&state->register_session_id, g_source_remove);
+ *out_state = NULL;
+}
+
+static gboolean
+on_sigterm (State *state)
+{
+ g_cancellable_cancel (state->cancellable);
+
+ if (g_main_loop_is_running (state->main_loop)) {
+ g_main_loop_quit (state->main_loop);
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+register_session_timeout_cb (gpointer user_data)
+{
+ State *state;
+ GError *error = NULL;
+
+ state = (State *) user_data;
+
+ gdm_dbus_manager_call_register_session_sync (state->display_manager_proxy,
+ g_variant_new ("a{sv}", NULL),
+ state->cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_warning ("Could not register session: %s", error->message);
+ g_error_free (error);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+connect_to_display_manager (State *state)
+{
+ g_autoptr (GError) error = NULL;
+
+ state->display_manager_proxy = gdm_dbus_manager_proxy_new_for_bus_sync (
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ "org.gnome.DisplayManager",
+ "/org/gnome/DisplayManager/Manager",
+ state->cancellable,
+ &error);
+
+ if (state->display_manager_proxy == NULL) {
+ g_printerr ("gdm-wayland-session: could not contact display manager: %s\n",
+ error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ State *state = NULL;
+ GOptionContext *context = NULL;
+ static char **args = NULL;
+ gboolean debug = FALSE;
+ gboolean ret;
+ int exit_status = EX_OK;
+ static gboolean register_session = FALSE;
+
+ static GOptionEntry entries [] = {
+ { "register-session", 0, 0, G_OPTION_ARG_NONE, &register_session, "Register session after a delay", NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" },
+ { NULL }
+ };
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ setlocale (LC_ALL, "");
+
+ gdm_log_init ();
+
+ context = g_option_context_new (_("GNOME Display Manager Wayland Session Launcher"));
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (args == NULL || args[0] == NULL || args[1] != NULL) {
+ g_warning ("gdm-wayland-session takes one argument (the session)");
+ exit_status = EX_USAGE;
+ goto out;
+ }
+
+ init_state (&state);
+
+ state->session_command = args[0];
+
+ state->settings = gdm_settings_new ();
+ ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/");
+
+ if (!ret) {
+ g_printerr ("Unable to initialize settings\n");
+ exit_status = EX_DATAERR;
+ goto out;
+ }
+
+ gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
+ state->debug_enabled = debug;
+
+ gdm_log_set_debug (debug);
+
+ state->main_loop = g_main_loop_new (NULL, FALSE);
+ state->cancellable = g_cancellable_new ();
+
+ g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state);
+
+ ret = spawn_bus (state, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to run session message bus\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ import_environment (state, state->cancellable);
+
+ ret = spawn_session (state, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to run session\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ if (!connect_to_display_manager (state))
+ goto out;
+
+ ret = register_display (state, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to register display with display manager\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ if (register_session) {
+ g_debug ("gdm-wayland-session: Will register session in %d seconds", REGISTER_SESSION_TIMEOUT);
+ state->register_session_id = g_timeout_add_seconds (REGISTER_SESSION_TIMEOUT,
+ register_session_timeout_cb,
+ state);
+ } else {
+ g_debug ("gdm-wayland-session: Session will register itself");
+ }
+
+ g_main_loop_run (state->main_loop);
+
+ /* Only use exit status of session if we're here because it exit */
+
+ if (state->session_subprocess == NULL) {
+ exit_status = state->session_exit_status;
+ }
+
+out:
+ if (state != NULL) {
+ signal_subprocesses (state);
+ wait_on_subprocesses (state);
+ clear_state (&state);
+ }
+
+ return exit_status;
+}
diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c
new file mode 100644
index 0000000..0b07ab5
--- /dev/null
+++ b/daemon/gdm-x-session.c
@@ -0,0 +1,997 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include "config.h"
+
+#include <locale.h>
+#include <sysexits.h>
+
+#include "gdm-common.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+#include "gdm-log.h"
+
+#include "gdm-manager-glue.h"
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-unix.h>
+#include <glib.h>
+#include <gio/gunixinputstream.h>
+#include <glib-unix.h>
+#include <X11/Xauth.h>
+
+#define DISPLAY_FILENO (STDERR_FILENO + 1)
+#define BUS_ADDRESS_FILENO (DISPLAY_FILENO + 1)
+
+typedef struct
+{
+ GdmSettings *settings;
+ GCancellable *cancellable;
+
+ GSubprocess *x_subprocess;
+ char *auth_file;
+ char *display_name;
+
+ GSubprocess *bus_subprocess;
+ GDBusConnection *bus_connection;
+ GdmDBusManager *display_manager_proxy;
+ char *bus_address;
+
+ char **environment;
+
+ GSubprocess *session_subprocess;
+ char *session_command;
+ int session_exit_status;
+
+ guint register_session_id;
+
+ GMainLoop *main_loop;
+
+ guint32 debug_enabled : 1;
+} State;
+
+static FILE *
+create_auth_file (char **filename)
+{
+ char *auth_dir = NULL;
+ char *auth_file = NULL;
+ int fd;
+ FILE *fp = NULL;
+
+ auth_dir = g_build_filename (g_get_user_runtime_dir (),
+ "gdm",
+ NULL);
+
+ g_mkdir_with_parents (auth_dir, 0711);
+ auth_file = g_build_filename (auth_dir, "Xauthority", NULL);
+ g_clear_pointer (&auth_dir, g_free);
+
+ fd = g_open (auth_file, O_RDWR | O_CREAT | O_TRUNC, 0700);
+
+ if (fd < 0) {
+ g_debug ("could not open %s to store auth cookie: %m",
+ auth_file);
+ g_clear_pointer (&auth_file, g_free);
+ goto out;
+ }
+
+ fp = fdopen (fd, "w+");
+
+ if (fp == NULL) {
+ g_debug ("could not set up stream for auth cookie file: %m");
+ g_clear_pointer (&auth_file, g_free);
+ close (fd);
+ goto out;
+ }
+
+ *filename = auth_file;
+out:
+ return fp;
+}
+
+static char *
+prepare_auth_file (void)
+{
+ FILE *fp = NULL;
+ char *filename = NULL;
+ GError *error = NULL;
+ gboolean prepared = FALSE;
+ Xauth auth_entry = { 0 };
+ char localhost[HOST_NAME_MAX + 1] = "";
+
+ g_debug ("Preparing auth file for X server");
+
+ fp = create_auth_file (&filename);
+
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ if (gethostname (localhost, HOST_NAME_MAX) < 0) {
+ strncpy (localhost, "localhost", sizeof (localhost) - 1);
+ }
+
+ auth_entry.family = FamilyLocal;
+ auth_entry.address = localhost;
+ auth_entry.address_length = strlen (auth_entry.address);
+ auth_entry.name = "MIT-MAGIC-COOKIE-1";
+ auth_entry.name_length = strlen (auth_entry.name);
+
+ auth_entry.data_length = 16;
+ auth_entry.data = gdm_generate_random_bytes (auth_entry.data_length, &error);
+
+ if (error != NULL) {
+ goto out;
+ }
+
+ if (!XauWriteAuth (fp, &auth_entry) || fflush (fp) == EOF) {
+ goto out;
+ }
+
+ auth_entry.family = FamilyWild;
+ if (!XauWriteAuth (fp, &auth_entry) || fflush (fp) == EOF) {
+ goto out;
+ }
+
+ prepared = TRUE;
+
+out:
+ g_clear_pointer (&auth_entry.data, g_free);
+ g_clear_pointer (&fp, fclose);
+
+ if (!prepared) {
+ g_clear_pointer (&filename, g_free);
+ }
+
+ return filename;
+}
+
+static void
+on_x_server_finished (GSubprocess *subprocess,
+ GAsyncResult *result,
+ State *state)
+{
+ gboolean cancelled;
+
+ cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
+
+ if (cancelled) {
+ goto out;
+ }
+
+ if (g_subprocess_get_if_exited (subprocess)) {
+ int exit_status;
+
+ exit_status = g_subprocess_get_exit_status (subprocess);
+
+ g_debug ("X server exited with status %d", exit_status);
+ } else {
+ int signal_number;
+
+ signal_number = g_subprocess_get_term_sig (subprocess);
+ g_debug ("X server was killed with status %d", signal_number);
+ }
+
+ g_clear_object (&state->x_subprocess);
+out:
+ g_main_loop_quit (state->main_loop);
+}
+
+static gboolean
+spawn_x_server (State *state,
+ gboolean allow_remote_connections,
+ GCancellable *cancellable)
+{
+ GPtrArray *arguments = NULL;
+ GSubprocessLauncher *launcher = NULL;
+ GSubprocess *subprocess = NULL;
+ GInputStream *input_stream = NULL;
+ GDataInputStream *data_stream = NULL;
+ GError *error = NULL;
+
+ char *auth_file;
+ gboolean is_running = FALSE;
+ int ret;
+ int pipe_fds[2];
+ char *display_fd_string = NULL;
+ char *vt_string = NULL;
+ char *display_number;
+ gsize display_number_size;
+
+ auth_file = prepare_auth_file ();
+
+ g_debug ("Running X server");
+
+ ret = g_unix_open_pipe (pipe_fds, FD_CLOEXEC, &error);
+
+ if (!ret) {
+ g_debug ("could not open pipe: %s", error->message);
+ goto out;
+ }
+
+ arguments = g_ptr_array_new ();
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_INHERIT);
+ g_subprocess_launcher_setenv (launcher, "XORG_RUN_AS_USER_OK", "1", TRUE);
+ g_subprocess_launcher_take_fd (launcher, pipe_fds[1], DISPLAY_FILENO);
+
+ if (g_getenv ("XDG_VTNR") != NULL) {
+ int vt;
+
+ vt = atoi (g_getenv ("XDG_VTNR"));
+
+ if (vt > 0 && vt < 64) {
+ vt_string = g_strdup_printf ("vt%d", vt);
+ }
+ }
+
+ display_fd_string = g_strdup_printf ("%d", DISPLAY_FILENO);
+
+ g_ptr_array_add (arguments, X_SERVER);
+
+ if (vt_string != NULL) {
+ g_ptr_array_add (arguments, vt_string);
+ }
+
+ g_ptr_array_add (arguments, "-displayfd");
+ g_ptr_array_add (arguments, display_fd_string);
+
+ g_ptr_array_add (arguments, "-auth");
+ g_ptr_array_add (arguments, auth_file);
+
+ /* If we were compiled with Xserver >= 1.17 we need to specify
+ * '-listen tcp' as the X server doesn't listen on tcp sockets
+ * by default anymore. In older versions we need to pass
+ * -nolisten tcp to disable listening on tcp sockets.
+ */
+ if (!allow_remote_connections) {
+ g_ptr_array_add (arguments, "-nolisten");
+ g_ptr_array_add (arguments, "tcp");
+ }
+
+#ifdef HAVE_XSERVER_WITH_LISTEN
+ if (allow_remote_connections) {
+ g_ptr_array_add (arguments, "-listen");
+ g_ptr_array_add (arguments, "tcp");
+ }
+#endif
+
+ g_ptr_array_add (arguments, "-background");
+ g_ptr_array_add (arguments, "none");
+
+ g_ptr_array_add (arguments, "-noreset");
+ g_ptr_array_add (arguments, "-keeptty");
+ g_ptr_array_add (arguments, "-novtswitch");
+
+ g_ptr_array_add (arguments, "-verbose");
+ if (state->debug_enabled) {
+ g_ptr_array_add (arguments, "7");
+ } else {
+ g_ptr_array_add (arguments, "3");
+ }
+
+ if (state->debug_enabled) {
+ g_ptr_array_add (arguments, "-core");
+ }
+ g_ptr_array_add (arguments, NULL);
+
+ subprocess = g_subprocess_launcher_spawnv (launcher,
+ (const char * const *) arguments->pdata,
+ &error);
+ g_free (display_fd_string);
+ g_clear_object (&launcher);
+ g_ptr_array_free (arguments, TRUE);
+
+ if (subprocess == NULL) {
+ g_debug ("could not start X server: %s", error->message);
+ goto out;
+ }
+
+ input_stream = g_unix_input_stream_new (pipe_fds[0], TRUE);
+ data_stream = g_data_input_stream_new (input_stream);
+ g_clear_object (&input_stream);
+
+ display_number = g_data_input_stream_read_line (data_stream,
+ &display_number_size,
+ cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_debug ("could not read display string from X server: %s", error->message);
+ goto out;
+ }
+
+ if (display_number == NULL) {
+ g_debug ("X server did not write display string");
+ goto out;
+ }
+
+ state->display_name = g_strdup_printf (":%s", display_number);
+ g_clear_pointer (&display_number, g_free);
+
+ state->auth_file = g_strdup (auth_file);
+ state->x_subprocess = g_object_ref (subprocess);
+
+ g_subprocess_wait_async (state->x_subprocess,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_x_server_finished,
+ state);
+
+ is_running = TRUE;
+out:
+ g_clear_pointer (&auth_file, g_free);
+ g_clear_object (&data_stream);
+ g_clear_object (&subprocess);
+ g_clear_object (&launcher);
+ g_clear_error (&error);
+
+ return is_running;
+}
+
+static void
+on_bus_finished (GSubprocess *subprocess,
+ GAsyncResult *result,
+ State *state)
+{
+ gboolean cancelled;
+
+ cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
+
+ if (cancelled) {
+ goto out;
+ }
+
+ if (g_subprocess_get_if_exited (subprocess)) {
+ int exit_status;
+
+ exit_status = g_subprocess_get_exit_status (subprocess);
+
+ g_debug ("message bus exited with status %d", exit_status);
+ } else {
+ int signal_number;
+
+ signal_number = g_subprocess_get_term_sig (subprocess);
+ g_debug ("message bus was killed with status %d", signal_number);
+ }
+
+ g_clear_object (&state->bus_subprocess);
+out:
+ g_main_loop_quit (state->main_loop);
+}
+
+static gboolean
+update_bus_environment (State *state,
+ GCancellable *cancellable)
+{
+ GVariantBuilder builder;
+ GVariant *reply = NULL;
+ GError *error = NULL;
+ gboolean environment_updated = FALSE;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}"));
+ g_variant_builder_add (&builder, "{ss}", "DISPLAY", state->display_name);
+ g_variant_builder_add (&builder, "{ss}", "XAUTHORITY", state->auth_file);
+
+ reply = g_dbus_connection_call_sync (state->bus_connection,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "UpdateActivationEnvironment",
+ g_variant_new ("(@a{ss})",
+ g_variant_builder_end (&builder)),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, cancellable, &error);
+
+ if (reply == NULL) {
+ g_debug ("could not update activation environment: %s", error->message);
+ goto out;
+ }
+
+ g_variant_unref (reply);
+
+ environment_updated = TRUE;
+
+out:
+ g_clear_error (&error);
+
+ return environment_updated;
+}
+
+static gboolean
+spawn_bus (State *state,
+ GCancellable *cancellable)
+{
+ GDBusConnection *bus_connection = NULL;
+ GPtrArray *arguments = NULL;
+ GSubprocessLauncher *launcher = NULL;
+ GSubprocess *subprocess = NULL;
+ GInputStream *input_stream = NULL;
+ GDataInputStream *data_stream = NULL;
+ GError *error = NULL;
+ char *bus_address_fd_string;
+ char *bus_address = NULL;
+ gsize bus_address_size;
+
+ gboolean is_running = FALSE;
+ int ret;
+ int pipe_fds[2];
+
+ g_debug ("Running session message bus");
+
+ bus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
+ cancellable,
+ NULL);
+
+ if (bus_connection != NULL) {
+ g_debug ("session message bus already running, not starting another one");
+ state->bus_connection = bus_connection;
+ return TRUE;
+ }
+
+ ret = g_unix_open_pipe (pipe_fds, FD_CLOEXEC, &error);
+
+ if (!ret) {
+ g_debug ("could not open pipe: %s", error->message);
+ goto out;
+ }
+
+ arguments = g_ptr_array_new ();
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+
+ g_subprocess_launcher_take_fd (launcher, pipe_fds[1], BUS_ADDRESS_FILENO);
+
+ bus_address_fd_string = g_strdup_printf ("%d", BUS_ADDRESS_FILENO);
+
+ g_ptr_array_add (arguments, "dbus-daemon");
+
+ g_ptr_array_add (arguments, "--print-address");
+ g_ptr_array_add (arguments, bus_address_fd_string);
+ g_ptr_array_add (arguments, "--session");
+ g_ptr_array_add (arguments, NULL);
+
+ subprocess = g_subprocess_launcher_spawnv (launcher,
+ (const char * const *) arguments->pdata,
+ &error);
+ g_free (bus_address_fd_string);
+ g_clear_object (&launcher);
+ g_ptr_array_free (arguments, TRUE);
+
+ if (subprocess == NULL) {
+ g_debug ("could not start dbus-daemon: %s", error->message);
+ goto out;
+ }
+
+ input_stream = g_unix_input_stream_new (pipe_fds[0], TRUE);
+ data_stream = g_data_input_stream_new (input_stream);
+ g_clear_object (&input_stream);
+
+ bus_address = g_data_input_stream_read_line (data_stream,
+ &bus_address_size,
+ cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_debug ("could not read address from session message bus: %s", error->message);
+ goto out;
+ }
+
+ if (bus_address == NULL) {
+ g_debug ("session message bus did not write address");
+ goto out;
+ }
+
+ state->bus_address = bus_address;
+
+ state->bus_subprocess = g_object_ref (subprocess);
+
+ g_subprocess_wait_async (state->bus_subprocess,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_bus_finished,
+ state);
+
+
+ bus_connection = g_dbus_connection_new_for_address_sync (state->bus_address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
+ NULL,
+ cancellable,
+ &error);
+
+ if (bus_connection == NULL) {
+ g_debug ("could not open connection to session bus: %s",
+ error->message);
+ goto out;
+ }
+
+ state->bus_connection = bus_connection;
+
+ is_running = TRUE;
+out:
+ g_clear_object (&data_stream);
+ g_clear_object (&subprocess);
+ g_clear_object (&launcher);
+ g_clear_error (&error);
+
+ return is_running;
+}
+
+static gboolean
+import_environment (State *state,
+ GCancellable *cancellable)
+{
+ g_autoptr(GVariant) reply = NULL;
+ g_autoptr(GVariant) environment_variant = NULL;
+ g_autoptr(GError) error = NULL;
+
+ reply = g_dbus_connection_call_sync (state->bus_connection,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.freedesktop.systemd1.Manager",
+ "Environment"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, cancellable, &error);
+
+ if (reply == NULL) {
+ g_debug ("could not fetch environment: %s", error->message);
+ return FALSE;
+ }
+
+ g_variant_get (reply, "(v)", &environment_variant);
+
+ state->environment = g_variant_dup_strv (environment_variant, NULL);
+
+ return TRUE;
+}
+
+static void
+on_session_finished (GSubprocess *subprocess,
+ GAsyncResult *result,
+ State *state)
+{
+ gboolean cancelled;
+
+ cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
+
+ if (cancelled) {
+ goto out;
+ }
+
+ if (g_subprocess_get_if_exited (subprocess)) {
+ int exit_status;
+
+ exit_status = g_subprocess_get_exit_status (subprocess);
+
+ g_debug ("session exited with status %d", exit_status);
+
+ state->session_exit_status = exit_status;
+ } else {
+ int signal_number;
+
+ signal_number = g_subprocess_get_term_sig (subprocess);
+ g_debug ("session was killed with status %d", signal_number);
+ }
+
+ g_clear_object (&state->session_subprocess);
+out:
+ g_main_loop_quit (state->main_loop);
+}
+
+static gboolean
+spawn_session (State *state,
+ gboolean run_script,
+ GCancellable *cancellable)
+{
+ GSubprocessLauncher *launcher = NULL;
+ GSubprocess *subprocess = NULL;
+ GError *error = NULL;
+ gboolean is_running = FALSE;
+ const char *vt;
+ static const char *session_variables[] = { "DISPLAY",
+ "XAUTHORITY",
+ "WAYLAND_DISPLAY",
+ "WAYLAND_SOCKET",
+ "GNOME_SHELL_SESSION_MODE",
+ NULL };
+ /* The environment variables listed below are those we have set (or
+ * received from our own execution environment) only as a fallback to
+ * make things work, as opposed to a information directly pertaining to
+ * the session about to be started. Variables listed here will not
+ * overwrite the existing environment (possibly) imported from the
+ * systemd --user instance.
+ * As an example: We need a PATH for some of the launched subprocesses
+ * to work, but if the user (or the distributor) has customized the PATH
+ * via one of systemds user-environment-generators, that version should
+ * be preferred. */
+ static const char *fallback_variables[] = { "PATH", NULL };
+
+ g_debug ("Running X session");
+
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+
+ if (state->environment != NULL) {
+ size_t i;
+
+ for (i = 0; state->environment[i] != NULL; i++) {
+ g_auto(GStrv) environment_entry = NULL;
+
+ if (state->environment[i][0] == '\0') {
+ continue;
+ }
+
+ environment_entry = g_strsplit (state->environment[i], "=", 2);
+
+ if (environment_entry[0] == NULL || environment_entry[1] == NULL) {
+ continue;
+ }
+
+ /* Merge the environment block imported from systemd --user with the
+ * environment we have set for ourselves (and thus pass on to the
+ * launcher process). Variables we have set have precedence, as to not
+ * import stale data from prior user sessions, with the exception of
+ * those listed in fallback_variables. See the comment there for more
+ * explanations. */
+ g_subprocess_launcher_setenv (launcher,
+ environment_entry[0],
+ environment_entry[1],
+ g_strv_contains (fallback_variables, environment_entry[0]));
+ }
+
+ /* Don't allow session specific environment variables from earlier sessions to
+ * leak through */
+ for (i = 0; session_variables[i] != NULL; i++) {
+ if (g_getenv (session_variables[i]) == NULL) {
+ g_subprocess_launcher_unsetenv (launcher, session_variables[i]);
+ }
+ }
+ }
+
+ g_subprocess_launcher_setenv (launcher, "DISPLAY", state->display_name, TRUE);
+ g_subprocess_launcher_setenv (launcher, "XAUTHORITY", state->auth_file, TRUE);
+
+ if (state->bus_address != NULL) {
+ g_subprocess_launcher_setenv (launcher, "DBUS_SESSION_BUS_ADDRESS", state->bus_address, TRUE);
+ }
+
+ vt = g_getenv ("XDG_VTNR");
+
+ if (vt != NULL) {
+ g_subprocess_launcher_setenv (launcher, "WINDOWPATH", vt, TRUE);
+ }
+
+ if (run_script) {
+ subprocess = g_subprocess_launcher_spawn (launcher,
+ &error,
+ GDMCONFDIR "/Xsession",
+ state->session_command,
+ NULL);
+ } else {
+ int ret;
+ char **argv;
+
+ ret = g_shell_parse_argv (state->session_command,
+ NULL,
+ &argv,
+ &error);
+
+ if (!ret) {
+ g_debug ("could not parse session arguments: %s", error->message);
+ goto out;
+ }
+ subprocess = g_subprocess_launcher_spawnv (launcher,
+ (const char * const *) argv,
+ &error);
+ g_strfreev (argv);
+ }
+
+ if (subprocess == NULL) {
+ g_debug ("could not start session: %s", error->message);
+ goto out;
+ }
+
+ state->session_subprocess = g_object_ref (subprocess);
+
+ g_subprocess_wait_async (state->session_subprocess,
+ cancellable,
+ (GAsyncReadyCallback)
+ on_session_finished,
+ state);
+
+ is_running = TRUE;
+out:
+ g_clear_object (&subprocess);
+ return is_running;
+}
+
+static void
+signal_subprocesses (State *state)
+{
+ if (state->session_subprocess != NULL) {
+ g_subprocess_send_signal (state->session_subprocess, SIGTERM);
+ }
+
+ if (state->bus_subprocess != NULL) {
+ g_subprocess_send_signal (state->bus_subprocess, SIGTERM);
+ }
+
+ if (state->x_subprocess != NULL) {
+ g_subprocess_send_signal (state->x_subprocess, SIGTERM);
+ }
+}
+
+static void
+wait_on_subprocesses (State *state)
+{
+ if (state->x_subprocess != NULL) {
+ g_subprocess_wait (state->x_subprocess, NULL, NULL);
+ }
+
+ if (state->bus_subprocess != NULL) {
+ g_subprocess_wait (state->bus_subprocess, NULL, NULL);
+ }
+
+ if (state->session_subprocess != NULL) {
+ g_subprocess_wait (state->session_subprocess, NULL, NULL);
+ }
+}
+
+static gboolean
+register_display (State *state,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ gboolean registered = FALSE;
+ GVariantBuilder details;
+
+ g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
+ g_variant_builder_add (&details, "{ss}", "session-type", "x11");
+ g_variant_builder_add (&details, "{ss}", "x11-display-name", state->display_name);
+
+ registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy,
+ g_variant_builder_end (&details),
+ cancellable,
+ &error);
+ if (error != NULL) {
+ g_debug ("Could not register display: %s", error->message);
+ g_error_free (error);
+ }
+
+ return registered;
+}
+
+static void
+init_state (State **state)
+{
+ static State state_allocation;
+
+ *state = &state_allocation;
+}
+
+static void
+clear_state (State **out_state)
+{
+ State *state = *out_state;
+
+ g_clear_object (&state->cancellable);
+ g_clear_object (&state->bus_connection);
+ g_clear_object (&state->session_subprocess);
+ g_clear_object (&state->x_subprocess);
+ g_clear_pointer (&state->environment, g_strfreev);
+ g_clear_pointer (&state->auth_file, g_free);
+ g_clear_pointer (&state->display_name, g_free);
+ g_clear_pointer (&state->main_loop, g_main_loop_unref);
+ g_clear_handle_id (&state->register_session_id, g_source_remove);
+ *out_state = NULL;
+}
+
+static gboolean
+on_sigterm (State *state)
+{
+ g_cancellable_cancel (state->cancellable);
+
+ if (g_main_loop_is_running (state->main_loop)) {
+ g_main_loop_quit (state->main_loop);
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+register_session_timeout_cb (gpointer user_data)
+{
+ State *state;
+ GError *error = NULL;
+
+ state = (State *) user_data;
+
+ gdm_dbus_manager_call_register_session_sync (state->display_manager_proxy,
+ g_variant_new ("a{sv}", NULL),
+ state->cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_warning ("Could not register session: %s", error->message);
+ g_error_free (error);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+connect_to_display_manager (State *state)
+{
+ g_autoptr (GError) error = NULL;
+
+ state->display_manager_proxy = gdm_dbus_manager_proxy_new_for_bus_sync (
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ "org.gnome.DisplayManager",
+ "/org/gnome/DisplayManager/Manager",
+ state->cancellable,
+ &error);
+
+ if (state->display_manager_proxy == NULL) {
+ g_printerr ("gdm-x-session: could not contact display manager: %s\n",
+ error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ State *state = NULL;
+ GOptionContext *context = NULL;
+ static char **args = NULL;
+ static gboolean run_script = FALSE;
+ static gboolean allow_remote_connections = FALSE;
+ gboolean debug = FALSE;
+ gboolean ret;
+ int exit_status = EX_OK;
+ static gboolean register_session = FALSE;
+
+ static GOptionEntry entries [] = {
+ { "run-script", 'r', 0, G_OPTION_ARG_NONE, &run_script, N_("Run program through /etc/gdm/Xsession wrapper script"), NULL },
+ { "allow-remote-connections", 'a', 0, G_OPTION_ARG_NONE, &allow_remote_connections, N_("Listen on TCP socket"), NULL },
+ { "register-session", 0, 0, G_OPTION_ARG_NONE, &register_session, "Register session after a delay", NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" },
+ { NULL }
+ };
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ setlocale (LC_ALL, "");
+
+ gdm_log_init ();
+
+ context = g_option_context_new (_("GNOME Display Manager X Session Launcher"));
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (args == NULL || args[0] == NULL || args[1] != NULL) {
+ g_warning ("gdm-x-session takes one argument (the session)");
+ exit_status = EX_USAGE;
+ goto out;
+ }
+
+ init_state (&state);
+
+ state->session_command = args[0];
+
+ state->settings = gdm_settings_new ();
+ ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/");
+
+ if (!ret) {
+ g_printerr ("Unable to initialize settings\n");
+ exit_status = EX_DATAERR;
+ goto out;
+ }
+
+ gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
+ state->debug_enabled = debug;
+
+ gdm_log_set_debug (debug);
+
+ state->main_loop = g_main_loop_new (NULL, FALSE);
+ state->cancellable = g_cancellable_new ();
+
+ g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state);
+
+ ret = spawn_x_server (state, allow_remote_connections, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to run X server\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ ret = spawn_bus (state, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to run session message bus\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ import_environment (state, state->cancellable);
+
+ ret = update_bus_environment (state, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to update bus environment\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ if (!connect_to_display_manager (state))
+ goto out;
+
+ ret = register_display (state, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to register display with display manager\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ ret = spawn_session (state, run_script, state->cancellable);
+
+ if (!ret) {
+ g_printerr ("Unable to run session\n");
+ exit_status = EX_SOFTWARE;
+ goto out;
+ }
+
+ if (register_session) {
+ g_debug ("gdm-x-session: Will register session in %d seconds", REGISTER_SESSION_TIMEOUT);
+ state->register_session_id = g_timeout_add_seconds (REGISTER_SESSION_TIMEOUT,
+ register_session_timeout_cb,
+ state);
+ } else {
+ g_debug ("gdm-x-session: Session will register itself");
+ }
+
+ g_main_loop_run (state->main_loop);
+
+ /* Only use exit status of session if we're here because it exit */
+
+ if (state->session_subprocess == NULL) {
+ exit_status = state->session_exit_status;
+ }
+
+out:
+ if (state != NULL) {
+ signal_subprocesses (state);
+ wait_on_subprocesses (state);
+ clear_state (&state);
+ }
+
+ return exit_status;
+}
diff --git a/daemon/gdm-xdmcp-chooser-display.c b/daemon/gdm-xdmcp-chooser-display.c
new file mode 100644
index 0000000..d593cb4
--- /dev/null
+++ b/daemon/gdm-xdmcp-chooser-display.c
@@ -0,0 +1,146 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-display.h"
+#include "gdm-launch-environment.h"
+#include "gdm-xdmcp-chooser-display.h"
+
+#include "gdm-common.h"
+#include "gdm-address.h"
+
+enum {
+ HOSTNAME_SELECTED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_xdmcp_chooser_display_class_init (GdmXdmcpChooserDisplayClass *klass);
+static void gdm_xdmcp_chooser_display_init (GdmXdmcpChooserDisplay *xdmcp_chooser_display);
+static gboolean gdm_xdmcp_chooser_display_prepare (GdmDisplay *display);
+
+G_DEFINE_TYPE (GdmXdmcpChooserDisplay, gdm_xdmcp_chooser_display, GDM_TYPE_XDMCP_DISPLAY)
+
+static void
+on_hostname_selected (GdmLaunchEnvironment *launch_environment,
+ const char *hostname,
+ GdmXdmcpChooserDisplay *display)
+{
+ g_debug ("GdmXdmcpChooserDisplay: hostname selected: %s", hostname);
+ g_signal_emit (display, signals [HOSTNAME_SELECTED], 0, hostname);
+}
+
+static void
+gdm_xdmcp_chooser_display_class_init (GdmXdmcpChooserDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass);
+
+ display_class->prepare = gdm_xdmcp_chooser_display_prepare;
+
+ signals [HOSTNAME_SELECTED] =
+ g_signal_new ("hostname-selected",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GdmXdmcpChooserDisplayClass, hostname_selected),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_STRING);
+}
+
+static void
+gdm_xdmcp_chooser_display_init (GdmXdmcpChooserDisplay *xdmcp_chooser_display)
+{
+}
+
+static gboolean
+gdm_xdmcp_chooser_display_prepare (GdmDisplay *display)
+{
+ GdmXdmcpDisplay *self = GDM_XDMCP_DISPLAY (display);
+ GdmLaunchEnvironment *launch_environment;
+ char *display_name;
+ char *seat_id;
+ char *hostname;
+
+ launch_environment = NULL;
+ display_name = NULL;
+ seat_id = NULL;
+ hostname = NULL;
+
+ g_object_get (self,
+ "x11-display-name", &display_name,
+ "seat-id", &seat_id,
+ "remote-hostname", &hostname,
+ NULL);
+
+ launch_environment = gdm_create_chooser_launch_environment (display_name,
+ seat_id,
+ hostname);
+ g_object_set (self, "launch-environment", launch_environment, NULL);
+ g_object_unref (launch_environment);
+
+ g_signal_connect (launch_environment, "hostname-selected",
+ G_CALLBACK (on_hostname_selected), display);
+
+ return GDM_DISPLAY_CLASS (gdm_xdmcp_chooser_display_parent_class)->prepare (display);
+}
+
+GdmDisplay *
+gdm_xdmcp_chooser_display_new (const char *hostname,
+ int number,
+ GdmAddress *address,
+ gint32 session_number)
+{
+ GObject *object;
+ char *x11_display;
+
+ x11_display = g_strdup_printf ("%s:%d", hostname, number);
+ object = g_object_new (GDM_TYPE_XDMCP_CHOOSER_DISPLAY,
+ "remote-hostname", hostname,
+ "x11-display-number", number,
+ "x11-display-name", x11_display,
+ "is-local", FALSE,
+ "remote-address", address,
+ "session-number", session_number,
+ NULL);
+ g_free (x11_display);
+
+ return GDM_DISPLAY (object);
+}
diff --git a/daemon/gdm-xdmcp-chooser-display.h b/daemon/gdm-xdmcp-chooser-display.h
new file mode 100644
index 0000000..7890afa
--- /dev/null
+++ b/daemon/gdm-xdmcp-chooser-display.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann <jmccann@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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_XDMCP_CHOOSER_DISPLAY_H
+#define __GDM_XDMCP_CHOOSER_DISPLAY_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <glib-object.h>
+
+#include "gdm-xdmcp-display.h"
+#include "gdm-address.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_XDMCP_CHOOSER_DISPLAY (gdm_xdmcp_chooser_display_get_type ())
+#define GDM_XDMCP_CHOOSER_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_XDMCP_CHOOSER_DISPLAY, GdmXdmcpChooserDisplay))
+#define GDM_XDMCP_CHOOSER_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_XDMCP_CHOOSER_DISPLAY, GdmXdmcpChooserDisplayClass))
+#define GDM_IS_XDMCP_CHOOSER_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_XDMCP_CHOOSER_DISPLAY))
+#define GDM_IS_XDMCP_CHOOSER_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_XDMCP_CHOOSER_DISPLAY))
+#define GDM_XDMCP_CHOOSER_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_XDMCP_CHOOSER_DISPLAY, GdmXdmcpChooserDisplayClass))
+
+typedef struct GdmXdmcpChooserDisplayPrivate GdmXdmcpChooserDisplayPrivate;
+
+typedef struct
+{
+ GdmXdmcpDisplay parent;
+} GdmXdmcpChooserDisplay;
+
+typedef struct
+{
+ GdmXdmcpDisplayClass parent_class;
+
+ void (* hostname_selected) (GdmXdmcpChooserDisplay *display,
+ const char *hostname);
+} GdmXdmcpChooserDisplayClass;
+
+GType gdm_xdmcp_chooser_display_get_type (void);
+
+
+GdmDisplay * gdm_xdmcp_chooser_display_new (const char *hostname,
+ int number,
+ GdmAddress *addr,
+ gint32 serial_number);
+
+G_END_DECLS
+
+#endif /* __GDM_XDMCP_CHOOSER_DISPLAY_H */
diff --git a/daemon/gdm-xdmcp-display-factory.c b/daemon/gdm-xdmcp-display-factory.c
new file mode 100644
index 0000000..abb58fa
--- /dev/null
+++ b/daemon/gdm-xdmcp-display-factory.c
@@ -0,0 +1,3486 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 1998, 1999, 2000 Martin K. Petersen <mkp@mkp.net>
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#include <sys/ioctl.h>
+
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xmd.h>
+#include <X11/Xdmcp.h>
+
+#include "gdm-common.h"
+#include "gdm-xdmcp-chooser-display.h"
+#include "gdm-display-factory.h"
+#include "gdm-launch-environment.h"
+#include "gdm-xdmcp-display-factory.h"
+#include "gdm-display-store.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+/*
+ * On Sun, we need to define allow_severity and deny_severity to link
+ * against libwrap.
+ */
+#ifdef __sun
+#include <syslog.h>
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif
+
+#define DEFAULT_PORT 177
+#define DEFAULT_USE_MULTICAST FALSE
+#define DEFAULT_MULTICAST_ADDRESS "ff02::1"
+#define DEFAULT_HONOR_INDIRECT TRUE
+#define DEFAULT_MAX_DISPLAYS_PER_HOST 1
+#define DEFAULT_MAX_DISPLAYS 16
+#define DEFAULT_MAX_PENDING_DISPLAYS 4
+#define DEFAULT_MAX_WAIT 30
+#define DEFAULT_MAX_WAIT_INDIRECT 30
+#define DEFAULT_WILLING_SCRIPT GDMCONFDIR "/Xwilling"
+
+#define GDM_MAX_FORWARD_QUERIES 10
+#define GDM_FORWARD_QUERY_TIMEOUT 30
+#define MANAGED_FORWARD_INTERVAL 1500 /* 1.5 seconds */
+
+/* some extra XDMCP opcodes that xdm will happily ignore since they'll be
+ * the wrong XDMCP version anyway */
+#define GDM_XDMCP_PROTOCOL_VERSION 1001
+enum {
+ GDM_XDMCP_FIRST_OPCODE = 1000, /*just a marker, not an opcode */
+
+ GDM_XDMCP_MANAGED_FORWARD = 1000,
+ /* manager (master) -> manager
+ * A packet with MANAGED_FORWARD is sent to the
+ * manager that sent the forward query from the manager to
+ * which forward query was sent. It indicates that the forward
+ * was fully processed and that the client now has either
+ * a managed session, or has been sent denial, refuse or failed.
+ * (if the denial gets lost then client gets dumped into the
+ * chooser again). This should be resent a few times
+ * until some (short) timeout or until GOT_MANAGED_FORWARD
+ * is sent. GDM sends at most 3 packates with 1.5 seconds
+ * between each.
+ *
+ * Argument is ARRAY8 with the address of the originating host */
+ GDM_XDMCP_GOT_MANAGED_FORWARD,
+ /* manager -> manager (master)
+ * A single packet with GOT_MANAGED_FORWARD is sent to indicate
+ * that we did receive the MANAGED_FORWARD packet. The argument
+ * must match the MANAGED_FORWARD one or it will just be ignored.
+ *
+ * Argument is ARRAY8 with the address of the originating host */
+ GDM_XDMCP_LAST_OPCODE /*just a marker, not an opcode */
+};
+
+/*
+ * We don't support XDM-AUTHENTICATION-1 and XDM-AUTHORIZATION-1.
+ *
+ * The latter would be quite useful to avoid sending unencrypted
+ * cookies over the wire. Unfortunately it isn't supported without
+ * XDM-AUTHENTICATION-1 which requires a key database with private
+ * keys from all X terminals on your LAN. Fun, fun, fun.
+ *
+ * Furthermore user passwords go over the wire in cleartext anyway,
+ * so protecting cookies is not that important.
+ */
+
+typedef struct _XdmAuth {
+ ARRAY8 authentication;
+ ARRAY8 authorization;
+} XdmAuthRec, *XdmAuthPtr;
+
+static XdmAuthRec serv_authlist = {
+ { (CARD16) 0, (CARD8 *) 0 },
+ { (CARD16) 0, (CARD8 *) 0 }
+};
+
+/* NOTE: Timeout and max are hardcoded */
+typedef struct _ForwardQuery {
+ time_t acctime;
+ GdmAddress *dsp_address;
+ GdmAddress *from_address;
+} ForwardQuery;
+
+typedef struct _IndirectClient {
+ int id;
+ GdmAddress *dsp_address;
+ GdmAddress *chosen_address;
+ time_t acctime;
+} IndirectClient;
+
+typedef struct {
+ int times;
+ guint handler;
+ GdmAddress *manager;
+ GdmAddress *origin;
+ GdmXdmcpDisplayFactory *xdmcp_display_factory;
+} ManagedForward;
+
+struct _GdmXdmcpDisplayFactory
+{
+ GdmDisplayFactory parent;
+
+ GSList *forward_queries;
+ GSList *managed_forwards;
+ GSList *indirect_clients;
+
+ int socket_fd;
+ gint32 session_serial;
+ guint socket_watch_id;
+ XdmcpBuffer buf;
+
+ guint num_sessions;
+ guint num_pending_sessions;
+
+ char *sysid;
+ char *hostname;
+ ARRAY8 servhost;
+
+ /* configuration */
+ guint port;
+ gboolean use_multicast;
+ char *multicast_address;
+ gboolean honor_indirect;
+ char *willing_script;
+ guint max_displays_per_host;
+ guint max_displays;
+ guint max_pending_displays;
+ guint max_wait;
+ guint max_wait_indirect;
+};
+
+enum {
+ PROP_0,
+ PROP_PORT,
+ PROP_USE_MULTICAST,
+ PROP_MULTICAST_ADDRESS,
+ PROP_HONOR_INDIRECT,
+ PROP_WILLING_SCRIPT,
+ PROP_MAX_DISPLAYS_PER_HOST,
+ PROP_MAX_DISPLAYS,
+ PROP_MAX_PENDING_DISPLAYS,
+ PROP_MAX_WAIT,
+ PROP_MAX_WAIT_INDIRECT,
+};
+
+static void gdm_xdmcp_display_factory_class_init (GdmXdmcpDisplayFactoryClass *klass);
+static void gdm_xdmcp_display_factory_init (GdmXdmcpDisplayFactory *manager);
+static void gdm_xdmcp_display_factory_finalize (GObject *object);
+static void gdm_xdmcp_send_alive (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ CARD16 dspnum,
+ CARD32 sessid);
+static gpointer xdmcp_display_factory_object = NULL;
+
+G_DEFINE_TYPE (GdmXdmcpDisplayFactory, gdm_xdmcp_display_factory, GDM_TYPE_DISPLAY_FACTORY)
+
+/* Theory of operation:
+ *
+ * Process idles waiting for UDP packets on port 177.
+ * Incoming packets are decoded and checked against tcp_wrapper.
+ *
+ * A typical session looks like this:
+ *
+ * Display sends Query/BroadcastQuery to Manager.
+ *
+ * Manager selects an appropriate authentication scheme from the
+ * display's list of supported ones and sends Willing/Unwilling.
+ *
+ * Assuming the display accepts the auth. scheme it sends back a
+ * Request.
+ *
+ * If the manager accepts to service the display (i.e. loadavg is low)
+ * it sends back an Accept containing a unique SessionID. The
+ * SessionID is stored in an accept queue by the Manager. Should the
+ * manager refuse to start a session a Decline is sent to the display.
+ *
+ * The display returns a Manage request containing the supplied
+ * SessionID. The manager will then start a session on the display. In
+ * case the SessionID is not on the accept queue the manager returns
+ * Refuse. If the manager fails to open the display for connections
+ * Failed is returned.
+ *
+ * During the session the display periodically sends KeepAlive packets
+ * to the manager. The manager responds with Alive.
+ *
+ * Similarly the manager xpings the display once in a while and shuts
+ * down the connection on failure.
+ *
+ */
+
+GQuark
+gdm_xdmcp_display_factory_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_xdmcp_display_factory_error");
+ }
+
+ return ret;
+}
+
+static gint32
+get_next_session_serial (GdmXdmcpDisplayFactory *factory)
+{
+ gint32 serial;
+
+ again:
+ if (factory->session_serial != G_MAXINT32) {
+ serial = factory->session_serial++;
+ } else {
+ serial = g_random_int ();
+ }
+
+ if (serial == 0) {
+ goto again;
+ }
+
+ return serial;
+}
+
+/* for debugging */
+static const char *
+ai_family_str (struct addrinfo *ai)
+{
+ const char *str;
+ switch (ai->ai_family) {
+ case AF_INET:
+ str = "inet";
+ break;
+ case AF_INET6:
+ str = "inet6";
+ break;
+ case AF_UNIX:
+ str = "unix";
+ break;
+ case AF_UNSPEC:
+ str = "unspecified";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+ return str;
+}
+
+/* for debugging */
+static const char *
+ai_type_str (struct addrinfo *ai)
+{
+ const char *str;
+ switch (ai->ai_socktype) {
+ case SOCK_STREAM:
+ str = "stream";
+ break;
+ case SOCK_DGRAM:
+ str = "datagram";
+ break;
+ case SOCK_SEQPACKET:
+ str = "seqpacket";
+ break;
+ case SOCK_RAW:
+ str = "raw";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+ return str;
+}
+
+/* for debugging */
+static const char *
+ai_protocol_str (struct addrinfo *ai)
+{
+ const char *str;
+ switch (ai->ai_protocol) {
+ case 0:
+ str = "default";
+ break;
+ case IPPROTO_TCP:
+ str = "TCP";
+ break;
+ case IPPROTO_UDP:
+ str = "UDP";
+ break;
+ case IPPROTO_RAW:
+ str = "raw";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+
+ return str;
+}
+
+/* for debugging */
+static char *
+ai_flags_str (struct addrinfo *ai)
+{
+ GString *str;
+
+ str = g_string_new ("");
+ if (ai->ai_flags == 0) {
+ g_string_append (str, "none");
+ } else {
+ if (ai->ai_flags & AI_PASSIVE) {
+ g_string_append (str, "passive ");
+ }
+ if (ai->ai_flags & AI_CANONNAME) {
+ g_string_append (str, "canon ");
+ }
+ if (ai->ai_flags & AI_NUMERICHOST) {
+ g_string_append (str, "numhost ");
+ }
+ if (ai->ai_flags & AI_NUMERICSERV) {
+ g_string_append (str, "numserv ");
+ }
+#ifdef AI_V4MAPPEP
+ if (ai->ai_flags & AI_V4MAPPED) {
+ g_string_append (str, "v4mapped ");
+ }
+#endif
+#ifdef AI_ALL
+ if (ai->ai_flags & AI_ALL) {
+ g_string_append (str, "all ");
+ }
+#endif
+ }
+ return g_string_free (str, FALSE);
+}
+
+/* for debugging */
+static void
+debug_addrinfo (struct addrinfo *ai)
+{
+ char *str;
+ str = ai_flags_str (ai);
+ g_debug ("GdmXdmcpDisplayFactory: addrinfo family=%s type=%s proto=%s flags=%s",
+ ai_family_str (ai),
+ ai_type_str (ai),
+ ai_protocol_str (ai),
+ str);
+ g_free (str);
+}
+
+static int
+create_socket (struct addrinfo *ai)
+{
+ int sock;
+
+ sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ g_warning ("socket: %s", g_strerror (errno));
+ return sock;
+ }
+
+#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY)
+ if (ai->ai_family == AF_INET6) {
+ int zero = 0;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0)
+ g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno));
+ }
+#endif
+
+ if (bind (sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+ g_warning ("bind: %s", g_strerror (errno));
+ close (sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+static int
+do_bind (guint port,
+ int family,
+ struct sockaddr_storage * hostaddr)
+{
+ struct addrinfo hints;
+ struct addrinfo *ai_list;
+ struct addrinfo *ai;
+ char strport[NI_MAXSERV];
+ int gaierr;
+ int sock;
+
+ sock = -1;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ snprintf (strport, sizeof (strport), "%u", port);
+
+ ai_list = NULL;
+ if ((gaierr = getaddrinfo (NULL, strport, &hints, &ai_list)) != 0) {
+ g_error ("Unable to connect to socket: %s", gai_strerror (gaierr));
+ }
+
+ /* should only be one but.. */
+ for (ai = ai_list; ai != NULL; ai = ai->ai_next) {
+ if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
+ continue;
+ }
+
+ debug_addrinfo (ai);
+
+ if (sock < 0) {
+ char *host;
+ char *serv;
+ GdmAddress *addr;
+
+ addr = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
+
+ host = NULL;
+ serv = NULL;
+ gdm_address_get_numeric_info (addr, &host, &serv);
+ g_debug ("GdmXdmcpDisplayFactory: Attempting to bind to host %s port %s",
+ host ? host : "(null)", serv ? serv : "(null)");
+ g_free (host);
+ g_free (serv);
+ gdm_address_free (addr);
+
+ sock = create_socket (ai);
+ if (sock >= 0) {
+ if (hostaddr != NULL) {
+ memcpy (hostaddr, ai->ai_addr, ai->ai_addrlen);
+ }
+ }
+ }
+ }
+
+ freeaddrinfo (ai_list);
+
+ return sock;
+}
+
+static void
+setup_multicast (GdmXdmcpDisplayFactory *factory)
+{
+#ifdef ENABLE_IPV6
+ /* Checking and Setting Multicast options */
+ {
+ /*
+ * socktemp is a temporary socket for getting info about
+ * available interfaces
+ */
+ int socktemp;
+ int i;
+ int num;
+ char *buf;
+ struct ipv6_mreq mreq;
+
+ /* For interfaces' list */
+ struct ifconf ifc;
+ struct ifreq *ifr;
+
+ socktemp = socket (AF_INET, SOCK_DGRAM, 0);
+#ifdef SIOCGIFNUM
+ if (ioctl (socktemp, SIOCGIFNUM, &num) < 0) {
+ num = 64;
+ }
+#else
+ num = 64;
+#endif /* SIOCGIFNUM */
+ ifc.ifc_len = sizeof (struct ifreq) * num;
+ ifc.ifc_buf = buf = malloc (ifc.ifc_len);
+
+ if (ioctl (socktemp, SIOCGIFCONF, &ifc) >= 0) {
+ ifr = ifc.ifc_req;
+ num = ifc.ifc_len / sizeof (struct ifreq); /* No of interfaces */
+
+ /* Joining multicast group with all interfaces */
+ for (i = 0 ; i < num ; i++) {
+ struct ifreq ifreq;
+ int ifindex;
+
+ memset (&ifreq, 0, sizeof (ifreq));
+ strncpy (ifreq.ifr_name, ifr[i].ifr_name, sizeof (ifreq.ifr_name));
+ /* paranoia */
+ ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0';
+
+ if (ioctl (socktemp, SIOCGIFFLAGS, &ifreq) < 0) {
+ g_debug ("GdmXdmcpDisplayFactory: Could not get SIOCGIFFLAGS for %s",
+ ifr[i].ifr_name);
+ }
+
+ ifindex = if_nametoindex (ifr[i].ifr_name);
+
+ if ((!(ifreq.ifr_flags & IFF_UP) ||
+ (ifreq.ifr_flags & IFF_LOOPBACK)) ||
+ ((ifindex == 0 ) && (errno == ENXIO))) {
+ /* Not a valid interface or loopback interface*/
+ continue;
+ }
+
+ mreq.ipv6mr_interface = ifindex;
+ inet_pton (AF_INET6,
+ factory->multicast_address,
+ &mreq.ipv6mr_multiaddr);
+
+ setsockopt (factory->socket_fd,
+ IPPROTO_IPV6,
+ IPV6_JOIN_GROUP,
+ &mreq,
+ sizeof (mreq));
+ }
+ }
+ g_free (buf);
+ close (socktemp);
+ }
+#endif /* ENABLE_IPV6 */
+}
+
+static void
+fd_set_close_on_exec (int fd)
+{
+ int flags;
+
+ flags = fcntl (fd, F_GETFD, 0);
+ if (flags < 0) {
+ return;
+ }
+
+ flags |= FD_CLOEXEC;
+
+ fcntl (fd, F_SETFD, flags);
+}
+
+static gboolean
+open_port (GdmXdmcpDisplayFactory *factory)
+{
+ struct sockaddr_storage serv_sa = { 0 };
+
+ g_debug ("GdmXdmcpDisplayFactory: Start up on host %s, port %d",
+ factory->hostname ? factory->hostname : "(null)",
+ factory->port);
+
+ /* Open socket for communications */
+#ifdef ENABLE_IPV6
+ factory->socket_fd = do_bind (factory->port, AF_INET6, &serv_sa);
+ if (factory->socket_fd < 0)
+#endif
+ factory->socket_fd = do_bind (factory->port, AF_INET, &serv_sa);
+
+ if G_UNLIKELY (factory->socket_fd < 0) {
+ g_warning (_("Could not create socket!"));
+ return FALSE;
+ }
+
+ fd_set_close_on_exec (factory->socket_fd);
+
+ if (factory->use_multicast) {
+ setup_multicast (factory);
+ }
+
+ return TRUE;
+}
+
+#ifdef HAVE_TCPWRAPPERS
+
+ /*
+ * Avoids a warning, my tcpd.h file doesn't include this prototype, even
+ * though the library does include the function and the manpage mentions it
+ */
+ extern int hosts_ctl (char *daemon,
+ char *client_name,
+ char *client_addr,
+ char *client_user);
+#endif
+
+static gboolean
+gdm_xdmcp_host_allow (GdmAddress *address)
+{
+#ifdef HAVE_TCPWRAPPERS
+ char *client;
+ char *host;
+ gboolean ret;
+
+ host = NULL;
+ client = NULL;
+
+ /* Find client hostname */
+ gdm_address_get_hostname (address, &client);
+ gdm_address_get_numeric_info (address, &host, NULL);
+
+ /* Check with tcp_wrappers if client is allowed to access */
+ ret = hosts_ctl ("gdm", client, host, "");
+
+ g_free (host);
+ g_free (client);
+
+ return ret;
+#else /* HAVE_TCPWRAPPERS */
+ return (TRUE);
+#endif /* HAVE_TCPWRAPPERS */
+}
+
+typedef struct {
+ GdmAddress *address;
+ int count;
+} CountDisplayData;
+
+static void
+count_displays_from_host (const char *id,
+ GdmDisplay *display,
+ CountDisplayData *data)
+{
+ GdmAddress *address;
+
+ if (GDM_IS_XDMCP_DISPLAY (display)) {
+ address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display));
+
+ if (gdm_address_equal (address, data->address)) {
+ data->count++;
+ }
+ }
+}
+
+static int
+gdm_xdmcp_num_displays_from_host (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address)
+{
+ CountDisplayData data;
+ GdmDisplayStore *store;
+
+ data.count = 0;
+ data.address = address;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ gdm_display_store_foreach (store,
+ (GdmDisplayStoreFunc)count_displays_from_host,
+ &data);
+
+ return data.count;
+}
+
+typedef struct {
+ GdmAddress *address;
+ int display_num;
+} LookupHostData;
+
+static gboolean
+lookup_by_host (const char *id,
+ GdmDisplay *display,
+ LookupHostData *data)
+{
+ GdmAddress *this_address;
+ int disp_num;
+
+ if (! GDM_IS_XDMCP_DISPLAY (display)) {
+ return FALSE;
+ }
+
+ this_address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display));
+ gdm_display_get_x11_display_number (display, &disp_num, NULL);
+
+ if (gdm_address_equal (this_address, data->address)
+ && disp_num == data->display_num) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GdmDisplay *
+gdm_xdmcp_display_lookup_by_host (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int display_num)
+{
+ GdmDisplay *display;
+ LookupHostData *data;
+ GdmDisplayStore *store;
+
+ data = g_new0 (LookupHostData, 1);
+ data->address = address;
+ data->display_num = display_num;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ display = gdm_display_store_find (store,
+ (GdmDisplayStoreFunc)lookup_by_host,
+ data);
+ g_free (data);
+
+ return display;
+}
+
+static char *
+get_willing_output (GdmXdmcpDisplayFactory *factory)
+{
+ char *output;
+ char **argv;
+ FILE *fd;
+ char buf[256];
+
+ output = NULL;
+ buf[0] = '\0';
+
+ if (factory->willing_script == NULL) {
+ goto out;
+ }
+
+ argv = NULL;
+ if (! g_shell_parse_argv (factory->willing_script, NULL, &argv, NULL)) {
+ goto out;
+ }
+
+ if (argv == NULL ||
+ argv[0] == NULL ||
+ g_access (argv[0], X_OK) != 0) {
+ goto out;
+ }
+
+ fd = popen (factory->willing_script, "r");
+ if (fd == NULL) {
+ goto out;
+ }
+
+ if (fgets (buf, sizeof (buf), fd) == NULL) {
+ pclose (fd);
+ goto out;
+ }
+
+ pclose (fd);
+
+ output = g_strdup (buf);
+
+ out:
+ return output;
+}
+
+static void
+gdm_xdmcp_send_willing (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address)
+{
+ ARRAY8 status;
+ XdmcpHeader header;
+ static char *last_status = NULL;
+ static time_t last_willing = 0;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending WILLING to %s",
+ host ? host : "(null)");
+ g_free (host);
+
+ if (last_willing == 0 || time (NULL) - 3 > last_willing) {
+ char *s;
+
+ g_free (last_status);
+
+ s = get_willing_output (factory);
+ if (s != NULL) {
+ last_status = s;
+ } else {
+ last_status = g_strdup (factory->sysid);
+ }
+ }
+
+ if (! gdm_address_is_local (address) &&
+ gdm_xdmcp_num_displays_from_host (factory, address) >= factory->max_displays_per_host) {
+ /*
+ * Don't translate, this goes over the wire to servers where we
+ * don't know the charset or language, so it must be ascii
+ */
+ status.data = (CARD8 *) g_strdup_printf ("%s (Server is busy)",
+ last_status);
+ } else {
+ status.data = (CARD8 *) g_strdup (last_status);
+ }
+
+ status.length = strlen ((char *) status.data);
+
+ header.opcode = (CARD16) WILLING;
+ header.length = 6 + serv_authlist.authentication.length;
+ header.length += factory->servhost.length + status.length;
+ header.version = XDM_PROTOCOL_VERSION;
+ XdmcpWriteHeader (&factory->buf, &header);
+
+ /* Hardcoded authentication */
+ XdmcpWriteARRAY8 (&factory->buf, &serv_authlist.authentication);
+ XdmcpWriteARRAY8 (&factory->buf, &factory->servhost);
+ XdmcpWriteARRAY8 (&factory->buf, &status);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ g_free (status.data);
+}
+
+static void
+gdm_xdmcp_send_unwilling (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int type)
+{
+ ARRAY8 status;
+ XdmcpHeader header;
+ static time_t last_time = 0;
+ char *host;
+
+ /* only send at most one packet per second,
+ no harm done if we don't send it at all */
+ if (last_time + 1 >= time (NULL)) {
+ return;
+ }
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending UNWILLING to %s",
+ host ? host : "(null)");
+ g_warning ("Denied XDMCP query from host %s",
+ host ? host : "(null)");
+ g_free (host);
+
+ /*
+ * Don't translate, this goes over the wire to servers where we
+ * don't know the charset or language, so it must be ascii
+ */
+ status.data = (CARD8 *) "Display not authorized to connect";
+ status.length = strlen ((char *) status.data);
+
+ header.opcode = (CARD16) UNWILLING;
+ header.length = 4 + factory->servhost.length + status.length;
+ header.version = XDM_PROTOCOL_VERSION;
+ XdmcpWriteHeader (&factory->buf, &header);
+
+ XdmcpWriteARRAY8 (&factory->buf, &factory->servhost);
+ XdmcpWriteARRAY8 (&factory->buf, &status);
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ last_time = time (NULL);
+}
+
+#define SIN(__s) ((struct sockaddr_in *) __s)
+#define SIN6(__s) ((struct sockaddr_in6 *) __s)
+
+static void
+set_port_for_request (GdmAddress *address,
+ ARRAY8 *port)
+{
+ struct sockaddr_storage *ss;
+
+ ss = gdm_address_peek_sockaddr_storage (address);
+
+ /* we depend on this being 2 elsewhere as well */
+ port->length = 2;
+
+ switch (ss->ss_family) {
+ case AF_INET:
+ port->data = (CARD8 *)g_memdup (&(SIN (ss)->sin_port), port->length);
+ break;
+ case AF_INET6:
+ port->data = (CARD8 *)g_memdup (&(SIN6 (ss)->sin6_port), port->length);
+ break;
+ default:
+ port->data = NULL;
+ break;
+ }
+}
+
+static void
+set_address_for_request (GdmAddress *address,
+ ARRAY8 *addr)
+{
+ struct sockaddr_storage *ss;
+
+ ss = gdm_address_peek_sockaddr_storage (address);
+
+ switch (ss->ss_family) {
+ case AF_INET:
+ addr->length = sizeof (struct in_addr);
+ addr->data = g_memdup (&SIN (ss)->sin_addr, addr->length);
+ break;
+ case AF_INET6:
+ addr->length = sizeof (struct in6_addr);
+ addr->data = g_memdup (&SIN6 (ss)->sin6_addr, addr->length);
+ break;
+ default:
+ addr->length = 0;
+ addr->data = NULL;
+ break;
+ }
+
+}
+
+static void
+gdm_xdmcp_send_forward_query (GdmXdmcpDisplayFactory *factory,
+ IndirectClient *ic,
+ GdmAddress *address,
+ GdmAddress *display_address,
+ ARRAYofARRAY8Ptr authlist)
+{
+ XdmcpHeader header;
+ int i;
+ ARRAY8 addr;
+ ARRAY8 port;
+ char *host;
+ char *serv;
+
+ g_assert (ic != NULL);
+ g_assert (ic->chosen_address != NULL);
+
+ host = NULL;
+ gdm_address_get_numeric_info (ic->chosen_address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending forward query to %s",
+ host ? host : "(null)");
+ g_free (host);
+
+ host = NULL;
+ serv = NULL;
+ gdm_address_get_numeric_info (display_address, &host, &serv);
+ g_debug ("GdmXdmcpDisplayFactory: Query contains %s:%s",
+ host ? host : "(null)", serv ? serv : "(null)");
+ g_free (host);
+ g_free (serv);
+
+ set_port_for_request (address, &port);
+ set_address_for_request (display_address, &addr);
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16) FORWARD_QUERY;
+ header.length = 0;
+ header.length += 2 + addr.length;
+ header.length += 2 + port.length;
+ header.length += 1;
+ for (i = 0; i < authlist->length; i++) {
+ header.length += 2 + authlist->data[i].length;
+ }
+
+ XdmcpWriteHeader (&factory->buf, &header);
+ XdmcpWriteARRAY8 (&factory->buf, &addr);
+ XdmcpWriteARRAY8 (&factory->buf, &port);
+ XdmcpWriteARRAYofARRAY8 (&factory->buf, authlist);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (ic->chosen_address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (ic->chosen_address)));
+
+ g_free (port.data);
+ g_free (addr.data);
+}
+
+static void
+handle_any_query (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ ARRAYofARRAY8Ptr authentication_names,
+ int type)
+{
+ gdm_xdmcp_send_willing (factory, address);
+}
+
+static void
+handle_direct_query (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len,
+ int type)
+{
+ ARRAYofARRAY8 clnt_authlist;
+ int expected_len;
+ int i;
+ int res;
+
+ res = XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authlist);
+ if G_UNLIKELY (! res) {
+ g_warning ("Could not extract authlist from packet");
+ return;
+ }
+
+ expected_len = 1;
+
+ for (i = 0 ; i < clnt_authlist.length ; i++) {
+ expected_len += 2 + clnt_authlist.data[i].length;
+ }
+
+ if (len == expected_len) {
+ handle_any_query (factory, address, &clnt_authlist, type);
+ } else {
+ g_warning ("Error in checksum");
+ }
+
+ XdmcpDisposeARRAYofARRAY8 (&clnt_authlist);
+}
+
+static void
+gdm_xdmcp_handle_broadcast_query (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ if (gdm_xdmcp_host_allow (address)) {
+ handle_direct_query (factory, address, len, BROADCAST_QUERY);
+ } else {
+ /* just ignore it */
+ }
+}
+
+static void
+gdm_xdmcp_handle_query (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ if (gdm_xdmcp_host_allow (address)) {
+ handle_direct_query (factory, address, len, QUERY);
+ } else {
+ gdm_xdmcp_send_unwilling (factory, address, QUERY);
+ }
+}
+
+static IndirectClient *
+indirect_client_create (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *dsp_address)
+{
+ IndirectClient *ic;
+
+ ic = g_new0 (IndirectClient, 1);
+ ic->dsp_address = gdm_address_copy (dsp_address);
+
+ factory->indirect_clients = g_slist_prepend (factory->indirect_clients, ic);
+
+ return ic;
+}
+
+static void
+indirect_client_destroy (GdmXdmcpDisplayFactory *factory,
+ IndirectClient *ic)
+{
+ if (ic == NULL) {
+ return;
+ }
+
+ factory->indirect_clients = g_slist_remove (factory->indirect_clients, ic);
+
+ ic->acctime = 0;
+
+ {
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (ic->dsp_address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Disposing IndirectClient for %s",
+ host ? host : "(null)");
+ g_free (host);
+ }
+
+ g_free (ic->dsp_address);
+ ic->dsp_address = NULL;
+ g_free (ic->chosen_address);
+ ic->chosen_address = NULL;
+
+ g_free (ic);
+}
+
+static IndirectClient *
+indirect_client_lookup_by_chosen (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *chosen_address,
+ GdmAddress *origin_address)
+{
+ GSList *li;
+ char *host;
+ IndirectClient *ret;
+
+ g_assert (chosen_address != NULL);
+ g_assert (origin_address != NULL);
+
+ ret = NULL;
+
+ for (li = factory->indirect_clients; li != NULL; li = li->next) {
+ IndirectClient *ic = li->data;
+
+ if (ic != NULL
+ && ic->chosen_address != NULL
+ && gdm_address_equal (ic->chosen_address, chosen_address)) {
+ if (gdm_address_equal (ic->dsp_address, origin_address)) {
+ ret = ic;
+ goto out;
+ } else if (gdm_address_is_loopback (ic->dsp_address)
+ && gdm_address_is_local (origin_address)) {
+ ret = ic;
+ goto out;
+ }
+ }
+ }
+
+ gdm_address_get_numeric_info (chosen_address, &host, NULL);
+
+ g_debug ("GdmXdmcpDisplayFactory: Chosen %s host not found",
+ host ? host : "(null)");
+ g_free (host);
+ out:
+ return ret;
+}
+
+/* lookup by origin */
+static IndirectClient *
+indirect_client_lookup (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address)
+{
+ GSList *li;
+ GSList *qlist;
+ IndirectClient *ret;
+ time_t curtime;
+
+ g_assert (address != NULL);
+
+ curtime = time (NULL);
+ ret = NULL;
+
+ qlist = g_slist_copy (factory->indirect_clients);
+
+ for (li = qlist; li != NULL; li = li->next) {
+ IndirectClient *ic;
+ char *host;
+ char *serv;
+
+ ic = (IndirectClient *) li->data;
+
+ if (ic == NULL) {
+ continue;
+ }
+
+ host = NULL;
+ serv = NULL;
+ gdm_address_get_numeric_info (ic->dsp_address, &host, &serv);
+
+ g_debug ("GdmXdmcpDisplayFactory: comparing %s:%s",
+ host ? host : "(null)", serv ? serv : "(null)");
+ if (gdm_address_equal (ic->dsp_address, address)) {
+ ret = ic;
+ g_free (host);
+ g_free (serv);
+ break;
+ }
+
+ if (ic->acctime > 0 && curtime > ic->acctime + factory->max_wait_indirect) {
+ g_debug ("GdmXdmcpDisplayFactory: Disposing stale forward query from %s:%s",
+ host ? host : "(null)", serv ? serv : "(null)");
+
+ indirect_client_destroy (factory, ic);
+ }
+
+ g_free (host);
+ g_free (serv);
+ }
+
+ g_slist_free (qlist);
+
+ if (ret == NULL) {
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Host %s not found",
+ host ? host : "(null)");
+ g_free (host);
+ }
+
+ return ret;
+}
+
+static void
+gdm_xdmcp_handle_indirect_query (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ ARRAYofARRAY8 clnt_authlist;
+ int expected_len;
+ int i;
+ int res;
+ IndirectClient *ic;
+
+ if (! gdm_xdmcp_host_allow (address)) {
+ /* ignore the request */
+ return;
+ }
+
+ if (! factory->honor_indirect) {
+ /* ignore it */
+ return;
+ }
+
+ if (factory->num_sessions > factory->max_displays ||
+ (!gdm_address_is_local (address) &&
+ gdm_xdmcp_num_displays_from_host (factory, address) > factory->max_displays_per_host)) {
+ g_debug ("GdmXdmcpDisplayFactory: reached maximum number of clients - ignoring indirect query");
+ return;
+ }
+
+ res = XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authlist);
+ if G_UNLIKELY (! res) {
+ g_warning ("Could not extract authlist from packet");
+ return;
+ }
+
+ expected_len = 1;
+
+ for (i = 0 ; i < clnt_authlist.length ; i++) {
+ expected_len += 2 + clnt_authlist.data[i].length;
+ }
+
+ /* Try to look up the display in
+ * the pending list. If found send a FORWARD_QUERY to the
+ * chosen manager. Otherwise alloc a new indirect display. */
+
+ if (len != expected_len) {
+ g_warning ("Error in checksum");
+ goto out;
+ }
+
+
+ ic = indirect_client_lookup (factory, address);
+
+ if (ic != NULL && ic->chosen_address != NULL) {
+ /* if user chose us, then just send willing */
+ if (gdm_address_is_local (ic->chosen_address)) {
+ g_debug ("GdmXdmcpDisplayFactory: the chosen address is local - dropping indirect");
+
+ /* get rid of indirect, so that we don't get
+ * the chooser */
+ indirect_client_destroy (factory, ic);
+ gdm_xdmcp_send_willing (factory, address);
+ } else if (gdm_address_is_loopback (address)) {
+ /* woohoo! fun, I have no clue how to get
+ * the correct ip, SO I just send forward
+ * queries with all the different IPs */
+ const GList *list = gdm_address_peek_local_list ();
+
+ g_debug ("GdmXdmcpDisplayFactory: the chosen address is a loopback");
+
+ while (list != NULL) {
+ GdmAddress *saddr = list->data;
+
+ if (! gdm_address_is_loopback (saddr)) {
+ /* forward query to * chosen host */
+ gdm_xdmcp_send_forward_query (factory,
+ ic,
+ address,
+ saddr,
+ &clnt_authlist);
+ }
+
+ list = list->next;
+ }
+ } else {
+ /* or send forward query to chosen host */
+ gdm_xdmcp_send_forward_query (factory,
+ ic,
+ address,
+ address,
+ &clnt_authlist);
+ }
+ } else if (ic == NULL) {
+ ic = indirect_client_create (factory, address);
+ if (ic != NULL) {
+ gdm_xdmcp_send_willing (factory, address);
+ }
+ } else {
+ gdm_xdmcp_send_willing (factory, address);
+ }
+
+out:
+ XdmcpDisposeARRAYofARRAY8 (&clnt_authlist);
+}
+
+static void
+forward_query_destroy (GdmXdmcpDisplayFactory *factory,
+ ForwardQuery *q)
+{
+ if (q == NULL) {
+ return;
+ }
+
+ factory->forward_queries = g_slist_remove (factory->forward_queries, q);
+
+ q->acctime = 0;
+
+ {
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (q->dsp_address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Disposing %s",
+ host ? host : "(null)");
+ g_free (host);
+ }
+
+ g_free (q->dsp_address);
+ q->dsp_address = NULL;
+ g_free (q->from_address);
+ q->from_address = NULL;
+
+ g_free (q);
+}
+
+static gboolean
+remove_oldest_forward (GdmXdmcpDisplayFactory *factory)
+{
+ GSList *li;
+ ForwardQuery *oldest = NULL;
+
+ for (li = factory->forward_queries; li != NULL; li = li->next) {
+ ForwardQuery *query = li->data;
+
+ if (oldest == NULL || query->acctime < oldest->acctime) {
+ oldest = query;
+ }
+ }
+
+ if (oldest != NULL) {
+ forward_query_destroy (factory, oldest);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static ForwardQuery *
+forward_query_create (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *mgr_address,
+ GdmAddress *dsp_address)
+{
+ ForwardQuery *q;
+ int count;
+
+ count = g_slist_length (factory->forward_queries);
+
+ while (count > GDM_MAX_FORWARD_QUERIES && remove_oldest_forward (factory)) {
+ count--;
+ }
+
+ q = g_new0 (ForwardQuery, 1);
+ q->dsp_address = gdm_address_copy (dsp_address);
+ q->from_address = gdm_address_copy (mgr_address);
+
+ factory->forward_queries = g_slist_prepend (factory->forward_queries, q);
+
+ return q;
+}
+
+static ForwardQuery *
+forward_query_lookup (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address)
+{
+ GSList *li;
+ GSList *qlist;
+ ForwardQuery *ret;
+ time_t curtime;
+
+ curtime = time (NULL);
+ ret = NULL;
+
+ qlist = g_slist_copy (factory->forward_queries);
+
+ for (li = qlist; li != NULL; li = li->next) {
+ ForwardQuery *q;
+ char *host;
+ char *serv;
+
+ q = (ForwardQuery *) li->data;
+
+ if (q == NULL) {
+ continue;
+ }
+
+ host = NULL;
+ serv = NULL;
+ gdm_address_get_numeric_info (q->dsp_address, &host, &serv);
+
+ g_debug ("GdmXdmcpDisplayFactory: comparing %s:%s",
+ host ? host : "(null)", serv ? serv : "(null)");
+ if (gdm_address_equal (q->dsp_address, address)) {
+ ret = q;
+ g_free (host);
+ g_free (serv);
+ break;
+ }
+
+ if (q->acctime > 0 && curtime > q->acctime + GDM_FORWARD_QUERY_TIMEOUT) {
+ g_debug ("GdmXdmcpDisplayFactory: Disposing stale forward query from %s:%s",
+ host ? host : "(null)", serv ? serv : "(null)");
+
+ forward_query_destroy (factory, q);
+ }
+
+ g_free (host);
+ g_free (serv);
+ }
+
+ g_slist_free (qlist);
+
+ if (ret == NULL) {
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Host %s not found",
+ host ? host : "(null)");
+ g_free (host);
+ }
+
+ return ret;
+}
+
+static gboolean
+create_address_from_request (ARRAY8 *req_addr,
+ ARRAY8 *req_port,
+ int family,
+ GdmAddress **address)
+{
+ uint16_t port;
+ char host_buf [NI_MAXHOST];
+ char serv_buf [NI_MAXSERV];
+ char *serv;
+ const char *host;
+ struct addrinfo hints;
+ struct addrinfo *ai_list;
+ struct addrinfo *ai;
+ int gaierr;
+ gboolean found;
+
+ if (address != NULL) {
+ *address = NULL;
+ }
+
+ if (req_addr == NULL) {
+ return FALSE;
+ }
+
+ serv = NULL;
+ if (req_port != NULL) {
+ /* port must always be length 2 */
+ if (req_port->length != 2) {
+ return FALSE;
+ }
+
+ memcpy (&port, req_port->data, 2);
+ snprintf (serv_buf, sizeof (serv_buf), "%d", ntohs (port));
+ serv = serv_buf;
+ } else {
+ /* assume XDM_UDP_PORT */
+ snprintf (serv_buf, sizeof (serv_buf), "%d", XDM_UDP_PORT);
+ serv = serv_buf;
+ }
+
+ host = NULL;
+ if (req_addr->length == 4) {
+ host = inet_ntop (AF_INET,
+ (const void *)req_addr->data,
+ host_buf,
+ sizeof (host_buf));
+ } else if (req_addr->length == 16) {
+ host = inet_ntop (AF_INET6,
+ (const void *)req_addr->data,
+ host_buf,
+ sizeof (host_buf));
+ }
+
+ if (host == NULL) {
+ g_warning ("Bad address");
+ return FALSE;
+ }
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = family;
+ /* this should convert IPv4 address to IPv6 if needed */
+#ifdef AI_V4MAPPED
+ hints.ai_flags = AI_V4MAPPED;
+#endif
+ hints.ai_socktype = SOCK_DGRAM;
+
+ if ((gaierr = getaddrinfo (host, serv, &hints, &ai_list)) != 0) {
+ g_warning ("Unable to get address: %s", gai_strerror (gaierr));
+ return FALSE;
+ }
+
+ /* just take the first one */
+ ai = ai_list;
+
+ found = FALSE;
+ if (ai != NULL) {
+ found = TRUE;
+ if (address != NULL) {
+ *address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
+ }
+ }
+
+ freeaddrinfo (ai_list);
+
+ return found;
+}
+
+static void
+gdm_xdmcp_whack_queued_managed_forwards (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ GdmAddress *origin)
+{
+ GSList *li;
+
+ for (li = factory->managed_forwards; li != NULL; li = li->next) {
+ ManagedForward *mf = li->data;
+
+ if (gdm_address_equal (mf->manager, address) &&
+ gdm_address_equal (mf->origin, origin)) {
+ factory->managed_forwards = g_slist_remove_link (factory->managed_forwards, li);
+ g_slist_free_1 (li);
+ g_source_remove (mf->handler);
+ /* mf freed by glib */
+ return;
+ }
+ }
+}
+
+static void
+gdm_xdmcp_handle_forward_query (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ ARRAY8 clnt_addr;
+ ARRAY8 clnt_port;
+ ARRAYofARRAY8 clnt_authlist;
+ int i;
+ int explen;
+ GdmAddress *disp_address;
+ char *host;
+ char *serv;
+
+ disp_address = NULL;
+
+ /* Check with tcp_wrappers if client is allowed to access */
+ if (! gdm_xdmcp_host_allow (address)) {
+ char *host2;
+
+ host2 = NULL;
+ gdm_address_get_numeric_info (address, &host2, NULL);
+
+ g_warning ("%s: Got FORWARD_QUERY from banned host %s",
+ "gdm_xdmcp_handle_forward query",
+ host2 ? host2 : "(null)");
+ g_free (host2);
+ return;
+ }
+
+ /* Read display address */
+ if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_addr)) {
+ g_warning ("%s: Could not read display address",
+ "gdm_xdmcp_handle_forward_query");
+ return;
+ }
+
+ /* Read display port */
+ if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_port)) {
+ XdmcpDisposeARRAY8 (&clnt_addr);
+ g_warning ("%s: Could not read display port number",
+ "gdm_xdmcp_handle_forward_query");
+ return;
+ }
+
+ /* Extract array of authentication names from Xdmcp packet */
+ if G_UNLIKELY (! XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authlist)) {
+ XdmcpDisposeARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAY8 (&clnt_port);
+ g_warning ("%s: Could not extract authlist from packet",
+ "gdm_xdmcp_handle_forward_query");
+ return;
+ }
+
+ /* Crude checksumming */
+ explen = 1;
+ explen += 2 + clnt_addr.length;
+ explen += 2 + clnt_port.length;
+
+ for (i = 0 ; i < clnt_authlist.length ; i++) {
+ char *s = g_strndup ((char *) clnt_authlist.data[i].data,
+ clnt_authlist.length);
+ g_debug ("GdmXdmcpDisplayFactory: authlist: %s", s);
+ g_free (s);
+
+ explen += 2 + clnt_authlist.data[i].length;
+ }
+
+ if G_UNLIKELY (len != explen) {
+ g_warning ("%s: Error in checksum",
+ "gdm_xdmcp_handle_forward_query");
+ goto out;
+ }
+
+ if (! create_address_from_request (&clnt_addr, &clnt_port, gdm_address_get_family_type (address), &disp_address)) {
+ g_warning ("Unable to parse address for request");
+ goto out;
+ }
+
+ gdm_xdmcp_whack_queued_managed_forwards (factory,
+ address,
+ disp_address);
+
+ host = NULL;
+ serv = NULL;
+ gdm_address_get_numeric_info (disp_address, &host, &serv);
+ g_debug ("GdmXdmcpDisplayFactory: Got FORWARD_QUERY for display: %s, port %s",
+ host ? host : "(null)", serv ? serv : "(null)");
+ g_free (host);
+ g_free (serv);
+
+ /* Check with tcp_wrappers if display is allowed to access */
+ if (gdm_xdmcp_host_allow (disp_address)) {
+ ForwardQuery *q;
+
+ q = forward_query_lookup (factory, disp_address);
+ if (q != NULL) {
+ forward_query_destroy (factory, q);
+ }
+
+ forward_query_create (factory, address, disp_address);
+
+ gdm_xdmcp_send_willing (factory, disp_address);
+ }
+
+ out:
+
+ gdm_address_free (disp_address);
+
+ XdmcpDisposeARRAYofARRAY8 (&clnt_authlist);
+ XdmcpDisposeARRAY8 (&clnt_port);
+ XdmcpDisposeARRAY8 (&clnt_addr);
+}
+
+static void
+gdm_xdmcp_really_send_managed_forward (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ GdmAddress *origin)
+{
+ ARRAY8 addr;
+ XdmcpHeader header;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending MANAGED_FORWARD to %s",
+ host ? host : "(null)");
+ g_free (host);
+
+ set_address_for_request (origin, &addr);
+
+ header.opcode = (CARD16) GDM_XDMCP_MANAGED_FORWARD;
+ header.length = 4 + addr.length;
+ header.version = GDM_XDMCP_PROTOCOL_VERSION;
+ XdmcpWriteHeader (&factory->buf, &header);
+
+ XdmcpWriteARRAY8 (&factory->buf, &addr);
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ g_free (addr.data);
+}
+
+static gboolean
+managed_forward_handler (ManagedForward *mf)
+{
+ if (mf->xdmcp_display_factory->socket_fd > 0) {
+ gdm_xdmcp_really_send_managed_forward (mf->xdmcp_display_factory,
+ mf->manager,
+ mf->origin);
+ }
+
+ mf->times++;
+ if (mf->xdmcp_display_factory->socket_fd <= 0 || mf->times >= 2) {
+ mf->xdmcp_display_factory->managed_forwards = g_slist_remove (mf->xdmcp_display_factory->managed_forwards, mf);
+ mf->handler = 0;
+ /* mf freed by glib */
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+managed_forward_free (ManagedForward *mf)
+{
+ gdm_address_free (mf->origin);
+ gdm_address_free (mf->manager);
+ g_free (mf);
+}
+
+static void
+gdm_xdmcp_send_managed_forward (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ GdmAddress *origin)
+{
+ ManagedForward *mf;
+
+ gdm_xdmcp_really_send_managed_forward (factory, address, origin);
+
+ mf = g_new0 (ManagedForward, 1);
+ mf->times = 0;
+ mf->xdmcp_display_factory = factory;
+
+ mf->manager = gdm_address_copy (address);
+ mf->origin = gdm_address_copy (origin);
+
+ mf->handler = g_timeout_add_full (G_PRIORITY_DEFAULT,
+ MANAGED_FORWARD_INTERVAL,
+ (GSourceFunc)managed_forward_handler,
+ mf,
+ (GDestroyNotify)managed_forward_free);
+ factory->managed_forwards = g_slist_prepend (factory->managed_forwards, mf);
+}
+
+static void
+gdm_xdmcp_send_got_managed_forward (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ GdmAddress *origin)
+{
+ ARRAY8 addr;
+ XdmcpHeader header;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending GOT_MANAGED_FORWARD to %s",
+ host ? host : "(null)");
+ g_free (host);
+
+ set_address_for_request (origin, &addr);
+
+ header.opcode = (CARD16) GDM_XDMCP_GOT_MANAGED_FORWARD;
+ header.length = 4 + addr.length;
+ header.version = GDM_XDMCP_PROTOCOL_VERSION;
+ XdmcpWriteHeader (&factory->buf, &header);
+
+ XdmcpWriteARRAY8 (&factory->buf, &addr);
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+}
+
+static void
+count_sessions (const char *id,
+ GdmDisplay *display,
+ GdmXdmcpDisplayFactory *factory)
+{
+ if (GDM_IS_XDMCP_DISPLAY (display)) {
+ int status;
+
+ status = gdm_display_get_status (display);
+
+ if (status == GDM_DISPLAY_MANAGED) {
+ factory->num_sessions++;
+ } else if (status == GDM_DISPLAY_UNMANAGED) {
+ factory->num_pending_sessions++;
+ }
+ }
+}
+
+static void
+gdm_xdmcp_recount_sessions (GdmXdmcpDisplayFactory *factory)
+{
+ GdmDisplayStore *store;
+
+ factory->num_sessions = 0;
+ factory->num_pending_sessions = 0;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ gdm_display_store_foreach (store,
+ (GdmDisplayStoreFunc)count_sessions,
+ factory);
+}
+
+static gboolean
+purge_displays (const char *id,
+ GdmDisplay *display,
+ GdmXdmcpDisplayFactory *factory)
+{
+ if (GDM_IS_XDMCP_DISPLAY (display)) {
+ int status;
+ time_t currtime;
+ time_t acctime;
+
+ currtime = time (NULL);
+ status = gdm_display_get_status (display);
+ acctime = gdm_display_get_creation_time (display);
+
+ if (status == GDM_DISPLAY_UNMANAGED &&
+ currtime > acctime + factory->max_wait) {
+ /* return TRUE to remove display */
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gdm_xdmcp_displays_purge (GdmXdmcpDisplayFactory *factory)
+{
+ GdmDisplayStore *store;
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ gdm_display_store_foreach_remove (store,
+ (GdmDisplayStoreFunc)purge_displays,
+ factory);
+
+ gdm_xdmcp_recount_sessions (factory);
+}
+
+typedef struct {
+ const char *hostname;
+ int display_num;
+} RemoveHostData;
+
+static gboolean
+remove_host (const char *id,
+ GdmDisplay *display,
+ RemoveHostData *data)
+{
+ char *hostname;
+ int disp_num;
+
+ if (! GDM_IS_XDMCP_DISPLAY (display)) {
+ return FALSE;
+ }
+
+ gdm_display_get_remote_hostname (display, &hostname, NULL);
+ gdm_display_get_x11_display_number (display, &disp_num, NULL);
+
+ if (disp_num == data->display_num &&
+ hostname != NULL &&
+ data->hostname != NULL &&
+ strcmp (hostname, data->hostname) == 0) {
+ /* return TRUE to remove */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+display_dispose_check (GdmXdmcpDisplayFactory *factory,
+ const char *hostname,
+ int display_num)
+{
+ RemoveHostData *data;
+ GdmDisplayStore *store;
+
+ if (hostname == NULL) {
+ return;
+ }
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+
+ g_debug ("GdmXdmcpDisplayFactory: display_dispose_check (%s:%d)",
+ hostname ? hostname : "(null)", display_num);
+
+ data = g_new0 (RemoveHostData, 1);
+ data->hostname = hostname;
+ data->display_num = display_num;
+ gdm_display_store_foreach_remove (store,
+ (GdmDisplayStoreFunc)remove_host,
+ data);
+ g_free (data);
+
+ gdm_xdmcp_recount_sessions (factory);
+}
+
+static void
+gdm_xdmcp_send_decline (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ const char *reason)
+{
+ XdmcpHeader header;
+ ARRAY8 authentype;
+ ARRAY8 authendata;
+ ARRAY8 status;
+ ForwardQuery *fq;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending DECLINE to %s",
+ host ? host : "(null)");
+ g_free (host);
+
+ authentype.data = (CARD8 *) 0;
+ authentype.length = (CARD16) 0;
+
+ authendata.data = (CARD8 *) 0;
+ authendata.length = (CARD16) 0;
+
+ status.data = (CARD8 *) reason;
+ status.length = strlen ((char *) status.data);
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16) DECLINE;
+ header.length = 2 + status.length;
+ header.length += 2 + authentype.length;
+ header.length += 2 + authendata.length;
+
+ XdmcpWriteHeader (&factory->buf, &header);
+ XdmcpWriteARRAY8 (&factory->buf, &status);
+ XdmcpWriteARRAY8 (&factory->buf, &authentype);
+ XdmcpWriteARRAY8 (&factory->buf, &authendata);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ /* Send MANAGED_FORWARD to indicate that the connection
+ * reached some sort of resolution */
+ fq = forward_query_lookup (factory, address);
+ if (fq != NULL) {
+ gdm_xdmcp_send_managed_forward (factory, fq->from_address, address);
+ forward_query_destroy (factory, fq);
+ }
+}
+
+static void
+on_hostname_selected (GdmXdmcpChooserDisplay *display,
+ const char *hostname,
+ GdmXdmcpDisplayFactory *factory)
+{
+ struct addrinfo hints;
+ struct addrinfo *ai_list;
+ struct addrinfo *ai;
+ int gaierr;
+ GdmAddress *address;
+ IndirectClient *ic;
+ gchar *xdmcp_port;
+
+ g_debug ("GdmXdmcpDisplayFactory: hostname selected: %s",
+ hostname ? hostname : "(null)");
+
+ address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display));
+
+ g_assert (address != NULL);
+
+ ic = indirect_client_lookup (factory, address);
+
+ if (ic->chosen_address != NULL) {
+ gdm_address_free (ic->chosen_address);
+ ic->chosen_address = NULL;
+ }
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = gdm_address_get_family_type (address);
+ /* this should convert IPv4 address to IPv6 if needed */
+#ifdef AI_V4MAPPED
+ hints.ai_flags = AI_V4MAPPED;
+#endif
+
+ xdmcp_port = g_strdup_printf ("%d", XDM_UDP_PORT);
+ if ((gaierr = getaddrinfo (hostname, xdmcp_port, &hints, &ai_list)) != 0) {
+ g_warning ("Unable to get address: %s", gai_strerror (gaierr));
+ g_free (xdmcp_port);
+ return;
+ }
+ g_free (xdmcp_port);
+
+ /* just take the first one */
+ ai = ai_list;
+
+ if (ai != NULL) {
+ char *ip;
+ ic->chosen_address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen);
+
+ ip = NULL;
+ gdm_address_get_numeric_info (ic->chosen_address, &ip, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: hostname resolves to %s",
+ ip ? ip : "(null)");
+ g_free (ip);
+ }
+
+ freeaddrinfo (ai_list);
+}
+
+static void
+on_client_disconnected (GdmDisplay *display)
+{
+ if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED)
+ return;
+
+ gdm_display_stop_greeter_session (display);
+ gdm_display_unmanage (display);
+ gdm_display_finish (display);
+}
+
+static void
+on_display_status_changed (GdmDisplay *display,
+ GParamSpec *arg1,
+ GdmXdmcpDisplayFactory *factory)
+{
+ int status;
+ GdmLaunchEnvironment *launch_environment;
+ GdmSession *session;
+ GdmAddress *address;
+ gint32 session_number;
+ int display_number;
+
+ launch_environment = NULL;
+ g_object_get (display, "launch-environment", &launch_environment, NULL);
+
+ session = NULL;
+ if (launch_environment != NULL) {
+ session = gdm_launch_environment_get_session (launch_environment);
+ }
+
+ status = gdm_display_get_status (display);
+
+ g_debug ("GdmXdmcpDisplayFactory: xdmcp display status changed: %d", status);
+ switch (status) {
+ case GDM_DISPLAY_FINISHED:
+ g_object_get (display,
+ "remote-address", &address,
+ "x11-display-number", &display_number,
+ "session-number", &session_number,
+ NULL);
+ gdm_xdmcp_send_alive (factory, address, display_number, session_number);
+
+ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory));
+ break;
+ case GDM_DISPLAY_FAILED:
+ gdm_display_factory_queue_purge_displays (GDM_DISPLAY_FACTORY (factory));
+ break;
+ case GDM_DISPLAY_UNMANAGED:
+ if (session != NULL) {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (session),
+ G_CALLBACK (on_client_disconnected),
+ display);
+ }
+ break;
+ case GDM_DISPLAY_PREPARED:
+ break;
+ case GDM_DISPLAY_MANAGED:
+ if (session != NULL) {
+ g_signal_connect_object (G_OBJECT (session),
+ "client-disconnected",
+ G_CALLBACK (on_client_disconnected),
+ display, G_CONNECT_SWAPPED);
+ g_signal_connect_object (G_OBJECT (session),
+ "disconnected",
+ G_CALLBACK (on_client_disconnected),
+ display, G_CONNECT_SWAPPED);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_clear_object (&launch_environment);
+}
+
+static GdmDisplay *
+gdm_xdmcp_display_create (GdmXdmcpDisplayFactory *factory,
+ const char *hostname,
+ GdmAddress *address,
+ int displaynum)
+{
+ GdmDisplay *display;
+ GdmDisplayStore *store;
+ gboolean use_chooser;
+ const char *session_types[] = { "x11", NULL };
+
+ g_debug ("GdmXdmcpDisplayFactory: Creating xdmcp display for %s:%d",
+ hostname ? hostname : "(null)", displaynum);
+
+ use_chooser = FALSE;
+ if (factory->honor_indirect) {
+ IndirectClient *ic;
+
+ ic = indirect_client_lookup (factory, address);
+
+ /* This was an indirect thingie and nothing was yet chosen,
+ * use a chooser */
+ if (ic != NULL && ic->chosen_address == NULL) {
+ use_chooser = TRUE;
+ }
+ }
+
+ if (use_chooser) {
+ display = gdm_xdmcp_chooser_display_new (hostname,
+ displaynum,
+ address,
+ get_next_session_serial (factory));
+ g_signal_connect (display, "hostname-selected", G_CALLBACK (on_hostname_selected), factory);
+ } else {
+ display = gdm_xdmcp_display_new (hostname,
+ displaynum,
+ address,
+ get_next_session_serial (factory));
+ }
+
+ if (display == NULL) {
+ goto out;
+ }
+
+ g_object_set (G_OBJECT (display),
+ "session-type", session_types[0],
+ "supported-session-types", session_types,
+ NULL);
+
+ if (! gdm_display_prepare (display)) {
+ gdm_display_unmanage (display);
+ g_object_unref (display);
+ display = NULL;
+ goto out;
+ }
+
+ g_signal_connect_after (display,
+ "notify::status",
+ G_CALLBACK (on_display_status_changed),
+ factory);
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ gdm_display_store_add (store, display);
+
+ factory->num_pending_sessions++;
+ out:
+
+ return display;
+}
+
+static void
+gdm_xdmcp_send_accept (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ CARD32 session_id,
+ ARRAY8Ptr authentication_name,
+ ARRAY8Ptr authentication_data,
+ ARRAY8Ptr authorization_name,
+ ARRAY8Ptr authorization_data)
+{
+ XdmcpHeader header;
+ char *host;
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16) ACCEPT;
+ header.length = 4;
+ header.length += 2 + authentication_name->length;
+ header.length += 2 + authentication_data->length;
+ header.length += 2 + authorization_name->length;
+ header.length += 2 + authorization_data->length;
+
+ XdmcpWriteHeader (&factory->buf, &header);
+ XdmcpWriteCARD32 (&factory->buf, session_id);
+ XdmcpWriteARRAY8 (&factory->buf, authentication_name);
+ XdmcpWriteARRAY8 (&factory->buf, authentication_data);
+ XdmcpWriteARRAY8 (&factory->buf, authorization_name);
+ XdmcpWriteARRAY8 (&factory->buf, authorization_data);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Sending ACCEPT to %s with SessionID=%ld",
+ host ? host : "(null)",
+ (long)session_id);
+ g_free (host);
+}
+
+static void
+gdm_xdmcp_handle_request (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ CARD16 clnt_dspnum;
+ ARRAY16 clnt_conntyp;
+ ARRAYofARRAY8 clnt_addr;
+ ARRAY8 clnt_authname;
+ ARRAY8 clnt_authdata;
+ ARRAYofARRAY8 clnt_authorization_names;
+ ARRAY8 clnt_manufacturer;
+ int explen;
+ int i;
+ gboolean mitauth;
+ gboolean entered;
+ char *hostname;
+
+ mitauth = FALSE;
+ entered = FALSE;
+
+ hostname = NULL;
+ gdm_address_get_numeric_info (address, &hostname, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Got REQUEST from %s",
+ hostname ? hostname : "(null)");
+
+ /* Check with tcp_wrappers if client is allowed to access */
+ if (! gdm_xdmcp_host_allow (address)) {
+ g_warning ("%s: Got REQUEST from banned host %s",
+ "gdm_xdmcp_handle_request",
+ hostname ? hostname : "(null)");
+ goto out;
+ }
+
+ gdm_xdmcp_displays_purge (factory); /* Purge pending displays */
+
+ /* Remote display number */
+ if G_UNLIKELY (! XdmcpReadCARD16 (&factory->buf, &clnt_dspnum)) {
+ g_warning ("%s: Could not read Display Number",
+ "gdm_xdmcp_handle_request");
+ goto out;
+ }
+
+ /* We don't care about connection type. Address says it all */
+ if G_UNLIKELY (! XdmcpReadARRAY16 (&factory->buf, &clnt_conntyp)) {
+ g_warning ("%s: Could not read Connection Type",
+ "gdm_xdmcp_handle_request");
+ goto out;
+ }
+
+ /* This is TCP/IP - we don't care */
+ if G_UNLIKELY (! XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_addr)) {
+ g_warning ("%s: Could not read Client Address",
+ "gdm_xdmcp_handle_request");
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ goto out;
+ }
+
+ /* Read authentication type */
+ if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_authname)) {
+ g_warning ("%s: Could not read Authentication Names",
+ "gdm_xdmcp_handle_request");
+ XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ goto out;
+ }
+
+ /* Read authentication data */
+ if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_authdata)) {
+ g_warning ("%s: Could not read Authentication Data",
+ "gdm_xdmcp_handle_request");
+ XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ XdmcpDisposeARRAY8 (&clnt_authname);
+ goto out;
+ }
+
+ /* Read and select from supported authorization list */
+ if G_UNLIKELY (! XdmcpReadARRAYofARRAY8 (&factory->buf, &clnt_authorization_names)) {
+ g_warning ("%s: Could not read Authorization List",
+ "gdm_xdmcp_handle_request");
+ XdmcpDisposeARRAY8 (&clnt_authdata);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ XdmcpDisposeARRAY8 (&clnt_authname);
+ goto out;
+ }
+
+ /* libXdmcp doesn't terminate strings properly so we cheat and use strncmp () */
+ for (i = 0 ; i < clnt_authorization_names.length ; i++) {
+ if (clnt_authorization_names.data[i].length == 18 &&
+ strncmp ((char *) clnt_authorization_names.data[i].data, "MIT-MAGIC-COOKIE-1", 18) == 0) {
+ mitauth = TRUE;
+ }
+ }
+
+ /* Manufacturer ID */
+ if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_manufacturer)) {
+ g_warning ("%s: Could not read Manufacturer ID",
+ "gdm_xdmcp_handle_request");
+ XdmcpDisposeARRAY8 (&clnt_authname);
+ XdmcpDisposeARRAY8 (&clnt_authdata);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_authorization_names);
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ goto out;
+ }
+
+ /* Crude checksumming */
+ explen = 2; /* Display Number */
+ explen += 1 + 2 * clnt_conntyp.length; /* Connection Type */
+ explen += 1; /* Connection Address */
+ for (i = 0 ; i < clnt_addr.length ; i++) {
+ explen += 2 + clnt_addr.data[i].length;
+ }
+ explen += 2 + clnt_authname.length; /* Authentication Name */
+ explen += 2 + clnt_authdata.length; /* Authentication Data */
+ explen += 1; /* Authorization Names */
+ for (i = 0 ; i < clnt_authorization_names.length ; i++) {
+ explen += 2 + clnt_authorization_names.data[i].length;
+ }
+
+ explen += 2 + clnt_manufacturer.length;
+
+ if G_UNLIKELY (explen != len) {
+ g_warning ("%s: Failed checksum from %s",
+ "gdm_xdmcp_handle_request",
+ hostname ? hostname : "(null)");
+
+ XdmcpDisposeARRAY8 (&clnt_authname);
+ XdmcpDisposeARRAY8 (&clnt_authdata);
+ XdmcpDisposeARRAY8 (&clnt_manufacturer);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_authorization_names);
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ goto out;
+ }
+
+ {
+ char *s = g_strndup ((char *) clnt_manufacturer.data, clnt_manufacturer.length);
+ g_debug ("GdmXdmcpDisplayFactory: xdmcp_pending=%d, MaxPending=%d, xdmcp_sessions=%d, MaxSessions=%d, ManufacturerID=%s",
+ factory->num_pending_sessions,
+ factory->max_pending_displays,
+ factory->num_sessions,
+ factory->max_displays,
+ s != NULL ? s : "");
+ g_free (s);
+ }
+
+ /* Check if ok to manage display */
+ if (mitauth &&
+ factory->num_sessions < factory->max_displays &&
+ (gdm_address_is_local (address) ||
+ gdm_xdmcp_num_displays_from_host (factory, address) < factory->max_displays_per_host)) {
+ entered = TRUE;
+ }
+
+ if (entered) {
+
+ /* Check if we are already talking to this host */
+ display_dispose_check (factory, hostname, clnt_dspnum);
+
+ if (factory->num_pending_sessions >= factory->max_pending_displays) {
+ g_debug ("GdmXdmcpDisplayFactory: maximum pending");
+ /* Don't translate, this goes over the wire to servers where we
+ * don't know the charset or language, so it must be ascii */
+ gdm_xdmcp_send_decline (factory, address, "Maximum pending servers");
+ } else {
+ GdmDisplay *display;
+
+ display = gdm_xdmcp_display_create (factory,
+ hostname,
+ address,
+ clnt_dspnum);
+
+ if (display != NULL) {
+ ARRAY8 authentication_name;
+ ARRAY8 authentication_data;
+ ARRAY8 authorization_name;
+ ARRAY8 authorization_data;
+ gint32 session_number;
+ const char *x11_cookie;
+ gsize x11_cookie_size;
+ char *name;
+
+ x11_cookie = NULL;
+ x11_cookie_size = 0;
+ gdm_display_get_x11_cookie (display, &x11_cookie, &x11_cookie_size, NULL);
+
+ name = NULL;
+ gdm_display_get_x11_display_name (display, &name, NULL);
+
+ g_debug ("GdmXdmcpDisplayFactory: Sending authorization key for display %s", name ? name : "(null)");
+ g_free (name);
+
+ g_debug ("GdmXdmcpDisplayFactory: cookie len %d", (int) x11_cookie_size);
+
+ session_number = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display));
+
+ /* the send accept will fail if cookie is null */
+ g_assert (x11_cookie != NULL);
+
+ authentication_name.data = NULL;
+ authentication_name.length = 0;
+ authentication_data.data = NULL;
+ authentication_data.length = 0;
+
+ authorization_name.data = (CARD8 *) "MIT-MAGIC-COOKIE-1";
+ authorization_name.length = strlen ((char *) authorization_name.data);
+
+ authorization_data.data = (CARD8 *) x11_cookie;
+ authorization_data.length = x11_cookie_size;
+
+ /* the addrs are NOT copied */
+ gdm_xdmcp_send_accept (factory,
+ address,
+ session_number,
+ &authentication_name,
+ &authentication_data,
+ &authorization_name,
+ &authorization_data);
+ }
+ }
+ } else {
+ /* Don't translate, this goes over the wire to servers where we
+ * don't know the charset or language, so it must be ascii */
+ if ( ! mitauth) {
+ gdm_xdmcp_send_decline (factory,
+ address,
+ "Only MIT-MAGIC-COOKIE-1 supported");
+ } else if (factory->num_sessions >= factory->max_displays) {
+ g_warning ("Maximum number of open XDMCP sessions reached");
+ gdm_xdmcp_send_decline (factory,
+ address,
+ "Maximum number of open sessions reached");
+ } else {
+ g_debug ("GdmXdmcpDisplayFactory: Maximum number of open XDMCP sessions from host %s reached",
+ hostname ? hostname : "(null)");
+ gdm_xdmcp_send_decline (factory,
+ address,
+ "Maximum number of open sessions from your host reached");
+ }
+ }
+
+ XdmcpDisposeARRAY8 (&clnt_authname);
+ XdmcpDisposeARRAY8 (&clnt_authdata);
+ XdmcpDisposeARRAY8 (&clnt_manufacturer);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_addr);
+ XdmcpDisposeARRAYofARRAY8 (&clnt_authorization_names);
+ XdmcpDisposeARRAY16 (&clnt_conntyp);
+ out:
+ g_free (hostname);
+}
+
+static gboolean
+lookup_by_session_id (const char *id,
+ GdmDisplay *display,
+ gpointer data)
+{
+ CARD32 sessid;
+ CARD32 session_id;
+
+ sessid = GPOINTER_TO_INT (data);
+
+ if (! GDM_IS_XDMCP_DISPLAY (display)) {
+ return FALSE;
+ }
+
+ session_id = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display));
+
+ if (session_id == sessid) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GdmDisplay *
+gdm_xdmcp_display_lookup (GdmXdmcpDisplayFactory *factory,
+ CARD32 sessid)
+{
+ GdmDisplay *display;
+ GdmDisplayStore *store;
+
+ if (sessid == 0) {
+ return NULL;
+ }
+
+ store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
+ display = gdm_display_store_find (store,
+ (GdmDisplayStoreFunc)lookup_by_session_id,
+ GINT_TO_POINTER (sessid));
+
+ return display;
+}
+
+static void
+gdm_xdmcp_send_failed (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ CARD32 sessid)
+{
+ XdmcpHeader header;
+ ARRAY8 status;
+
+ g_debug ("GdmXdmcpDisplayFactory: Sending FAILED to %ld", (long)sessid);
+
+ /*
+ * Don't translate, this goes over the wire to servers where we
+ * don't know the charset or language, so it must be ascii
+ */
+ status.data = (CARD8 *) "Failed to start session";
+ status.length = strlen ((char *) status.data);
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16) FAILED;
+ header.length = 6 + status.length;
+
+ XdmcpWriteHeader (&factory->buf, &header);
+ XdmcpWriteCARD32 (&factory->buf, sessid);
+ XdmcpWriteARRAY8 (&factory->buf, &status);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+}
+
+static void
+gdm_xdmcp_send_refuse (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ CARD32 sessid)
+{
+ XdmcpHeader header;
+ ForwardQuery *fq;
+
+ g_debug ("GdmXdmcpDisplayFactory: Sending REFUSE to %ld",
+ (long)sessid);
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16) REFUSE;
+ header.length = 4;
+
+ XdmcpWriteHeader (&factory->buf, &header);
+ XdmcpWriteCARD32 (&factory->buf, sessid);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+
+ /*
+ * This was from a forwarded query quite apparently so
+ * send MANAGED_FORWARD
+ */
+ fq = forward_query_lookup (factory, address);
+ if (fq != NULL) {
+ gdm_xdmcp_send_managed_forward (factory, fq->from_address, address);
+ forward_query_destroy (factory, fq);
+ }
+}
+
+static void
+gdm_xdmcp_handle_manage (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ CARD32 clnt_sessid;
+ CARD16 clnt_dspnum;
+ ARRAY8 clnt_dspclass;
+ GdmDisplay *display;
+ ForwardQuery *fq;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Got MANAGE from %s",
+ host ? host : "(null)");
+
+ /* Check with tcp_wrappers if client is allowed to access */
+ if (! gdm_xdmcp_host_allow (address)) {
+ g_warning ("%s: Got Manage from banned host %s",
+ "gdm_xdmcp_handle_manage",
+ host ? host : "(null)");
+ g_free (host);
+ return;
+ }
+
+ /* SessionID */
+ if G_UNLIKELY (! XdmcpReadCARD32 (&factory->buf, &clnt_sessid)) {
+ g_warning ("%s: Could not read Session ID",
+ "gdm_xdmcp_handle_manage");
+ goto out;
+ }
+
+ /* Remote display number */
+ if G_UNLIKELY (! XdmcpReadCARD16 (&factory->buf, &clnt_dspnum)) {
+ g_warning ("%s: Could not read Display Number",
+ "gdm_xdmcp_handle_manage");
+ goto out;
+ }
+
+ /* Display Class */
+ if G_UNLIKELY (! XdmcpReadARRAY8 (&factory->buf, &clnt_dspclass)) {
+ g_warning ("%s: Could not read Display Class",
+ "gdm_xdmcp_handle_manage");
+ goto out;
+ }
+
+ {
+ char *s = g_strndup ((char *) clnt_dspclass.data, clnt_dspclass.length);
+ g_debug ("GdmXdmcpDisplayFactory: Got display=%d, SessionID=%ld Class=%s from %s",
+ (int)clnt_dspnum,
+ (long)clnt_sessid,
+ s != NULL ? s : "",
+ host);
+
+ g_free (s);
+ }
+
+ display = gdm_xdmcp_display_lookup (factory, clnt_sessid);
+ if (display != NULL &&
+ gdm_display_get_status (display) == GDM_DISPLAY_PREPARED) {
+ char *name;
+
+ name = NULL;
+ gdm_display_get_x11_display_name (display, &name, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Looked up %s",
+ name ? name : "(null)");
+ g_free (name);
+
+ if (factory->honor_indirect) {
+ IndirectClient *ic;
+
+ ic = indirect_client_lookup (factory, address);
+
+ /* This was an indirect thingie and nothing was yet chosen,
+ * use a chooser */
+ if (ic != NULL && ic->chosen_address == NULL) {
+ g_debug ("GdmXdmcpDisplayFactory: use chooser");
+ /*d->use_chooser = TRUE;
+ d->indirect_id = ic->id;*/
+ } else {
+ /*d->indirect_id = 0;
+ d->use_chooser = FALSE;*/
+ if (ic != NULL) {
+ indirect_client_destroy (factory, ic);
+ }
+ }
+ } else {
+
+ }
+
+ /* this was from a forwarded query quite apparently so
+ * send MANAGED_FORWARD */
+ fq = forward_query_lookup (factory, address);
+ if (fq != NULL) {
+ gdm_xdmcp_send_managed_forward (factory, fq->from_address, address);
+ forward_query_destroy (factory, fq);
+ }
+
+ factory->num_sessions++;
+ factory->num_pending_sessions--;
+
+ /* Start greeter/session */
+ if (! gdm_display_manage (display)) {
+ gdm_xdmcp_send_failed (factory, address, clnt_sessid);
+ g_debug ("GdmXdmcpDisplayFactory: Failed to manage display");
+ }
+ } else if (display != NULL &&
+ gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) {
+ g_debug ("GdmXdmcpDisplayFactory: Session ID %ld already managed",
+ (long)clnt_sessid);
+ } else {
+ g_warning ("GdmXdmcpDisplayFactory: Failed to look up session ID %ld",
+ (long)clnt_sessid);
+ gdm_xdmcp_send_refuse (factory, address, clnt_sessid);
+ }
+
+ out:
+ XdmcpDisposeARRAY8 (&clnt_dspclass);
+ g_free (host);
+}
+
+static void
+gdm_xdmcp_handle_managed_forward (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ ARRAY8 clnt_address;
+ char *host;
+ GdmAddress *disp_address;
+ IndirectClient *ic;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Got MANAGED_FORWARD from %s",
+ host ? host : "(null)");
+
+ /* Check with tcp_wrappers if client is allowed to access */
+ if (! gdm_xdmcp_host_allow (address)) {
+ g_warning ("GdmXdmcpDisplayFactory: Got MANAGED_FORWARD from banned host %s",
+ host ? host : "(null)");
+ g_free (host);
+ return;
+ }
+ g_free (host);
+
+ /* Hostname */
+ if G_UNLIKELY ( ! XdmcpReadARRAY8 (&factory->buf, &clnt_address)) {
+ g_warning ("%s: Could not read address",
+ "gdm_xdmcp_handle_managed_forward");
+ return;
+ }
+
+ disp_address = NULL;
+ if (! create_address_from_request (&clnt_address, NULL, gdm_address_get_family_type (address), &disp_address)) {
+ g_warning ("Unable to parse address for request");
+ XdmcpDisposeARRAY8 (&clnt_address);
+ return;
+ }
+
+ ic = indirect_client_lookup_by_chosen (factory, address, disp_address);
+ if (ic != NULL) {
+ indirect_client_destroy (factory, ic);
+ }
+
+ /* Note: we send GOT even on not found, just in case our previous
+ * didn't get through and this was a second managed forward */
+ gdm_xdmcp_send_got_managed_forward (factory, address, disp_address);
+
+ gdm_address_free (disp_address);
+
+ XdmcpDisposeARRAY8 (&clnt_address);
+}
+
+static void
+gdm_xdmcp_handle_got_managed_forward (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ GdmAddress *disp_address;
+ ARRAY8 clnt_address;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Got MANAGED_FORWARD from %s",
+ host ? host : "(null)");
+
+ if (! gdm_xdmcp_host_allow (address)) {
+ g_warning ("%s: Got GOT_MANAGED_FORWARD from banned host %s",
+ "gdm_xdmcp_handle_request", host ? host : "(null)");
+ g_free (host);
+ return;
+ }
+ g_free (host);
+
+ /* Hostname */
+ if G_UNLIKELY ( ! XdmcpReadARRAY8 (&factory->buf, &clnt_address)) {
+ g_warning ("%s: Could not read address",
+ "gdm_xdmcp_handle_got_managed_forward");
+ return;
+ }
+
+ if (! create_address_from_request (&clnt_address, NULL, gdm_address_get_family_type (address), &disp_address)) {
+ g_warning ("%s: Could not read address",
+ "gdm_xdmcp_handle_got_managed_forward");
+ XdmcpDisposeARRAY8 (&clnt_address);
+ return;
+ }
+
+ gdm_xdmcp_whack_queued_managed_forwards (factory, address, disp_address);
+
+ gdm_address_free (disp_address);
+
+ XdmcpDisposeARRAY8 (&clnt_address);
+}
+
+static void
+gdm_xdmcp_send_alive (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ CARD16 dspnum,
+ CARD32 sessid)
+{
+ XdmcpHeader header;
+ GdmDisplay *display;
+ int send_running = 0;
+ CARD32 send_sessid = 0;
+
+ display = gdm_xdmcp_display_lookup (factory, sessid);
+ if (display == NULL) {
+ display = gdm_xdmcp_display_lookup_by_host (factory, address, dspnum);
+ }
+
+ if (display != NULL) {
+ int status;
+
+ send_sessid = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display));
+ status = gdm_display_get_status (display);
+
+ if (status == GDM_DISPLAY_MANAGED) {
+ send_running = 1;
+ }
+ }
+
+ g_debug ("GdmXdmcpDisplayFactory: Sending ALIVE to %ld (running %d, sessid %ld)",
+ (long)sessid,
+ send_running,
+ (long)send_sessid);
+
+ header.version = XDM_PROTOCOL_VERSION;
+ header.opcode = (CARD16) ALIVE;
+ header.length = 5;
+
+ XdmcpWriteHeader (&factory->buf, &header);
+ XdmcpWriteCARD8 (&factory->buf, send_running);
+ XdmcpWriteCARD32 (&factory->buf, send_sessid);
+
+ XdmcpFlush (factory->socket_fd,
+ &factory->buf,
+ (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address),
+ (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address)));
+}
+
+static void
+gdm_xdmcp_handle_keepalive (GdmXdmcpDisplayFactory *factory,
+ GdmAddress *address,
+ int len)
+{
+ CARD16 clnt_dspnum;
+ CARD32 clnt_sessid;
+ char *host;
+
+ host = NULL;
+ gdm_address_get_numeric_info (address, &host, NULL);
+ g_debug ("GdmXdmcpDisplayFactory: Got KEEPALIVE from %s",
+ host ? host : "(null)");
+
+ /* Check with tcp_wrappers if client is allowed to access */
+ if (! gdm_xdmcp_host_allow (address)) {
+ g_warning ("%s: Got KEEPALIVE from banned host %s",
+ "gdm_xdmcp_handle_keepalive",
+ host ? host : "(null)");
+ g_free (host);
+ return;
+ }
+ g_free (host);
+
+ /* Remote display number */
+ if G_UNLIKELY (! XdmcpReadCARD16 (&factory->buf, &clnt_dspnum)) {
+ g_warning ("%s: Could not read Display Number",
+ "gdm_xdmcp_handle_keepalive");
+ return;
+ }
+
+ /* SessionID */
+ if G_UNLIKELY (! XdmcpReadCARD32 (&factory->buf, &clnt_sessid)) {
+ g_warning ("%s: Could not read Session ID",
+ "gdm_xdmcp_handle_keepalive");
+ return;
+ }
+
+ gdm_xdmcp_send_alive (factory, address, clnt_dspnum, clnt_sessid);
+}
+
+static const char *
+opcode_string (int opcode)
+{
+ static const char * const opcode_names[] = {
+ NULL,
+ "BROADCAST_QUERY",
+ "QUERY",
+ "INDIRECT_QUERY",
+ "FORWARD_QUERY",
+ "WILLING",
+ "UNWILLING",
+ "REQUEST",
+ "ACCEPT",
+ "DECLINE",
+ "MANAGE",
+ "REFUSE",
+ "FAILED",
+ "KEEPALIVE",
+ "ALIVE"
+ };
+ static const char * const gdm_opcode_names[] = {
+ "MANAGED_FORWARD",
+ "GOT_MANAGED_FORWARD"
+ };
+
+
+ if (opcode < G_N_ELEMENTS (opcode_names)) {
+ return opcode_names [opcode];
+ } else if (opcode >= GDM_XDMCP_FIRST_OPCODE &&
+ opcode < GDM_XDMCP_LAST_OPCODE) {
+ return gdm_opcode_names [opcode - GDM_XDMCP_FIRST_OPCODE];
+ } else {
+ return "UNKNOWN";
+ }
+}
+
+static gboolean
+decode_packet (GIOChannel *source,
+ GIOCondition cond,
+ GdmXdmcpDisplayFactory *factory)
+{
+ struct sockaddr_storage clnt_ss;
+ GdmAddress *address;
+ gint ss_len;
+ XdmcpHeader header;
+ char *host;
+ char *port;
+ int res;
+
+ g_debug ("GdmXdmcpDisplayFactory: decode_packet: GIOCondition %d", (int)cond);
+
+ if ( ! (cond & G_IO_IN)) {
+ return TRUE;
+ }
+
+ ss_len = (int) sizeof (clnt_ss);
+
+ res = XdmcpFill (factory->socket_fd, &factory->buf, (XdmcpNetaddr)&clnt_ss, &ss_len);
+ if G_UNLIKELY (! res) {
+ g_debug ("GdmXdmcpDisplayFactory: Could not create XDMCP buffer!");
+ return TRUE;
+ }
+
+ res = XdmcpReadHeader (&factory->buf, &header);
+ if G_UNLIKELY (! res) {
+ g_warning ("GdmXdmcpDisplayFactory: Could not read XDMCP header!");
+ return TRUE;
+ }
+
+ if G_UNLIKELY (header.version != XDM_PROTOCOL_VERSION &&
+ header.version != GDM_XDMCP_PROTOCOL_VERSION) {
+ g_warning ("XDMCP: Incorrect XDMCP version!");
+ return TRUE;
+ }
+
+ address = gdm_address_new_from_sockaddr ((struct sockaddr *) &clnt_ss, ss_len);
+ if (address == NULL) {
+ g_warning ("XDMCP: Unable to parse address");
+ return TRUE;
+ }
+
+ gdm_address_debug (address);
+
+ host = NULL;
+ port = NULL;
+ gdm_address_get_numeric_info (address, &host, &port);
+
+ g_debug ("GdmXdmcpDisplayFactory: Received opcode %s from client %s : %s",
+ opcode_string (header.opcode),
+ host ? host : "(null)",
+ port ? port : "(null)");
+
+ switch (header.opcode) {
+ case BROADCAST_QUERY:
+ gdm_xdmcp_handle_broadcast_query (factory, address, header.length);
+ break;
+
+ case QUERY:
+ gdm_xdmcp_handle_query (factory, address, header.length);
+ break;
+
+ case INDIRECT_QUERY:
+ gdm_xdmcp_handle_indirect_query (factory, address, header.length);
+ break;
+
+ case FORWARD_QUERY:
+ gdm_xdmcp_handle_forward_query (factory, address, header.length);
+ break;
+
+ case REQUEST:
+ gdm_xdmcp_handle_request (factory, address, header.length);
+ break;
+
+ case MANAGE:
+ gdm_xdmcp_handle_manage (factory, address, header.length);
+ break;
+
+ case KEEPALIVE:
+ gdm_xdmcp_handle_keepalive (factory, address, header.length);
+ break;
+
+ case GDM_XDMCP_MANAGED_FORWARD:
+ gdm_xdmcp_handle_managed_forward (factory, address, header.length);
+ break;
+
+ case GDM_XDMCP_GOT_MANAGED_FORWARD:
+ gdm_xdmcp_handle_got_managed_forward (factory, address, header.length);
+ break;
+
+ default:
+ g_debug ("GdmXdmcpDisplayFactory: Unknown opcode from client %s : %s",
+ host ? host : "(null)",
+ port ? port : "(null)");
+
+ break;
+ }
+
+ g_free (host);
+ g_free (port);
+
+ gdm_address_free (address);
+
+ return TRUE;
+}
+
+static gboolean
+gdm_xdmcp_display_factory_start (GdmDisplayFactory *base_factory)
+{
+ gboolean ret;
+ GIOChannel *ioc;
+ GdmXdmcpDisplayFactory *factory = GDM_XDMCP_DISPLAY_FACTORY (base_factory);
+ gboolean res;
+
+ g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory), FALSE);
+ g_return_val_if_fail (factory->socket_fd == -1, FALSE);
+
+ /* read configuration */
+ res = gdm_settings_direct_get_uint (GDM_KEY_UDP_PORT,
+ &(factory->port));
+ res = res && gdm_settings_direct_get_boolean (GDM_KEY_MULTICAST,
+ &(factory->use_multicast));
+ res = res && gdm_settings_direct_get_string (GDM_KEY_MULTICAST_ADDR,
+ &(factory->multicast_address));
+ res = res && gdm_settings_direct_get_boolean (GDM_KEY_INDIRECT,
+ &(factory->honor_indirect));
+ res = res && gdm_settings_direct_get_uint (GDM_KEY_DISPLAYS_PER_HOST,
+ &(factory->max_displays_per_host));
+ res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_SESSIONS,
+ &(factory->max_displays));
+ res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_PENDING,
+ &(factory->max_pending_displays));
+ res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_WAIT,
+ &(factory->max_wait));
+ res = res && gdm_settings_direct_get_uint (GDM_KEY_MAX_WAIT_INDIRECT,
+ &(factory->max_wait_indirect));
+ res = res && gdm_settings_direct_get_string (GDM_KEY_WILLING,
+ &(factory->willing_script));
+
+ if (! res) {
+ return res;
+ }
+
+ ret = open_port (factory);
+ if (! ret) {
+ return ret;
+ }
+
+ g_debug ("GdmXdmcpDisplayFactory: Starting to listen on XDMCP port");
+
+ ioc = g_io_channel_unix_new (factory->socket_fd);
+
+ g_io_channel_set_encoding (ioc, NULL, NULL);
+ g_io_channel_set_buffered (ioc, FALSE);
+
+ factory->socket_watch_id = g_io_add_watch_full (ioc,
+ G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc)decode_packet,
+ factory,
+ NULL);
+ g_io_channel_unref (ioc);
+
+ return ret;
+}
+
+static gboolean
+gdm_xdmcp_display_factory_stop (GdmDisplayFactory *base_factory)
+{
+ GdmXdmcpDisplayFactory *factory = GDM_XDMCP_DISPLAY_FACTORY (base_factory);
+
+ g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory), FALSE);
+ g_return_val_if_fail (factory->socket_fd != -1, FALSE);
+
+ if (factory->socket_watch_id > 0) {
+ g_source_remove (factory->socket_watch_id);
+ factory->socket_watch_id = 0;
+ }
+
+ if (factory->socket_fd > 0) {
+ VE_IGNORE_EINTR (close (factory->socket_fd));
+ factory->socket_fd = -1;
+ }
+
+ return TRUE;
+}
+
+void
+gdm_xdmcp_display_factory_set_port (GdmXdmcpDisplayFactory *factory,
+ guint port)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->port = port;
+}
+
+static void
+gdm_xdmcp_display_factory_set_use_multicast (GdmXdmcpDisplayFactory *factory,
+ gboolean use_multicast)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->use_multicast = use_multicast;
+}
+
+static void
+gdm_xdmcp_display_factory_set_multicast_address (GdmXdmcpDisplayFactory *factory,
+ const char *address)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ g_free (factory->multicast_address);
+ factory->multicast_address = g_strdup (address);
+}
+
+static void
+gdm_xdmcp_display_factory_set_honor_indirect (GdmXdmcpDisplayFactory *factory,
+ gboolean honor_indirect)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->honor_indirect = honor_indirect;
+}
+
+static void
+gdm_xdmcp_display_factory_set_max_displays_per_host (GdmXdmcpDisplayFactory *factory,
+ guint num)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->max_displays_per_host = num;
+}
+
+static void
+gdm_xdmcp_display_factory_set_max_displays (GdmXdmcpDisplayFactory *factory,
+ guint num)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->max_displays = num;
+}
+
+static void
+gdm_xdmcp_display_factory_set_max_pending_displays (GdmXdmcpDisplayFactory *factory,
+ guint num)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->max_pending_displays = num;
+}
+
+static void
+gdm_xdmcp_display_factory_set_max_wait (GdmXdmcpDisplayFactory *factory,
+ guint num)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->max_wait = num;
+}
+
+static void
+gdm_xdmcp_display_factory_set_max_wait_indirect (GdmXdmcpDisplayFactory *factory,
+ guint num)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ factory->max_wait_indirect = num;
+}
+
+static void
+gdm_xdmcp_display_factory_set_willing_script (GdmXdmcpDisplayFactory *factory,
+ const char *script)
+{
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (factory));
+
+ g_free (factory->willing_script);
+ factory->willing_script = g_strdup (script);
+}
+
+static void
+gdm_xdmcp_display_factory_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmXdmcpDisplayFactory *self;
+
+ self = GDM_XDMCP_DISPLAY_FACTORY (object);
+
+ switch (prop_id) {
+ case PROP_PORT:
+ gdm_xdmcp_display_factory_set_port (self, g_value_get_uint (value));
+ break;
+ case PROP_USE_MULTICAST:
+ gdm_xdmcp_display_factory_set_use_multicast (self, g_value_get_boolean (value));
+ break;
+ case PROP_MULTICAST_ADDRESS:
+ gdm_xdmcp_display_factory_set_multicast_address (self, g_value_get_string (value));
+ break;
+ case PROP_HONOR_INDIRECT:
+ gdm_xdmcp_display_factory_set_honor_indirect (self, g_value_get_boolean (value));
+ break;
+ case PROP_MAX_DISPLAYS_PER_HOST:
+ gdm_xdmcp_display_factory_set_max_displays_per_host (self, g_value_get_uint (value));
+ break;
+ case PROP_MAX_DISPLAYS:
+ gdm_xdmcp_display_factory_set_max_displays (self, g_value_get_uint (value));
+ break;
+ case PROP_MAX_PENDING_DISPLAYS:
+ gdm_xdmcp_display_factory_set_max_pending_displays (self, g_value_get_uint (value));
+ break;
+ case PROP_MAX_WAIT:
+ gdm_xdmcp_display_factory_set_max_wait (self, g_value_get_uint (value));
+ break;
+ case PROP_MAX_WAIT_INDIRECT:
+ gdm_xdmcp_display_factory_set_max_wait_indirect (self, g_value_get_uint (value));
+ break;
+ case PROP_WILLING_SCRIPT:
+ gdm_xdmcp_display_factory_set_willing_script (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_xdmcp_display_factory_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmXdmcpDisplayFactory *self;
+
+ self = GDM_XDMCP_DISPLAY_FACTORY (object);
+
+ switch (prop_id) {
+ case PROP_PORT:
+ g_value_set_uint (value, self->port);
+ break;
+ case PROP_USE_MULTICAST:
+ g_value_set_boolean (value, self->use_multicast);
+ break;
+ case PROP_MULTICAST_ADDRESS:
+ g_value_set_string (value, self->multicast_address);
+ break;
+ case PROP_HONOR_INDIRECT:
+ g_value_set_boolean (value, self->honor_indirect);
+ break;
+ case PROP_MAX_DISPLAYS_PER_HOST:
+ g_value_set_uint (value, self->max_displays_per_host);
+ break;
+ case PROP_MAX_DISPLAYS:
+ g_value_set_uint (value, self->max_displays);
+ break;
+ case PROP_MAX_PENDING_DISPLAYS:
+ g_value_set_uint (value, self->max_pending_displays);
+ break;
+ case PROP_MAX_WAIT:
+ g_value_set_uint (value, self->max_wait);
+ break;
+ case PROP_MAX_WAIT_INDIRECT:
+ g_value_set_uint (value, self->max_wait_indirect);
+ break;
+ case PROP_WILLING_SCRIPT:
+ g_value_set_string (value, self->willing_script);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_xdmcp_display_factory_class_init (GdmXdmcpDisplayFactoryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdmDisplayFactoryClass *factory_class = GDM_DISPLAY_FACTORY_CLASS (klass);
+
+ object_class->get_property = gdm_xdmcp_display_factory_get_property;
+ object_class->set_property = gdm_xdmcp_display_factory_set_property;
+ object_class->finalize = gdm_xdmcp_display_factory_finalize;
+
+ factory_class->start = gdm_xdmcp_display_factory_start;
+ factory_class->stop = gdm_xdmcp_display_factory_stop;
+
+ g_object_class_install_property (object_class,
+ PROP_PORT,
+ g_param_spec_uint ("port",
+ "UDP port",
+ "UDP port",
+ 0,
+ G_MAXINT,
+ DEFAULT_PORT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_USE_MULTICAST,
+ g_param_spec_boolean ("use-multicast",
+ NULL,
+ NULL,
+ DEFAULT_USE_MULTICAST,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_MULTICAST_ADDRESS,
+ g_param_spec_string ("multicast-address",
+ "multicast-address",
+ "multicast-address",
+ DEFAULT_MULTICAST_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_HONOR_INDIRECT,
+ g_param_spec_boolean ("honor-indirect",
+ NULL,
+ NULL,
+ DEFAULT_HONOR_INDIRECT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_WILLING_SCRIPT,
+ g_param_spec_string ("willing-script",
+ "willing-script",
+ "willing-script",
+ DEFAULT_WILLING_SCRIPT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_MAX_DISPLAYS_PER_HOST,
+ g_param_spec_uint ("max-displays-per-host",
+ "max-displays-per-host",
+ "max-displays-per-host",
+ 0,
+ G_MAXINT,
+ DEFAULT_MAX_DISPLAYS_PER_HOST,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_MAX_DISPLAYS,
+ g_param_spec_uint ("max-displays",
+ "max-displays",
+ "max-displays",
+ 0,
+ G_MAXINT,
+ DEFAULT_MAX_DISPLAYS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_MAX_PENDING_DISPLAYS,
+ g_param_spec_uint ("max-pending-displays",
+ "max-pending-displays",
+ "max-pending-displays",
+ 0,
+ G_MAXINT,
+ DEFAULT_MAX_PENDING_DISPLAYS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_MAX_WAIT,
+ g_param_spec_uint ("max-wait",
+ "max-wait",
+ "max-wait",
+ 0,
+ G_MAXINT,
+ DEFAULT_MAX_WAIT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_MAX_WAIT_INDIRECT,
+ g_param_spec_uint ("max-wait-indirect",
+ "max-wait-indirect",
+ "max-wait-indirect",
+ 0,
+ G_MAXINT,
+ DEFAULT_MAX_WAIT_INDIRECT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gdm_xdmcp_display_factory_init (GdmXdmcpDisplayFactory *factory)
+{
+ char hostbuf[1024];
+ struct utsname name;
+
+ factory->socket_fd = -1;
+
+ factory->session_serial = g_random_int ();
+
+ /* Fetch and store local hostname in XDMCP friendly format */
+ hostbuf[1023] = '\0';
+ if G_UNLIKELY (gethostname (hostbuf, 1023) != 0) {
+ g_warning ("Could not get server hostname: %s!", g_strerror (errno));
+ strcpy (hostbuf, "localhost.localdomain");
+ }
+
+ uname (&name);
+ factory->sysid = g_strconcat (name.sysname,
+ " ",
+ name.release,
+ NULL);
+
+ factory->hostname = g_strdup (hostbuf);
+
+ factory->servhost.data = (CARD8 *) g_strdup (hostbuf);
+ factory->servhost.length = strlen ((char *) factory->servhost.data);
+}
+
+static void
+gdm_xdmcp_display_factory_finalize (GObject *object)
+{
+ GdmXdmcpDisplayFactory *factory;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_XDMCP_DISPLAY_FACTORY (object));
+
+ factory = GDM_XDMCP_DISPLAY_FACTORY (object);
+
+ g_return_if_fail (factory != NULL);
+
+ if (factory->socket_watch_id > 0) {
+ g_source_remove (factory->socket_watch_id);
+ }
+
+ if (factory->socket_fd > 0) {
+ close (factory->socket_fd);
+ factory->socket_fd = -1;
+ }
+
+ g_slist_free (factory->forward_queries);
+ g_slist_free (factory->managed_forwards);
+
+ g_free (factory->sysid);
+ g_free (factory->hostname);
+ g_free (factory->multicast_address);
+ g_free (factory->willing_script);
+
+ /* FIXME: Free servhost */
+
+ G_OBJECT_CLASS (gdm_xdmcp_display_factory_parent_class)->finalize (object);
+}
+
+GdmXdmcpDisplayFactory *
+gdm_xdmcp_display_factory_new (GdmDisplayStore *store)
+{
+ if (xdmcp_display_factory_object != NULL) {
+ g_object_ref (xdmcp_display_factory_object);
+ } else {
+ xdmcp_display_factory_object = g_object_new (GDM_TYPE_XDMCP_DISPLAY_FACTORY,
+ "display-store", store,
+ NULL);
+ g_object_add_weak_pointer (xdmcp_display_factory_object,
+ (gpointer *) &xdmcp_display_factory_object);
+ }
+
+ return GDM_XDMCP_DISPLAY_FACTORY (xdmcp_display_factory_object);
+}
diff --git a/daemon/gdm-xdmcp-display-factory.h b/daemon/gdm-xdmcp-display-factory.h
new file mode 100644
index 0000000..2a2e821
--- /dev/null
+++ b/daemon/gdm-xdmcp-display-factory.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_XDMCP_DISPLAY_FACTORY_H
+#define __GDM_XDMCP_DISPLAY_FACTORY_H
+
+#include <glib-object.h>
+
+#include "gdm-display-factory.h"
+#include "gdm-display-store.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_XDMCP_DISPLAY_FACTORY (gdm_xdmcp_display_factory_get_type ())
+G_DECLARE_FINAL_TYPE (GdmXdmcpDisplayFactory, gdm_xdmcp_display_factory, GDM, XDMCP_DISPLAY_FACTORY, GdmDisplayFactory)
+
+typedef enum
+{
+ GDM_XDMCP_DISPLAY_FACTORY_ERROR_GENERAL
+} GdmXdmcpDisplayFactoryError;
+
+#define GDM_XDMCP_DISPLAY_FACTORY_ERROR gdm_xdmcp_display_factory_error_quark ()
+
+GQuark gdm_xdmcp_display_factory_error_quark (void);
+
+GdmXdmcpDisplayFactory * gdm_xdmcp_display_factory_new (GdmDisplayStore *display_store);
+
+void gdm_xdmcp_display_factory_set_port (GdmXdmcpDisplayFactory *manager,
+ guint port);
+
+G_END_DECLS
+
+#endif /* __GDM_XDMCP_DISPLAY_FACTORY_H */
diff --git a/daemon/gdm-xdmcp-display.c b/daemon/gdm-xdmcp-display.c
new file mode 100644
index 0000000..18e2575
--- /dev/null
+++ b/daemon/gdm-xdmcp-display.c
@@ -0,0 +1,295 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-display.h"
+#include "gdm-launch-environment.h"
+#include "gdm-xdmcp-display.h"
+
+#include "gdm-common.h"
+#include "gdm-address.h"
+
+#include "gdm-settings.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+typedef struct _GdmXdmcpDisplayPrivate
+{
+ GdmAddress *remote_address;
+ gint32 session_number;
+ guint connection_attempts;
+} GdmXdmcpDisplayPrivate;
+
+enum {
+ PROP_0,
+ PROP_REMOTE_ADDRESS,
+ PROP_SESSION_NUMBER,
+};
+
+#define MAX_CONNECT_ATTEMPTS 10
+
+static void gdm_xdmcp_display_class_init (GdmXdmcpDisplayClass *klass);
+static void gdm_xdmcp_display_init (GdmXdmcpDisplay *xdmcp_display);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdmXdmcpDisplay, gdm_xdmcp_display, GDM_TYPE_DISPLAY)
+
+gint32
+gdm_xdmcp_display_get_session_number (GdmXdmcpDisplay *display)
+{
+ GdmXdmcpDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY (display), 0);
+
+ priv = gdm_xdmcp_display_get_instance_private (display);
+ return priv->session_number;
+}
+
+GdmAddress *
+gdm_xdmcp_display_get_remote_address (GdmXdmcpDisplay *display)
+{
+ GdmXdmcpDisplayPrivate *priv;
+
+ g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY (display), NULL);
+
+ priv = gdm_xdmcp_display_get_instance_private (display);
+ return priv->remote_address;
+}
+
+static void
+_gdm_xdmcp_display_set_remote_address (GdmXdmcpDisplay *display,
+ GdmAddress *address)
+{
+ GdmXdmcpDisplayPrivate *priv;
+
+ priv = gdm_xdmcp_display_get_instance_private (display);
+ if (priv->remote_address != NULL) {
+ gdm_address_free (priv->remote_address);
+ }
+
+ g_assert (address != NULL);
+
+ gdm_address_debug (address);
+ priv->remote_address = gdm_address_copy (address);
+}
+
+static void
+gdm_xdmcp_display_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmXdmcpDisplay *self;
+ GdmXdmcpDisplayPrivate *priv;
+
+ self = GDM_XDMCP_DISPLAY (object);
+ priv = gdm_xdmcp_display_get_instance_private (self);
+
+ switch (prop_id) {
+ case PROP_REMOTE_ADDRESS:
+ _gdm_xdmcp_display_set_remote_address (self, g_value_get_boxed (value));
+ break;
+ case PROP_SESSION_NUMBER:
+ priv->session_number = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_xdmcp_display_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmXdmcpDisplay *self;
+ GdmXdmcpDisplayPrivate *priv;
+
+ self = GDM_XDMCP_DISPLAY (object);
+ priv = gdm_xdmcp_display_get_instance_private (self);
+
+ switch (prop_id) {
+ case PROP_REMOTE_ADDRESS:
+ g_value_set_boxed (value, priv->remote_address);
+ break;
+ case PROP_SESSION_NUMBER:
+ g_value_set_int (value, priv->session_number);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gdm_xdmcp_display_prepare (GdmDisplay *display)
+{
+ GdmXdmcpDisplay *self = GDM_XDMCP_DISPLAY (display);
+ GdmLaunchEnvironment *launch_environment;
+ char *display_name;
+ char *seat_id;
+ char *hostname;
+
+ launch_environment = NULL;
+ display_name = NULL;
+ seat_id = NULL;
+ hostname = NULL;
+
+ g_object_get (self,
+ "x11-display-name", &display_name,
+ "seat-id", &seat_id,
+ "remote-hostname", &hostname,
+ "launch-environment", &launch_environment,
+ NULL);
+
+ if (launch_environment == NULL) {
+ launch_environment = gdm_create_greeter_launch_environment (display_name,
+ seat_id,
+ NULL,
+ hostname,
+ FALSE);
+ g_object_set (self, "launch-environment", launch_environment, NULL);
+ g_object_unref (launch_environment);
+ }
+
+ if (!gdm_display_create_authority (display)) {
+ g_warning ("Unable to set up access control for display %s",
+ display_name);
+ return FALSE;
+ }
+
+ return GDM_DISPLAY_CLASS (gdm_xdmcp_display_parent_class)->prepare (display);
+}
+
+static gboolean
+idle_connect_to_display (GdmXdmcpDisplay *self)
+{
+ GdmXdmcpDisplayPrivate *priv;
+ gboolean res;
+
+ priv = gdm_xdmcp_display_get_instance_private (self);
+ priv->connection_attempts++;
+
+ res = gdm_display_connect (GDM_DISPLAY (self));
+ if (res) {
+ g_object_set (G_OBJECT (self), "status", GDM_DISPLAY_MANAGED, NULL);
+ } else {
+ if (priv->connection_attempts >= MAX_CONNECT_ATTEMPTS) {
+ g_warning ("Unable to connect to display after %d tries - bailing out", priv->connection_attempts);
+ gdm_display_unmanage (GDM_DISPLAY (self));
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gdm_xdmcp_display_manage (GdmDisplay *display)
+{
+ GdmXdmcpDisplay *self = GDM_XDMCP_DISPLAY (display);
+
+ g_timeout_add (500, (GSourceFunc)idle_connect_to_display, self);
+}
+
+static void
+gdm_xdmcp_display_class_init (GdmXdmcpDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass);
+
+ object_class->get_property = gdm_xdmcp_display_get_property;
+ object_class->set_property = gdm_xdmcp_display_set_property;
+
+ display_class->prepare = gdm_xdmcp_display_prepare;
+ display_class->manage = gdm_xdmcp_display_manage;
+
+ g_object_class_install_property (object_class,
+ PROP_REMOTE_ADDRESS,
+ g_param_spec_boxed ("remote-address",
+ "Remote address",
+ "Remote address",
+ GDM_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SESSION_NUMBER,
+ g_param_spec_int ("session-number",
+ "session-number",
+ "session-number",
+ G_MININT,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+}
+
+static void
+gdm_xdmcp_display_init (GdmXdmcpDisplay *xdmcp_display)
+{
+
+ gboolean allow_remote_autologin;
+
+ allow_remote_autologin = FALSE;
+ gdm_settings_direct_get_boolean (GDM_KEY_ALLOW_REMOTE_AUTOLOGIN, &allow_remote_autologin);
+
+ g_object_set (G_OBJECT (xdmcp_display), "allow-timed-login", allow_remote_autologin, NULL);
+}
+
+GdmDisplay *
+gdm_xdmcp_display_new (const char *hostname,
+ int number,
+ GdmAddress *address,
+ gint32 session_number)
+{
+ GObject *object;
+ char *x11_display;
+
+ x11_display = g_strdup_printf ("%s:%d", hostname, number);
+ object = g_object_new (GDM_TYPE_XDMCP_DISPLAY,
+ "remote-hostname", hostname,
+ "x11-display-number", number,
+ "x11-display-name", x11_display,
+ "is-local", FALSE,
+ "remote-address", address,
+ "session-number", session_number,
+ NULL);
+ g_free (x11_display);
+
+ return GDM_DISPLAY (object);
+}
diff --git a/daemon/gdm-xdmcp-display.h b/daemon/gdm-xdmcp-display.h
new file mode 100644
index 0000000..c103eaa
--- /dev/null
+++ b/daemon/gdm-xdmcp-display.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#ifndef __GDM_XDMCP_DISPLAY_H
+#define __GDM_XDMCP_DISPLAY_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <glib-object.h>
+
+#include "gdm-display.h"
+#include "gdm-address.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_XDMCP_DISPLAY (gdm_xdmcp_display_get_type ())
+G_DECLARE_DERIVABLE_TYPE (GdmXdmcpDisplay, gdm_xdmcp_display, GDM, XDMCP_DISPLAY, GdmDisplay)
+
+struct _GdmXdmcpDisplayClass
+{
+ GdmDisplayClass parent_class;
+};
+
+gint32 gdm_xdmcp_display_get_session_number (GdmXdmcpDisplay *display);
+GdmAddress * gdm_xdmcp_display_get_remote_address (GdmXdmcpDisplay *display);
+
+GdmDisplay * gdm_xdmcp_display_new (const char *hostname,
+ int number,
+ GdmAddress *address,
+ gint32 session_number);
+
+G_END_DECLS
+
+#endif /* __GDM_XDMCP_DISPLAY_H */
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644
index 0000000..344d1b7
--- /dev/null
+++ b/daemon/main.c
@@ -0,0 +1,463 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <pwd.h>
+#include <grp.h>
+#include <sys/wait.h>
+#include <locale.h>
+#include <signal.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gdm-manager.h"
+#include "gdm-log.h"
+#include "gdm-common.h"
+
+#include "gdm-settings.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+#define GDM_DBUS_NAME "org.gnome.DisplayManager"
+
+static GDBusConnection *get_system_bus (void);
+static gboolean bus_reconnect (void);
+
+extern char **environ;
+
+static GdmManager *manager = NULL;
+static int name_id = -1;
+static GdmSettings *settings = NULL;
+static uid_t gdm_uid = -1;
+static gid_t gdm_gid = -1;
+
+static gboolean
+timed_exit_cb (GMainLoop *loop)
+{
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+bus_connection_closed (void)
+{
+ g_debug ("Disconnected from D-Bus");
+
+ if (manager == NULL) {
+ /* probably shutting down or something */
+ return;
+ }
+
+ g_clear_object (&manager);
+
+ g_timeout_add_seconds (3, (GSourceFunc)bus_reconnect, NULL);
+}
+
+static GDBusConnection *
+get_system_bus (void)
+{
+ GError *error;
+ GDBusConnection *bus;
+
+ error = NULL;
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (bus == NULL) {
+ g_warning ("Couldn't connect to system bus: %s",
+ error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_signal_connect (bus, "closed",
+ G_CALLBACK (bus_connection_closed), NULL);
+ g_dbus_connection_set_exit_on_close (bus, FALSE);
+
+ out:
+ return bus;
+}
+
+static void
+delete_pid (void)
+{
+ g_unlink (GDM_PID_FILE);
+}
+
+static void
+write_pid (void)
+{
+ int pf;
+ ssize_t written;
+ char pid[9];
+
+ errno = 0;
+ pf = open (GDM_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+ if (pf < 0) {
+ g_warning (_("Cannot write PID file %s: possibly out of disk space: %s"),
+ GDM_PID_FILE,
+ g_strerror (errno));
+
+ return;
+ }
+
+ snprintf (pid, sizeof (pid), "%lu\n", (long unsigned) getpid ());
+ errno = 0;
+ written = write (pf, pid, strlen (pid));
+ close (pf);
+
+ if (written < 0) {
+ g_warning (_("Cannot write PID file %s: possibly out of disk space: %s"),
+ GDM_PID_FILE,
+ g_strerror (errno));
+ return;
+ }
+
+ atexit (delete_pid);
+}
+
+static gboolean
+ensure_dir_with_perms (const char *path,
+ uid_t uid,
+ gid_t gid,
+ mode_t mode,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (g_mkdir_with_parents (path, 0755) == -1) {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno));
+ goto out;
+ }
+ if (g_chmod (path, mode) == -1) {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno));
+ goto out;
+ }
+ if (chown (path, uid, gid) == -1) {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno));
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static void
+gdm_daemon_ensure_dirs (uid_t uid,
+ gid_t gid)
+{
+ GError *error = NULL;
+
+ /* Set up /var/run/gdm */
+ if (!ensure_dir_with_perms (GDM_RAN_ONCE_MARKER_DIR, 0, gid, 0711, &error)) {
+ gdm_fail (_("Failed to create ran once marker dir %s: %s"),
+ GDM_RAN_ONCE_MARKER_DIR, error->message);
+ }
+
+ /* Set up /var/log/gdm */
+ if (!ensure_dir_with_perms (LOGDIR, 0, gid, 0711, &error)) {
+ gdm_fail (_("Failed to create LogDir %s: %s"),
+ LOGDIR, error->message);
+ }
+}
+
+static void
+gdm_daemon_lookup_user (uid_t *uidp,
+ gid_t *gidp)
+{
+ char *username;
+ char *groupname;
+ uid_t uid;
+ gid_t gid;
+ struct passwd *pwent;
+ struct group *grent;
+
+ username = NULL;
+ groupname = NULL;
+ uid = 0;
+ gid = 0;
+
+ gdm_settings_direct_get_string (GDM_KEY_USER, &username);
+ gdm_settings_direct_get_string (GDM_KEY_GROUP, &groupname);
+
+ if (username == NULL || groupname == NULL) {
+ return;
+ }
+
+ g_debug ("Changing user:group to %s:%s", username, groupname);
+
+ /* Lookup user and groupid for the GDM user */
+ gdm_get_pwent_for_name (username, &pwent);
+
+ /* Set uid and gid */
+ if G_UNLIKELY (pwent == NULL) {
+ gdm_fail (_("Can’t find the GDM user “%s”. Aborting!"), username);
+ } else {
+ uid = pwent->pw_uid;
+ }
+
+ if G_UNLIKELY (uid == 0) {
+ gdm_fail (_("The GDM user should not be root. Aborting!"));
+ }
+
+ grent = getgrnam (groupname);
+
+ if G_UNLIKELY (grent == NULL) {
+ gdm_fail (_("Can’t find the GDM group “%s”. Aborting!"), groupname);
+ } else {
+ gid = grent->gr_gid;
+ }
+
+ if G_UNLIKELY (gid == 0) {
+ gdm_fail (_("The GDM group should not be root. Aborting!"));
+ }
+
+ if (uidp != NULL) {
+ *uidp = uid;
+ }
+
+ if (gidp != NULL) {
+ *gidp = gid;
+ }
+
+ g_free (username);
+ g_free (groupname);
+}
+
+static gboolean
+on_shutdown_signal_cb (gpointer user_data)
+{
+ GMainLoop *mainloop = user_data;
+
+ g_main_loop_quit (mainloop);
+
+ return FALSE;
+}
+
+static gboolean
+on_sighup_cb (gpointer user_data)
+{
+ g_debug ("Got HUP signal");
+
+ gdm_settings_reload (settings);
+
+ return TRUE;
+}
+
+static gboolean
+is_debug_set (void)
+{
+ gboolean debug;
+ gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
+ return debug;
+}
+
+/* SIGUSR1 is used by the X server to tell us that we're ready, so
+ * block it. We'll unblock it in the worker thread in gdm-server.c
+ */
+static void
+block_sigusr1 (void)
+{
+ sigset_t mask;
+
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGUSR1);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GMainLoop *main_loop;
+ GOptionContext *context;
+ GError *error = NULL;
+ gboolean res;
+ static gboolean do_timed_exit = FALSE;
+ static gboolean print_version = FALSE;
+ static gboolean fatal_warnings = FALSE;
+ static GOptionEntry entries [] = {
+ { "fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &fatal_warnings, N_("Make all warnings fatal"), NULL },
+ { "timed-exit", 0, 0, G_OPTION_ARG_NONE, &do_timed_exit, N_("Exit after a time (for debugging)"), NULL },
+ { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, N_("Print GDM version"), NULL },
+
+ { NULL }
+ };
+
+ block_sigusr1 ();
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ setlocale (LC_ALL, "");
+
+ context = g_option_context_new (_("GNOME Display Manager"));
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ error = NULL;
+ res = g_option_context_parse (context, &argc, &argv, &error);
+ g_option_context_free (context);
+ if (! res) {
+ g_printerr ("Failed to parse options: %s\n", error->message);
+ g_error_free (error);
+ return EXIT_FAILURE;
+ }
+
+ if (print_version) {
+ g_print ("GDM %s\n", VERSION);
+ return EXIT_SUCCESS;
+ }
+
+ /* XDM compliant error message */
+ if (getuid () != 0) {
+ /* make sure the pid file doesn't get wiped */
+ g_printerr ("%s\n", _("Only the root user can run GDM"));
+ return EXIT_FAILURE;
+ }
+
+ if (fatal_warnings) {
+ GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);
+ }
+
+ gdm_log_init ();
+
+ settings = gdm_settings_new ();
+ if (! gdm_settings_direct_init (settings, DATADIR "/gdm/gdm.schemas", "/")) {
+ g_warning ("Unable to initialize settings");
+ return EXIT_FAILURE;
+ }
+
+ gdm_log_set_debug (is_debug_set ());
+
+ gdm_daemon_lookup_user (&gdm_uid, &gdm_gid);
+
+ gdm_daemon_ensure_dirs (gdm_uid, gdm_gid);
+
+ /* Connect to the bus, own the name and start the manager */
+ bus_reconnect ();
+
+ /* pid file */
+ delete_pid ();
+ write_pid ();
+
+ g_chdir ("/");
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ g_unix_signal_add (SIGTERM, on_shutdown_signal_cb, main_loop);
+ g_unix_signal_add (SIGINT, on_shutdown_signal_cb, main_loop);
+ g_unix_signal_add (SIGHUP, on_sighup_cb, NULL);
+
+ if (do_timed_exit) {
+ g_timeout_add_seconds (30, (GSourceFunc) timed_exit_cb, main_loop);
+ }
+
+ g_main_loop_run (main_loop);
+
+ g_debug ("GDM finished, cleaning up...");
+
+ g_clear_object (&manager);
+ g_clear_object (&settings);
+
+ gdm_settings_direct_shutdown ();
+ gdm_log_shutdown ();
+
+ g_main_loop_unref (main_loop);
+
+ return EXIT_SUCCESS;
+}
+
+static void
+on_name_acquired (GDBusConnection *bus,
+ const char *name,
+ gpointer user_data)
+{
+ gboolean xdmcp_enabled;
+ gboolean show_local_greeter;
+
+ manager = gdm_manager_new ();
+ if (manager == NULL) {
+ g_warning ("Could not construct manager object");
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Successfully connected to D-Bus");
+
+ show_local_greeter = TRUE;
+ gdm_settings_direct_get_boolean (GDM_KEY_SHOW_LOCAL_GREETER, &show_local_greeter);
+ gdm_manager_set_show_local_greeter (manager, show_local_greeter);
+
+ xdmcp_enabled = FALSE;
+ gdm_settings_direct_get_boolean (GDM_KEY_XDMCP_ENABLE, &xdmcp_enabled);
+ gdm_manager_set_xdmcp_enabled (manager, xdmcp_enabled);
+
+ gdm_manager_start (manager);
+}
+
+static void
+on_name_lost (GDBusConnection *bus,
+ const char *name,
+ gpointer user_data)
+{
+ g_debug ("Lost GDM name on bus");
+
+ bus_connection_closed ();
+}
+
+static gboolean
+bus_reconnect ()
+{
+ GDBusConnection *bus;
+ gboolean ret;
+
+ ret = TRUE;
+
+ bus = get_system_bus ();
+ if (bus == NULL) {
+ goto out;
+ }
+
+ name_id = g_bus_own_name_on_connection (bus,
+ GDM_DBUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_name_acquired,
+ on_name_lost,
+ NULL,
+ NULL);
+
+ ret = FALSE;
+ out:
+ return ret;
+}
diff --git a/daemon/meson.build b/daemon/meson.build
new file mode 100644
index 0000000..41f30ab
--- /dev/null
+++ b/daemon/meson.build
@@ -0,0 +1,217 @@
+# D-Bus interfaces
+dbus_gen = gnome.gdbus_codegen('gdm-dbus-glue',
+ 'org.freedesktop.DBus.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.freedesktop.DBus',
+ autocleanup: 'all',
+)
+display_dbus_gen = gnome.gdbus_codegen('gdm-display-glue',
+ 'gdm-display.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.gnome.DisplayManager',
+ autocleanup: 'all',
+)
+local_display_dbus_gen = gnome.gdbus_codegen('gdm-local-display-glue',
+ 'gdm-local-display.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.gnome.DisplayManager',
+ autocleanup: 'all',
+)
+local_display_factory_dbus_gen = gnome.gdbus_codegen('gdm-local-display-factory-glue',
+ 'gdm-local-display-factory.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.gnome.DisplayManager',
+ autocleanup: 'all',
+)
+manager_dbus_gen = gnome.gdbus_codegen('gdm-manager-glue',
+ 'gdm-manager.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.gnome.DisplayManager',
+ autocleanup: 'all',
+)
+session_dbus_gen = gnome.gdbus_codegen('gdm-session-glue',
+ 'gdm-session.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.gnome.DisplayManager',
+ autocleanup: 'all',
+)
+session_worker_dbus_gen = gnome.gdbus_codegen('gdm-session-worker-glue',
+ 'gdm-session-worker.xml',
+ namespace: 'GdmDBus',
+ interface_prefix: 'org.gnome.DisplayManager',
+ autocleanup: 'all',
+)
+
+gdm_session_enums = gnome.mkenums('gdm-session-enum-types',
+ h_template: 'gdm-session-enum-types.h.in',
+ c_template: 'gdm-session-enum-types.c.in',
+ sources: 'gdm-session.h',
+)
+gdm_session_worker_enums = gnome.mkenums('gdm-session-worker-enum-types',
+ h_template: 'gdm-session-worker-enum-types.h.in',
+ c_template: 'gdm-session-worker-enum-types.c.in',
+ sources: 'gdm-session-worker.h',
+)
+
+# Daemons deps
+gdm_daemon_deps = [
+ libgdmcommon_dep,
+ accountsservice_dep,
+ gobject_dep,
+ gio_dep,
+ gio_unix_dep,
+ libpam_dep,
+ x_deps,
+ xcb_dep,
+]
+
+if xdmcp_dep.found() and get_option('tcp-wrappers')
+ gdm_daemon_deps += libwrap_dep
+endif
+
+# test-session-client
+test_session_client_src = [
+ 'test-session-client.c',
+ session_dbus_gen,
+ manager_dbus_gen,
+]
+
+test_session_client = executable('test-session-client',
+ test_session_client_src,
+ dependencies: gdm_daemon_deps,
+ include_directories: config_h_dir,
+)
+
+# Session worker
+gdm_session_worker_src = [
+ 'session-worker-main.c',
+ 'gdm-session.c',
+ 'gdm-session-settings.c',
+ 'gdm-session-auditor.c',
+ 'gdm-session-record.c',
+ 'gdm-session-worker.c',
+ 'gdm-session-worker-job.c',
+ 'gdm-session-worker-common.c',
+ 'gdm-dbus-util.c',
+ dbus_gen,
+ session_dbus_gen,
+ session_worker_dbus_gen,
+ gdm_session_enums,
+ gdm_session_worker_enums,
+]
+
+gdm_session_worker_deps = [
+ gdm_daemon_deps,
+]
+
+gdm_session_worker_includes = [
+ config_h_dir,
+]
+
+if pam_extensions_supported
+ gdm_session_worker_src += '../pam-extensions/gdm-pam-extensions.h'
+ gdm_session_worker_includes += pam_extensions_inc
+endif
+
+if libaudit_dep.found()
+ gdm_session_worker_deps += libaudit_dep
+
+ gdm_session_worker_src += [
+ 'gdm-session-linux-auditor.c',
+ ]
+endif
+
+if have_adt
+ gdm_session_worker_src += 'gdm-session-solaris-auditor.c'
+endif
+
+gdm_session_worker = executable('gdm-session-worker',
+ gdm_session_worker_src,
+ dependencies: gdm_session_worker_deps,
+ include_directories: gdm_session_worker_includes,
+ install: true,
+ install_dir: get_option('libexecdir'),
+)
+
+# Wayland session
+gdm_wayland_session_src = [
+ 'gdm-wayland-session.c',
+ manager_dbus_gen,
+]
+
+gdm_wayland_session = executable('gdm-wayland-session',
+ gdm_wayland_session_src,
+ dependencies: gdm_daemon_deps,
+ include_directories: gdm_session_worker_includes,
+ install: true,
+ install_dir: get_option('libexecdir'),
+)
+
+# X session
+gdm_x_session_src = [
+ 'gdm-x-session.c',
+ manager_dbus_gen,
+]
+
+gdm_x_session = executable('gdm-x-session',
+ gdm_x_session_src,
+ dependencies: gdm_daemon_deps,
+ include_directories: gdm_session_worker_includes,
+ install: true,
+ install_dir: get_option('libexecdir'),
+)
+
+# GDM daemon
+gdm_daemon_sources = files(
+ 'gdm-dbus-util.c',
+ 'gdm-display-access-file.c',
+ 'gdm-display-factory.c',
+ 'gdm-display-store.c',
+ 'gdm-display.c',
+ 'gdm-launch-environment.c',
+ 'gdm-legacy-display.c',
+ 'gdm-local-display-factory.c',
+ 'gdm-local-display.c',
+ 'gdm-manager.c',
+ 'gdm-server.c',
+ 'gdm-session-record.c',
+ 'gdm-session-worker-common.c',
+ 'gdm-session-worker-job.c',
+ 'gdm-session.c',
+ 'main.c',
+)
+
+gdm_daemon_gen_sources = [
+ display_dbus_gen,
+ local_display_factory_dbus_gen,
+ manager_dbus_gen,
+ local_display_dbus_gen,
+ session_dbus_gen,
+ session_worker_dbus_gen,
+ gdm_session_enums,
+]
+
+if xdmcp_dep.found()
+ gdm_daemon_deps += xdmcp_dep
+
+ gdm_daemon_sources = [
+ gdm_daemon_sources,
+ files(
+ 'gdm-xdmcp-display-factory.c',
+ 'gdm-xdmcp-display.c',
+ 'gdm-xdmcp-chooser-display.c',
+ ),
+ ]
+endif
+
+if gudev_dep.found()
+ gdm_daemon_deps += gudev_dep
+endif
+
+gdm_daemon = executable('gdm',
+ [ gdm_daemon_sources, gdm_daemon_gen_sources ],
+ dependencies: gdm_daemon_deps,
+ include_directories: config_h_dir,
+ install: true,
+ install_dir: get_option('sbindir')
+)
diff --git a/daemon/org.freedesktop.DBus.xml b/daemon/org.freedesktop.DBus.xml
new file mode 100644
index 0000000..5e0814b
--- /dev/null
+++ b/daemon/org.freedesktop.DBus.xml
@@ -0,0 +1,12 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.DBus.Peer">
+ <method name="GetMachineId">
+ <arg direction="out" type="s"/>
+ </method>
+ <method name="Ping">
+ </method>
+ </interface>
+</node>
+
diff --git a/daemon/session-worker-main.c b/daemon/session-worker-main.c
new file mode 100644
index 0000000..d96844d
--- /dev/null
+++ b/daemon/session-worker-main.c
@@ -0,0 +1,173 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <signal.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+
+#include "gdm-common.h"
+#include "gdm-log.h"
+#include "gdm-session-worker.h"
+
+#include "gdm-settings.h"
+#include "gdm-settings-direct.h"
+#include "gdm-settings-keys.h"
+
+static GdmSettings *settings = NULL;
+
+static gboolean
+on_sigusr1_cb (gpointer user_data)
+{
+ g_debug ("Got USR1 signal");
+
+ gdm_log_toggle_debug ();
+
+ return TRUE;
+}
+
+static gboolean
+is_debug_set (void)
+{
+ gboolean debug;
+ gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
+ return debug;
+}
+
+static gboolean
+on_shutdown_signal_cb (gpointer user_data)
+{
+ GMainLoop *mainloop = user_data;
+
+ g_main_loop_quit (mainloop);
+
+ return FALSE;
+}
+
+static void
+on_state_changed (GdmSessionWorker *worker,
+ GParamSpec *pspec,
+ GMainLoop *main_loop)
+{
+ GdmSessionWorkerState state;
+
+ g_object_get (G_OBJECT (worker), "state", &state, NULL);
+
+ if (state != GDM_SESSION_WORKER_STATE_SESSION_STARTED)
+ return;
+
+ g_unix_signal_add (SIGTERM, on_shutdown_signal_cb, main_loop);
+}
+
+static void
+on_sigterm_cb (int signal_number)
+{
+ _exit (EXIT_SUCCESS);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GMainLoop *main_loop;
+ GOptionContext *context;
+ GdmSessionWorker *worker;
+ const char *address;
+ gboolean is_for_reauth;
+ static GOptionEntry entries [] = {
+ { NULL }
+ };
+
+ signal (SIGTERM, on_sigterm_cb);
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ setlocale (LC_ALL, "");
+
+ /* Translators: worker is a helper process that does the work
+ of starting up a session */
+ 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 ();
+
+ settings = gdm_settings_new ();
+ if (settings == NULL) {
+ g_warning ("Unable to initialize settings");
+ exit (EXIT_FAILURE);
+ }
+
+ if (! gdm_settings_direct_init (settings, DATADIR "/gdm/gdm.schemas", "/")) {
+ g_warning ("Unable to initialize settings");
+ exit (EXIT_FAILURE);
+ }
+
+ gdm_log_set_debug (is_debug_set ());
+
+ address = g_getenv ("GDM_SESSION_DBUS_ADDRESS");
+ if (address == NULL) {
+ g_warning ("GDM_SESSION_DBUS_ADDRESS not set");
+ exit (EXIT_FAILURE);
+ }
+
+ is_for_reauth = g_getenv ("GDM_SESSION_FOR_REAUTH") != NULL;
+
+ worker = gdm_session_worker_new (address, is_for_reauth);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ g_signal_connect (G_OBJECT (worker),
+ "notify::state",
+ G_CALLBACK (on_state_changed),
+ main_loop);
+
+ g_unix_signal_add (SIGUSR1, on_sigusr1_cb, NULL);
+
+ g_main_loop_run (main_loop);
+
+ if (worker != NULL) {
+ g_signal_handlers_disconnect_by_func (worker,
+ G_CALLBACK (on_state_changed),
+ main_loop);
+ g_object_unref (worker);
+ }
+
+ g_main_loop_unref (main_loop);
+
+ g_debug ("Worker finished");
+
+ return 0;
+}
diff --git a/daemon/test-session-client.c b/daemon/test-session-client.c
new file mode 100644
index 0000000..ae4f59d
--- /dev/null
+++ b/daemon/test-session-client.c
@@ -0,0 +1,257 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+
+#include <glib.h>
+
+#include "gdm-manager-glue.h"
+#include "gdm-session-glue.h"
+
+static GMainLoop *loop;
+
+static void
+on_conversation_stopped (GdmDBusUserVerifier *user_verifier,
+ const char *service_name)
+{
+ g_print ("\n** WARNING: conversation stopped\n");
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_reset (GdmDBusUserVerifier *user_verifier)
+{
+ g_print ("\n** NOTE: reset\n");
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_verification_complete (GdmDBusUserVerifier *user_verifier,
+ const char *service_name)
+{
+ g_print ("\n** INFO: verification complete\n");
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_info_query (GdmDBusUserVerifier *user_verifier,
+ const char *service_name,
+ const char *query_text)
+{
+ char answer[1024];
+ char *res;
+
+ g_print ("%s ", query_text);
+
+ answer[0] = '\0';
+ res = fgets (answer, sizeof (answer), stdin);
+ if (res == NULL) {
+ g_warning ("Couldn't get an answer");
+ }
+
+ answer[strlen (answer) - 1] = '\0';
+
+ if (answer[0] == '\0') {
+ gdm_dbus_user_verifier_call_cancel_sync (user_verifier,
+ NULL,
+ NULL);
+ g_main_loop_quit (loop);
+ } else {
+ gdm_dbus_user_verifier_call_answer_query_sync (user_verifier,
+ service_name,
+ answer,
+ NULL,
+ NULL);
+ }
+}
+
+static void
+on_info (GdmDBusUserVerifier *user_verifier,
+ const char *service_name,
+ const char *info)
+{
+ g_print ("\n** NOTE: %s\n", info);
+}
+
+static void
+on_problem (GdmDBusUserVerifier *user_verifier,
+ const char *service_name,
+ const char *problem)
+{
+ g_print ("\n** WARNING: %s\n", problem);
+}
+
+static void
+on_secret_info_query (GdmDBusUserVerifier *user_verifier,
+ const char *service_name,
+ const char *query_text)
+{
+ char answer[1024];
+ char *res;
+ struct termios ts0;
+ struct termios ts1;
+
+ tcgetattr (fileno (stdin), &ts0);
+ ts1 = ts0;
+ ts1.c_lflag &= ~ECHO;
+
+ g_print ("%s", query_text);
+
+ if (tcsetattr (fileno (stdin), TCSAFLUSH, &ts1) != 0) {
+ fprintf (stderr, "Could not set terminal attributes\n");
+ exit (EXIT_FAILURE);
+ }
+
+ answer[0] = '\0';
+ res = fgets (answer, sizeof (answer), stdin);
+ answer[strlen (answer) - 1] = '\0';
+ if (res == NULL) {
+ g_warning ("Couldn't get an answer");
+ }
+
+ tcsetattr (fileno (stdin), TCSANOW, &ts0);
+
+ g_print ("\n");
+
+ gdm_dbus_user_verifier_call_answer_query_sync (user_verifier,
+ service_name,
+ answer,
+ NULL,
+ NULL);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ GError *error;
+ GdmDBusManager *manager;
+ GdmDBusUserVerifier *user_verifier;
+ GDBusConnection *system_bus;
+ GDBusConnection *connection;
+ char *address;
+ gboolean ok;
+
+ g_debug ("creating instance of GdmDBusDisplay object...");
+
+ error = NULL;
+ system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (system_bus == NULL) {
+ g_critical ("Failed connecting to the system bus (this is pretty bad): %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ manager = GDM_DBUS_MANAGER (gdm_dbus_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.gnome.DisplayManager",
+ "/org/gnome/DisplayManager/Manager",
+ NULL,
+ &error));
+ if (manager == NULL) {
+ g_critical ("Failed creating display proxy: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ address = NULL;
+ gdm_dbus_manager_call_open_reauthentication_channel_sync (manager,
+ g_get_user_name (),
+ &address,
+ NULL,
+ &error);
+ if (address == NULL) {
+ g_critical ("Failed opening reauthentication channel: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ connection = g_dbus_connection_new_for_address_sync (address,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ NULL,
+ NULL,
+ &error);
+ if (connection == NULL) {
+ g_critical ("Failed connecting to the manager: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ user_verifier = GDM_DBUS_USER_VERIFIER (gdm_dbus_user_verifier_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "/org/gnome/DisplayManager/Session",
+ NULL,
+ &error));
+ if (user_verifier == NULL) {
+ g_critical ("Failed creating user verifier proxy: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_signal_connect (user_verifier,
+ "info",
+ G_CALLBACK (on_info),
+ NULL);
+ g_signal_connect (user_verifier,
+ "problem",
+ G_CALLBACK (on_problem),
+ NULL);
+ g_signal_connect (user_verifier,
+ "info-query",
+ G_CALLBACK (on_info_query),
+ NULL);
+ g_signal_connect (user_verifier,
+ "secret-info-query",
+ G_CALLBACK (on_secret_info_query),
+ NULL);
+ g_signal_connect (user_verifier,
+ "conversation-stopped",
+ G_CALLBACK (on_conversation_stopped),
+ NULL);
+ g_signal_connect (user_verifier,
+ "verification-complete",
+ G_CALLBACK (on_verification_complete),
+ NULL);
+ g_signal_connect (user_verifier,
+ "reset",
+ G_CALLBACK (on_reset),
+ NULL);
+
+ ok = gdm_dbus_user_verifier_call_begin_verification_for_user_sync (user_verifier,
+ "gdm-password",
+ g_get_user_name (),
+ NULL,
+ &error);
+ if (!ok) {
+ g_critical ("Failed to start PAM session: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ return 0;
+}