diff options
Diffstat (limited to '')
-rw-r--r-- | daemon/gdm-server.c | 1088 |
1 files changed, 1088 insertions, 0 deletions
diff --git a/daemon/gdm-server.c b/daemon/gdm-server.c new file mode 100644 index 0000000..1ba00d4 --- /dev/null +++ b/daemon/gdm-server.c @@ -0,0 +1,1088 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <ctype.h> +#include <pwd.h> +#include <grp.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/resource.h> + +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + +#ifdef WITH_PLYMOUTH +#include <linux/vt.h> +#endif + +#include <systemd/sd-daemon.h> + +#ifdef ENABLE_SYSTEMD_JOURNAL +#include <systemd/sd-journal.h> +#endif + +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include <X11/Xlib.h> /* for Display */ + +#include "gdm-common.h" +#include "gdm-settings-direct.h" +#include "gdm-settings-keys.h" + +#include "gdm-server.h" + +extern char **environ; + +#define MAX_LOGS 5 + +struct _GdmServer +{ + GObject parent; + + char *command; + GPid pid; + + gboolean disable_tcp; + int priority; + char *user_name; + char *session_args; + + char *log_dir; + char *display_name; + char *display_device; + char *display_seat_id; + char *auth_file; + + guint child_watch_id; + + gboolean is_initial; +}; + +enum { + PROP_0, + PROP_DISPLAY_NAME, + PROP_DISPLAY_SEAT_ID, + PROP_DISPLAY_DEVICE, + PROP_AUTH_FILE, + PROP_USER_NAME, + PROP_DISABLE_TCP, + PROP_IS_INITIAL, +}; + +enum { + READY, + EXITED, + DIED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void gdm_server_class_init (GdmServerClass *klass); +static void gdm_server_init (GdmServer *server); +static void gdm_server_finalize (GObject *object); + +G_DEFINE_TYPE (GdmServer, gdm_server, G_TYPE_OBJECT) + +char * +gdm_server_get_display_device (GdmServer *server) +{ + /* systemd finds the display device out on its own based on the display */ + return NULL; +} + +static void +gdm_server_ready (GdmServer *server) +{ + g_debug ("GdmServer: Got USR1 from X server - emitting READY"); + + gdm_run_script (GDMCONFDIR "/Init", GDM_USERNAME, + server->display_name, + NULL, /* hostname */ + server->auth_file); + + g_signal_emit (server, signals[READY], 0); +} + +static GSList *active_servers; +static gboolean sigusr1_thread_running; +static GCond sigusr1_thread_cond; +static GMutex sigusr1_thread_mutex; + +static gboolean +got_sigusr1 (gpointer user_data) +{ + GPid pid = GPOINTER_TO_UINT (user_data); + GSList *l; + + g_debug ("GdmServer: got SIGUSR1 from PID %d", pid); + + for (l = active_servers; l; l = l->next) { + GdmServer *server = l->data; + + if (server->pid == pid) + gdm_server_ready (server); + } + + return G_SOURCE_REMOVE; +} + +static gpointer +sigusr1_thread_main (gpointer user_data) +{ + sigset_t sigusr1_mask; + + /* Handle only SIGUSR1 */ + sigemptyset (&sigusr1_mask); + sigaddset (&sigusr1_mask, SIGUSR1); + sigprocmask (SIG_SETMASK, &sigusr1_mask, NULL); + + g_mutex_lock (&sigusr1_thread_mutex); + sigusr1_thread_running = TRUE; + g_cond_signal (&sigusr1_thread_cond); + g_mutex_unlock (&sigusr1_thread_mutex); + + /* Spin waiting for a SIGUSR1 */ + while (TRUE) { + siginfo_t info; + + if (sigwaitinfo (&sigusr1_mask, &info) == -1) + continue; + + g_idle_add (got_sigusr1, GUINT_TO_POINTER (info.si_pid)); + } + + return NULL; +} + +static void +gdm_server_launch_sigusr1_thread_if_needed (void) +{ + static GThread *sigusr1_thread; + + if (sigusr1_thread == NULL) { + sigusr1_thread = g_thread_new ("gdm SIGUSR1 catcher", sigusr1_thread_main, NULL); + + g_mutex_lock (&sigusr1_thread_mutex); + while (!sigusr1_thread_running) + g_cond_wait (&sigusr1_thread_cond, &sigusr1_thread_mutex); + g_mutex_unlock (&sigusr1_thread_mutex); + } +} + +static void +gdm_server_init_command (GdmServer *server) +{ + gboolean debug = FALSE; + const char *debug_options; + const char *verbosity = ""; + + if (server->command != NULL) { + return; + } + + gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug); + if (debug) { + debug_options = " -logverbose 7 -core "; + } else { + debug_options = ""; + } + +#define X_SERVER_ARG_FORMAT " -background none -noreset -verbose %s%s" + + /* This is a temporary hack to work around the fact that XOrg + * currently lacks support for multi-seat hotplugging for + * display devices. This bit should be removed as soon as XOrg + * gains native support for automatically enumerating usb + * based graphics adapters at start-up via udev. */ + + /* systemd ships an X server wrapper tool which simply invokes + * the usual X but ensures it only uses the display devices of + * the seat. */ + + /* We do not rely on this wrapper server if, a) the machine + * wasn't booted using systemd, or b) the wrapper tool is + * missing, or c) we are running for the main seat 'seat0'. */ + +#ifdef ENABLE_SYSTEMD_JOURNAL + /* For systemd, we don't have a log file but instead log to stdout, + so set it to the xserver's built-in default verbosity */ + if (debug) + verbosity = "7 -logfile /dev/null"; + else + verbosity = "3 -logfile /dev/null"; +#endif + + if (g_access (SYSTEMD_X_SERVER, X_OK) < 0) { + goto fallback; + } + + if (server->display_seat_id == NULL || + strcmp (server->display_seat_id, "seat0") == 0) { + goto fallback; + } + + server->command = g_strdup_printf (SYSTEMD_X_SERVER X_SERVER_ARG_FORMAT, verbosity, debug_options); + return; + +fallback: + server->command = g_strdup_printf (X_SERVER X_SERVER_ARG_FORMAT, verbosity, debug_options); + +} + +static gboolean +gdm_server_resolve_command_line (GdmServer *server, + const char *vtarg, + int *argcp, + char ***argvp) +{ + int argc; + char **argv; + int len; + int i; + gboolean gotvtarg = FALSE; + gboolean query_in_arglist = FALSE; + + gdm_server_init_command (server); + + g_shell_parse_argv (server->command, &argc, &argv, NULL); + + for (len = 0; argv != NULL && argv[len] != NULL; len++) { + char *arg = argv[len]; + + /* HACK! Not to add vt argument to servers that already force + * allocation. Mostly for backwards compat only */ + if (strncmp (arg, "vt", 2) == 0 && + isdigit (arg[2]) && + (arg[3] == '\0' || + (isdigit (arg[3]) && arg[4] == '\0'))) + gotvtarg = TRUE; + if (strcmp (arg, "-query") == 0 || + strcmp (arg, "-indirect") == 0) + query_in_arglist = TRUE; + } + + argv = g_renew (char *, argv, len + 12); + /* shift args down one */ + for (i = len - 1; i >= 1; i--) { + argv[i+1] = argv[i]; + } + + /* server number is the FIRST argument, before any others */ + argv[1] = g_strdup (server->display_name); + len++; + + if (server->auth_file != NULL) { + argv[len++] = g_strdup ("-auth"); + argv[len++] = g_strdup (server->auth_file); + } + + if (server->display_seat_id != NULL) { + argv[len++] = g_strdup ("-seat"); + argv[len++] = g_strdup (server->display_seat_id); + } + + /* If we were compiled with Xserver >= 1.17 we need to specify + * '-listen tcp' as the X server dosen't listen on tcp sockets + * by default anymore. In older versions we need to pass + * -nolisten tcp to disable listening on tcp sockets. + */ +#ifdef HAVE_XSERVER_THAT_DEFAULTS_TO_LOCAL_ONLY + if (!server->disable_tcp && ! query_in_arglist) { + argv[len++] = g_strdup ("-listen"); + argv[len++] = g_strdup ("tcp"); + } +#else + if (server->disable_tcp && ! query_in_arglist) { + argv[len++] = g_strdup ("-nolisten"); + argv[len++] = g_strdup ("tcp"); + } + +#endif + + if (vtarg != NULL && ! gotvtarg) { + argv[len++] = g_strdup (vtarg); + } + + argv[len++] = NULL; + + *argvp = argv; + *argcp = len; + + return TRUE; +} + +static void +rotate_logs (const char *path, + guint n_copies) +{ + int i; + + for (i = n_copies - 1; i > 0; i--) { + char *name_n; + char *name_n1; + + name_n = g_strdup_printf ("%s.%d", path, i); + if (i > 1) { + name_n1 = g_strdup_printf ("%s.%d", path, i - 1); + } else { + name_n1 = g_strdup (path); + } + + VE_IGNORE_EINTR (g_unlink (name_n)); + VE_IGNORE_EINTR (g_rename (name_n1, name_n)); + + g_free (name_n1); + g_free (name_n); + } + + VE_IGNORE_EINTR (g_unlink (path)); +} + +static void +change_user (GdmServer *server) +{ + struct passwd *pwent; + + if (server->user_name == NULL) { + return; + } + + gdm_get_pwent_for_name (server->user_name, &pwent); + if (pwent == NULL) { + g_warning (_("Server was to be spawned by user %s but that user doesn’t exist"), + server->user_name); + _exit (EXIT_FAILURE); + } + + g_debug ("GdmServer: Changing (uid:gid) for child process to (%d:%d)", + pwent->pw_uid, + pwent->pw_gid); + + if (pwent->pw_uid != 0) { + if (setgid (pwent->pw_gid) < 0) { + g_warning (_("Couldn’t set groupid to %d"), + pwent->pw_gid); + _exit (EXIT_FAILURE); + } + + if (initgroups (pwent->pw_name, pwent->pw_gid) < 0) { + g_warning (_("initgroups () failed for %s"), + pwent->pw_name); + _exit (EXIT_FAILURE); + } + + if (setuid (pwent->pw_uid) < 0) { + g_warning (_("Couldn’t set userid to %d"), + (int)pwent->pw_uid); + _exit (EXIT_FAILURE); + } + } else { + gid_t groups[1] = { 0 }; + + if (setgid (0) < 0) { + g_warning (_("Couldn’t set groupid to %d"), 0); + /* Don't error out, it's not fatal, if it fails we'll + * just still be */ + } + + /* this will get rid of any suplementary groups etc... */ + setgroups (1, groups); + } +} + +static gboolean +gdm_server_setup_journal_fds (GdmServer *server) +{ +#ifdef ENABLE_SYSTEMD_JOURNAL + if (sd_booted () > 0) { + int out, err; + const char *prefix = "gdm-Xorg-"; + char *identifier; + gsize size; + + size = strlen (prefix) + strlen (server->display_name) + 1; + identifier = g_alloca (size); + strcpy (identifier, prefix); + strcat (identifier, server->display_name); + identifier[size - 1] = '\0'; + + out = sd_journal_stream_fd (identifier, LOG_INFO, FALSE); + if (out < 0) + return FALSE; + + err = sd_journal_stream_fd (identifier, LOG_WARNING, FALSE); + if (err < 0) { + close (out); + return FALSE; + } + + VE_IGNORE_EINTR (dup2 (out, 1)); + VE_IGNORE_EINTR (dup2 (err, 2)); + return TRUE; + } +#endif + return FALSE; +} + +static void +gdm_server_setup_logfile (GdmServer *server) +{ + int logfd; + char *log_file; + char *log_path; + + log_file = g_strdup_printf ("%s.log", server->display_name); + log_path = g_build_filename (server->log_dir, log_file, NULL); + g_free (log_file); + + /* Rotate the X server logs */ + rotate_logs (log_path, MAX_LOGS); + + g_debug ("GdmServer: Opening logfile for server %s", log_path); + + VE_IGNORE_EINTR (g_unlink (log_path)); + VE_IGNORE_EINTR (logfd = open (log_path, O_CREAT|O_APPEND|O_TRUNC|O_WRONLY|O_EXCL, 0644)); + + g_free (log_path); + + if (logfd != -1) { + VE_IGNORE_EINTR (dup2 (logfd, 1)); + VE_IGNORE_EINTR (dup2 (logfd, 2)); + close (logfd); + } else { + g_warning (_("%s: Could not open log file for display %s!"), + "gdm_server_spawn", + server->display_name); + } +} + +static void +server_child_setup (GdmServer *server) +{ + struct sigaction ign_signal; + sigset_t mask; + + if (!gdm_server_setup_journal_fds(server)) + gdm_server_setup_logfile(server); + + /* The X server expects USR1/TTIN/TTOU to be SIG_IGN */ + ign_signal.sa_handler = SIG_IGN; + ign_signal.sa_flags = SA_RESTART; + sigemptyset (&ign_signal.sa_mask); + + if (sigaction (SIGUSR1, &ign_signal, NULL) < 0) { + g_warning (_("%s: Error setting %s to %s"), + "gdm_server_spawn", "USR1", "SIG_IGN"); + _exit (EXIT_FAILURE); + } + + if (sigaction (SIGTTIN, &ign_signal, NULL) < 0) { + g_warning (_("%s: Error setting %s to %s"), + "gdm_server_spawn", "TTIN", "SIG_IGN"); + _exit (EXIT_FAILURE); + } + + if (sigaction (SIGTTOU, &ign_signal, NULL) < 0) { + g_warning (_("%s: Error setting %s to %s"), + "gdm_server_spawn", "TTOU", "SIG_IGN"); + _exit (EXIT_FAILURE); + } + + /* And HUP and TERM are at SIG_DFL from gdm_unset_signals, + we also have an empty mask and all that fun stuff */ + + /* unblock signals (especially HUP/TERM/USR1) so that we + * can control the X server */ + sigemptyset (&mask); + sigprocmask (SIG_SETMASK, &mask, NULL); + + /* Terminate the process when the parent dies */ +#ifdef HAVE_SYS_PRCTL_H + prctl (PR_SET_PDEATHSIG, SIGTERM); +#endif + + if (server->priority != 0) { + if (setpriority (PRIO_PROCESS, 0, server->priority)) { + g_warning (_("%s: Server priority couldn’t be set to %d: %s"), + "gdm_server_spawn", + server->priority, + g_strerror (errno)); + } + } + + setpgid (0, 0); + + change_user (server); +} + +static void +listify_hash (const char *key, + const char *value, + GPtrArray *env) +{ + char *str; + str = g_strdup_printf ("%s=%s", key, value); + g_ptr_array_add (env, str); +} + +static GPtrArray * +get_server_environment (GdmServer *server) +{ + GPtrArray *env; + char **l; + GHashTable *hash; + + env = g_ptr_array_new (); + + /* create a hash table of current environment, then update keys has necessary */ + hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + for (l = environ; *l != NULL; l++) { + char **str; + str = g_strsplit (*l, "=", 2); + g_hash_table_insert (hash, str[0], str[1]); + g_free (str); + } + + /* modify environment here */ + g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (server->display_name)); + + if (server->user_name != NULL) { + struct passwd *pwent; + + gdm_get_pwent_for_name (server->user_name, &pwent); + + if (pwent->pw_dir != NULL + && g_file_test (pwent->pw_dir, G_FILE_TEST_EXISTS)) { + g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir)); + } else { + /* Hack */ + g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/")); + } + g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell)); + g_hash_table_remove (hash, "MAIL"); + } + + g_hash_table_foreach (hash, (GHFunc)listify_hash, env); + g_hash_table_destroy (hash); + + g_ptr_array_add (env, NULL); + + return env; +} + +static void +server_add_xserver_args (GdmServer *server, + int *argc, + char ***argv) +{ + int count; + char **args; + int len; + int i; + + len = *argc; + g_shell_parse_argv (server->session_args, &count, &args, NULL); + *argv = g_renew (char *, *argv, len + count + 1); + + for (i=0; i < count;i++) { + *argv[len++] = g_strdup (args[i]); + } + + *argc += count; + + argv[len] = NULL; + g_strfreev (args); +} + +static void +server_child_watch (GPid pid, + int status, + GdmServer *server) +{ + g_debug ("GdmServer: child (pid:%d) done (%s:%d)", + (int) pid, + WIFEXITED (status) ? "status" + : WIFSIGNALED (status) ? "signal" + : "unknown", + WIFEXITED (status) ? WEXITSTATUS (status) + : WIFSIGNALED (status) ? WTERMSIG (status) + : -1); + + g_object_ref (server); + + if (WIFEXITED (status)) { + int code = WEXITSTATUS (status); + g_signal_emit (server, signals [EXITED], 0, code); + } else if (WIFSIGNALED (status)) { + int num = WTERMSIG (status); + g_signal_emit (server, signals [DIED], 0, num); + } + + g_spawn_close_pid (server->pid); + server->pid = -1; + + g_object_unref (server); +} + +static void +prune_active_servers_list (GdmServer *server) +{ + active_servers = g_slist_remove (active_servers, server); +} + +static gboolean +gdm_server_spawn (GdmServer *server, + const char *vtarg, + GError **error) +{ + int argc; + gchar **argv = NULL; + GPtrArray *env = NULL; + gboolean ret = FALSE; + char *freeme; + + /* Figure out the server command */ + argv = NULL; + argc = 0; + gdm_server_resolve_command_line (server, + vtarg, + &argc, + &argv); + + if (server->session_args) { + server_add_xserver_args (server, &argc, &argv); + } + + if (argv[0] == NULL) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("%s: Empty server command for display %s"), + "gdm_server_spawn", + server->display_name); + goto out; + } + + env = get_server_environment (server); + + freeme = g_strjoinv (" ", argv); + g_debug ("GdmServer: Starting X server process: %s", freeme); + g_free (freeme); + + active_servers = g_slist_append (active_servers, server); + + g_object_weak_ref (G_OBJECT (server), + (GWeakNotify) + prune_active_servers_list, + server); + + gdm_server_launch_sigusr1_thread_if_needed (); + + if (!g_spawn_async_with_pipes (NULL, + argv, + (char **)env->pdata, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + (GSpawnChildSetupFunc)server_child_setup, + server, + &server->pid, + NULL, + NULL, + NULL, + error)) + goto out; + + g_debug ("GdmServer: Started X server process %d - waiting for READY", (int)server->pid); + + server->child_watch_id = g_child_watch_add (server->pid, + (GChildWatchFunc)server_child_watch, + server); + + ret = TRUE; + out: + g_strfreev (argv); + if (env) { + g_ptr_array_foreach (env, (GFunc)g_free, NULL); + g_ptr_array_free (env, TRUE); + } + return ret; +} + +/** + * gdm_server_start: + * @disp: Pointer to a GdmDisplay structure + * + * Starts a local X server. Handles retries and fatal errors properly. + */ + +gboolean +gdm_server_start (GdmServer *server) +{ + gboolean res = FALSE; + const char *vtarg = NULL; + GError *local_error = NULL; + GError **error = &local_error; + + /* Hardcode the VT for the initial X server, but nothing else */ + if (server->is_initial) { + vtarg = "vt" G_STRINGIFY (GDM_INITIAL_VT); + } + + /* fork X server process */ + if (!gdm_server_spawn (server, vtarg, error)) { + goto out; + } + + res = TRUE; + out: + if (local_error) { + g_printerr ("%s\n", local_error->message); + g_clear_error (&local_error); + } + return res; +} + +static void +server_died (GdmServer *server) +{ + int exit_status; + + g_debug ("GdmServer: Waiting on process %d", server->pid); + exit_status = gdm_wait_on_pid (server->pid); + + if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) { + g_debug ("GdmServer: Wait on child process failed"); + } else { + /* exited normally */ + } + + g_spawn_close_pid (server->pid); + server->pid = -1; + + if (server->display_device != NULL) { + g_free (server->display_device); + server->display_device = NULL; + g_object_notify (G_OBJECT (server), "display-device"); + } + + g_debug ("GdmServer: Server died"); +} + +gboolean +gdm_server_stop (GdmServer *server) +{ + int res; + + if (server->pid <= 1) { + return TRUE; + } + + /* remove watch source before we can wait on child */ + if (server->child_watch_id > 0) { + g_source_remove (server->child_watch_id); + server->child_watch_id = 0; + } + + g_debug ("GdmServer: Stopping server"); + + res = gdm_signal_pid (server->pid, SIGTERM); + if (res < 0) { + } else { + server_died (server); + } + + return TRUE; +} + + +static void +_gdm_server_set_display_name (GdmServer *server, + const char *name) +{ + g_free (server->display_name); + server->display_name = g_strdup (name); +} + +static void +_gdm_server_set_display_seat_id (GdmServer *server, + const char *name) +{ + g_free (server->display_seat_id); + server->display_seat_id = g_strdup (name); +} + +static void +_gdm_server_set_auth_file (GdmServer *server, + const char *auth_file) +{ + g_free (server->auth_file); + server->auth_file = g_strdup (auth_file); +} + +static void +_gdm_server_set_user_name (GdmServer *server, + const char *name) +{ + g_free (server->user_name); + server->user_name = g_strdup (name); +} + +static void +_gdm_server_set_disable_tcp (GdmServer *server, + gboolean disabled) +{ + server->disable_tcp = disabled; +} + +static void +_gdm_server_set_is_initial (GdmServer *server, + gboolean initial) +{ + server->is_initial = initial; +} + +static void +gdm_server_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmServer *self; + + self = GDM_SERVER (object); + + switch (prop_id) { + case PROP_DISPLAY_NAME: + _gdm_server_set_display_name (self, g_value_get_string (value)); + break; + case PROP_DISPLAY_SEAT_ID: + _gdm_server_set_display_seat_id (self, g_value_get_string (value)); + break; + case PROP_AUTH_FILE: + _gdm_server_set_auth_file (self, g_value_get_string (value)); + break; + case PROP_USER_NAME: + _gdm_server_set_user_name (self, g_value_get_string (value)); + break; + case PROP_DISABLE_TCP: + _gdm_server_set_disable_tcp (self, g_value_get_boolean (value)); + break; + case PROP_IS_INITIAL: + _gdm_server_set_is_initial (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_server_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmServer *self; + + self = GDM_SERVER (object); + + switch (prop_id) { + case PROP_DISPLAY_NAME: + g_value_set_string (value, self->display_name); + break; + case PROP_DISPLAY_SEAT_ID: + g_value_set_string (value, self->display_seat_id); + break; + case PROP_DISPLAY_DEVICE: + g_value_take_string (value, + gdm_server_get_display_device (self)); + break; + case PROP_AUTH_FILE: + g_value_set_string (value, self->auth_file); + break; + case PROP_USER_NAME: + g_value_set_string (value, self->user_name); + break; + case PROP_DISABLE_TCP: + g_value_set_boolean (value, self->disable_tcp); + break; + case PROP_IS_INITIAL: + g_value_set_boolean (value, self->is_initial); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_server_class_init (GdmServerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_server_get_property; + object_class->set_property = gdm_server_set_property; + object_class->finalize = gdm_server_finalize; + + signals [READY] = + g_signal_new ("ready", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals [EXITED] = + g_signal_new ("exited", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + signals [DIED] = + g_signal_new ("died", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); + + g_object_class_install_property (object_class, + PROP_DISPLAY_NAME, + g_param_spec_string ("display-name", + "name", + "name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_DISPLAY_SEAT_ID, + g_param_spec_string ("display-seat-id", + "Seat ID", + "ID of the seat this display is running on", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_DISPLAY_DEVICE, + g_param_spec_string ("display-device", + "Display Device", + "Path to terminal display is running on", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_AUTH_FILE, + g_param_spec_string ("auth-file", + "Authorization File", + "Path to X authorization file", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, + PROP_USER_NAME, + g_param_spec_string ("user-name", + "user name", + "user name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_DISABLE_TCP, + g_param_spec_boolean ("disable-tcp", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_IS_INITIAL, + g_param_spec_boolean ("is-initial", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); +} + +static void +gdm_server_init (GdmServer *server) +{ + server->pid = -1; + + server->log_dir = g_strdup (LOGDIR); +} + +static void +gdm_server_finalize (GObject *object) +{ + GdmServer *server; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_SERVER (object)); + + server = GDM_SERVER (object); + + gdm_server_stop (server); + + g_free (server->command); + g_free (server->user_name); + g_free (server->session_args); + g_free (server->log_dir); + g_free (server->display_name); + g_free (server->display_seat_id); + g_free (server->display_device); + g_free (server->auth_file); + + G_OBJECT_CLASS (gdm_server_parent_class)->finalize (object); +} + +GdmServer * +gdm_server_new (const char *display_name, + const char *seat_id, + const char *auth_file, + gboolean initial) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_SERVER, + "display-name", display_name, + "display-seat-id", seat_id, + "auth-file", auth_file, + "is-initial", initial, + NULL); + + return GDM_SERVER (object); +} |