/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright 2008 Red Hat, Inc, * 2007 William Jon McCann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Written by : William Jon McCann * Ray Strode */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "gdm-sessions.h" typedef struct _GdmSessionFile { char *id; char *path; char *translated_name; char *translated_comment; } GdmSessionFile; static GHashTable *gdm_available_sessions_map; static gboolean gdm_sessions_map_is_initialized = FALSE; /* adapted from gnome-menus desktop-entries.c */ static gboolean key_file_is_relevant (GKeyFile *key_file) { GError *error; gboolean no_display; gboolean hidden; gboolean tryexec_failed; char *tryexec; error = NULL; no_display = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "NoDisplay", &error); if (error) { no_display = FALSE; g_error_free (error); } error = NULL; hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "Hidden", &error); if (error) { hidden = FALSE; g_error_free (error); } tryexec_failed = FALSE; tryexec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "TryExec", NULL); if (tryexec) { char *path; path = g_find_program_in_path (g_strstrip (tryexec)); tryexec_failed = (path == NULL); g_free (path); g_free (tryexec); } if (no_display || hidden || tryexec_failed) { return FALSE; } return TRUE; } static void load_session_file (const char *id, const char *path) { GKeyFile *key_file; GError *error; gboolean res; GdmSessionFile *session; key_file = g_key_file_new (); error = NULL; res = g_key_file_load_from_file (key_file, path, 0, &error); if (!res) { g_debug ("Failed to load \"%s\": %s\n", path, error->message); g_error_free (error); goto out; } if (! g_key_file_has_group (key_file, G_KEY_FILE_DESKTOP_GROUP)) { goto out; } res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL); if (! res) { g_debug ("\"%s\" contains no \"Name\" key\n", path); goto out; } if (!key_file_is_relevant (key_file)) { g_debug ("\"%s\" is hidden or contains non-executable TryExec program\n", path); goto out; } session = g_new0 (GdmSessionFile, 1); session->id = g_strdup (id); session->path = g_strdup (path); session->translated_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL, NULL); session->translated_comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Comment", NULL, NULL); g_hash_table_insert (gdm_available_sessions_map, g_strdup (id), session); out: g_key_file_free (key_file); } static void collect_sessions_from_directory (const char *dirname) { GDir *dir; const char *filename; /* FIXME: add file monitor to directory */ dir = g_dir_open (dirname, 0, NULL); if (dir == NULL) { return; } while ((filename = g_dir_read_name (dir))) { char *id; char *full_path; if (! g_str_has_suffix (filename, ".desktop")) { continue; } id = g_strndup (filename, strlen (filename) - strlen (".desktop")); full_path = g_build_filename (dirname, filename, NULL); load_session_file (id, full_path); g_free (id); g_free (full_path); } g_dir_close (dir); } static void collect_sessions (void) { int i; const char *search_dirs[] = { "/etc/X11/sessions/", DMCONFDIR "/Sessions/", DATADIR "/gdm/BuiltInSessions/", DATADIR "/xsessions/", #ifdef ENABLE_WAYLAND_SUPPORT DATADIR "/wayland-sessions/", #endif NULL }; if (gdm_available_sessions_map == NULL) { gdm_available_sessions_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } for (i = 0; search_dirs [i] != NULL; i++) { collect_sessions_from_directory (search_dirs [i]); } } /** * gdm_get_session_ids: * * Reads /usr/share/xsessions and other relevant places for possible sessions * to log into and returns the complete list. * * Returns: (transfer full): a %NULL terminated list of session ids */ char ** gdm_get_session_ids (void) { GHashTableIter iter; gpointer key, value; GPtrArray *array; if (!gdm_sessions_map_is_initialized) { collect_sessions (); gdm_sessions_map_is_initialized = TRUE; } array = g_ptr_array_new (); g_hash_table_iter_init (&iter, gdm_available_sessions_map); while (g_hash_table_iter_next (&iter, &key, &value)) { GdmSessionFile *session; session = (GdmSessionFile *) value; g_ptr_array_add (array, g_strdup (session->id)); } g_ptr_array_add (array, NULL); return (char **) g_ptr_array_free (array, FALSE); } /** * gdm_get_session_name_and_description: * @id: an id from gdm_get_session_ids() * @description: (out): optional returned session description * * Takes an xsession id and returns the name and comment about it. * * Returns: The session name if found, or %NULL otherwise */ char * gdm_get_session_name_and_description (const char *id, char **description) { GdmSessionFile *session; char *name; if (!gdm_sessions_map_is_initialized) { collect_sessions (); gdm_sessions_map_is_initialized = TRUE; } session = (GdmSessionFile *) g_hash_table_lookup (gdm_available_sessions_map, id); if (session == NULL) { return NULL; } name = g_strdup (session->translated_name); if (description != NULL) { *description = g_strdup (session->translated_comment); } return name; }