diff options
Diffstat (limited to 'gnome-session/gsm-session-fill.c')
-rw-r--r-- | gnome-session/gsm-session-fill.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/gnome-session/gsm-session-fill.c b/gnome-session/gsm-session-fill.c new file mode 100644 index 0000000..93a6dbc --- /dev/null +++ b/gnome-session/gsm-session-fill.c @@ -0,0 +1,334 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2006, 2010 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "gsm-session-fill.h" + +#include "gsm-system.h" +#include "gsm-manager.h" +#include "gsm-process-helper.h" +#include "gsm-util.h" + +#define GSM_KEYFILE_SESSION_GROUP "GNOME Session" +#define GSM_KEYFILE_RUNNABLE_KEY "IsRunnableHelper" +#define GSM_KEYFILE_FALLBACK_KEY "FallbackSession" +#define GSM_KEYFILE_REQUIRED_COMPONENTS_KEY "RequiredComponents" + +/* See https://bugzilla.gnome.org/show_bug.cgi?id=641992 for discussion */ +#define GSM_RUNNABLE_HELPER_TIMEOUT 3000 /* ms */ + +typedef void (*GsmFillHandleComponent) (const char *component, + const char *app_path, + gpointer user_data); + +static void +handle_required_components (GKeyFile *keyfile, + gboolean look_in_saved_session, + GsmFillHandleComponent callback, + gpointer user_data) +{ + char **required_components; + int i; + + g_assert (keyfile != NULL); + g_assert (callback != NULL); + + required_components = g_key_file_get_string_list (keyfile, + GSM_KEYFILE_SESSION_GROUP, + GSM_KEYFILE_REQUIRED_COMPONENTS_KEY, + NULL, NULL); + + if (!required_components) + return; + + for (i = 0; required_components[i] != NULL; i++) { + char *app_path; + + app_path = gsm_util_find_desktop_file_for_app_name (required_components[i], + look_in_saved_session, TRUE); + callback (required_components[i], app_path, user_data); + g_free (app_path); + } + + g_strfreev (required_components); +} + +static void +check_required_components_helper (const char *component, + const char *app_path, + gpointer user_data) +{ + gboolean *error = user_data; + + if (app_path == NULL) { + g_warning ("Unable to find required component '%s'", component); + *error = TRUE; + } +} + +static gboolean +check_required (GKeyFile *keyfile) +{ + gboolean error = FALSE; + + g_debug ("fill: *** Checking required components"); + + handle_required_components (keyfile, FALSE, + check_required_components_helper, &error); + + g_debug ("fill: *** Done checking required components"); + + return !error; +} + +static void +maybe_load_saved_session_apps (GsmManager *manager) +{ + GsmSystem *system; + gboolean is_login; + + system = gsm_get_system (); + is_login = gsm_system_is_login_session (system); + g_object_unref (system); + + if (is_login) + return; + + gsm_manager_add_autostart_apps_from_dir (manager, gsm_util_get_saved_session_dir ()); +} + +static void +append_required_components_helper (const char *component, + const char *app_path, + gpointer user_data) +{ + GsmManager *manager = user_data; + + if (app_path == NULL) + g_warning ("Unable to find required component '%s'", component); + else + gsm_manager_add_required_app (manager, app_path); +} + + +static void +load_standard_apps (GsmManager *manager, + GKeyFile *keyfile) +{ + /* Note that saving/restoring sessions is not really possible on systemd, as + * XSMP clients cannot be reliably mapped to .desktop files. */ + g_debug ("fill: *** Adding required components"); + handle_required_components (keyfile, + !gsm_manager_get_failsafe (manager) && !gsm_manager_get_systemd_managed (manager), + append_required_components_helper, manager); + g_debug ("fill: *** Done adding required components"); + + if (!gsm_manager_get_failsafe (manager)) { + char **autostart_dirs; + int i; + + autostart_dirs = gsm_util_get_autostart_dirs (); + + if (!gsm_manager_get_systemd_managed (manager)) + maybe_load_saved_session_apps (manager); + + for (i = 0; autostart_dirs[i]; i++) { + gsm_manager_add_autostart_apps_from_dir (manager, + autostart_dirs[i]); + } + + g_strfreev (autostart_dirs); + } +} + +static GKeyFile * +get_session_keyfile_if_valid (const char *path) +{ + GKeyFile *keyfile; + gsize len; + char **list; + + g_debug ("fill: *** Looking if %s is a valid session file", path); + + keyfile = g_key_file_new (); + + if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { + g_debug ("Cannot use session '%s': non-existing or invalid file.", path); + goto error; + } + + if (!g_key_file_has_group (keyfile, GSM_KEYFILE_SESSION_GROUP)) { + g_warning ("Cannot use session '%s': no '%s' group.", path, GSM_KEYFILE_SESSION_GROUP); + goto error; + } + + list = g_key_file_get_string_list (keyfile, + GSM_KEYFILE_SESSION_GROUP, + GSM_KEYFILE_REQUIRED_COMPONENTS_KEY, + &len, NULL); + if (list) + g_strfreev (list); + if (len == 0) + g_warning ("Session '%s': no component in the session.", path); + + return keyfile; + +error: + g_key_file_free (keyfile); + return NULL; +} + +/** + * find_valid_session_keyfile: + * @session: name of session + * + * We look for the session file in XDG_CONFIG_HOME, XDG_CONFIG_DIRS and + * XDG_DATA_DIRS. This enables users and sysadmins to override a specific + * session that is shipped in XDG_DATA_DIRS. + */ +static GKeyFile * +find_valid_session_keyfile (const char *session) +{ + GPtrArray *dirs; + const char * const *system_config_dirs; + const char * const *system_data_dirs; + int i; + GKeyFile *keyfile; + char *basename; + + dirs = g_ptr_array_new (); + + g_ptr_array_add (dirs, (gpointer) g_get_user_config_dir ()); + + system_config_dirs = g_get_system_config_dirs (); + for (i = 0; system_config_dirs[i]; i++) + g_ptr_array_add (dirs, (gpointer) system_config_dirs[i]); + + system_data_dirs = g_get_system_data_dirs (); + for (i = 0; system_data_dirs[i]; i++) + g_ptr_array_add (dirs, (gpointer) system_data_dirs[i]); + + keyfile = NULL; + basename = g_strdup_printf ("%s.session", session); + + for (i = 0; i < dirs->len; i++) { + g_autofree gchar *path = g_build_filename (dirs->pdata[i], "gnome-session", "sessions", basename, NULL); + keyfile = get_session_keyfile_if_valid (path); + if (keyfile != NULL) + break; + } + + if (dirs) + g_ptr_array_free (dirs, TRUE); + if (basename) + g_free (basename); + + return keyfile; +} + +static GKeyFile * +get_session_keyfile (const char *session, + char **actual_session, + gboolean *is_fallback) +{ + GKeyFile *keyfile; + gboolean session_runnable; + char *value; + GError *error = NULL; + + *actual_session = NULL; + + g_debug ("fill: *** Getting session '%s'", session); + + keyfile = find_valid_session_keyfile (session); + + if (!keyfile) + return NULL; + + session_runnable = TRUE; + + value = g_key_file_get_string (keyfile, + GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_RUNNABLE_KEY, + NULL); + if (!IS_STRING_EMPTY (value)) { + g_debug ("fill: *** Launching helper '%s' to know if session is runnable", value); + session_runnable = gsm_process_helper (value, GSM_RUNNABLE_HELPER_TIMEOUT, &error); + if (!session_runnable) { + g_warning ("Session '%s' runnable check failed: %s", session, + error->message); + g_clear_error (&error); + } + } + g_free (value); + + if (session_runnable) { + session_runnable = check_required (keyfile); + } + + if (session_runnable) { + *actual_session = g_strdup (session); + if (is_fallback) + *is_fallback = FALSE; + return keyfile; + } + + g_debug ("fill: *** Session is not runnable"); + + /* We can't run this session, so try to use the fallback */ + value = g_key_file_get_string (keyfile, + GSM_KEYFILE_SESSION_GROUP, GSM_KEYFILE_FALLBACK_KEY, + NULL); + + g_key_file_free (keyfile); + keyfile = NULL; + + if (!IS_STRING_EMPTY (value)) { + if (is_fallback) + *is_fallback = TRUE; + keyfile = get_session_keyfile (value, actual_session, NULL); + } + g_free (value); + + return keyfile; +} + +gboolean +gsm_session_fill (GsmManager *manager, + const char *session) +{ + GKeyFile *keyfile; + gboolean is_fallback; + char *actual_session; + + keyfile = get_session_keyfile (session, &actual_session, &is_fallback); + + if (!keyfile) + return FALSE; + + _gsm_manager_set_active_session (manager, actual_session, is_fallback); + + g_free (actual_session); + + load_standard_apps (manager, keyfile); + + g_key_file_free (keyfile); + + return TRUE; +} |