diff options
Diffstat (limited to '')
-rw-r--r-- | daemon/gdm-display.c | 1931 |
1 files changed, 1931 insertions, 0 deletions
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c new file mode 100644 index 0000000..687e7da --- /dev/null +++ b/daemon/gdm-display.c @@ -0,0 +1,1931 @@ +/* -*- 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; +} 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, +}; + +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; + GError *error = NULL; + GVariant *call_result; + GVariant *user_list; + + 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); + goto out; + } + + 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); + goto out; + } + + g_variant_get (call_result, "(@ao)", &user_list); + priv->have_existing_user_accounts = g_variant_n_children (user_list) > 0; + g_variant_unref (user_list); + g_variant_unref (call_result); +out: + g_clear_error (&error); + return priv->accountsservice_proxy != NULL && call_result != NULL; +} + +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); + + g_debug ("GdmDisplay: unmanage display"); + + 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_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; + 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; + 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_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)); +} + +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); + if (gdm_display_get_status (self) == GDM_DISPLAY_MANAGED) { + 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, + 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; +} + |