diff options
Diffstat (limited to 'daemon/main.c')
-rw-r--r-- | daemon/main.c | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/daemon/main.c b/daemon/main.c new file mode 100644 index 0000000..344d1b7 --- /dev/null +++ b/daemon/main.c @@ -0,0 +1,463 @@ +/* -*- 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 <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> +#include <sys/wait.h> +#include <locale.h> +#include <signal.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "gdm-manager.h" +#include "gdm-log.h" +#include "gdm-common.h" + +#include "gdm-settings.h" +#include "gdm-settings-direct.h" +#include "gdm-settings-keys.h" + +#define GDM_DBUS_NAME "org.gnome.DisplayManager" + +static GDBusConnection *get_system_bus (void); +static gboolean bus_reconnect (void); + +extern char **environ; + +static GdmManager *manager = NULL; +static int name_id = -1; +static GdmSettings *settings = NULL; +static uid_t gdm_uid = -1; +static gid_t gdm_gid = -1; + +static gboolean +timed_exit_cb (GMainLoop *loop) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +bus_connection_closed (void) +{ + g_debug ("Disconnected from D-Bus"); + + if (manager == NULL) { + /* probably shutting down or something */ + return; + } + + g_clear_object (&manager); + + g_timeout_add_seconds (3, (GSourceFunc)bus_reconnect, NULL); +} + +static GDBusConnection * +get_system_bus (void) +{ + GError *error; + GDBusConnection *bus; + + error = NULL; + bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to system bus: %s", + error->message); + g_error_free (error); + goto out; + } + + g_signal_connect (bus, "closed", + G_CALLBACK (bus_connection_closed), NULL); + g_dbus_connection_set_exit_on_close (bus, FALSE); + + out: + return bus; +} + +static void +delete_pid (void) +{ + g_unlink (GDM_PID_FILE); +} + +static void +write_pid (void) +{ + int pf; + ssize_t written; + char pid[9]; + + errno = 0; + pf = open (GDM_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644); + if (pf < 0) { + g_warning (_("Cannot write PID file %s: possibly out of disk space: %s"), + GDM_PID_FILE, + g_strerror (errno)); + + return; + } + + snprintf (pid, sizeof (pid), "%lu\n", (long unsigned) getpid ()); + errno = 0; + written = write (pf, pid, strlen (pid)); + close (pf); + + if (written < 0) { + g_warning (_("Cannot write PID file %s: possibly out of disk space: %s"), + GDM_PID_FILE, + g_strerror (errno)); + return; + } + + atexit (delete_pid); +} + +static gboolean +ensure_dir_with_perms (const char *path, + uid_t uid, + gid_t gid, + mode_t mode, + GError **error) +{ + gboolean ret = FALSE; + + if (g_mkdir_with_parents (path, 0755) == -1) { + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno)); + goto out; + } + if (g_chmod (path, mode) == -1) { + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno)); + goto out; + } + if (chown (path, uid, gid) == -1) { + g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), g_strerror (errno)); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +static void +gdm_daemon_ensure_dirs (uid_t uid, + gid_t gid) +{ + GError *error = NULL; + + /* Set up /var/run/gdm */ + if (!ensure_dir_with_perms (GDM_RAN_ONCE_MARKER_DIR, 0, gid, 0711, &error)) { + gdm_fail (_("Failed to create ran once marker dir %s: %s"), + GDM_RAN_ONCE_MARKER_DIR, error->message); + } + + /* Set up /var/log/gdm */ + if (!ensure_dir_with_perms (LOGDIR, 0, gid, 0711, &error)) { + gdm_fail (_("Failed to create LogDir %s: %s"), + LOGDIR, error->message); + } +} + +static void +gdm_daemon_lookup_user (uid_t *uidp, + gid_t *gidp) +{ + char *username; + char *groupname; + uid_t uid; + gid_t gid; + struct passwd *pwent; + struct group *grent; + + username = NULL; + groupname = NULL; + uid = 0; + gid = 0; + + gdm_settings_direct_get_string (GDM_KEY_USER, &username); + gdm_settings_direct_get_string (GDM_KEY_GROUP, &groupname); + + if (username == NULL || groupname == NULL) { + return; + } + + g_debug ("Changing user:group to %s:%s", username, groupname); + + /* Lookup user and groupid for the GDM user */ + gdm_get_pwent_for_name (username, &pwent); + + /* Set uid and gid */ + if G_UNLIKELY (pwent == NULL) { + gdm_fail (_("Can’t find the GDM user “%s”. Aborting!"), username); + } else { + uid = pwent->pw_uid; + } + + if G_UNLIKELY (uid == 0) { + gdm_fail (_("The GDM user should not be root. Aborting!")); + } + + grent = getgrnam (groupname); + + if G_UNLIKELY (grent == NULL) { + gdm_fail (_("Can’t find the GDM group “%s”. Aborting!"), groupname); + } else { + gid = grent->gr_gid; + } + + if G_UNLIKELY (gid == 0) { + gdm_fail (_("The GDM group should not be root. Aborting!")); + } + + if (uidp != NULL) { + *uidp = uid; + } + + if (gidp != NULL) { + *gidp = gid; + } + + g_free (username); + g_free (groupname); +} + +static gboolean +on_shutdown_signal_cb (gpointer user_data) +{ + GMainLoop *mainloop = user_data; + + g_main_loop_quit (mainloop); + + return FALSE; +} + +static gboolean +on_sighup_cb (gpointer user_data) +{ + g_debug ("Got HUP signal"); + + gdm_settings_reload (settings); + + return TRUE; +} + +static gboolean +is_debug_set (void) +{ + gboolean debug; + gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug); + return debug; +} + +/* SIGUSR1 is used by the X server to tell us that we're ready, so + * block it. We'll unblock it in the worker thread in gdm-server.c + */ +static void +block_sigusr1 (void) +{ + sigset_t mask; + + sigemptyset (&mask); + sigaddset (&mask, SIGUSR1); + sigprocmask (SIG_BLOCK, &mask, NULL); +} + +int +main (int argc, + char **argv) +{ + GMainLoop *main_loop; + GOptionContext *context; + GError *error = NULL; + gboolean res; + static gboolean do_timed_exit = FALSE; + static gboolean print_version = FALSE; + static gboolean fatal_warnings = FALSE; + static GOptionEntry entries [] = { + { "fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &fatal_warnings, N_("Make all warnings fatal"), NULL }, + { "timed-exit", 0, 0, G_OPTION_ARG_NONE, &do_timed_exit, N_("Exit after a time (for debugging)"), NULL }, + { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, N_("Print GDM version"), NULL }, + + { NULL } + }; + + block_sigusr1 (); + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + context = g_option_context_new (_("GNOME Display Manager")); + g_option_context_add_main_entries (context, entries, NULL); + + error = NULL; + res = g_option_context_parse (context, &argc, &argv, &error); + g_option_context_free (context); + if (! res) { + g_printerr ("Failed to parse options: %s\n", error->message); + g_error_free (error); + return EXIT_FAILURE; + } + + if (print_version) { + g_print ("GDM %s\n", VERSION); + return EXIT_SUCCESS; + } + + /* XDM compliant error message */ + if (getuid () != 0) { + /* make sure the pid file doesn't get wiped */ + g_printerr ("%s\n", _("Only the root user can run GDM")); + return EXIT_FAILURE; + } + + if (fatal_warnings) { + GLogLevelFlags fatal_mask; + + fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); + fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; + g_log_set_always_fatal (fatal_mask); + } + + gdm_log_init (); + + settings = gdm_settings_new (); + if (! gdm_settings_direct_init (settings, DATADIR "/gdm/gdm.schemas", "/")) { + g_warning ("Unable to initialize settings"); + return EXIT_FAILURE; + } + + gdm_log_set_debug (is_debug_set ()); + + gdm_daemon_lookup_user (&gdm_uid, &gdm_gid); + + gdm_daemon_ensure_dirs (gdm_uid, gdm_gid); + + /* Connect to the bus, own the name and start the manager */ + bus_reconnect (); + + /* pid file */ + delete_pid (); + write_pid (); + + g_chdir ("/"); + + main_loop = g_main_loop_new (NULL, FALSE); + + g_unix_signal_add (SIGTERM, on_shutdown_signal_cb, main_loop); + g_unix_signal_add (SIGINT, on_shutdown_signal_cb, main_loop); + g_unix_signal_add (SIGHUP, on_sighup_cb, NULL); + + if (do_timed_exit) { + g_timeout_add_seconds (30, (GSourceFunc) timed_exit_cb, main_loop); + } + + g_main_loop_run (main_loop); + + g_debug ("GDM finished, cleaning up..."); + + g_clear_object (&manager); + g_clear_object (&settings); + + gdm_settings_direct_shutdown (); + gdm_log_shutdown (); + + g_main_loop_unref (main_loop); + + return EXIT_SUCCESS; +} + +static void +on_name_acquired (GDBusConnection *bus, + const char *name, + gpointer user_data) +{ + gboolean xdmcp_enabled; + gboolean show_local_greeter; + + manager = gdm_manager_new (); + if (manager == NULL) { + g_warning ("Could not construct manager object"); + exit (EXIT_FAILURE); + } + + g_debug ("Successfully connected to D-Bus"); + + show_local_greeter = TRUE; + gdm_settings_direct_get_boolean (GDM_KEY_SHOW_LOCAL_GREETER, &show_local_greeter); + gdm_manager_set_show_local_greeter (manager, show_local_greeter); + + xdmcp_enabled = FALSE; + gdm_settings_direct_get_boolean (GDM_KEY_XDMCP_ENABLE, &xdmcp_enabled); + gdm_manager_set_xdmcp_enabled (manager, xdmcp_enabled); + + gdm_manager_start (manager); +} + +static void +on_name_lost (GDBusConnection *bus, + const char *name, + gpointer user_data) +{ + g_debug ("Lost GDM name on bus"); + + bus_connection_closed (); +} + +static gboolean +bus_reconnect () +{ + GDBusConnection *bus; + gboolean ret; + + ret = TRUE; + + bus = get_system_bus (); + if (bus == NULL) { + goto out; + } + + name_id = g_bus_own_name_on_connection (bus, + GDM_DBUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + ret = FALSE; + out: + return ret; +} |