1
0
Fork 0
gdm3/daemon/gdm-session.c
Daniel Baumann 83b37a3d94
Adding upstream version 48.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 19:45:29 +02:00

4505 lines
169 KiB
C

/* -*- 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 <json-glib/json-glib.h>
#include <systemd/sd-login.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;
char *remote_id;
guint32 is_program_session : 1;
guint32 display_is_initial : 1;
guint32 is_opened : 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,
PROP_REMOTE_ID,
};
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,
CREDENTIALS_ESTABLISHED,
VERIFICATION_COMPLETE,
SESSION_OPENED,
SESSION_OPENED_FAILED,
SESSION_STARTED,
SESSION_START_FAILED,
SESSION_EXITED,
SESSION_DIED,
REAUTHENTICATION_STARTED,
REAUTHENTICATED,
STOP_CONFLICTING_SESSION,
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) {
g_signal_emit (self,
signals[CREDENTIALS_ESTABLISHED],
0,
service_name,
conversation->worker_pid);
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
is_wayland_headless (GdmSession *self)
{
return g_strcmp0 (self->session_type, "wayland") == 0 &&
!self->display_is_local;
}
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;
}
if (is_wayland_headless (self)) {
gboolean can_run_headless;
can_run_headless = g_key_file_get_boolean (key_file,
G_KEY_FILE_DESKTOP_GROUP,
"X-GDM-CanRunHeadless",
NULL);
if (!can_run_headless && is_wayland_headless (self)) {
g_debug ("GdmSession: Session %s is not headless capable", 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_return_if_fail (GDM_IS_SESSION (self));
g_return_if_fail (text != NULL);
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_literal (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_custom_json_request (GdmDBusWorkerManager *worker_manager_interface,
GDBusMethodInvocation *invocation,
const char *service_name,
const char *protocol,
unsigned int version,
const char *request,
GdmSession *self)
{
GdmSessionConversation *conversation;
GdmDBusUserVerifierCustomJSON *custom_json_interface = NULL;
g_debug ("GdmSession: custom JSON request for service '%s'", service_name);
if (self->user_verifier_extensions != NULL) {
custom_json_interface =
g_hash_table_lookup (self->user_verifier_extensions,
gdm_dbus_user_verifier_custom_json_interface_info ()->name);
}
if (custom_json_interface == NULL) {
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_NOT_SUPPORTED,
"custom JSON 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 custom JSON request '%s' v%u",
protocol, version);
gdm_dbus_user_verifier_custom_json_emit_request (custom_json_interface,
service_name,
protocol,
version,
request);
}
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,
session_id);
}
g_debug ("GdmSession: Emitting 'session-opened' signal");
g_signal_emit (self, signals[SESSION_OPENED], 0, service_name, session_id);
self->is_opened = TRUE;
} 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,
int reauth_pid,
GdmSessionConversation *conversation)
{
GdmSession *self = conversation->session;
g_debug ("GdmSession: Emitting 'reauthenticated' signal ");
g_signal_emit (self, signals[REAUTHENTICATED], 0, service_name, reauth_pid);
}
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_set_data (G_OBJECT (connection),
"gdm-dbus-worker-manager-interface",
NULL);
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_object_set_data (G_OBJECT (connection),
"gdm-dbus-worker-manager-interface",
NULL);
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_literal (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_object (worker_manager_interface,
"handle-hello",
G_CALLBACK (register_worker),
self,
0);
g_signal_connect_object (worker_manager_interface,
"handle-info-query",
G_CALLBACK (gdm_session_handle_info_query),
self,
0);
g_signal_connect_object (worker_manager_interface,
"handle-secret-info-query",
G_CALLBACK (gdm_session_handle_secret_info_query),
self,
0);
g_signal_connect_object (worker_manager_interface,
"handle-info",
G_CALLBACK (gdm_session_handle_info),
self,
0);
g_signal_connect_object (worker_manager_interface,
"handle-problem",
G_CALLBACK (gdm_session_handle_problem),
self,
0);
g_signal_connect_object (worker_manager_interface,
"handle-choice-list-query",
G_CALLBACK (gdm_session_handle_choice_list_query),
self,
0);
g_signal_connect_object (worker_manager_interface,
"handle-custom-json-request",
G_CALLBACK (gdm_session_handle_custom_json_request),
self,
0);
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (worker_manager_interface),
connection,
GDM_SESSION_DBUS_OBJECT_PATH,
NULL);
g_object_set_data_full (G_OBJECT (connection),
"gdm-dbus-worker-manager-interface",
g_object_ref (worker_manager_interface),
g_object_unref);
}
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);
g_signal_handlers_disconnect_by_func (worker_manager_interface,
G_CALLBACK (gdm_session_handle_custom_json_request),
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_literal (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_custom_json_reply (GdmDBusUserVerifierCustomJSON *custom_json_interface,
GDBusMethodInvocation *invocation,
const char *service_name,
const char *json,
GdmSession *self)
{
g_autoptr(GError) error = NULL;
g_autoptr(JsonParser) parser = NULL;
g_debug ("GdmSession: user replied with custom JSON");
parser = json_parser_new_immutable ();
if (!json_parser_load_from_data (parser, json, -1, &error)) {
g_autofree char *message = NULL;
message = g_strdup_printf ("JSON reply is not valid: %s", error->message);
g_warning ("GdmSession: %s", message);
g_dbus_method_invocation_return_error_literal (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_NOT_SUPPORTED,
message);
gdm_session_report_error (self, service_name,
G_DBUS_ERROR_NOT_SUPPORTED,
message);
return TRUE;
}
gdm_dbus_user_verifier_custom_json_complete_reply (custom_json_interface, invocation);
gdm_session_answer_query (self, service_name, json);
return TRUE;
}
static gboolean
gdm_session_handle_client_custom_json_report_error (GdmDBusUserVerifierCustomJSON *custom_json_interface,
GDBusMethodInvocation *invocation,
const char *service_name,
const char *message,
GdmSession *self)
{
g_debug ("GdmSession: user reported custom JSON error: %s", message);
gdm_dbus_user_verifier_custom_json_complete_report_error (custom_json_interface, invocation);
gdm_session_report_error (self, service_name, G_DBUS_ERROR_ACCESS_DENIED, message);
return TRUE;
}
static void
export_user_verifier_custom_json_interface (GdmSession *self,
GDBusConnection *connection)
{
GdmDBusUserVerifierCustomJSON *interface;
interface = GDM_DBUS_USER_VERIFIER_CUSTOM_JSON (gdm_dbus_user_verifier_custom_json_skeleton_new ());
g_signal_connect (interface,
"handle-reply",
G_CALLBACK (gdm_session_handle_client_custom_json_reply),
self);
g_signal_connect (interface,
"handle-report-error",
G_CALLBACK (gdm_session_handle_client_custom_json_report_error),
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_custom_json_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);
if (g_str_equal (extensions[i],
gdm_dbus_user_verifier_custom_json_interface_info ()->name))
export_user_verifier_custom_json_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_stop_conflicting_session (GdmDBusGreeter *greeter_interface,
GDBusMethodInvocation *invocation,
GdmSession *self)
{
if (!self->is_opened) {
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Can't stop conflicting session if this session is not opened yet");
return TRUE;
}
g_signal_emit (self, signals[STOP_CONFLICTING_SESSION], 0, self->selected_user);
if (self->greeter_interface != NULL) {
gdm_dbus_greeter_complete_stop_conflicting_session (self->greeter_interface,
invocation);
}
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_signal_connect (greeter_interface,
"handle-stop-conflicting-session",
G_CALLBACK (gdm_session_handle_client_stop_conflicting_session),
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);
g_return_val_if_fail (service_name != NULL, 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_return_if_fail (service_name != NULL);
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));
g_return_if_fail (service_name != NULL);
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 (service_name != NULL);
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));
g_return_if_fail (service_name != NULL);
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));
g_return_if_fail (service_name != NULL);
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));
g_return_if_fail (service_name != NULL);
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));
g_return_if_fail (service_name != NULL);
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));
g_return_if_fail (service_name != NULL);
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 (GDM_IS_SESSION (self));
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));
g_return_if_fail (service_name != NULL);
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 (service_name != NULL);
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
free_pending_worker_connection (GdmSession *self,
GDBusConnection *connection)
{
GdmDBusWorkerManager *worker_manager_interface;
worker_manager_interface = g_object_get_data (G_OBJECT (connection),
"gdm-dbus-worker-manager-interface");
if (worker_manager_interface != NULL) {
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (worker_manager_interface));
g_object_set_data (G_OBJECT (connection),
"gdm-dbus-worker-manager-interface",
NULL);
}
g_object_unref (connection);
}
static void
free_pending_worker_connections (GdmSession *self)
{
GList *node;
for (node = self->pending_worker_connections; node != NULL; node = node->next) {
GDBusConnection *connection = node->data;
free_pending_worker_connection (self, connection);
}
g_list_free (self->pending_worker_connections);
self->pending_worker_connections = NULL;
}
static void
do_reset (GdmSession *self)
{
stop_all_conversations (self);
free_pending_worker_connections (self);
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));
g_return_if_fail (service_name != NULL);
g_return_if_fail (text != NULL);
conversation = find_conversation_by_name (self, service_name);
if (conversation != NULL) {
answer_pending_query (conversation, text);
}
}
void
gdm_session_report_error (GdmSession *self,
const char *service_name,
GDBusError code,
const char *message)
{
GdmSessionConversation *conversation;
g_return_if_fail (GDM_IS_SESSION (self));
g_return_if_fail (service_name != NULL);
conversation = find_conversation_by_name (self, service_name);
if (conversation == NULL)
return;
g_dbus_method_invocation_return_error_literal (g_steal_pointer (&conversation->pending_invocation),
G_DBUS_ERROR, code, message);
}
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)
{
g_return_if_fail (GDM_IS_SESSION (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_return_if_fail (GDM_IS_SESSION (self));
g_return_if_fail (username != NULL);
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)
{
g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
return self->session_pid > 0;
}
gboolean
gdm_session_is_frozen (GdmSession *self)
{
g_autofree char *cgroup = NULL, *path = NULL, *data = NULL;
g_auto (GStrv) arr = NULL;
g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
if (self->session_pid <= 0)
return FALSE;
if (sd_pid_get_cgroup (self->session_pid, &cgroup) < 0)
return FALSE;
path = g_build_filename ("/sys/fs/cgroup", cgroup, "cgroup.events", NULL);
if (!g_file_get_contents (path, &data, NULL, NULL))
return FALSE;
arr = g_strsplit_set (data, " \n", -1);
for (gsize i = 0; arr[i] != NULL; i++) {
if (g_str_equal (arr[i], "frozen"))
return g_str_equal (arr[i + 1], "1");
}
return FALSE;
}
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 *self,
GPid pid_of_caller,
uid_t uid_of_caller)
{
GdmSessionConversation *conversation;
g_return_if_fail (GDM_IS_SESSION (self));
g_return_if_fail (self->session_conversation != NULL);
conversation = self->session_conversation;
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 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);
g_return_val_if_fail (service_name != NULL, 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 (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 (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_return_val_if_fail (GDM_IS_SESSION (self), GDM_SESSION_DISPLAY_MODE_NEW_VT);
g_debug ("GdmSession: type %s, program? %s, seat %s",
self->session_type,
self->is_program_session? "yes" : "no",
self->display_seat_id);
if (self->display_seat_id == NULL &&
g_strcmp0 (self->session_type, "wayland") != 0) {
return GDM_SESSION_DISPLAY_MODE_REUSE_VT;
}
if (g_strcmp0 (self->display_seat_id, "seat0") != 0) {
return GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED;
}
#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_return_if_fail (GDM_IS_SESSION (self));
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_return_if_fail (GDM_IS_SESSION (self));
g_return_if_fail (text != NULL);
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
set_remote_id (GdmSession *self,
const char *remote_id)
{
g_free (self->remote_id);
self->remote_id = g_strdup (remote_id);
}
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;
case PROP_REMOTE_ID:
set_remote_id (self, g_value_get_string (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;
case PROP_REMOTE_ID:
g_value_set_string (value, self->remote_id);
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 [CREDENTIALS_ESTABLISHED] =
g_signal_new ("credentials-established",
GDM_TYPE_SESSION,
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING);
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,
2,
G_TYPE_STRING,
G_TYPE_INT);
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);
signals [STOP_CONFLICTING_SESSION] =
g_signal_new ("stop-conflicting-session",
GDM_TYPE_SESSION,
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING);
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));
g_object_class_install_property (object_class,
PROP_REMOTE_ID,
g_param_spec_string ("remote-id",
"remote id",
"remote id",
NULL,
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 *self)
{
g_return_val_if_fail (GDM_IS_SESSION (self), 0);
return self->session_pid;
}