summaryrefslogtreecommitdiffstats
path: root/daemon/gdm-server.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 14:30:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 14:30:14 +0000
commitf38bddc70a6ec7c1ab26d9a8b06eed25d618ae22 (patch)
tree6437d0de9ba7fb9b0933a7c0eaa610a404d79e4e /daemon/gdm-server.c
parentInitial commit. (diff)
downloadgdm3-f38bddc70a6ec7c1ab26d9a8b06eed25d618ae22.tar.xz
gdm3-f38bddc70a6ec7c1ab26d9a8b06eed25d618ae22.zip
Adding upstream version 3.38.2.1.upstream/3.38.2.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'daemon/gdm-server.c')
-rw-r--r--daemon/gdm-server.c1088
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);
+}