diff options
author | William Jon McCann <mccann@src.gnome.org> | 2007-05-10 20:12:49 +0000 |
---|---|---|
committer | William Jon McCann <mccann@src.gnome.org> | 2007-05-10 20:12:49 +0000 |
commit | b961c279e609ae1080c8a21e5e51f901f17cf223 (patch) | |
tree | cf06abdc5ea0d7fef67b713bd874aada9e25a2e3 | |
parent | 088ff8dba8a807cb4ed0e7a5f5bfc4b784cec2a7 (diff) | |
download | gdm-b961c279e609ae1080c8a21e5e51f901f17cf223.tar.gz |
Add skeleton for gobject redesign. Doesn't really work but should compile.
svn path=/branches/mccann-gobject/; revision=4911
62 files changed, 9695 insertions, 6727 deletions
diff --git a/Makefile.am b/Makefile.am index 03d0dadb..fe16cf47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ NULL = SUBDIRS = \ - po \ + data \ config \ pixmaps \ common \ @@ -9,6 +9,7 @@ SUBDIRS = \ gui \ utils \ docs \ + po \ $(NULL) # add these when help gets added back diff --git a/common/Makefile.am b/common/Makefile.am index ed049d63..cc6b0e11 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -28,6 +28,8 @@ noinst_LIBRARIES = \ $(null) libgdmcommon_a_SOURCES = \ + gdm-address.h \ + gdm-address.c \ gdm-common.h \ gdm-common.c \ gdm-common-config.h \ diff --git a/common/gdm-address.c b/common/gdm-address.c new file mode 100644 index 00000000..b5cf08da --- /dev/null +++ b/common/gdm-address.c @@ -0,0 +1,354 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#ifndef G_OS_WIN32 +#include <sys/socket.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#else +#include <winsock2.h> +#include <ws2tcpip.h> +#endif + +#include <glib-object.h> + +#include "gdm-address.h" + +struct _GdmAddress +{ + struct sockaddr_storage *ss; +}; + +/* Register GdmAddress in the glib type system */ +GType +gdm_address_get_type (void) +{ + static GType addr_type = 0; + + if (addr_type == 0) { + addr_type = g_boxed_type_register_static ("GdmAddress", + (GBoxedCopyFunc) gdm_address_copy, + (GBoxedFreeFunc) gdm_address_free); + } + + return addr_type; +} + +/** + * gdm_address_get_family_type: + * @address: A pointer to a #GdmAddress + * + * Use this function to retrive the address family of @address. + * + * Return value: The address family of @address. + **/ +int +gdm_address_get_family_type (GdmAddress *address) +{ + g_return_val_if_fail (address != NULL, -1); + + return address->ss->ss_family; +} + + +/** + * gdm_address_new_from_sockaddr: + * @sa: A pointer to a sockaddr_storage. + * + * Creates a new #GdmAddress from @ss. + * + * Return value: The new #GdmAddress + * or %NULL if @sa was invalid or the address family isn't supported. + **/ +GdmAddress * +gdm_address_new_from_sockaddr_storage (struct sockaddr_storage *ss) +{ + GdmAddress *addr; + + g_return_val_if_fail (ss != NULL, NULL); + + addr = g_new0 (GdmAddress, 1); + addr->ss = g_memdup (ss, sizeof (struct sockaddr_storage)); + + return addr; +} + +/** + * gdm_address_get_sockaddr_storage: + * @address: A #GdmAddress + * + * This function tanslates @address into a equivalent + * sockaddr_storage + * + * Return value: A newly allocated sockaddr_storage structure the caller must free + * or %NULL if @address did not point to a valid #GdmAddress. + **/ +struct sockaddr_storage * +gdm_address_get_sockaddr_storage (GdmAddress *address) +{ + struct sockaddr_storage *ss; + + g_return_val_if_fail (address != NULL, NULL); + + ss = g_memdup (address->ss, sizeof (struct sockaddr_storage)); + + return ss; +} + +struct sockaddr_storage * +gdm_address_peek_sockaddr_storage (GdmAddress *address) +{ + g_return_val_if_fail (address != NULL, NULL); + + return address->ss; +} + +static gboolean +v4_v4_equal (const struct sockaddr_in *a, + const struct sockaddr_in *b) +{ + return a->sin_addr.s_addr == b->sin_addr.s_addr; +} + +#ifdef ENABLE_IPV6 +static gboolean +v6_v6_equal (struct sockaddr_in6 *a, + struct sockaddr_in6 *b) +{ + return IN6_ARE_ADDR_EQUAL (&a->sin6_addr, &b->sin6_addr); +} +#endif + +#define SA(__s) ((struct sockaddr *) __s) +#define SIN(__s) ((struct sockaddr_in *) __s) +#define SIN6(__s) ((struct sockaddr_in6 *) __s) + +gboolean +gdm_address_equal (GdmAddress *a, + GdmAddress *b) +{ + guint8 fam_a; + guint8 fam_b; + + g_return_val_if_fail (a != NULL || a->ss != NULL, FALSE); + g_return_val_if_fail (b != NULL || b->ss != NULL, FALSE); + + fam_a = a->ss->ss_family; + fam_b = b->ss->ss_family; + + if (fam_a == AF_INET && fam_b == AF_INET) { + return v4_v4_equal (SIN (a->ss), SIN (b->ss)); + } +#ifdef ENABLE_IPV6 + else if (fam_a == AF_INET6 && fam_b == AF_INET6) { + return v6_v6_equal (SIN6 (a->ss), SIN6 (b->ss)); + } +#endif + return FALSE; +} + +char * +gdm_address_get_hostname (GdmAddress *address) +{ + char host [NI_MAXHOST]; + + g_return_val_if_fail (address != NULL || address->ss != NULL, NULL); + + host [0] = '\0'; + getnameinfo ((const struct sockaddr *)address->ss, + sizeof (struct sockaddr_storage), + host, sizeof (host), + NULL, 0, + 0); + + return g_strdup (host); +} + +void +gdm_address_get_numeric_info (GdmAddress *address, + char **hostp, + char **servp) +{ + char host [NI_MAXHOST]; + char serv [NI_MAXSERV]; + + g_return_if_fail (address != NULL || address->ss != NULL); + + host [0] = '\0'; + serv [0] = '\0'; + getnameinfo ((const struct sockaddr *)address->ss, + sizeof (struct sockaddr_storage), + host, sizeof (host), + serv, sizeof (serv), + NI_NUMERICHOST | NI_NUMERICSERV); + if (servp != NULL) { + *servp = g_strdup (serv); + } + if (hostp != NULL) { + *hostp = g_strdup (host); + } +} + +gboolean +gdm_address_is_loopback (GdmAddress *address) +{ + g_return_val_if_fail (address != NULL || address->ss != NULL, FALSE); + + switch (address->ss->ss_family){ +#ifdef AF_INET6 + case AF_INET6: + return IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *)address->ss)->sin6_addr); + break; +#endif + case AF_INET: + return (INADDR_LOOPBACK == (((struct sockaddr_in *)address->ss)->sin_addr.s_addr)); + break; + default: + break; + } + + return FALSE; +} + +const GList * +gdm_address_peek_local_list (void) +{ + static GList *the_list = NULL; + static time_t last_time = 0; + char hostbuf[BUFSIZ]; + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *res; + + /* Don't check more then every 5 seconds */ + if (last_time + 5 > time (NULL)) { + return the_list; + } + + g_list_foreach (the_list, (GFunc)gdm_address_free, NULL); + g_list_free (the_list); + the_list = NULL; + + last_time = time (NULL); + + hostbuf[BUFSIZ-1] = '\0'; + if (gethostname (hostbuf, BUFSIZ-1) != 0) { + g_debug ("%s: Could not get server hostname, using localhost", "gdm_peek_local_address_list"); + snprintf (hostbuf, BUFSIZ-1, "localhost"); + } + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_INET; +#ifdef ENABLE_IPV6 + hints.ai_family |= AF_INET6; +#endif + + if (getaddrinfo (hostbuf, NULL, &hints, &result) != 0) { + g_debug ("%s: Could not get address from hostname!", "gdm_peek_local_address_list"); + + return NULL; + } + + for (res = result; res != NULL; res = res->ai_next) { + GdmAddress *address; + + address = gdm_address_new_from_sockaddr_storage ((struct sockaddr_storage *)res->ai_addr); + the_list = g_list_append (the_list, address); + } + + if (result != NULL) { + freeaddrinfo (result); + result = NULL; + } + + return the_list; +} + +gboolean +gdm_address_is_local (GdmAddress *address) +{ + const GList *list; + + if (gdm_address_is_loopback (address)) { + return TRUE; + } + + list = gdm_address_peek_local_list (); + + while (list != NULL) { + GdmAddress *addr = list->data; + + if (gdm_address_equal (address, addr)) { + return TRUE; + } + + list = list->next; + } + + return FALSE; +} + +/** + * gdm_address_copy: + * @address: A #GdmAddress. + * + * Duplicates @address. + * + * Return value: Duplicated @address or %NULL if @address was not valid. + **/ +GdmAddress * +gdm_address_copy (GdmAddress *address) +{ + GdmAddress *addr; + + g_return_val_if_fail (address != NULL, NULL); + + addr = g_new0 (GdmAddress, 1); + addr->ss = g_memdup (address->ss, sizeof (struct sockaddr_storage)); + + return addr; +} + +/** + * gdm_address_free: + * @address: A #GdmAddress. + * + * Frees the memory allocated for @address. + **/ +void +gdm_address_free (GdmAddress *address) +{ + g_return_if_fail (address != NULL); + + g_free (address->ss); + g_free (address); +} + + diff --git a/common/gdm-address.h b/common/gdm-address.h new file mode 100644 index 00000000..14e1ef13 --- /dev/null +++ b/common/gdm-address.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_ADDRESS_H +#define __GDM_ADDRESS_H + +#include <glib-object.h> +#ifndef G_OS_WIN32 +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#else +#include <winsock2.h> +#undef interface +#endif + +G_BEGIN_DECLS + +#define GDM_TYPE_ADDRESS (gdm_address_get_type ()) + +typedef struct _GdmAddress GdmAddress; + +GType gdm_address_get_type (void); + +GdmAddress * gdm_address_new_from_sockaddr_storage (struct sockaddr_storage *ss); + +int gdm_address_get_family_type (GdmAddress *address); +struct sockaddr_storage *gdm_address_get_sockaddr_storage (GdmAddress *address); +struct sockaddr_storage *gdm_address_peek_sockaddr_storage (GdmAddress *address); + +char * gdm_address_get_hostname (GdmAddress *address); +void gdm_address_get_numeric_info (GdmAddress *address, + char **numeric_hostname, + char **service); +gboolean gdm_address_is_local (GdmAddress *address); +gboolean gdm_address_is_loopback (GdmAddress *address); + +gboolean gdm_address_equal (GdmAddress *a, + GdmAddress *b); + +GdmAddress * gdm_address_copy (GdmAddress *address); +void gdm_address_free (GdmAddress *address); + + + +const GList * gdm_address_peek_local_list (void); + + +G_END_DECLS + +#endif /* __GDM_ADDRESS_H */ diff --git a/common/gdm-common.c b/common/gdm-common.c index dbb3ff31..73e78b9f 100644 --- a/common/gdm-common.c +++ b/common/gdm-common.c @@ -25,98 +25,726 @@ #include <unistd.h> #include <stdlib.h> #include <locale.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <setjmp.h> +#include <dirent.h> #ifdef HAVE_CRT_EXTERNS_H #include <crt_externs.h> #endif +#include <glib.h> +#include <glib/gi18n.h> + #include "gdm-common.h" -static gboolean -v4_v4_equal (const struct sockaddr_in *a, - const struct sockaddr_in *b) +int +gdm_fdgetc (int fd) { - return a->sin_addr.s_addr == b->sin_addr.s_addr; + char buf[1]; + int bytes; + + VE_IGNORE_EINTR (bytes = read (fd, buf, 1)); + if (bytes != 1) + return EOF; + else + return (int)buf[0]; } -#ifdef ENABLE_IPV6 -static gboolean -v6_v6_equal (struct sockaddr_in6 *a, - struct sockaddr_in6 *b) +char * +gdm_fdgets (int fd) { - return IN6_ARE_ADDR_EQUAL (&a->sin6_addr, &b->sin6_addr); + int c; + int bytes = 0; + GString *gs = g_string_new (NULL); + for (;;) { + c = gdm_fdgetc (fd); + if (c == '\n') + return g_string_free (gs, FALSE); + /* on EOF */ + if (c < 0) { + if (bytes == 0) { + g_string_free (gs, TRUE); + return NULL; + } else { + return g_string_free (gs, FALSE); + } + } else { + bytes++; + g_string_append_c (gs, c); + } + } } -#endif -#define SA(__s) ((struct sockaddr *) __s) -#define SIN(__s) ((struct sockaddr_in *) __s) -#define SIN6(__s) ((struct sockaddr_in6 *) __s) +void +gdm_fdprintf (int fd, const gchar *format, ...) +{ + va_list args; + gchar *s; + int written, len; -gboolean -gdm_address_equal (struct sockaddr_storage *sa, - struct sockaddr_storage *sb) + va_start (args, format); + s = g_strdup_vprintf (format, args); + va_end (args); + + len = strlen (s); + + if (len == 0) { + g_free (s); + return; + } + + written = 0; + while (written < len) { + int w; + VE_IGNORE_EINTR (w = write (fd, &s[written], len - written)); + if (w < 0) + /* evil! */ + break; + written += w; + } + + g_free (s); +} + +void +gdm_close_all_descriptors (int from, int except, int except2) { - guint8 fam_a; - guint8 fam_b; + DIR *dir; + struct dirent *ent; + GSList *openfds = NULL; + + /* + * Evil, but less evil then going to _SC_OPEN_MAX + * which can be very VERY large + */ + dir = opendir ("/proc/self/fd/"); /* This is the Linux dir */ + if (dir == NULL) + dir = opendir ("/dev/fd/"); /* This is the FreeBSD dir */ + if G_LIKELY (dir != NULL) { + GSList *li; + while ((ent = readdir (dir)) != NULL) { + int fd; + if (ent->d_name[0] == '.') + continue; + fd = atoi (ent->d_name); + if (fd >= from && fd != except && fd != except2) + openfds = g_slist_prepend (openfds, GINT_TO_POINTER (fd)); + } + closedir (dir); + for (li = openfds; li != NULL; li = li->next) { + int fd = GPOINTER_TO_INT (li->data); + VE_IGNORE_EINTR (close (fd)); + } + g_slist_free (openfds); + } else { + int i; + int max = sysconf (_SC_OPEN_MAX); + /* + * Don't go higher then this. This is + * a safety measure to not hang on crazy + * systems + */ + if G_UNLIKELY (max > 4096) { + /* FIXME: warn about this perhaps */ + /* + * Try an open, in case we're really + * leaking fds somewhere badly, this + * should be very high + */ + i = gdm_open_dev_null (O_RDONLY); + max = MAX (i+1, 4096); + } + for (i = from; i < max; i++) { + if G_LIKELY (i != except && i != except2) + VE_IGNORE_EINTR (close (i)); + } + } +} + +void +gdm_signal_ignore (int signal) +{ + struct sigaction ign_signal; + + ign_signal.sa_handler = SIG_IGN; + ign_signal.sa_flags = SA_RESTART; + sigemptyset (&ign_signal.sa_mask); + + if G_UNLIKELY (sigaction (signal, &ign_signal, NULL) < 0) + g_warning (_("%s: Error setting signal %d to %s"), + "gdm_signal_ignore", signal, "SIG_IGN"); +} + +void +gdm_signal_default (int signal) +{ + struct sigaction def_signal; - g_return_val_if_fail (sa != NULL, FALSE); - g_return_val_if_fail (sb != NULL, FALSE); + def_signal.sa_handler = SIG_DFL; + def_signal.sa_flags = SA_RESTART; + sigemptyset (&def_signal.sa_mask); - fam_a = sa->ss_family; - fam_b = sb->ss_family; + if G_UNLIKELY (sigaction (signal, &def_signal, NULL) < 0) + g_warning (_("%s: Error setting signal %d to %s"), + "gdm_signal_ignore", signal, "SIG_DFL"); +} - if (fam_a == AF_INET && fam_b == AF_INET) { - return v4_v4_equal (SIN (sa), SIN (sb)); +int +gdm_open_dev_null (mode_t mode) +{ + int ret; + VE_IGNORE_EINTR (ret = open ("/dev/null", mode)); + if G_UNLIKELY (ret < 0) { + /* + * Never output anything, we're likely in some + * strange state right now + */ + gdm_signal_ignore (SIGPIPE); + VE_IGNORE_EINTR (close (2)); + g_error ("Cannot open /dev/null, system on crack!"); } -#ifdef ENABLE_IPV6 - else if (fam_a == AF_INET6 && fam_b == AF_INET6) { - return v6_v6_equal (SIN6 (sa), SIN6 (sb)); + + return ret; +} + +char * +gdm_make_filename (const char *dir, + const char *name, + const char *extension) +{ + char *base = g_strconcat (name, extension, NULL); + char *full = g_build_filename (dir, base, NULL); + g_free (base); + return full; +} + + +static int sigchld_blocked = 0; +static sigset_t sigchldblock_mask, sigchldblock_oldmask; + +static int sigterm_blocked = 0; +static sigset_t sigtermblock_mask, sigtermblock_oldmask; + +static int sigusr2_blocked = 0; +static sigset_t sigusr2block_mask, sigusr2block_oldmask; + +void +gdm_sigchld_block_push (void) +{ + sigchld_blocked++; + + if (sigchld_blocked == 1) { + /* Set signal mask */ + sigemptyset (&sigchldblock_mask); + sigaddset (&sigchldblock_mask, SIGCHLD); + sigprocmask (SIG_BLOCK, &sigchldblock_mask, &sigchldblock_oldmask); } -#endif - return FALSE; } -gboolean -gdm_address_is_loopback (struct sockaddr_storage *sa) +void +gdm_sigchld_block_pop (void) +{ + sigchld_blocked --; + + if (sigchld_blocked == 0) { + /* Reset signal mask back */ + sigprocmask (SIG_SETMASK, &sigchldblock_oldmask, NULL); + } +} + +void +gdm_sigterm_block_push (void) +{ + sigterm_blocked++; + + if (sigterm_blocked == 1) { + /* Set signal mask */ + sigemptyset (&sigtermblock_mask); + sigaddset (&sigtermblock_mask, SIGTERM); + sigaddset (&sigtermblock_mask, SIGINT); + sigaddset (&sigtermblock_mask, SIGHUP); + sigprocmask (SIG_BLOCK, &sigtermblock_mask, &sigtermblock_oldmask); + } +} + +void +gdm_sigterm_block_pop (void) +{ + sigterm_blocked --; + + if (sigterm_blocked == 0) { + /* Reset signal mask back */ + sigprocmask (SIG_SETMASK, &sigtermblock_oldmask, NULL); + } +} + +void +gdm_sigusr2_block_push (void) +{ + sigset_t oldmask; + + if (sigusr2_blocked == 0) { + /* Set signal mask */ + sigemptyset (&sigusr2block_mask); + sigaddset (&sigusr2block_mask, SIGUSR2); + sigprocmask (SIG_BLOCK, &sigusr2block_mask, &oldmask); + } + + sigusr2_blocked++; + + sigusr2block_oldmask = oldmask; +} + +void +gdm_sigusr2_block_pop (void) +{ + sigset_t oldmask; + + oldmask = sigusr2block_oldmask; + + sigusr2_blocked--; + + if (sigusr2_blocked == 0) { + /* Reset signal mask back */ + sigprocmask (SIG_SETMASK, &sigusr2block_oldmask, NULL); + } +} + +static GdmHostent * +fillout_addrinfo (struct addrinfo *res, + struct sockaddr *ia, + const char *name) { - switch(sa->ss_family){ -#ifdef AF_INET6 - case AF_INET6: - return IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *)sa)->sin6_addr); - break; + GdmHostent *he; + gint i; + gint addr_count = 0; + struct addrinfo *tempaddrinfo; + + he = g_new0 (GdmHostent, 1); + + he->addrs = NULL; + he->addr_count = 0; + + if (res != NULL && res->ai_canonname != NULL) { + he->hostname = g_strdup (res->ai_canonname); + he->not_found = FALSE; + } else { + he->not_found = TRUE; + if (name != NULL) + he->hostname = g_strdup (name); + else { + static char buffer6[INET6_ADDRSTRLEN]; + static char buffer[INET_ADDRSTRLEN]; + const char *new = NULL; + + if (ia->sa_family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *)ia)->sin6_addr)) { + new = inet_ntop (AF_INET, &(((struct sockaddr_in6 *)ia)->sin6_addr.s6_addr[12]), buffer, sizeof (buffer)); + } else { + new = inet_ntop (AF_INET6, &((struct sockaddr_in6 *)ia)->sin6_addr, buffer6, sizeof (buffer6)); + } + } else if (ia->sa_family == AF_INET) { + new = inet_ntop (AF_INET, &((struct sockaddr_in *)ia)->sin_addr, buffer, sizeof (buffer)); + } + + if (new != NULL) { + he->hostname = g_strdup (new); + } else { + he->hostname = NULL; + } + } + } + + tempaddrinfo = res; + + while (res != NULL) { + addr_count++; + res = res->ai_next; + } + + he->addrs = g_new0 (struct sockaddr_storage, addr_count); + he->addr_count = addr_count; + res = tempaddrinfo; + for (i = 0; ; i++) { + if (res == NULL) + break; + + if ((res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) { + (he->addrs)[i] = *(struct sockaddr_storage *)(res->ai_addr); + } + + res = res->ai_next; + } + + /* We don't want the ::ffff: that could arise here */ + if (he->hostname != NULL && + strncmp (he->hostname, "::ffff:", 7) == 0) { + strcpy (he->hostname, he->hostname + 7); + } + + return he; +} + +/* stolen from xdm sources */ +#if defined(X_NOT_POSIX) || defined(__EMX__) || defined(__NetBSD__) && defined(__sparc__) +#define Setjmp(e) setjmp(e) +#define Longjmp(e,v) longjmp(e,v) +#define Jmp_buf jmp_buf +#else +#define Setjmp(e) sigsetjmp(e,1) +#define Longjmp(e,v) siglongjmp(e,v) +#define Jmp_buf sigjmp_buf #endif - case AF_INET: - return (INADDR_LOOPBACK == (((struct sockaddr_in *)sa)->sin_addr.s_addr)); - break; - default: - break; + +static gboolean do_jumpback = FALSE; +static Jmp_buf signal_jumpback; +static struct sigaction oldterm, oldint, oldhup; + +static void +jumpback_sighandler (int signal) +{ + /* + * This avoids a race see Note below. + * We want to jump back only on the first + * signal invocation, even if the signal + * handler didn't return. + */ + gboolean old_do_jumpback = do_jumpback; + do_jumpback = FALSE; + + if (signal == SIGINT) + oldint.sa_handler (signal); + else if (signal == SIGTERM) + oldint.sa_handler (signal); + else if (signal == SIGHUP) + oldint.sa_handler (signal); + /* No others should be set up */ + + /* Note that we may not get here since + the SIGTERM handler in slave.c + might have in fact done the big Longjmp + to the slave's death */ + + if (old_do_jumpback) { + Longjmp (signal_jumpback, 1); + } +} + +/* + * This sets up interruptes to be proxied and the + * gethostbyname/addr to be whacked using longjmp, + * in case INT/TERM/HUP was gotten in which case + * we no longer care for the result of the + * resolution. + */ +#define SETUP_INTERRUPTS_FOR_TERM_DECLS \ + struct sigaction term; + +#define SETUP_INTERRUPTS_FOR_TERM_SETUP \ + do_jumpback = FALSE; \ + \ + term.sa_handler = jumpback_sighandler; \ + term.sa_flags = SA_RESTART; \ + sigemptyset (&term.sa_mask); \ + \ + if G_UNLIKELY (sigaction (SIGTERM, &term, &oldterm) < 0) \ + g_critical (_("%s: Error setting up %s signal handler: %s"), \ + "SETUP_INTERRUPTS_FOR_TERM", "TERM", g_strerror (errno)); \ + \ + if G_UNLIKELY (sigaction (SIGINT, &term, &oldint) < 0) \ + g_critical (_("%s: Error setting up %s signal handler: %s"), \ + "SETUP_INTERRUPTS_FOR_TERM", "INT", g_strerror (errno)); \ + \ + if G_UNLIKELY (sigaction (SIGHUP, &term, &oldhup) < 0) \ + g_critical (_("%s: Error setting up %s signal handler: %s"), \ + "SETUP_INTERRUPTS_FOR_TERM", "HUP", g_strerror (errno)); \ + +#define SETUP_INTERRUPTS_FOR_TERM_TEARDOWN \ + do_jumpback = FALSE; \ + \ + if G_UNLIKELY (sigaction (SIGTERM, &oldterm, NULL) < 0) \ + g_critical (_("%s: Error setting up %s signal handler: %s"), \ + "SETUP_INTERRUPTS_FOR_TERM", "TERM", g_strerror (errno)); \ + \ + if G_UNLIKELY (sigaction (SIGINT, &oldint, NULL) < 0) \ + g_critical (_("%s: Error setting up %s signal handler: %s"), \ + "SETUP_INTERRUPTS_FOR_TERM", "INT", g_strerror (errno)); \ + \ + if G_UNLIKELY (sigaction (SIGHUP, &oldhup, NULL) < 0) \ + g_critical (_("%s: Error setting up %s signal handler: %s"), \ + "SETUP_INTERRUPTS_FOR_TERM", "HUP", g_strerror (errno)); + +GdmHostent * +gdm_gethostbyname (const char *name) +{ + struct addrinfo hints; + /* static because of Setjmp */ + static struct addrinfo *result; + + SETUP_INTERRUPTS_FOR_TERM_DECLS + + /* The cached address */ + static GdmHostent *he = NULL; + static time_t last_time = 0; + static char *cached_hostname = NULL; + + if (cached_hostname != NULL && + strcmp (cached_hostname, name) == 0) { + /* Don't check more then every 60 seconds */ + if (last_time + 60 > time (NULL)) + return gdm_hostent_copy (he); + } + + SETUP_INTERRUPTS_FOR_TERM_SETUP + + if (Setjmp (signal_jumpback) == 0) { + do_jumpback = TRUE; + + /* Find client hostname */ + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_CANONNAME; + + if (result) { + freeaddrinfo (result); + result = NULL; + } + + getaddrinfo (name, NULL, &hints, &result); + do_jumpback = FALSE; + } else { + /* Here we got interrupted */ + result = NULL; + } + + SETUP_INTERRUPTS_FOR_TERM_TEARDOWN + + g_free (cached_hostname); + cached_hostname = g_strdup (name); + + gdm_hostent_free (he); + + he = fillout_addrinfo (result, NULL, name); + + last_time = time (NULL); + return gdm_hostent_copy (he); +} + +GdmHostent * +gdm_gethostbyaddr (struct sockaddr_storage *ia) +{ + struct addrinfo hints; + /* static because of Setjmp */ + static struct addrinfo *result = NULL; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + static struct in6_addr cached_addr6; + + SETUP_INTERRUPTS_FOR_TERM_DECLS + + /* The cached address */ + static GdmHostent *he = NULL; + static time_t last_time = 0; + static struct in_addr cached_addr; + + if (last_time != 0) { + if ((ia->ss_family == AF_INET6) && (memcmp (cached_addr6.s6_addr, ((struct sockaddr_in6 *) ia)->sin6_addr.s6_addr, sizeof (struct in6_addr)) == 0)) { + /* Don't check more then every 60 seconds */ + if (last_time + 60 > time (NULL)) + return gdm_hostent_copy (he); + } else if (ia->ss_family == AF_INET) { + if (memcmp (&cached_addr, &(((struct sockaddr_in *)ia)->sin_addr), sizeof (struct in_addr)) == 0) { + /* Don't check more then every 60 seconds */ + if (last_time + 60 > time (NULL)) + return gdm_hostent_copy (he); + } + } + } + + SETUP_INTERRUPTS_FOR_TERM_SETUP + + if (Setjmp (signal_jumpback) == 0) { + do_jumpback = TRUE; + + /* Find client hostname */ + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_CANONNAME; + + if (result) { + freeaddrinfo (result); + result = NULL; + } + + if (ia->ss_family == AF_INET6) { + char buffer6[INET6_ADDRSTRLEN]; + + inet_ntop (AF_INET6, &((struct sockaddr_in6 *)ia)->sin6_addr, buffer6, sizeof (buffer6)); + + /* + * In the case of IPv6 mapped address strip the + * ::ffff: and lookup as an IPv4 address + */ + if (strncmp (buffer6, "::ffff:", 7) == 0) { + char *temp= (buffer6 + 7); + strcpy (buffer6, temp); + } + getaddrinfo (buffer6, NULL, &hints, &result); + + } else if (ia->ss_family == AF_INET) { + char buffer[INET_ADDRSTRLEN]; + + inet_ntop (AF_INET, &((struct sockaddr_in *)ia)->sin_addr, buffer, sizeof (buffer)); + + getaddrinfo (buffer, NULL, &hints, &result); + } + + do_jumpback = FALSE; + } else { + /* Here we got interrupted */ + result = NULL; + } + + SETUP_INTERRUPTS_FOR_TERM_TEARDOWN + + if (ia->ss_family == AF_INET6) { + memcpy (cached_addr6.s6_addr, ((struct sockaddr_in6 *)ia)->sin6_addr.s6_addr, sizeof (struct in6_addr)); + memset (&sin6, 0, sizeof (sin6)); + memcpy (sin6.sin6_addr.s6_addr, cached_addr6.s6_addr, sizeof (struct in6_addr)); + sin6.sin6_family = AF_INET6; + he = fillout_addrinfo (result, (struct sockaddr *)&sin6, NULL); + } + else if (ia->ss_family == AF_INET) { + memcpy (&(cached_addr.s_addr), &(((struct sockaddr_in *)ia)->sin_addr.s_addr), sizeof (struct in_addr)); + memset (&sin, 0, sizeof (sin)); + memcpy (&sin.sin_addr, &cached_addr, sizeof (struct in_addr)); + sin.sin_family = AF_INET; + he = fillout_addrinfo (result, (struct sockaddr *)&sin, NULL); } - return FALSE; + last_time = time (NULL); + return gdm_hostent_copy (he); +} + +GdmHostent * +gdm_hostent_copy (GdmHostent *he) +{ + GdmHostent *cpy; + + if (he == NULL) + return NULL; + + cpy = g_new0 (GdmHostent, 1); + cpy->not_found = he->not_found; + cpy->hostname = g_strdup (he->hostname); + if (he->addr_count == 0) { + cpy->addr_count = 0; + cpy->addrs = NULL; + } else { + cpy->addr_count = he->addr_count; + cpy->addrs = g_new0 (struct sockaddr_storage, he->addr_count); + memcpy (cpy->addrs, he->addrs, sizeof (struct sockaddr_storage) * he->addr_count); + } + return cpy; } void -gdm_address_get_info (struct sockaddr_storage *ss, - char **hostp, - char **servp) +gdm_hostent_free (GdmHostent *he) { - char host [NI_MAXHOST]; - char serv [NI_MAXSERV]; + if (he == NULL) + return; + g_free (he->hostname); + he->hostname = NULL; + + g_free (he->addrs); + he->addrs = NULL; + he->addr_count = 0; + + g_free (he); +} + - host [0] = '\0'; - serv [0] = '\0'; - getnameinfo ((const struct sockaddr *)ss, - sizeof (struct sockaddr_storage), - host, sizeof (host), - serv, sizeof (serv), - NI_NUMERICHOST | NI_NUMERICSERV); - if (servp != NULL) { - *servp = g_strdup (serv); +/* Like fopen with "w" */ +FILE * +gdm_safe_fopen_w (const char *file, mode_t perm) +{ + int fd; + FILE *ret; + VE_IGNORE_EINTR (g_unlink (file)); + do { + errno = 0; + fd = open (file, O_EXCL|O_CREAT|O_TRUNC|O_WRONLY +#ifdef O_NOCTTY + |O_NOCTTY +#endif +#ifdef O_NOFOLLOW + |O_NOFOLLOW +#endif + , perm); + } while G_UNLIKELY (errno == EINTR); + if (fd < 0) + return NULL; + VE_IGNORE_EINTR (ret = fdopen (fd, "w")); + return ret; +} + +/* Like fopen with "a+" */ +FILE * +gdm_safe_fopen_ap (const char *file, mode_t perm) +{ + int fd; + FILE *ret; + + if (g_access (file, F_OK) == 0) { + do { + errno = 0; + fd = open (file, O_APPEND|O_RDWR +#ifdef O_NOCTTY + |O_NOCTTY +#endif +#ifdef O_NOFOLLOW + |O_NOFOLLOW +#endif + ); + } while G_UNLIKELY (errno == EINTR); + } else { + /* Doesn't exist, open with O_EXCL */ + do { + errno = 0; + fd = open (file, O_EXCL|O_CREAT|O_RDWR +#ifdef O_NOCTTY + |O_NOCTTY +#endif +#ifdef O_NOFOLLOW + |O_NOFOLLOW +#endif + , perm); + } while G_UNLIKELY (errno == EINTR); } - if (hostp != NULL) { - *hostp = g_strdup (host); + if (fd < 0) + return NULL; + VE_IGNORE_EINTR (ret = fdopen (fd, "a+")); + return ret; +} + +void +gdm_fd_set_close_on_exec (int fd) +{ + int flags; + + flags = fcntl (fd, F_GETFD, 0); + if (flags < 0) { + return; } + + flags |= FD_CLOEXEC; + + fcntl (fd, F_SETFD, flags); } /** @@ -172,8 +800,9 @@ ve_first_word (const char *s) return ret; } -gboolean -ve_first_word_executable (const char *s, gboolean only_existance) +static gboolean +ve_first_word_executable (const char *s, + gboolean only_existance) { char *bin = ve_first_word (s); if (bin == NULL) diff --git a/common/gdm-common.h b/common/gdm-common.h index c189d41a..d509271f 100644 --- a/common/gdm-common.h +++ b/common/gdm-common.h @@ -38,26 +38,105 @@ G_BEGIN_DECLS +#define ve_string_empty(x) ((x)==NULL||(x)[0]=='\0') +#define ve_sure_string(x) ((x)!=NULL?(x):"") +#define VE_IGNORE_EINTR(expr) \ + do { \ + errno = 0; \ + expr; \ + } while G_UNLIKELY (errno == EINTR); + +#define NEVER_FAILS_seteuid(uid) \ + { int r = 0; \ + if (geteuid () != uid) \ + r = seteuid (uid); \ + if G_UNLIKELY (r != 0) \ + g_error ("GDM file %s: line %d (%s): Cannot run seteuid to %d: %s", \ + __FILE__, \ + __LINE__, \ + G_GNUC_PRETTY_FUNCTION, \ + (int)uid, \ + strerror (errno)); } +#define NEVER_FAILS_setegid(gid) \ + { int r = 0; \ + if (getegid () != gid) \ + r = setegid (gid); \ + if G_UNLIKELY (r != 0) \ + g_error ("GDM file %s: line %d (%s): Cannot run setegid to %d: %s", \ + __FILE__, \ + __LINE__, \ + G_GNUC_PRETTY_FUNCTION, \ + (int)gid, \ + strerror (errno)); } + +/* first goes to euid-root and then sets the egid and euid, to make sure + * this succeeds */ +#define NEVER_FAILS_root_set_euid_egid(uid,gid) \ + { NEVER_FAILS_seteuid (0); \ + NEVER_FAILS_setegid (gid); \ + if (uid != 0) { NEVER_FAILS_seteuid (uid); } } + + +/* like fopen with "w" but unlinks and uses O_EXCL */ +FILE * gdm_safe_fopen_w (const char *file, + mode_t perm); +/* like fopen with "a+" and uses O_EXCL and O_NOFOLLOW */ +FILE * gdm_safe_fopen_ap (const char *file, + mode_t perm); + + +typedef struct { + gboolean not_found; /* hostname below set to fallback, + as gethostbyaddr/name failed */ + char *hostname; /* never a bogus dot, if + invalid/unknown, then set to the + ip address in string form */ + + struct sockaddr_storage *addrs; + int addr_count; +} GdmHostent; + +GdmHostent * gdm_gethostbyname (const char *name); + +GdmHostent *gdm_gethostbyaddr (struct sockaddr_storage *ia); +GdmHostent * gdm_hostent_copy (GdmHostent *he); +void gdm_hostent_free (GdmHostent *he); + +/* This is for race free forks */ +void gdm_sigchld_block_push (void); +void gdm_sigchld_block_pop (void); +void gdm_sigterm_block_push (void); +void gdm_sigterm_block_pop (void); +void gdm_sigusr2_block_push (void); +void gdm_sigusr2_block_pop (void); + +void gdm_fdprintf (int fd, const gchar *format, ...) G_GNUC_PRINTF (2, 3); +int gdm_fdgetc (int fd); +char *gdm_fdgets (int fd); + +void gdm_signal_ignore (int signal); +void gdm_signal_default (int signal); + +void gdm_close_all_descriptors (int from, int except, int except2); + +int gdm_open_dev_null (mode_t mode); + +/* somewhat like g_build_filename, but does somet hing like + * <dir> "/" <name> <extension> + */ +char * gdm_make_filename (const char *dir, + const char *name, + const char *extension); -gboolean gdm_address_equal (struct sockaddr_storage *sa, - struct sockaddr_storage *sb); -gboolean gdm_address_is_loopback (struct sockaddr_storage *sa); -void gdm_address_get_info (struct sockaddr_storage *sa, - char **host, - char **port); +void gdm_fd_set_close_on_exec (int fd); void ve_clearenv (void); char * ve_first_word (const char *s); -gboolean ve_first_word_executable (const char *s, - gboolean only_existance); /* Gets the first existing command out of a list separated by semicolons */ char * ve_get_first_working_command (const char *list, gboolean only_existance); -#define ve_string_empty(x) ((x)==NULL||(x)[0]=='\0') -#define ve_sure_string(x) ((x)!=NULL?(x):"") - /* These two functions will ALWAYS return a non-NULL string, * if there is an error, they return the unconverted string */ char * ve_locale_to_utf8 (const char *str); @@ -74,12 +153,6 @@ pid_t ve_waitpid_no_signal (pid_t pid, int *status, int options); /* Testing for existance of a certain locale */ gboolean ve_locale_exists (const char *loc); -#define VE_IGNORE_EINTR(expr) \ - do { \ - errno = 0; \ - expr; \ - } while G_UNLIKELY (errno == EINTR); - G_END_DECLS #endif /* _GDM_COMMON_H */ diff --git a/configure.ac b/configure.ac index 11186a22..7e4de566 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ dnl AC_ARG_ENABLE(console-helper, [ --enable-console-helper=[auto/no/yes] Enable PAM console helper [default=auto]],, enable_console_helper=auto) - + AC_ARG_ENABLE(authentication-scheme, [ --enable-authentication-scheme=[auto/pam/crypt/shadow] Choose a specific authentication scheme [default=auto]],, @@ -813,14 +813,37 @@ fi use_console_kit=no if test "x$with_console_kit" != "xno" ; then use_console_kit=yes - PKG_CHECK_MODULES(DBUS, dbus-glib-1 >= $DBUS_REQUIRED) AC_DEFINE(WITH_CONSOLE_KIT, 1, [Define to enable ConsoleKit support]) fi AM_CONDITIONAL(WITH_CONSOLE_KIT, test x$use_console_kit = xyes) AC_SUBST(WITH_CONSOLE_KIT) + +dnl --------------------------------------------------------------------------- +dnl - D-Bus +dnl --------------------------------------------------------------------------- + +PKG_CHECK_MODULES(DBUS, dbus-glib-1 >= $DBUS_REQUIRED) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) +dnl --------------------------------------------------------------------------- +dnl - Are we specifying a different dbus root ? +dnl --------------------------------------------------------------------------- + +AC_ARG_WITH(dbus-sys, + [AC_HELP_STRING([--with-dbus-sys=<dir>], + [where D-BUS system.d directory is])]) +AC_ARG_WITH(dbus-services, + [AC_HELP_STRING([--with-dbus-services=<dir>], + [where D-BUS services directory is])]) +if ! test -z "$with_dbus_sys" ; then + DBUS_SYS_DIR="$with_dbus_sys" +else + DBUS_SYS_DIR="$sysconfdir/dbus-1/system.d" +fi +AC_SUBST(DBUS_SYS_DIR) + + # # Define some variables to represent the directories we use. # @@ -1273,9 +1296,10 @@ pixmaps/Makefile pixmaps/16x16/Makefile pixmaps/32x32/Makefile pixmaps/48x48/Makefile +data/Makefile config/Makefile -po/Makefile.in common/Makefile +po/Makefile.in docs/Makefile docs/de/Makefile docs/es/Makefile diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 3ccb4b61..02fcef7d 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -26,48 +26,102 @@ INCLUDES = \ -DLANG_CONFIG_FILE=\"$(LANG_CONFIG_FILE)\" \ $(GNOME_INCLUDEDIR) \ -DGREETERTHEMEDIR=\""$(datadir)/gdm/themes"\" \ + $(DBUS_CFLAGS) \ $(NULL) -sbin_PROGRAMS = gdm-binary \ +BUILT_SOURCES = \ + gdm-slave-glue.h \ + gdm-manager-glue.h \ + gdm-display-glue.h \ + gdm-xdmcp-display-glue.h \ + gdm-static-display-glue.h \ $(NULL) -gdm_binary_SOURCES = \ - gdm.c \ - gdm.h \ - gdm-daemon-config.c \ - gdm-daemon-config.h \ - gdm-daemon-config-entries.h \ - gdm-daemon-config-keys.h \ - gdm-socket-protocol.h \ - display.c \ - display.h \ +gdm-manager-glue.h: gdm-manager.xml Makefile.am + dbus-binding-tool --prefix=gdm_manager --mode=glib-server --output=gdm-manager-glue.h gdm-manager.xml +gdm-slave-glue.h: gdm-slave.xml Makefile.am + dbus-binding-tool --prefix=gdm_slave --mode=glib-server --output=gdm-slave-glue.h gdm-slave.xml +gdm-display-glue.h: gdm-display.xml Makefile.am + dbus-binding-tool --prefix=gdm_display --mode=glib-server --output=gdm-display-glue.h gdm-display.xml +gdm-xdmcp-display-glue.h: gdm-xdmcp-display.xml Makefile.am + dbus-binding-tool --prefix=gdm_xdmcp_display --mode=glib-server --output=gdm-xdmcp-display-glue.h gdm-xdmcp-display.xml +gdm-static-display-glue.h: gdm-static-display.xml Makefile.am + dbus-binding-tool --prefix=gdm_static_display --mode=glib-server --output=gdm-static-display-glue.h gdm-static-display.xml + +libexec_PROGRAMS = \ + gdm-slave \ + $(NULL) + +gdm_slave_SOURCES = \ + slave-main.c \ + gdm-greeter.c \ + gdm-greeter.h \ + gdm-server.c \ + gdm-server.h \ + gdm-slave.c \ + gdm-slave.h \ + fstype.c \ + filecheck.c \ + filecheck.h \ + $(NULL) + +# Note that these libs are in LDFLAGS because they should come before +# everything else on the link line as they may override stuff +gdm_slave_LDFLAGS = \ + $(EXTRA_DAEMON_LIBS) \ + $(NULL) + +gdm_slave_LDADD = \ + $(DAEMON_LIBS) \ + $(INTLLIBS) \ + $(GLIB_LIBS) \ + $(GOBJECT_LIBS) \ + $(GDK_LIBS) \ + $(top_builddir)/common/libgdmcommon.a \ + $(X_LIBS) \ + $(XINERAMA_LIBS) \ + $(DBUS_LIBS) \ + -lXau \ + -lX11 \ + -lXext \ + $(NULL) + +sbin_PROGRAMS = \ + gdm-binary \ + $(NULL) + +gdm_binary_SOURCES = \ + gdm.h \ + main.c \ + gdm-master-config.c \ + gdm-master-config.h \ + gdm-daemon-config-entries.h \ + gdm-display-store.c \ + gdm-display-store.h \ + gdm-display.c \ + gdm-display.h \ + gdm-xdmcp-display.c \ + gdm-xdmcp-display.h \ + gdm-static-display.c \ + gdm-static-display.h \ + gdm-manager.c \ + gdm-manager.h \ + gdm-slave-proxy.c \ + gdm-slave-proxy.h \ + gdm-daemon-config-keys.h \ + gdm-socket-protocol.h \ + auth.c \ + auth.h \ fstype.c \ - slave.c \ - slave.h \ - server.c \ server.h \ - misc.c \ - misc.h \ - auth.c \ - auth.h \ cookie.c \ cookie.h \ - xdmcp.c \ - xdmcp.h \ choose.c \ choose.h \ filecheck.c \ filecheck.h \ md5.c \ md5.h \ - @VRFY@.c \ - verify.h \ - errorgui.c \ - errorgui.h \ - gdm-net.c \ - gdm-net.h \ - getvt.c \ - getvt.h \ $(NULL) XDMCP_SOURCES = \ @@ -109,6 +163,7 @@ gdm_binary_LDADD = \ $(X_LIBS) \ $(XINERAMA_LIBS) \ $(XDMCP_LIBS) \ + $(DBUS_LIBS) \ -lXau \ -lX11 \ -lXext \ @@ -116,14 +171,25 @@ gdm_binary_LDADD = \ if WITH_CONSOLE_KIT gdm_binary_SOURCES += $(CONSOLE_KIT_SOURCES) -gdm_binary_LDADD += $(DBUS_LIBS) -INCLUDES += $(DBUS_CFLAGS) endif -sbin_SCRIPTS = gdm -CLEANFILES = gdm +sbin_SCRIPTS = \ + gdm \ + $(NULL) gdm: $(srcdir)/gdm.in sed -e 's,[@]sbindir[@],$(sbindir),g' <$(srcdir)/gdm.in >gdm -EXTRA_DIST = gdm.in +CLEANFILES = \ + gdm \ + $(BUILT_SOURCES) \ + $(NULL) + +EXTRA_DIST = \ + gdm.in \ + gdm-slave.xml \ + gdm-manager.xml \ + gdm-display.xml \ + gdm-xdmcp-display.xml \ + gdm-static-display.xml \ + $(NULL) diff --git a/daemon/auth.c b/daemon/auth.c index 0927c288..4b3e889f 100644 --- a/daemon/auth.c +++ b/daemon/auth.c @@ -34,17 +34,17 @@ #include <errno.h> #include <X11/Xauth.h> + +#include <glib.h> #include <glib/gi18n.h> -#include "gdm.h" #include "cookie.h" -#include "misc.h" #include "filecheck.h" #include "auth.h" #include "gdm-common.h" #include "gdm-log.h" -#include "gdm-daemon-config.h" +#include "gdm-master-config.h" /* Ensure we know about FamilyInternetV6 even if what we're compiling against doesn't */ @@ -57,41 +57,17 @@ /* Local prototypes */ static FILE *gdm_auth_purge (GdmDisplay *d, FILE *af, gboolean remove_when_empty); -static void -display_add_error (GdmDisplay *d) -{ - if (errno != 0) - gdm_error (_("%s: Could not write new authorization entry: %s"), - "add_auth_entry", strerror (errno)); - else - gdm_error (_("%s: Could not write new authorization entry. " - "Possibly out of diskspace"), - "add_auth_entry"); - if (d->attached) { - char *s = g_strdup_printf - (C_(N_("GDM could not write a new authorization " - "entry to disk. Possibly out of diskspace.%s%s")), - errno != 0 ? " Error: " : "", - errno != 0 ? strerror (errno) : ""); - gdm_text_message_dialog (s); - g_free (s); - } -} - -static gboolean -add_auth_entry (GdmDisplay *d, - GSList **authlist, - FILE *af, - FILE *af2, - unsigned short family, - const char *addr, - int addrlen) +gboolean +gdm_auth_add_entry (int display_num, + const char *bcookie, + GSList **authlist, + FILE *af, + unsigned short family, + const char *addr, + int addrlen) { Xauth *xa; - gchar *dispnum; - - if G_UNLIKELY (!d) - return FALSE; + char *dispnum; xa = malloc (sizeof (Xauth)); @@ -113,7 +89,7 @@ add_auth_entry (GdmDisplay *d, xa->address_length = addrlen; } - dispnum = g_strdup_printf ("%d", d->dispnum); + dispnum = g_strdup_printf ("%d", display_num); xa->number = strdup (dispnum); xa->number_length = strlen (dispnum); g_free (dispnum); @@ -128,7 +104,8 @@ add_auth_entry (GdmDisplay *d, free (xa); return FALSE; } - memcpy (xa->data, d->bcookie, 16); + + memcpy (xa->data, bcookie, 16); xa->data_length = 16; if (af != NULL) { @@ -139,21 +116,17 @@ add_auth_entry (GdmDisplay *d, free (xa->name); free (xa->address); free (xa); - display_add_error (d); - return FALSE; - } - if (af2 != NULL) { - errno = 0; - if G_UNLIKELY ( ! XauWriteAuth (af2, xa)) { - free (xa->data); - free (xa->number); - free (xa->name); - free (xa->address); - free (xa); - display_add_error (d); - return FALSE; + if (errno != 0) { + g_warning (_("%s: Could not write new authorization entry: %s"), + "add_auth_entry", g_strerror (errno)); + } else { + g_warning (_("%s: Could not write new authorization entry. " + "Possibly out of diskspace"), + "add_auth_entry"); } + + return FALSE; } } @@ -162,152 +135,23 @@ add_auth_entry (GdmDisplay *d, return TRUE; } -/** - * gdm_auth_secure_display: - * @d: Pointer to a GdmDisplay struct - * - * Create authentication cookies for local and remote displays. - * - * Returns TRUE on success and FALSE on error. - */ - gboolean -gdm_auth_secure_display (GdmDisplay *d) +gdm_auth_add_entry_for_display (int display_num, + const char *bcookie, + GSList **authlist, + FILE *af) { - FILE *af, *af_gdm; - int closeret; - - if G_UNLIKELY (!d) - return FALSE; - - umask (022); - - gdm_debug ("gdm_auth_secure_display: Setting up access for %s", d->name); - - g_free (d->authfile); - d->authfile = NULL; - g_free (d->authfile_gdm); - d->authfile_gdm = NULL; - - if (d->server_uid != 0) { - int authfd; - - /* Note, nested display can't use the GDM_KEY_SERV_AUTHDIR unless - * running as root, which is rare anyway. */ - - d->authfile = g_build_filename (gdm_daemon_config_get_value_string (GDM_KEY_USER_AUTHDIR_FALLBACK), ".gdmXXXXXX", NULL); - - umask (077); - authfd = g_mkstemp (d->authfile); - umask (022); - - if G_UNLIKELY (authfd == -1) { - gdm_error (_("%s: Could not make new cookie file in %s"), - "gdm_auth_secure_display", gdm_daemon_config_get_value_string (GDM_KEY_USER_AUTHDIR_FALLBACK)); - g_free (d->authfile); - d->authfile = NULL; - return FALSE; - } - - /* Make it owned by the user that nested display is started as */ - fchown (authfd, d->server_uid, -1); - - VE_IGNORE_EINTR (af = fdopen (authfd, "w")); - - if G_UNLIKELY (af == NULL) { - g_free (d->authfile); - d->authfile = NULL; - return FALSE; - } - - /* Make another authfile since the greeter can't read the server/user - * readable file */ - d->authfile_gdm = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), d->name, ".Xauth"); - af_gdm = gdm_safe_fopen_w (d->authfile_gdm, 0644); - - if G_UNLIKELY (af_gdm == NULL) { - gdm_error (_("%s: Cannot safely open %s"), - "gdm_auth_secure_display", - d->authfile_gdm); - - g_free (d->authfile_gdm); - d->authfile_gdm = NULL; - g_free (d->authfile); - d->authfile = NULL; - VE_IGNORE_EINTR (fclose (af)); - return FALSE; - } - } else { - /* gdm and xserver authfile can be the same, server will run as root */ - d->authfile = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), d->name, ".Xauth"); - af = gdm_safe_fopen_w (d->authfile, 0644); - - if G_UNLIKELY (af == NULL) { - gdm_error (_("%s: Cannot safely open %s"), - "gdm_auth_secure_display", - d->authfile); - - g_free (d->authfile); - d->authfile = NULL; - return FALSE; - } - - af_gdm = NULL; - } - - /* If this is a local display the struct hasn't changed and we - * have to eat up old authentication cookies before baking new - * ones... */ - if (SERVER_IS_LOCAL (d) && d->auths) { - gdm_auth_free_auth_list (d->auths); - d->auths = NULL; - - g_free (d->cookie); - d->cookie = NULL; - g_free (d->bcookie); - d->bcookie = NULL; - } - - /* Create new random cookie */ - gdm_cookie_generate (&d->cookie, &d->bcookie); - - /* reget local host if local as it may have changed */ - if (SERVER_IS_LOCAL (d)) { - char hostname[1024]; - - hostname[1023] = '\0'; - if G_LIKELY (gethostname (hostname, 1023) == 0) { - g_free (d->hostname); - d->hostname = g_strdup (hostname); - } - } - - if ( ! add_auth_entry (d, &(d->auths), af, af_gdm, FamilyWild, NULL, 0)) - return FALSE; - - gdm_debug ("gdm_auth_secure_display: Setting up access"); - - VE_IGNORE_EINTR (closeret = fclose (af)); - if G_UNLIKELY (closeret < 0) { - display_add_error (d); - return FALSE; - } - if (af_gdm != NULL) { - VE_IGNORE_EINTR (closeret = fclose (af_gdm)); - if G_UNLIKELY (closeret < 0) { - display_add_error (d); - return FALSE; - } - } - g_setenv ("XAUTHORITY", GDM_AUTHFILE (d), TRUE); - - if G_UNLIKELY (gdm_daemon_config_get_value_bool (GDM_KEY_DEBUG)) - gdm_debug ("gdm_auth_secure_display: Setting up access for %s - %d entries", - d->name, g_slist_length (d->auths)); - - return TRUE; + gdm_auth_add_entry (display_num, + bcookie, + authlist, + af, + FamilyWild, + NULL, + 0); } +#if 0 + #define SA(__s) ((struct sockaddr *) __s) #define SIN(__s) ((struct sockaddr_in *) __s) #define SIN6(__s) ((struct sockaddr_in6 *) __s) @@ -352,7 +196,7 @@ get_local_auths (GdmDisplay *d) if G_UNLIKELY (!d) return NULL; - if (SERVER_IS_LOCAL (d)) { + if (gdm_display_is_local (d)) { char hostname[1024]; /* reget local host if local as it may have changed */ @@ -391,7 +235,7 @@ get_local_auths (GdmDisplay *d) /* local machine but not local if you get my meaning, add * the host gotten by gethostname as well if it's different * since the above is probably localhost */ - if ( ! SERVER_IS_LOCAL (d)) { + if ( ! gdm_display_is_local (d)) { char hostname[1024]; hostname[1023] = '\0'; @@ -418,7 +262,7 @@ get_local_auths (GdmDisplay *d) gdm_debug ("get_local_auths: Setting up network access"); - if ( ! SERVER_IS_LOCAL (d)) { + if ( ! gdm_display_is_local (d)) { /* we should write out an entry for d->addr since possibly it is not in d->addrs */ @@ -468,7 +312,7 @@ get_local_auths (GdmDisplay *d) } /* if local server add loopback */ - if (SERVER_IS_LOCAL (d) && ! added_lo && ! d->tcp_disallowed) { + if (gdm_display_is_local (d) && ! added_lo && ! d->tcp_disallowed) { struct sockaddr_storage *lo_ss = NULL; /* FIXME: get loobback ss */ if (! add_auth_entry_for_addr (d, &auths, lo_ss)) { @@ -476,9 +320,8 @@ get_local_auths (GdmDisplay *d) } } - if G_UNLIKELY (gdm_daemon_config_get_value_bool (GDM_KEY_DEBUG)) - gdm_debug ("get_local_auths: Setting up access for %s - %d entries", - d->name, g_slist_length (auths)); + g_debug ("get_local_auths: Setting up access for %s - %d entries", + d->name, g_slist_length (auths)); return auths; @@ -1005,3 +848,4 @@ gdm_auth_free_auth_list (GSList *list) g_slist_free (list); } +#endif diff --git a/daemon/auth.h b/daemon/auth.h index f01ebcbf..0f84dde5 100644 --- a/daemon/auth.h +++ b/daemon/auth.h @@ -19,17 +19,33 @@ #ifndef GDM_AUTH_H #define GDM_AUTH_H -#include "gdm.h" +#include "gdm-display.h" -gboolean gdm_auth_secure_display (GdmDisplay *d); -gboolean gdm_auth_user_add (GdmDisplay *d, uid_t user, const char *homedir); -void gdm_auth_user_remove (GdmDisplay *d, uid_t user); +G_BEGIN_DECLS + +gboolean gdm_auth_add_entry_for_display (int display_num, + const char *bcookie, + GSList **authlist, + FILE *af); +gboolean gdm_auth_add_entry (int display_num, + const char *bcookie, + GSList **authlist, + FILE *af, + unsigned short family, + const char *addr, + int addrlen); + +gboolean gdm_auth_user_add (GdmDisplay *d, + uid_t user, + const char *homedir); +void gdm_auth_user_remove (GdmDisplay *d, + uid_t user); /* Call XSetAuthorization */ void gdm_auth_set_local_auth (GdmDisplay *d); -void gdm_auth_free_auth_list (GSList *list); +void gdm_auth_free_auth_list (GSList *list); -#endif /* GDM_AUTH_H */ +G_END_DECLS -/* EOF */ +#endif /* GDM_AUTH_H */ diff --git a/daemon/choose.c b/daemon/choose.c index bd08b10c..a460b31e 100644 --- a/daemon/choose.c +++ b/daemon/choose.c @@ -40,14 +40,13 @@ #include <fcntl.h> #include <string.h> -#include "gdm.h" -#include "misc.h" #include "choose.h" -#include "xdmcp.h" +#include "gdm-address.h" #include "gdm-common.h" #include "gdm-log.h" -#include "gdm-daemon-config.h" +#include "gdm-master-config.h" +#include "gdm-daemon-config-entries.h" #include "gdm-socket-protocol.h" @@ -86,8 +85,8 @@ remove_oldest_pending (void) #endif static gboolean -get_first_address_for_node (const char *node, - struct sockaddr_storage **sa) +get_first_address_for_node (const char *node, + GdmAddress **address) { struct addrinfo hints; struct addrinfo *ai_list; @@ -122,8 +121,8 @@ get_first_address_for_node (const char *node, } if (ai != NULL) { - if (sa != NULL) { - *sa = g_memdup (ai->ai_addr, ai->ai_addrlen); + if (address != NULL) { + *address = gdm_address_new_from_sockaddr_storage ((struct sockaddr_storage *)ai->ai_addr); } } @@ -132,19 +131,33 @@ get_first_address_for_node (const char *node, return found; } +static int +get_config_int (int id) +{ + GdmDaemonConfig *dc; + int val; + + dc = gdm_daemon_config_new (); + gdm_daemon_config_get_int_for_id (dc, id, &val); + + g_object_unref (dc); + + return val; +} + gboolean gdm_choose_data (const char *data) { - int id; - struct sockaddr_storage *sa; - GSList *li; - char *msg; - char *p; - char *host; - gboolean ret; + int id; + GdmAddress *address; + GSList *li; + char *msg; + char *p; + char *host; + gboolean ret; msg = g_strdup (data); - sa = NULL; + address = NULL; ret = FALSE; p = strtok (msg, " "); @@ -163,28 +176,28 @@ gdm_choose_data (const char *data) goto out; } - if (! get_first_address_for_node (p, &sa)) { + if (! get_first_address_for_node (p, &address)) { goto out; } - gdm_address_get_info (sa, &host, NULL); - gdm_debug ("gdm_choose_data: got indirect id: %d address: %s", - id, - host); + gdm_address_get_numeric_info (address, &host, NULL); + g_debug ("gdm_choose_data: got indirect id: %d address: %s", + id, + host); g_free (host); for (li = indirect; li != NULL; li = li->next) { GdmIndirectDisplay *idisp = li->data; if (idisp->id == id) { /* whack the oldest if more then allowed */ - while (ipending >= gdm_daemon_config_get_value_int (GDM_KEY_MAX_INDIRECT) && + while (ipending >= get_config_int (GDM_ID_MAX_INDIRECT) && remove_oldest_pending ()) ; idisp->acctime = time (NULL); g_free (idisp->chosen_host); - idisp->chosen_host = g_memdup (sa, sizeof (struct sockaddr_storage)); + idisp->chosen_host = gdm_address_copy (address); /* Now this display is pending */ ipending++; @@ -194,7 +207,7 @@ gdm_choose_data (const char *data) } } out: - g_free (sa); + gdm_address_free (address); g_free (msg); return ret; @@ -202,13 +215,12 @@ gdm_choose_data (const char *data) GdmIndirectDisplay * -gdm_choose_indirect_alloc (struct sockaddr_storage *clnt_sa) +gdm_choose_indirect_alloc (GdmAddress *address) { GdmIndirectDisplay *id; - char *host; + char *host; - if (clnt_sa == NULL) - return NULL; + g_assert (address != NULL); id = g_new0 (GdmIndirectDisplay, 1); id->id = indirect_id++; @@ -217,18 +229,18 @@ gdm_choose_indirect_alloc (struct sockaddr_storage *clnt_sa) if (id->id == 0) id->id = indirect_id++; - id->dsp_sa = g_memdup (clnt_sa, sizeof (struct sockaddr_storage)); + id->dsp_address = gdm_address_copy (address); id->chosen_host = NULL; id->acctime = 0; indirect = g_slist_prepend (indirect, id); - gdm_address_get_info (id->dsp_sa, &host, NULL); + gdm_address_get_numeric_info (id->dsp_address, &host, NULL); - gdm_debug ("gdm_choose_display_alloc: display=%s, pending=%d ", - host, - ipending); + g_debug ("gdm_choose_display_alloc: display=%s, pending=%d ", + host, + ipending); g_free (host); return (id); @@ -258,8 +270,8 @@ gdm_choose_indirect_dispose_empty_id (guint id) } GdmIndirectDisplay * -gdm_choose_indirect_lookup_by_chosen (struct sockaddr_storage *chosen, - struct sockaddr_storage *origin) +gdm_choose_indirect_lookup_by_chosen (GdmAddress *chosen, + GdmAddress *origin) { GSList *li; char *host; @@ -270,21 +282,21 @@ gdm_choose_indirect_lookup_by_chosen (struct sockaddr_storage *chosen, if (id != NULL && id->chosen_host != NULL && gdm_address_equal (id->chosen_host, chosen)) { - if (gdm_address_equal (id->dsp_sa, origin)) { + if (gdm_address_equal (id->dsp_address, origin)) { return id; - } else if (gdm_address_is_loopback (id->dsp_sa) && + } else if (gdm_address_is_loopback (id->dsp_address) && gdm_address_is_local (origin)) { return id; } } } - gdm_address_get_info (chosen, &host, NULL); + gdm_address_get_numeric_info (chosen, &host, NULL); - gdm_debug ("gdm_choose_indirect_lookup_by_chosen: Chosen %s host not found", - host); - gdm_debug ("gdm_choose_indirect_lookup_by_chosen: Origin was: %s", - host); + g_debug ("gdm_choose_indirect_lookup_by_chosen: Chosen %s host not found", + host); + g_debug ("gdm_choose_indirect_lookup_by_chosen: Origin was: %s", + host); g_free (host); return NULL; @@ -292,7 +304,7 @@ gdm_choose_indirect_lookup_by_chosen (struct sockaddr_storage *chosen, GdmIndirectDisplay * -gdm_choose_indirect_lookup (struct sockaddr_storage *clnt_sa) +gdm_choose_indirect_lookup (GdmAddress *address) { GSList *li, *ilist; GdmIndirectDisplay *id; @@ -307,27 +319,27 @@ gdm_choose_indirect_lookup (struct sockaddr_storage *clnt_sa) continue; if (id->acctime > 0 && - curtime > id->acctime + gdm_daemon_config_get_value_int (GDM_KEY_MAX_WAIT_INDIRECT)) { + curtime > id->acctime + get_config_int (GDM_ID_MAX_WAIT_INDIRECT)) { - gdm_address_get_info (clnt_sa, &host, NULL); - gdm_debug ("gdm_choose_indirect_check: Disposing stale INDIRECT query from %s", - host); + gdm_address_get_numeric_info (address, &host, NULL); + g_debug ("gdm_choose_indirect_check: Disposing stale INDIRECT query from %s", + host); g_free (host); gdm_choose_indirect_dispose (id); continue; } - if (gdm_address_equal (id->dsp_sa, clnt_sa)) { + if (gdm_address_equal (id->dsp_address, address)) { g_slist_free (ilist); return id; } } g_slist_free (ilist); - gdm_address_get_info (clnt_sa, &host, NULL); - gdm_debug ("gdm_choose_indirect_lookup: Host %s not found", - host); + gdm_address_get_numeric_info (address, &host, NULL); + g_debug ("gdm_choose_indirect_lookup: Host %s not found", + host); g_free (host); return NULL; @@ -348,16 +360,16 @@ gdm_choose_indirect_dispose (GdmIndirectDisplay *id) ipending--; id->acctime = 0; - gdm_address_get_info (id->dsp_sa, &host, NULL); - gdm_debug ("gdm_choose_indirect_dispose: Disposing %s", + gdm_address_get_numeric_info (id->dsp_address, &host, NULL); + g_debug ("gdm_choose_indirect_dispose: Disposing %s", host); g_free (host); g_free (id->chosen_host); id->chosen_host = NULL; - g_free (id->dsp_sa); - id->dsp_sa = NULL; + gdm_address_free (id->dsp_address); + id->dsp_address = NULL; g_free (id); } diff --git a/daemon/choose.h b/daemon/choose.h index 3fface4d..ea2fe87b 100644 --- a/daemon/choose.h +++ b/daemon/choose.h @@ -21,20 +21,20 @@ #ifndef CHOOSE_H #define CHOOSE_H -#include "gdm.h" +#include "gdm-address.h" typedef struct _GdmIndirectDisplay GdmIndirectDisplay; struct _GdmIndirectDisplay { - int id; - struct sockaddr_storage* dsp_sa; - struct sockaddr_storage* chosen_host; - time_t acctime; + int id; + GdmAddress *dsp_address; + GdmAddress *chosen_host; + time_t acctime; }; -GdmIndirectDisplay * gdm_choose_indirect_alloc (struct sockaddr_storage *clnt_sa); -GdmIndirectDisplay * gdm_choose_indirect_lookup (struct sockaddr_storage *clnt_sa); -GdmIndirectDisplay * gdm_choose_indirect_lookup_by_chosen (struct sockaddr_storage *chosen, - struct sockaddr_storage *origin); +GdmIndirectDisplay * gdm_choose_indirect_alloc (GdmAddress *address); +GdmIndirectDisplay * gdm_choose_indirect_lookup (GdmAddress *address); +GdmIndirectDisplay * gdm_choose_indirect_lookup_by_chosen (GdmAddress *chosen, + GdmAddress *origin); void gdm_choose_indirect_dispose (GdmIndirectDisplay *id); /* dispose of indirect display of id, if no host is set */ diff --git a/daemon/cookie.c b/daemon/cookie.c index 55f9621b..35016fb0 100644 --- a/daemon/cookie.c +++ b/daemon/cookie.c @@ -44,7 +44,7 @@ #include "cookie.h" #include "gdm-common.h" -#include "gdm-daemon-config.h" +#include "gdm-master-config.h" #define MAXBUFFERSIZE 1024 diff --git a/daemon/cookie.h b/daemon/cookie.h index 9342f037..cdede2b8 100644 --- a/daemon/cookie.h +++ b/daemon/cookie.h @@ -27,5 +27,3 @@ void gdm_cookie_generate (char **cookie, void gdm_random_tick (void); #endif /* GDM_COOKIE_H */ - -/* EOF */ diff --git a/daemon/display.c b/daemon/display.c index 311f5647..f02c7acd 100644 --- a/daemon/display.c +++ b/daemon/display.c @@ -43,10 +43,6 @@ #include "gdm-daemon-config.h" /* External vars */ -extern GdmConnection *fifoconn; -extern GdmConnection *pipeconn; -extern GdmConnection *unixconn; -extern int slave_fifo_pipe_fd; /* the slavepipe (like fifo) connection, this is the write end */ extern gint flexi_servers; /** @@ -371,17 +367,10 @@ gdm_display_manage (GdmDisplay *d) d->slavepid = getpid (); - gdm_connection_close (fifoconn); - fifoconn = NULL; - gdm_connection_close (pipeconn); - pipeconn = NULL; - gdm_connection_close (unixconn); - unixconn = NULL; - gdm_log_shutdown (); /* Close everything */ - gdm_close_all_descriptors (0 /* from */, fds[0] /* except */, slave_fifo_pipe_fd /* except2 */); + gdm_close_all_descriptors (0 /* from */, fds[0] /* except */, -1 /* except2 */); /* No error checking here - if it's messed the best response * is to ignore & try to continue */ diff --git a/daemon/display.h b/daemon/display.h index f25373d5..8c2bfd8a 100644 --- a/daemon/display.h +++ b/daemon/display.h @@ -29,6 +29,32 @@ typedef struct _GdmDisplay GdmDisplay; #include "gdm-net.h" /* for GdmConnection */ +/* DO NOTE USE 1, that's used as error if x connection fails usually */ +/* Note that there is no reason why these were a power of two, and note + * that they have to fit in 256 */ +/* These are the exit codes */ +#define DISPLAY_REMANAGE 2 /* Restart display */ +#define DISPLAY_ABORT 4 /* Houston, we have a problem */ +#define DISPLAY_REBOOT 8 /* Rebewt */ +#define DISPLAY_HALT 16 /* Halt */ +#define DISPLAY_SUSPEND 17 /* Suspend (don't use, use the interrupt) */ +#define DISPLAY_CHOSEN 20 /* successful chooser session, + restart display */ +#define DISPLAY_RUN_CHOOSER 30 /* Run chooser */ +#define DISPLAY_XFAILED 64 /* X failed */ +#define DISPLAY_GREETERFAILED 65 /* greeter failed (crashed) */ +#define DISPLAY_RESTARTGREETER 127 /* Restart greeter */ +#define DISPLAY_RESTARTGDM 128 /* Restart GDM */ + +enum { + DISPLAY_UNBORN /* Not yet started */, + DISPLAY_ALIVE /* Yay! we're alive (non-XDMCP) */, + XDMCP_PENDING /* Pending XDMCP display */, + XDMCP_MANAGED /* Managed XDMCP display */, + DISPLAY_DEAD /* Left for dead */, + DISPLAY_CONFIG /* in process of being configured */ +}; + #define TYPE_STATIC 1 /* X server defined in GDM configuration */ #define TYPE_XDMCP 2 /* Remote display/Xserver */ #define TYPE_FLEXI 3 /* Local Flexi X server */ @@ -177,6 +203,7 @@ struct _GdmDisplay /* order in the Xservers file for sessreg, -1 if unset yet */ int x_servers_order; + gboolean wait_for_go; /* STATIC TYPE */ diff --git a/daemon/filecheck.c b/daemon/filecheck.c index 027a5321..8c43d193 100644 --- a/daemon/filecheck.c +++ b/daemon/filecheck.c @@ -25,7 +25,6 @@ #include "gdm.h" #include "gdm-common.h" -#include "gdm-daemon-config.h" #include "filecheck.h" @@ -63,18 +62,8 @@ gdm_file_check (const gchar *caller, ve_string_empty (file)) return FALSE; - /* Stat on automounted directory - append the '/.' to dereference mount point. - Do this only if GdmSupportAutomount is true (default is false) - 2006-09-22, Jerzy Borkowski, CAMK */ - if G_UNLIKELY (gdm_daemon_config_get_value_bool (GDM_KEY_SUPPORT_AUTOMOUNT)) { - dirautofs = g_strconcat(dir, "/.", NULL); - VE_IGNORE_EINTR (r = stat (dirautofs, &statbuf)); - g_free(dirautofs); - } - /* Stat directory */ - else { - VE_IGNORE_EINTR (r = stat (dir, &statbuf)); - } + + VE_IGNORE_EINTR (r = stat (dir, &statbuf)); if (r < 0) { if ( ! absentdirok) @@ -83,17 +72,6 @@ gdm_file_check (const gchar *caller, return FALSE; } - /* Check if dir is owned by the user ... - Only, if GDM_KEY_CHECK_DIR_OWNER is true (default) - This is a "hack" for directories not owned by - the user. - 2004-06-22, Andreas Schubert, MATHEMA Software GmbH */ - - if G_UNLIKELY (gdm_daemon_config_get_value_bool (GDM_KEY_CHECK_DIR_OWNER) && (statbuf.st_uid != user)) { - g_warning (_("%s: %s is not owned by uid %d."), caller, dir, user); - return FALSE; - } - /* ... if group has write permission ... */ if G_UNLIKELY (perms < 1 && (statbuf.st_mode & S_IWGRP) == S_IWGRP) { g_warning (_("%s: %s is writable by group."), caller, dir); @@ -209,14 +187,6 @@ gdm_auth_file_check (const gchar *caller, return FALSE; } - usermaxfile = gdm_daemon_config_get_value_int (GDM_KEY_USER_MAX_FILE); - /* ... and smaller than sysadmin specified limit. */ - if G_UNLIKELY (usermaxfile && statbuf.st_size > usermaxfile) { - g_warning (_("%s: %s is bigger than sysadmin specified maximum file size."), - caller, authfile); - return FALSE; - } - /* Yeap, this file is ok */ return TRUE; } diff --git a/daemon/gdm-daemon-config-entries.h b/daemon/gdm-daemon-config-entries.h index f335ed17..343bbf3d 100644 --- a/daemon/gdm-daemon-config-entries.h +++ b/daemon/gdm-daemon-config-entries.h @@ -73,7 +73,6 @@ typedef enum { GDM_ID_CUSTOM_CMD_NO_RESTART_TEMPLATE, GDM_ID_CUSTOM_CMD_IS_PERSISTENT_TEMPLATE, GDM_ID_ROOT_PATH, - GDM_ID_SERV_AUTHDIR, GDM_ID_SESSION_DESKTOP_DIR, GDM_ID_BASE_XSESSION, GDM_ID_DEFAULT_SESSION, @@ -313,7 +312,6 @@ static const GdmConfigEntry gdm_daemon_config_entries [] = { { GDM_CONFIG_GROUP_DAEMON, "FailsafeXServer", GDM_CONFIG_VALUE_STRING, NULL, GDM_ID_FAILSAFE_XSERVER }, { GDM_CONFIG_GROUP_DAEMON, "XKeepsCrashing", GDM_CONFIG_VALUE_STRING, GDMCONFDIR "/XKeepsCrashing", GDM_ID_X_KEEPS_CRASHING }, { GDM_CONFIG_GROUP_DAEMON, "RootPath", GDM_CONFIG_VALUE_STRING, "/sbin:/usr/sbin:" GDM_USER_PATH, GDM_ID_ROOT_PATH }, - { GDM_CONFIG_GROUP_DAEMON, "ServAuthDir", GDM_CONFIG_VALUE_STRING, AUTHDIR, GDM_ID_SERV_AUTHDIR }, { GDM_CONFIG_GROUP_DAEMON, "SessionDesktopDir", GDM_CONFIG_VALUE_STRING, "/etc/X11/sessions/:" DMCONFDIR "/Sessions/:" DATADIR "/gdm/BuiltInSessions/:" DATADIR "/xsessions/", GDM_ID_SESSION_DESKTOP_DIR }, { GDM_CONFIG_GROUP_DAEMON, "BaseXsession", GDM_CONFIG_VALUE_STRING, GDMCONFDIR "/Xsession", GDM_ID_BASE_XSESSION }, { GDM_CONFIG_GROUP_DAEMON, "DefaultSession", GDM_CONFIG_VALUE_STRING, "gnome.desktop", GDM_ID_DEFAULT_SESSION }, diff --git a/daemon/gdm-daemon-config.c b/daemon/gdm-daemon-config.c index 36a02851..323b8fd8 100644 --- a/daemon/gdm-daemon-config.c +++ b/daemon/gdm-daemon-config.c @@ -2324,268 +2324,6 @@ gdm_daemon_config_signal_terminthup_was_notified (void) } } -/** - * check_user_file - * check_global_file - * is_in_trusted_pic_dir - * get_facefile_from_gnome2_dir_config - * path_is_local - * gdm_daemon_config_get_facefile_from_home - * gdm_daemon_config_get_facefile_from_global - * - * These functions are used for accessing the user's face image from their - * home directory. - */ -static gboolean -check_user_file (const char *path, - guint uid) -{ - char *dir; - char *file; - gboolean is_ok; - - if (path == NULL) - return FALSE; - - if (g_access (path, R_OK) != 0) - return FALSE; - - dir = g_path_get_dirname (path); - file = g_path_get_basename (path); - - is_ok = gdm_file_check ("run_pictures", - uid, - dir, - file, - TRUE, TRUE, - gdm_daemon_config_get_value_int (GDM_KEY_USER_MAX_FILE), - gdm_daemon_config_get_value_int (GDM_KEY_RELAX_PERM)); - g_free (dir); - g_free (file); - - return is_ok; -} - -static gboolean -check_global_file (const char *path, - guint uid) -{ - if (path == NULL) - return FALSE; - - if (g_access (path, R_OK) != 0) - return FALSE; - - return TRUE; -} - -/* If path starts with a "trusted" directory, don't sanity check things */ -/* This is really somewhat "outdated" as we now really want things in - * the picture dir or in ~/.gnome2/photo */ -static gboolean -is_in_trusted_pic_dir (const char *path) -{ - /* our own pixmap dir is trusted */ - if (strncmp (path, PIXMAPDIR, sizeof (PIXMAPDIR)) == 0) - return TRUE; - - return FALSE; -} - -static gchar * -get_facefile_from_gnome2_dir_config (const char *homedir, - guint uid) -{ - char *picfile = NULL; - char *cfgdir; - - /* Sanity check on ~user/.gnome2/gdm */ - cfgdir = g_build_filename (homedir, ".gnome2", "gdm", NULL); - if (G_LIKELY (check_user_file (cfgdir, uid))) { - GKeyFile *cfg; - char *cfgfile; - - cfgfile = g_build_filename (homedir, ".gnome2", "gdm", NULL); - cfg = gdm_common_config_load (cfgfile, NULL); - g_free (cfgfile); - - if (cfg != NULL) { - gdm_common_config_get_string (cfg, "face/picture=", &picfile, NULL); - g_key_file_free (cfg); - } - - /* must exist and be absolute (note that this check - * catches empty strings)*/ - /* Note that these days we just set ~/.face */ - if G_UNLIKELY (picfile != NULL && - (picfile[0] != '/' || - /* this catches readability by user */ - g_access (picfile, R_OK) != 0)) { - g_free (picfile); - picfile = NULL; - } - - if (picfile != NULL) { - char buf[PATH_MAX]; - if (realpath (picfile, buf) == NULL) { - g_free (picfile); - picfile = NULL; - } else { - g_free (picfile); - picfile = g_strdup (buf); - } - } - - if G_UNLIKELY (picfile != NULL) { - if (! is_in_trusted_pic_dir (picfile)) { - /* if not in trusted dir, check it out */ - - /* Note that strict permissions checking is done - * on this file. Even if it may not even be owned by the - * user. This setting should ONLY point to pics in trusted - * dirs. */ - if (! check_user_file (picfile, uid)) { - g_free (picfile); - picfile = NULL; - } - } - } - } - g_free (cfgdir); - - return picfile; -} - -static GHashTable *fstype_hash = NULL; -extern char *filesystem_type (char *path, char *relpath, struct stat *statp); - -static gboolean -path_is_local (const char *path) -{ - gpointer local = NULL; - - if (path == NULL) - return FALSE; - - if (fstype_hash == NULL) - fstype_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - else - local = g_hash_table_lookup (fstype_hash, path); - - if (local == NULL) { - struct stat statbuf; - - if (g_stat (path, &statbuf) == 0) { - char *type = filesystem_type ((char *)path, (char *)path, &statbuf); - gboolean is_local = ((strcmp (ve_sure_string (type), "nfs") != 0) && - (strcmp (ve_sure_string (type), "afs") != 0) && - (strcmp (ve_sure_string (type), "autofs") != 0) && - (strcmp (ve_sure_string (type), "unknown") != 0) && - (strcmp (ve_sure_string (type), "ncpfs") != 0)); - local = GINT_TO_POINTER (is_local ? 1 : -1); - g_hash_table_insert (fstype_hash, g_strdup (path), local); - } - } - - return GPOINTER_TO_INT (local) > 0; -} - -gchar * -gdm_daemon_config_get_facefile_from_home (const char *homedir, - guint uid) -{ - char *picfile = NULL; - char *path; - gboolean is_local; - - /* special case: look at parent of home to detect autofs - this is so we don't try to trigger an automount */ - path = g_path_get_dirname (homedir); - is_local = path_is_local (path); - g_free (path); - - /* now check that home dir itself is local */ - if (is_local) { - is_local = path_is_local (homedir); - } - - /* Only look at local home directories so we don't try to - read from remote (e.g. NFS) volumes */ - if (! is_local) - return NULL; - - picfile = g_build_filename (homedir, ".face", NULL); - - if (check_user_file (picfile, uid)) - return picfile; - else { - g_free (picfile); - picfile = NULL; - } - - picfile = g_build_filename (homedir, ".face.icon", NULL); - - if (check_user_file (picfile, uid)) - return picfile; - else { - g_free (picfile); - picfile = NULL; - } - - picfile = get_facefile_from_gnome2_dir_config (homedir, uid); - if (check_user_file (picfile, uid)) - return picfile; - else { - g_free (picfile); - picfile = NULL; - } - - /* Nothing found yet, try the old locations */ - - picfile = g_build_filename (homedir, ".gnome2", "photo", NULL); - if (check_user_file (picfile, uid)) - return picfile; - else { - g_free (picfile); - picfile = NULL; - } - - picfile = g_build_filename (homedir, ".gnome", "photo", NULL); - if (check_user_file (picfile, uid)) - return picfile; - else { - g_free (picfile); - picfile = NULL; - } - - return NULL; -} - -gchar * -gdm_daemon_config_get_facefile_from_global (const char *username, - guint uid) -{ - char *picfile = NULL; - const char *facedir; - - facedir = gdm_daemon_config_get_value_string (GDM_KEY_GLOBAL_FACE_DIR); - - /* Try the global face directory */ - - picfile = g_build_filename (facedir, username, NULL); - - if (check_global_file (picfile, uid)) - return picfile; - - g_free (picfile); - picfile = gdm_make_filename (facedir, username, ".png"); - - if (check_global_file (picfile, uid)) - return picfile; - - g_free (picfile); - return NULL; -} static gboolean is_prog_in_path (const char *prog) diff --git a/daemon/gdm-daemon-config.h b/daemon/gdm-daemon-config.h index 7c4f3767..d15d6f10 100644 --- a/daemon/gdm-daemon-config.h +++ b/daemon/gdm-daemon-config.h @@ -22,7 +22,6 @@ #ifndef _GDM_DAEMON_CONFIG_H #define _GDM_DAEMON_CONFIG_H -#include "server.h" #include "gdm-daemon-config-entries.h" G_BEGIN_DECLS diff --git a/daemon/gdm-display-store.c b/daemon/gdm-display-store.c new file mode 100644 index 00000000..f6b469f7 --- /dev/null +++ b/daemon/gdm-display-store.c @@ -0,0 +1,219 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gdm-display-store.h" +#include "gdm-display.h" + +#define GDM_DISPLAY_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DISPLAY_STORE, GdmDisplayStorePrivate)) + +struct GdmDisplayStorePrivate +{ + GHashTable *displays; +}; + +enum { + DISPLAY_ADDED, + DISPLAY_REMOVED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void gdm_display_store_class_init (GdmDisplayStoreClass *klass); +static void gdm_display_store_init (GdmDisplayStore *display_store); +static void gdm_display_store_finalize (GObject *object); + +G_DEFINE_TYPE (GdmDisplayStore, gdm_display_store, G_TYPE_OBJECT) + +GQuark +gdm_display_store_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_display_store_error"); + } + + return ret; +} + +void +gdm_display_store_clear (GdmDisplayStore *store) +{ + g_hash_table_remove_all (store->priv->displays); +} + +gboolean +gdm_display_store_remove (GdmDisplayStore *store, + GdmDisplay *display) +{ + g_warning ("Implement me"); + return FALSE; +} + +void +gdm_display_store_foreach (GdmDisplayStore *store, + GdmDisplayStoreFunc func, + gpointer user_data) +{ + g_return_if_fail (store != NULL); + g_return_if_fail (func != NULL); + + g_hash_table_find (store->priv->displays, + (GHRFunc)func, + user_data); +} + +GdmDisplay * +gdm_display_store_find (GdmDisplayStore *store, + GdmDisplayStoreFunc predicate, + gpointer user_data) +{ + GdmDisplay *display; + + g_return_val_if_fail (store != NULL, NULL); + g_return_val_if_fail (predicate != NULL, NULL); + + display = g_hash_table_find (store->priv->displays, + (GHRFunc)predicate, + user_data); + return display; +} + +guint +gdm_display_store_foreach_remove (GdmDisplayStore *store, + GdmDisplayStoreFunc func, + gpointer user_data) +{ + guint ret; + + g_return_val_if_fail (store != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + ret = g_hash_table_foreach_remove (store->priv->displays, + (GHRFunc)func, + user_data); + + return ret; +} + +void +gdm_display_store_add (GdmDisplayStore *store, + GdmDisplay *display) +{ + char *id; + + g_return_if_fail (store != NULL); + g_return_if_fail (display != NULL); + + gdm_display_get_id (display, &id, NULL); + + g_debug ("Adding display %s to store", id); + + g_hash_table_insert (store->priv->displays, + id, + g_object_ref (display)); +} + +static void +gdm_display_store_class_init (GdmDisplayStoreClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdm_display_store_finalize; + + signals [DISPLAY_ADDED] = + g_signal_new ("display-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmDisplayStoreClass, display_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + signals [DISPLAY_REMOVED] = + g_signal_new ("display-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmDisplayStoreClass, display_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + + g_type_class_add_private (klass, sizeof (GdmDisplayStorePrivate)); +} + +static void +display_unref (GdmDisplay *display) +{ + /* nothing yet */ +} + +static void +gdm_display_store_init (GdmDisplayStore *store) +{ + + store->priv = GDM_DISPLAY_STORE_GET_PRIVATE (store); + + store->priv->displays = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) display_unref); +} + +static void +gdm_display_store_finalize (GObject *object) +{ + GdmDisplayStore *display_store; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_DISPLAY_STORE (object)); + + display_store = GDM_DISPLAY_STORE (object); + + g_return_if_fail (display_store->priv != NULL); + + G_OBJECT_CLASS (gdm_display_store_parent_class)->finalize (object); +} + +GdmDisplayStore * +gdm_display_store_new (void) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_DISPLAY_STORE, + NULL); + + return GDM_DISPLAY_STORE (object); +} diff --git a/daemon/gdm-display-store.h b/daemon/gdm-display-store.h new file mode 100644 index 00000000..1c7f78bc --- /dev/null +++ b/daemon/gdm-display-store.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_DISPLAY_STORE_H +#define __GDM_DISPLAY_STORE_H + +#include <glib-object.h> +#include "gdm-display.h" + +G_BEGIN_DECLS + +#define GDM_TYPE_DISPLAY_STORE (gdm_display_store_get_type ()) +#define GDM_DISPLAY_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DISPLAY_STORE, GdmDisplayStore)) +#define GDM_DISPLAY_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DISPLAY_STORE, GdmDisplayStoreClass)) +#define GDM_IS_DISPLAY_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DISPLAY_STORE)) +#define GDM_IS_DISPLAY_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DISPLAY_STORE)) +#define GDM_DISPLAY_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DISPLAY_STORE, GdmDisplayStoreClass)) + +typedef struct GdmDisplayStorePrivate GdmDisplayStorePrivate; + +typedef struct +{ + GObject parent; + GdmDisplayStorePrivate *priv; +} GdmDisplayStore; + +typedef struct +{ + GObjectClass parent_class; + + void (* display_added) (GdmDisplayStore *display_store, + GdmDisplay *display); + void (* display_removed) (GdmDisplayStore *display_store, + GdmDisplay *display); +} GdmDisplayStoreClass; + +typedef enum +{ + GDM_DISPLAY_STORE_ERROR_GENERAL +} GdmDisplayStoreError; + +#define GDM_DISPLAY_STORE_ERROR gdm_display_store_error_quark () + +typedef gboolean (*GdmDisplayStoreFunc) (const char *id, + GdmDisplay *display, + gpointer user_data); + +GQuark gdm_display_store_error_quark (void); +GType gdm_display_store_get_type (void); + +GdmDisplayStore * gdm_display_store_new (void); + +void gdm_display_store_add (GdmDisplayStore *store, + GdmDisplay *display); +void gdm_display_store_clear (GdmDisplayStore *store); +gboolean gdm_display_store_remove (GdmDisplayStore *store, + GdmDisplay *display); +void gdm_display_store_foreach (GdmDisplayStore *store, + GdmDisplayStoreFunc func, + gpointer user_data); +guint gdm_display_store_foreach_remove (GdmDisplayStore *store, + GdmDisplayStoreFunc func, + gpointer user_data); +GdmDisplay * gdm_display_store_find (GdmDisplayStore *store, + GdmDisplayStoreFunc predicate, + gpointer user_data); + + +G_END_DECLS + +#endif /* __GDM_DISPLAY_STORE_H */ diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c new file mode 100644 index 00000000..f48cc232 --- /dev/null +++ b/daemon/gdm-display.c @@ -0,0 +1,546 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gdm-display.h" +#include "gdm-display-glue.h" + +#include "gdm-slave-proxy.h" + +#include "auth.h" + +static guint32 display_serial = 1; + +#define GDM_DISPLAY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DISPLAY, GdmDisplayPrivate)) + +struct GdmDisplayPrivate +{ + char *id; + char *name; + int number; + int status; + time_t creation_time; + char *cookie; + char *binary_cookie; + char *authority_file; + + gboolean is_local; + + GdmSlaveProxy *slave_proxy; + DBusGConnection *connection; +}; + +enum { + PROP_0, + PROP_ID, + PROP_NAME, + PROP_NUMBER, + PROP_COOKIE, + PROP_BINARY_COOKIE, + PROP_AUTHORITY_FILE, + PROP_IS_LOCAL, +}; + +static void gdm_display_class_init (GdmDisplayClass *klass); +static void gdm_display_init (GdmDisplay *display); +static void gdm_display_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (GdmDisplay, gdm_display, G_TYPE_OBJECT) + +GQuark +gdm_display_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_display_error"); + } + + return ret; +} + +static guint32 +get_next_display_serial (void) +{ + guint32 serial; + + serial = display_serial++; + + if ((gint32)display_serial < 0) { + display_serial = 1; + } + + return serial; +} + +char * +gdm_display_get_binary_cookie (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), NULL); + + return g_strdup (display->priv->binary_cookie); +} + +time_t +gdm_display_get_creation_time (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), 0); + + return display->priv->creation_time; +} + +int +gdm_display_get_status (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), 0); + + return display->priv->status; +} + +static gboolean +gdm_display_real_create_authority (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + return TRUE; +} + +gboolean +gdm_display_create_authority (GdmDisplay *display) +{ + gboolean ret; + + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + g_object_ref (display); + ret = GDM_DISPLAY_GET_CLASS (display)->create_authority (display); + g_object_unref (display); + + return ret; +} + +static gboolean +gdm_display_real_manage (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + g_debug ("Manage display"); + + display->priv->status = GDM_DISPLAY_MANAGED; + + g_assert (display->priv->slave_proxy == NULL); + + display->priv->slave_proxy = gdm_slave_proxy_new (display->priv->id); + gdm_slave_proxy_start (display->priv->slave_proxy); + + return TRUE; +} + +gboolean +gdm_display_manage (GdmDisplay *display) +{ + gboolean ret; + + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + g_object_ref (display); + ret = GDM_DISPLAY_GET_CLASS (display)->manage (display); + g_object_unref (display); + + return ret; +} + +static gboolean +gdm_display_real_unmanage (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + display->priv->status = GDM_DISPLAY_UNMANAGED; + + if (display->priv->slave_proxy != NULL) { + gdm_slave_proxy_stop (display->priv->slave_proxy); + + g_object_unref (display->priv->slave_proxy); + display->priv->slave_proxy = NULL; + } + + return TRUE; +} + +gboolean +gdm_display_unmanage (GdmDisplay *display) +{ + gboolean ret; + + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + g_object_ref (display); + ret = GDM_DISPLAY_GET_CLASS (display)->unmanage (display); + g_object_unref (display); + + return ret; +} + +gboolean +gdm_display_get_id (GdmDisplay *display, + char **id, + GError **error) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + if (id != NULL) { + *id = g_strdup (display->priv->id); + } + + return TRUE; +} + +gboolean +gdm_display_get_name (GdmDisplay *display, + char **name, + GError **error) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + if (name != NULL) { + *name = g_strdup (display->priv->name); + } + + return TRUE; +} + +gboolean +gdm_display_get_number (GdmDisplay *display, + int *number, + GError **error) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + if (number != NULL) { + *number = display->priv->number; + } + + return TRUE; +} + +gboolean +gdm_display_is_local (GdmDisplay *display, + gboolean *local, + GError **error) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + if (local != NULL) { + *local = display->priv->is_local; + } + + return TRUE; +} + +static void +_gdm_display_set_id (GdmDisplay *display, + const char *id) +{ + g_free (display->priv->id); + display->priv->id = g_strdup (id); +} + +static void +_gdm_display_set_name (GdmDisplay *display, + const char *name) +{ + g_free (display->priv->name); + display->priv->name = g_strdup (name); +} + +static void +_gdm_display_set_cookie (GdmDisplay *display, + const char *cookie) +{ + g_free (display->priv->cookie); + display->priv->cookie = g_strdup (cookie); +} + +static void +_gdm_display_set_binary_cookie (GdmDisplay *display, + const char *cookie) +{ + g_free (display->priv->binary_cookie); + display->priv->binary_cookie = g_strdup (cookie); +} + +static void +_gdm_display_set_authority_file (GdmDisplay *display, + const char *file) +{ + g_free (display->priv->authority_file); + display->priv->authority_file = g_strdup (file); +} + +static void +_gdm_display_set_number (GdmDisplay *display, + int num) +{ + display->priv->number = num; +} + +static void +_gdm_display_set_is_local (GdmDisplay *display, + gboolean is_local) +{ + display->priv->is_local = is_local; +} + +static void +gdm_display_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmDisplay *self; + + self = GDM_DISPLAY (object); + + switch (prop_id) { + case PROP_ID: + _gdm_display_set_id (self, g_value_get_string (value)); + break; + case PROP_NAME: + _gdm_display_set_name (self, g_value_get_string (value)); + break; + case PROP_COOKIE: + _gdm_display_set_cookie (self, g_value_get_string (value)); + break; + case PROP_BINARY_COOKIE: + _gdm_display_set_binary_cookie (self, g_value_get_string (value)); + break; + case PROP_AUTHORITY_FILE: + _gdm_display_set_authority_file (self, g_value_get_string (value)); + break; + case PROP_NUMBER: + _gdm_display_set_number (self, g_value_get_int (value)); + break; + case PROP_IS_LOCAL: + _gdm_display_set_is_local (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_display_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmDisplay *self; + + self = GDM_DISPLAY (object); + + switch (prop_id) { + case PROP_ID: + g_value_set_string (value, self->priv->id); + break; + case PROP_NAME: + g_value_set_string (value, self->priv->name); + break; + case PROP_COOKIE: + g_value_set_string (value, self->priv->cookie); + break; + case PROP_BINARY_COOKIE: + g_value_set_string (value, self->priv->binary_cookie); + break; + case PROP_AUTHORITY_FILE: + g_value_set_string (value, self->priv->authority_file); + break; + case PROP_NUMBER: + g_value_set_int (value, self->priv->number); + break; + case PROP_IS_LOCAL: + g_value_set_boolean (value, self->priv->is_local); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +register_display (GdmDisplay *display) +{ + GError *error = NULL; + + error = NULL; + display->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (display->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + exit (1); + } + + dbus_g_connection_register_g_object (display->priv->connection, display->priv->id, G_OBJECT (display)); + + return TRUE; +} + +/* + dbus-send --system --print-reply --dest=org.gnome.DisplayManager /org/gnome/DisplayManager/Display1 org.freedesktop.DBus.Introspectable.Introspect +*/ + +static GObject * +gdm_display_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GdmDisplay *display; + GdmDisplayClass *klass; + gboolean res; + + klass = GDM_DISPLAY_CLASS (g_type_class_peek (GDM_TYPE_DISPLAY)); + + display = GDM_DISPLAY (G_OBJECT_CLASS (gdm_display_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + display->priv->id = g_strdup_printf ("/org/gnome/DisplayManager/Display%u", get_next_display_serial ()); + + res = register_display (display); + if (! res) { + g_warning ("Unable to register display with system bus"); + } + + return G_OBJECT (display); +} + +static void +gdm_display_class_init (GdmDisplayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_display_get_property; + object_class->set_property = gdm_display_set_property; + object_class->constructor = gdm_display_constructor; + object_class->finalize = gdm_display_finalize; + + klass->create_authority = gdm_display_real_create_authority; + klass->manage = gdm_display_real_manage; + klass->unmanage = gdm_display_real_unmanage; + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "id", + "id", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_COOKIE, + g_param_spec_string ("cookie", + "cookie", + "cookie", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_BINARY_COOKIE, + g_param_spec_string ("binary-cookie", + "binary-cookie", + "binary-cookie", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_AUTHORITY_FILE, + g_param_spec_string ("authority-file", + "authority file", + "authority file", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_NUMBER, + g_param_spec_int ("number", + "number", + "number", + -1, + G_MAXINT, + -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_IS_LOCAL, + g_param_spec_boolean ("is-local", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GdmDisplayPrivate)); + + dbus_g_object_type_install_info (GDM_TYPE_DISPLAY, &dbus_glib_gdm_display_object_info); +} + +static void +gdm_display_init (GdmDisplay *display) +{ + + display->priv = GDM_DISPLAY_GET_PRIVATE (display); + + display->priv->status = GDM_DISPLAY_UNMANAGED; + display->priv->creation_time = time (NULL); +} + +static void +gdm_display_finalize (GObject *object) +{ + GdmDisplay *display; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_DISPLAY (object)); + + display = GDM_DISPLAY (object); + + g_return_if_fail (display->priv != NULL); + + g_free (display->priv->id); + + G_OBJECT_CLASS (gdm_display_parent_class)->finalize (object); +} diff --git a/daemon/gdm-display.h b/daemon/gdm-display.h new file mode 100644 index 00000000..8dc7ae5e --- /dev/null +++ b/daemon/gdm-display.h @@ -0,0 +1,98 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_DISPLAY_H +#define __GDM_DISPLAY_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_DISPLAY (gdm_display_get_type ()) +#define GDM_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DISPLAY, GdmDisplay)) +#define GDM_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DISPLAY, GdmDisplayClass)) +#define GDM_IS_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DISPLAY)) +#define GDM_IS_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DISPLAY)) +#define GDM_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DISPLAY, GdmDisplayClass)) + +typedef struct GdmDisplayPrivate GdmDisplayPrivate; + +typedef enum { + GDM_DISPLAY_UNMANAGED, + GDM_DISPLAY_MANAGED +} GdmDisplayStatus; + +typedef struct +{ + GObject parent; + GdmDisplayPrivate *priv; +} GdmDisplay; + +typedef struct +{ + GObjectClass parent_class; + + /* methods */ + gboolean (*create_authority) (GdmDisplay *display); + gboolean (*manage) (GdmDisplay *display); + gboolean (*unmanage) (GdmDisplay *display); + +} GdmDisplayClass; + +typedef enum +{ + GDM_DISPLAY_ERROR_GENERAL +} GdmDisplayError; + +#define GDM_DISPLAY_ERROR gdm_display_error_quark () + +GQuark gdm_display_error_quark (void); +GType gdm_display_get_type (void); + +int gdm_display_get_status (GdmDisplay *display); +time_t gdm_display_get_creation_time (GdmDisplay *display); +char * gdm_display_get_cookie (GdmDisplay *display); +char * gdm_display_get_binary_cookie (GdmDisplay *display); +char * gdm_display_get_user_auth (GdmDisplay *display); + +gboolean gdm_display_create_authority (GdmDisplay *display); +gboolean gdm_display_manage (GdmDisplay *display); +gboolean gdm_display_unmanage (GdmDisplay *display); + + +/* exported to bus */ +gboolean gdm_display_get_id (GdmDisplay *display, + char **id, + GError **error); +gboolean gdm_display_get_number (GdmDisplay *display, + int *number, + GError **error); +gboolean gdm_display_get_name (GdmDisplay *display, + char **name, + GError **error); +gboolean gdm_display_is_local (GdmDisplay *display, + gboolean *local, + GError **error); + +G_END_DECLS + +#endif /* __GDM_DISPLAY_H */ diff --git a/daemon/gdm-display.xml b/daemon/gdm-display.xml new file mode 100644 index 00000000..07a27e20 --- /dev/null +++ b/daemon/gdm-display.xml @@ -0,0 +1,17 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.gnome.DisplayManager.Display"> + <method name="GetId"> + <arg name="id" direction="out" type="o"/> + </method> + <method name="GetName"> + <arg name="name" direction="out" type="s"/> + </method> + <method name="GetNumber"> + <arg name="number" direction="out" type="i"/> + </method> + <method name="IsLocal"> + <arg name="local" direction="out" type="b"/> + </method> + </interface> +</node> diff --git a/daemon/gdm-greeter.c b/daemon/gdm-greeter.c new file mode 100644 index 00000000..1c10e847 --- /dev/null +++ b/daemon/gdm-greeter.c @@ -0,0 +1,1147 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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> + +#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) +#include <sched.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include <X11/Xlib.h> /* for Display */ + +#include "gdm-common.h" +#include "filecheck.h" + +#include "gdm-greeter.h" +#include "gdm-socket-protocol.h" + +extern char **environ; + +#define GDM_GREETER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER, GdmGreeterPrivate)) + +struct GdmGreeterPrivate +{ + char *command; + GPid pid; + + char *username; + uid_t uid; + gid_t gid; + int pipe1[2]; + int pipe2[2]; + + int greeter_fd_in; + int greeter_fd_out; + + char *display_name; + char *display_auth_file; + + int user_max_filesize; + + gboolean interrupted; + gboolean always_restart_greeter; +}; + +enum { + PROP_0, + PROP_DISPLAY_NAME, +}; + +static void gdm_greeter_class_init (GdmGreeterClass *klass); +static void gdm_greeter_init (GdmGreeter *greeter); +static void gdm_greeter_finalize (GObject *object); + +G_DEFINE_TYPE (GdmGreeter, gdm_greeter, G_TYPE_OBJECT) + +gboolean +gdm_greeter_stop (GdmGreeter *greeter) +{ + g_debug ("Stopping greeter"); + + return TRUE; +} + +static void +change_user (GdmGreeter *greeter) +{ + if (greeter->priv->uid != 0) { + struct passwd *pwent; + + pwent = getpwuid (greeter->priv->uid); + if (pwent == NULL) { + g_warning (_("%s: Greeter was to be spawned by uid %d but " + "that user doesn't exist"), + "gdm_greeter_spawn", + (int)greeter->priv->uid); + _exit (1); + } + + if (setgid (greeter->priv->gid) < 0) { + g_warning (_("%s: Couldn't set groupid to %d"), + "gdm_greeter_spawn", (int)greeter->priv->gid); + _exit (1); + } + + if (initgroups (pwent->pw_name, pwent->pw_gid) < 0) { + g_warning (_("%s: initgroups () failed for %s"), + "gdm_greeter_spawn", pwent->pw_name); + _exit (1); + } + + if (setuid (greeter->priv->uid) < 0) { + g_warning (_("%s: Couldn't set userid to %d"), + "gdm_greeter_spawn", (int)greeter->priv->uid); + _exit (1); + } + } else { + gid_t groups[1] = { 0 }; + + if (setgid (0) < 0) { + g_warning (_("%s: Couldn't set groupid to 0"), + "gdm_greeter_spawn"); + /* 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 void +greeter_child_setup (GdmGreeter *greeter) +{ + + /* Plumbing */ + VE_IGNORE_EINTR (close (greeter->priv->pipe1[1])); + VE_IGNORE_EINTR (close (greeter->priv->pipe2[0])); + + VE_IGNORE_EINTR (dup2 (greeter->priv->pipe1[0], STDIN_FILENO)); + VE_IGNORE_EINTR (dup2 (greeter->priv->pipe2[1], STDOUT_FILENO)); + + gdm_close_all_descriptors (2 /* from */, + -1 /* except */, + -1 /* except2 */); + + gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ + + change_user (greeter); +} + +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_greeter_environment (GdmGreeter *greeter) +{ + GPtrArray *env; + char **l; + GHashTable *hash; + struct passwd *pwent; + + 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_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (greeter->priv->display_auth_file)); + g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (greeter->priv->display_name)); + +#if 0 + /* hackish ain't it */ + set_xnest_parent_stuff (); +#endif + + g_hash_table_insert (hash, g_strdup ("LOGNAME"), g_strdup (greeter->priv->username)); + g_hash_table_insert (hash, g_strdup ("USER"), g_strdup (greeter->priv->username)); + g_hash_table_insert (hash, g_strdup ("USERNAME"), g_strdup (greeter->priv->username)); + + g_hash_table_insert (hash, g_strdup ("GDM_GREETER_PROTOCOL_VERSION"), g_strdup (GDM_GREETER_PROTOCOL_VERSION)); + g_hash_table_insert (hash, g_strdup ("GDM_VERSION"), g_strdup (VERSION)); + g_hash_table_remove (hash, "MAIL"); + + g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/")); + g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup ("/")); + g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup ("/bin/sh")); + + pwent = getpwnam (greeter->priv->username); + if (pwent != NULL) { + if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') { + g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir)); + g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup (pwent->pw_dir)); + } + + g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell)); + } + +#if 0 + defaultpath = gdm_daemon_config_get_value_string (GDM_KEY_PATH); + if (ve_string_empty (g_getenv ("PATH"))) { + g_setenv ("PATH", defaultpath, TRUE); + } else if ( ! ve_string_empty (defaultpath)) { + gchar *temp_string = g_strconcat (g_getenv ("PATH"), + ":", defaultpath, NULL); + g_setenv ("PATH", temp_string, TRUE); + g_free (temp_string); + } +#endif + + g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true")); + +#if 0 + if ( ! ve_string_empty (d->theme_name)) + g_setenv ("GDM_GTK_THEME", d->theme_name, TRUE); + if (gdm_daemon_config_get_value_bool (GDM_KEY_DEBUG_GESTURES)) { + g_setenv ("G_DEBUG_GESTURES", "true", TRUE); + } +#endif + +#if 0 + if (SERVER_IS_FLEXI (d)) { + g_setenv ("GDM_FLEXI_SERVER", "yes", TRUE); + } else { + g_unsetenv ("GDM_FLEXI_SERVER"); + } +#endif + + + g_hash_table_foreach (hash, (GHFunc)listify_hash, env); + g_hash_table_destroy (hash); + + g_ptr_array_add (env, NULL); + + return env; +} + +static void +gdm_slave_whack_temp_auth_file (GdmGreeter *greeter) +{ +#if 0 + uid_t old; + + old = geteuid (); + if (old != 0) + seteuid (0); + if (d->parent_temp_auth_file != NULL) { + VE_IGNORE_EINTR (g_unlink (d->parent_temp_auth_file)); + } + g_free (d->parent_temp_auth_file); + d->parent_temp_auth_file = NULL; + if (old != 0) + seteuid (old); +#endif +} + + +static void +create_temp_auth_file (GdmGreeter *greeter) +{ +#if 0 + if (d->type == TYPE_FLEXI_XNEST && + d->parent_auth_file != NULL) { + if (d->parent_temp_auth_file != NULL) { + VE_IGNORE_EINTR (g_unlink (d->parent_temp_auth_file)); + } + g_free (d->parent_temp_auth_file); + d->parent_temp_auth_file = + copy_auth_file (d->server_uid, + gdm_daemon_config_get_gdmuid (), + d->parent_auth_file); + } +#endif +} + +static void +whack_greeter_fds (GdmGreeter *greeter) +{ + if (greeter->priv->greeter_fd_out > 0) + VE_IGNORE_EINTR (close (greeter->priv->greeter_fd_out)); + greeter->priv->greeter_fd_out = -1; + if (greeter->priv->greeter_fd_in > 0) + VE_IGNORE_EINTR (close (greeter->priv->greeter_fd_in)); + greeter->priv->greeter_fd_in = -1; +} + +/* return true for "there was an interruption received", + and interrupted will be TRUE if we are actually interrupted from doing what + we want. If FALSE is returned, just continue on as we would normally */ +static gboolean +check_for_interruption (GdmGreeter *greeter, + const char *msg) +{ + /* Hell yeah we were interrupted, the greeter died */ + if (msg == NULL) { + greeter->priv->interrupted = TRUE; + return TRUE; + } + + if (msg[0] == BEL) { + /* Different interruptions come here */ + /* Note that we don't want to actually do anything. We want + * to just set some flag and go on and schedule it after we + * dump out of the login in the main login checking loop */ +#if 0 + switch (msg[1]) { + case GDM_INTERRUPT_TIMED_LOGIN: + /* only allow timed login if display is local, + * it is allowed for this display (it's only allowed + * for the first local display) and if it's set up + * correctly */ + if ((d->attached || gdm_daemon_config_get_value_bool (GDM_KEY_ALLOW_REMOTE_AUTOLOGIN)) + && d->timed_login_ok && + ! ve_string_empty (ParsedTimedLogin) && + strcmp (ParsedTimedLogin, gdm_root_user ()) != 0 && + gdm_daemon_config_get_value_int (GDM_KEY_TIMED_LOGIN_DELAY) > 0) { + do_timed_login = TRUE; + } + break; + case GDM_INTERRUPT_CONFIGURE: + if (d->attached && + gdm_daemon_config_get_value_bool_per_display (GDM_KEY_CONFIG_AVAILABLE, d->name) && + gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, d->name) && + ! ve_string_empty (gdm_daemon_config_get_value_string (GDM_KEY_CONFIGURATOR))) { + do_configurator = TRUE; + } + break; + case GDM_INTERRUPT_SUSPEND: + if (d->attached && + gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, d->name) && + ! ve_string_empty (gdm_daemon_config_get_value_string (GDM_KEY_SUSPEND))) { + gchar *msg = g_strdup_printf ("%s %ld", + GDM_SOP_SUSPEND_MACHINE, + (long)getpid ()); + + gdm_slave_send (msg, FALSE /* wait_for_ack */); + g_free (msg); + } + /* Not interrupted, continue reading input, + * just proxy this to the master server */ + return TRUE; + case GDM_INTERRUPT_LOGIN_SOUND: + if (d->attached && + ! play_login_sound (gdm_daemon_config_get_value_string (GDM_KEY_SOUND_ON_LOGIN_FILE))) { + gdm_error (_("Login sound requested on non-local display or the play software " + "cannot be run or the sound does not exist")); + } + return TRUE; + case GDM_INTERRUPT_SELECT_USER: + gdm_verify_select_user (&msg[2]); + break; + case GDM_INTERRUPT_CANCEL: + do_cancel = TRUE; + break; + case GDM_INTERRUPT_CUSTOM_CMD: + if (d->attached && + ! ve_string_empty (&msg[2])) { + gchar *message = g_strdup_printf ("%s %ld %s", + GDM_SOP_CUSTOM_CMD, + (long)getpid (), &msg[2]); + + gdm_slave_send (message, TRUE); + g_free (message); + } + return TRUE; + case GDM_INTERRUPT_THEME: + g_free (d->theme_name); + d->theme_name = NULL; + if ( ! ve_string_empty (&msg[2])) + d->theme_name = g_strdup (&msg[2]); + gdm_slave_send_string (GDM_SOP_CHOSEN_THEME, &msg[2]); + return TRUE; + case GDM_INTERRUPT_SELECT_LANG: + if (msg + 2) { + const char *locale; + const char *gdm_system_locale; + + locale = (gchar*)(msg + 3); + gdm_system_locale = setlocale (LC_CTYPE, NULL); + + greeter->priv->always_restart_greeter = (gboolean)(*(msg + 2)); + ve_clearenv (); + if (!strcmp (locale, DEFAULT_LANGUAGE)) { + locale = gdm_system_locale; + } + g_setenv ("GDM_LANG", locale, TRUE); + g_setenv ("LANG", locale, TRUE); + g_unsetenv ("LC_ALL"); + g_unsetenv ("LC_MESSAGES"); + setlocale (LC_ALL, ""); + setlocale (LC_MESSAGES, ""); + gdm_saveenv (); + + do_restart_greeter = TRUE; + } + break; + default: + break; + } +#endif + /* this was an interruption, if it wasn't + * handled then the user will just get an error as if he + * entered an invalid login or passward. Seriously BEL + * cannot be part of a login/password really */ + greeter->priv->interrupted = TRUE; + return TRUE; + } + return FALSE; +} + +static char * +gdm_slave_greeter_ctl (GdmGreeter *greeter, + char cmd, + const char *str) +{ + char *buf = NULL; + int c; + + if ( ! ve_string_empty (str)) { + gdm_fdprintf (greeter->priv->greeter_fd_out, "%c%c%s\n", STX, cmd, str); + } else { + gdm_fdprintf (greeter->priv->greeter_fd_out, "%c%c\n", STX, cmd); + } + +#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) + /* let the other process (greeter) do its stuff */ + sched_yield (); +#endif + + do { + g_free (buf); + buf = NULL; + /* Skip random junk that might have accumulated */ + do { + c = gdm_fdgetc (greeter->priv->greeter_fd_in); + } while (c != EOF && c != STX); + + if (c == EOF || + (buf = gdm_fdgets (greeter->priv->greeter_fd_in)) == NULL) { + greeter->priv->interrupted = TRUE; + /* things don't seem well with the greeter, it probably died */ + return NULL; + } + } while (check_for_interruption (greeter, buf) && ! greeter->priv->interrupted); + + if ( ! ve_string_empty (buf)) { + return buf; + } else { + g_free (buf); + return NULL; + } +} + +static void +gdm_slave_greeter_ctl_no_ret (GdmGreeter *greeter, + char cmd, + const char *str) +{ + g_free (gdm_slave_greeter_ctl (greeter, cmd, str)); +} +/** + * check_user_file + * check_global_file + * is_in_trusted_pic_dir + * get_facefile_from_gnome2_dir_config + * path_is_local + * gdm_daemon_config_get_facefile_from_home + * gdm_daemon_config_get_facefile_from_global + * + * These functions are used for accessing the user's face image from their + * home directory. + */ +static gboolean +check_user_file (const char *path, + guint uid) +{ + char *dir; + char *file; + gboolean is_ok; + + if (path == NULL) + return FALSE; + + if (g_access (path, R_OK) != 0) + return FALSE; + + dir = g_path_get_dirname (path); + file = g_path_get_basename (path); + + is_ok = gdm_file_check ("run_pictures", + uid, + dir, + file, + TRUE, TRUE, + 0, + 0); + g_free (dir); + g_free (file); + + return is_ok; +} + +static gboolean +check_global_file (const char *path, + guint uid) +{ + if (path == NULL) + return FALSE; + + if (g_access (path, R_OK) != 0) + return FALSE; + + return TRUE; +} + +/* If path starts with a "trusted" directory, don't sanity check things */ +/* This is really somewhat "outdated" as we now really want things in + * the picture dir or in ~/.gnome2/photo */ +static gboolean +is_in_trusted_pic_dir (const char *path) +{ + /* our own pixmap dir is trusted */ + if (strncmp (path, PIXMAPDIR, sizeof (PIXMAPDIR)) == 0) + return TRUE; + + return FALSE; +} + +static gchar * +get_facefile_from_gnome2_dir_config (const char *homedir, + guint uid) +{ + char *picfile = NULL; + char *cfgdir; + + /* Sanity check on ~user/.gnome2/gdm */ + cfgdir = g_build_filename (homedir, ".gnome2", "gdm", NULL); + if (G_LIKELY (check_user_file (cfgdir, uid))) { + GKeyFile *cfg; + char *cfgfile; + + cfgfile = g_build_filename (homedir, ".gnome2", "gdm", NULL); + cfg = gdm_common_config_load (cfgfile, NULL); + g_free (cfgfile); + + if (cfg != NULL) { + gdm_common_config_get_string (cfg, "face/picture=", &picfile, NULL); + g_key_file_free (cfg); + } + + /* must exist and be absolute (note that this check + * catches empty strings)*/ + /* Note that these days we just set ~/.face */ + if G_UNLIKELY (picfile != NULL && + (picfile[0] != '/' || + /* this catches readability by user */ + g_access (picfile, R_OK) != 0)) { + g_free (picfile); + picfile = NULL; + } + + if (picfile != NULL) { + char buf[PATH_MAX]; + if (realpath (picfile, buf) == NULL) { + g_free (picfile); + picfile = NULL; + } else { + g_free (picfile); + picfile = g_strdup (buf); + } + } + + if G_UNLIKELY (picfile != NULL) { + if (! is_in_trusted_pic_dir (picfile)) { + /* if not in trusted dir, check it out */ + + /* Note that strict permissions checking is done + * on this file. Even if it may not even be owned by the + * user. This setting should ONLY point to pics in trusted + * dirs. */ + if (! check_user_file (picfile, uid)) { + g_free (picfile); + picfile = NULL; + } + } + } + } + g_free (cfgdir); + + return picfile; +} + +static GHashTable *fstype_hash = NULL; +extern char *filesystem_type (char *path, char *relpath, struct stat *statp); + +static gboolean +path_is_local (const char *path) +{ + gpointer local = NULL; + + if (path == NULL) + return FALSE; + + if (fstype_hash == NULL) + fstype_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + else + local = g_hash_table_lookup (fstype_hash, path); + + if (local == NULL) { + struct stat statbuf; + + if (g_stat (path, &statbuf) == 0) { + char *type = filesystem_type ((char *)path, (char *)path, &statbuf); + gboolean is_local = ((strcmp (ve_sure_string (type), "nfs") != 0) && + (strcmp (ve_sure_string (type), "afs") != 0) && + (strcmp (ve_sure_string (type), "autofs") != 0) && + (strcmp (ve_sure_string (type), "unknown") != 0) && + (strcmp (ve_sure_string (type), "ncpfs") != 0)); + local = GINT_TO_POINTER (is_local ? 1 : -1); + g_hash_table_insert (fstype_hash, g_strdup (path), local); + } + } + + return GPOINTER_TO_INT (local) > 0; +} + +static char * +gdm_daemon_config_get_facefile_from_home (const char *homedir, + guint uid) +{ + char *picfile = NULL; + char *path; + gboolean is_local; + + /* special case: look at parent of home to detect autofs + this is so we don't try to trigger an automount */ + path = g_path_get_dirname (homedir); + is_local = path_is_local (path); + g_free (path); + + /* now check that home dir itself is local */ + if (is_local) { + is_local = path_is_local (homedir); + } + + /* Only look at local home directories so we don't try to + read from remote (e.g. NFS) volumes */ + if (! is_local) + return NULL; + + picfile = g_build_filename (homedir, ".face", NULL); + + if (check_user_file (picfile, uid)) + return picfile; + else { + g_free (picfile); + picfile = NULL; + } + + picfile = g_build_filename (homedir, ".face.icon", NULL); + + if (check_user_file (picfile, uid)) + return picfile; + else { + g_free (picfile); + picfile = NULL; + } + + picfile = get_facefile_from_gnome2_dir_config (homedir, uid); + if (check_user_file (picfile, uid)) + return picfile; + else { + g_free (picfile); + picfile = NULL; + } + + /* Nothing found yet, try the old locations */ + + picfile = g_build_filename (homedir, ".gnome2", "photo", NULL); + if (check_user_file (picfile, uid)) + return picfile; + else { + g_free (picfile); + picfile = NULL; + } + + picfile = g_build_filename (homedir, ".gnome", "photo", NULL); + if (check_user_file (picfile, uid)) + return picfile; + else { + g_free (picfile); + picfile = NULL; + } + + return NULL; +} + +static char * +gdm_daemon_config_get_facefile_from_global (const char *username, + guint uid) +{ +#if 0 + char *picfile = NULL; + const char *facedir; + + facedir = gdm_daemon_config_get_value_string (GDM_KEY_GLOBAL_FACE_DIR); + + /* Try the global face directory */ + + picfile = g_build_filename (facedir, username, NULL); + + if (check_global_file (picfile, uid)) + return picfile; + + g_free (picfile); + picfile = gdm_make_filename (facedir, username, ".png"); + + if (check_global_file (picfile, uid)) + return picfile; + + g_free (picfile); +#endif + return NULL; +} + +static void +gdm_slave_quick_exit (GdmGreeter *greeter) +{ + /* FIXME */ + _exit (1); +} + +/* This is VERY evil! */ +static void +run_pictures (GdmGreeter *greeter) +{ + char *response; + int max_write; + char buf[1024]; + size_t bytes; + struct passwd *pwent; + char *picfile; + FILE *fp; + + response = NULL; + for (;;) { + struct stat s; + char *tmp, *ret; + int i, r; + + g_free (response); + response = gdm_slave_greeter_ctl (greeter, GDM_NEEDPIC, ""); + if (ve_string_empty (response)) { + g_free (response); + return; + } + + pwent = getpwnam (response); + if G_UNLIKELY (pwent == NULL) { + gdm_slave_greeter_ctl_no_ret (greeter, GDM_READPIC, ""); + continue; + } + + picfile = NULL; + + NEVER_FAILS_seteuid (0); + if G_UNLIKELY (setegid (pwent->pw_gid) != 0 || + seteuid (pwent->pw_uid) != 0) { + gdm_slave_greeter_ctl_no_ret (greeter, GDM_READPIC, ""); + continue; + } + + picfile = gdm_daemon_config_get_facefile_from_home (pwent->pw_dir, pwent->pw_uid); + + if (! picfile) + picfile = gdm_daemon_config_get_facefile_from_global (pwent->pw_name, pwent->pw_uid); + + if (! picfile) { + gdm_slave_greeter_ctl_no_ret (greeter, GDM_READPIC, ""); + continue; + } + + VE_IGNORE_EINTR (r = g_stat (picfile, &s)); + if G_UNLIKELY (r < 0 || s.st_size > greeter->priv->user_max_filesize) { + gdm_slave_greeter_ctl_no_ret (greeter, GDM_READPIC, ""); + continue; + } + + VE_IGNORE_EINTR (fp = fopen (picfile, "r")); + g_free (picfile); + if G_UNLIKELY (fp == NULL) { + gdm_slave_greeter_ctl_no_ret (greeter, GDM_READPIC, ""); + continue; + } + + tmp = g_strdup_printf ("buffer:%d", (int)s.st_size); + ret = gdm_slave_greeter_ctl (greeter, GDM_READPIC, tmp); + g_free (tmp); + + if G_UNLIKELY (ret == NULL || strcmp (ret, "OK") != 0) { + VE_IGNORE_EINTR (fclose (fp)); + g_free (ret); + continue; + } + g_free (ret); + + gdm_fdprintf (greeter->priv->greeter_fd_out, "%c", STX); + +#ifdef PIPE_BUF + max_write = MIN (PIPE_BUF, sizeof (buf)); +#else + /* apparently Hurd doesn't have PIPE_BUF */ + max_write = fpathconf (greeter->priv->greeter_fd_out, _PC_PIPE_BUF); + /* could return -1 if no limit */ + if (max_write > 0) + max_write = MIN (max_write, sizeof (buf)); + else + max_write = sizeof (buf); +#endif + + i = 0; + while (i < s.st_size) { + int written; + + VE_IGNORE_EINTR (bytes = fread (buf, sizeof (char), + max_write, fp)); + + if (bytes <= 0) + break; + + if G_UNLIKELY (i + bytes > s.st_size) + bytes = s.st_size - i; + + /* write until we succeed in writing something */ + VE_IGNORE_EINTR (written = write (greeter->priv->greeter_fd_out, buf, bytes)); + if G_UNLIKELY (written < 0 && + (errno == EPIPE || errno == EBADF)) { + /* something very, very bad has happened */ + + gdm_slave_quick_exit (greeter); + } + + if G_UNLIKELY (written < 0) + written = 0; + + /* write until we succeed in writing everything */ + while (written < bytes) { + int n; + VE_IGNORE_EINTR (n = write (greeter->priv->greeter_fd_out, &buf[written], bytes-written)); + if G_UNLIKELY (n < 0 && + (errno == EPIPE || errno == EBADF)) { + + /* something very, very bad has happened */ + gdm_slave_quick_exit (greeter); + + } else if G_LIKELY (n > 0) { + written += n; + } + } + + /* we have written bytes bytes if it likes it or not */ + i += bytes; + } + + VE_IGNORE_EINTR (fclose (fp)); + + /* eek, this "could" happen, so just send some garbage */ + while G_UNLIKELY (i < s.st_size) { + bytes = MIN (sizeof (buf), s.st_size - i); + errno = 0; + bytes = write (greeter->priv->greeter_fd_out, buf, bytes); + if G_UNLIKELY (bytes < 0 && (errno == EPIPE || errno == EBADF)) { + + /* something very, very bad has happened */ + gdm_slave_quick_exit (greeter); + + } + if (bytes > 0) + i += bytes; + } + + gdm_slave_greeter_ctl_no_ret (greeter, GDM_READPIC, "done"); + + } + + g_free (response); /* not reached */ +} + +static gboolean +gdm_greeter_spawn (GdmGreeter *greeter) +{ + gchar **argv; + GError *error; + GPtrArray *env; + gboolean ret; + + ret = FALSE; + + /* Open a pipe for greeter communications */ + if (pipe (greeter->priv->pipe1) < 0) { + g_warning ("Can't init pipe to gdmgreeter"); + exit (1); + } + + if (pipe (greeter->priv->pipe2) < 0) { + VE_IGNORE_EINTR (close (greeter->priv->pipe1[0])); + VE_IGNORE_EINTR (close (greeter->priv->pipe1[1])); + g_warning ("Can't init pipe to gdmgreeter"); + exit (1); + } + + create_temp_auth_file (greeter); + + g_debug ("Running greeter process: %s", greeter->priv->command); + + argv = NULL; + if (! g_shell_parse_argv (greeter->priv->command, NULL, &argv, &error)) { + g_warning ("Could not parse command: %s", error->message); + g_error_free (error); + goto out; + } + + env = get_greeter_environment (greeter); + + error = NULL; + ret = g_spawn_async_with_pipes (NULL, + argv, + (char **)env->pdata, + G_SPAWN_SEARCH_PATH | G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD, + (GSpawnChildSetupFunc)greeter_child_setup, + greeter, + &greeter->priv->pid, + NULL, + NULL, + NULL, + &error); + + g_ptr_array_foreach (env, (GFunc)g_free, NULL); + g_ptr_array_free (env, TRUE); + + if (! ret) { + g_warning ("Could not start command '%s': %s", + greeter->priv->command, + error->message); + g_error_free (error); + } else { + + whack_greeter_fds (greeter); + + greeter->priv->greeter_fd_out = greeter->priv->pipe1[1]; + greeter->priv->greeter_fd_in = greeter->priv->pipe2[0]; + + g_debug ("gdm_slave_greeter: Greeter on pid %d", (int)greeter->priv->pid); + } + + + g_strfreev (argv); + out: + + return ret; +} + +/** + * gdm_greeter_start: + * @disp: Pointer to a GdmDisplay structure + * + * Starts a local X greeter. Handles retries and fatal errors properly. + */ + +gboolean +gdm_greeter_start (GdmGreeter *greeter) +{ + gboolean res; + const char *gdmlang; + + res = gdm_greeter_spawn (greeter); + + if (res) { + run_pictures (greeter); /* Append pictures to greeter if browsing is on */ + + if (greeter->priv->always_restart_greeter) + gdm_slave_greeter_ctl_no_ret (greeter, GDM_ALWAYS_RESTART, "Y"); + else + gdm_slave_greeter_ctl_no_ret (greeter, GDM_ALWAYS_RESTART, "N"); + + gdmlang = g_getenv ("GDM_LANG"); + if (gdmlang) + gdm_slave_greeter_ctl_no_ret (greeter, GDM_SETLANG, gdmlang); + } + + + return res; +} + +static void +_gdm_greeter_set_display_name (GdmGreeter *greeter, + const char *name) +{ + g_free (greeter->priv->display_name); + greeter->priv->display_name = g_strdup (name); +} + +static void +gdm_greeter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmGreeter *self; + + self = GDM_GREETER (object); + + switch (prop_id) { + case PROP_DISPLAY_NAME: + _gdm_greeter_set_display_name (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_greeter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmGreeter *self; + + self = GDM_GREETER (object); + + switch (prop_id) { + case PROP_DISPLAY_NAME: + g_value_set_string (value, self->priv->display_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +gdm_greeter_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GdmGreeter *greeter; + GdmGreeterClass *klass; + struct passwd *pwent; + + klass = GDM_GREETER_CLASS (g_type_class_peek (GDM_TYPE_GREETER)); + + greeter = GDM_GREETER (G_OBJECT_CLASS (gdm_greeter_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + pwent = getpwuid (greeter->priv->uid); + if (pwent != NULL) { + greeter->priv->username = g_strdup (pwent->pw_name); + } + + return G_OBJECT (greeter); +} + +static void +gdm_greeter_class_init (GdmGreeterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_greeter_get_property; + object_class->set_property = gdm_greeter_set_property; + object_class->constructor = gdm_greeter_constructor; + object_class->finalize = gdm_greeter_finalize; + + g_type_class_add_private (klass, sizeof (GdmGreeterPrivate)); + + 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)); +} + +static void +gdm_greeter_init (GdmGreeter *greeter) +{ + + greeter->priv = GDM_GREETER_GET_PRIVATE (greeter); + + greeter->priv->pid = -1; + + greeter->priv->command = g_strdup (LIBEXECDIR "/gdmgreeter"); + greeter->priv->user_max_filesize = 65536; +} + +static void +gdm_greeter_finalize (GObject *object) +{ + GdmGreeter *greeter; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_GREETER (object)); + + greeter = GDM_GREETER (object); + + g_return_if_fail (greeter->priv != NULL); + + G_OBJECT_CLASS (gdm_greeter_parent_class)->finalize (object); +} + +GdmGreeter * +gdm_greeter_new (const char *display_name) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_GREETER, + "display-name", display_name, + NULL); + + return GDM_GREETER (object); +} diff --git a/daemon/gdm-greeter.h b/daemon/gdm-greeter.h new file mode 100644 index 00000000..8a000342 --- /dev/null +++ b/daemon/gdm-greeter.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_GREETER_H +#define __GDM_GREETER_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_GREETER (gdm_greeter_get_type ()) +#define GDM_GREETER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_GREETER, GdmGreeter)) +#define GDM_GREETER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_GREETER, GdmGreeterClass)) +#define GDM_IS_GREETER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_GREETER)) +#define GDM_IS_GREETER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_GREETER)) +#define GDM_GREETER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_GREETER, GdmGreeterClass)) + +typedef struct GdmGreeterPrivate GdmGreeterPrivate; + +typedef struct +{ + GObject parent; + GdmGreeterPrivate *priv; +} GdmGreeter; + +typedef struct +{ + GObjectClass parent_class; + +} GdmGreeterClass; + +GType gdm_greeter_get_type (void); +GdmGreeter * gdm_greeter_new (const char *display_id); +gboolean gdm_greeter_start (GdmGreeter *greeter); +gboolean gdm_greeter_stop (GdmGreeter *greeter); + +G_END_DECLS + +#endif /* __GDM_GREETER_H */ diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c new file mode 100644 index 00000000..63b5ca43 --- /dev/null +++ b/daemon/gdm-manager.c @@ -0,0 +1,433 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "gdm-manager.h" +#include "gdm-manager-glue.h" +#include "gdm-display-store.h" +#include "gdm-xdmcp-manager.h" +#include "gdm-common.h" + +#include "gdm-static-display.h" + +#include "gdm-master-config.h" +#include "gdm-daemon-config-entries.h" + +#include "cookie.h" + +#define GDM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_MANAGER, GdmManagerPrivate)) + +#define GDM_DBUS_PATH "/org/gnome/DisplayManager" +#define GDM_MANAGER_DBUS_PATH GDM_DBUS_PATH "/Manager" +#define GDM_MANAGER_DBUS_NAME "org.gnome.DisplayManager.Manager" + +struct GdmManagerPrivate +{ + GdmDisplayStore *display_store; + GdmDaemonConfig *daemon_config; + GdmXdmcpManager *xdmcp_manager; + + gboolean xdmcp_enabled; + + char *global_cookie; + gboolean wait_for_go; + gboolean no_console; + + DBusGProxy *bus_proxy; + DBusGConnection *connection; +}; + +static void gdm_manager_class_init (GdmManagerClass *klass); +static void gdm_manager_init (GdmManager *manager); +static void gdm_manager_finalize (GObject *object); + +static gpointer manager_object = NULL; + +G_DEFINE_TYPE (GdmManager, gdm_manager, G_TYPE_OBJECT) + +GQuark +gdm_manager_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_manager_error"); + } + + return ret; +} + +static gboolean +listify_display_ids (const char *id, + GdmDisplay *display, + GPtrArray **array) +{ + g_ptr_array_add (*array, g_strdup (id)); + + /* return FALSE to continue */ + return FALSE; +} + +/* + Example: + dbus-send --system --dest=org.gnome.DisplayManager \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/gnome/DisplayManager/Manager \ + org.gnome.DisplayManager.Manager.GetDisplays +*/ +gboolean +gdm_manager_get_displays (GdmManager *manager, + GPtrArray **displays, + GError **error) +{ + g_return_val_if_fail (GDM_IS_MANAGER (manager), FALSE); + + if (displays == NULL) { + return FALSE; + } + + *displays = g_ptr_array_new (); + gdm_display_store_foreach (manager->priv->display_store, + (GdmDisplayStoreFunc)listify_display_ids, + displays); + + return TRUE; +} + +static gboolean +start_local_display (const char *id, + GdmDisplay *d, + GdmManager *manager) +{ + gboolean ret; + + ret = TRUE; + + g_assert (d != NULL); + + if (GDM_IS_STATIC_DISPLAY (d) && + gdm_display_get_status (d) == GDM_DISPLAY_UNMANAGED) { + if (! gdm_display_manage (d)) { + gdm_display_unmanage (d); + } else { + ret = FALSE; + } + } + + return ret; +} + +static void +start_unborn_local_displays (GdmManager *manager) +{ + gdm_display_store_foreach (manager->priv->display_store, + (GdmDisplayStoreFunc)start_local_display, + manager); +} + +static void +make_global_cookie (GdmManager *manager) +{ + FILE *fp; + char *file; + + gdm_cookie_generate ((char **)&manager->priv->global_cookie, NULL); + + file = g_build_filename (AUTHDIR, ".cookie", NULL); + VE_IGNORE_EINTR (g_unlink (file)); + + fp = gdm_safe_fopen_w (file, 077); + if G_UNLIKELY (fp == NULL) { + g_warning (_("Can't open %s for writing"), file); + g_free (file); + return; + } + + VE_IGNORE_EINTR (fprintf (fp, "%s\n", manager->priv->global_cookie)); + + /* FIXME: What about out of disk space errors? */ + errno = 0; + VE_IGNORE_EINTR (fclose (fp)); + if G_UNLIKELY (errno != 0) { + g_warning (_("Can't write to %s: %s"), + file, + g_strerror (errno)); + } + + g_free (file); +} + +static void +load_static_displays_from_file (GdmManager *manager) +{ + GSList *xservers; + GSList *l; + + xservers = gdm_daemon_config_get_xserver_list (manager->priv->daemon_config); + + for (l = xservers; l != NULL; l = l->next) { + GdmXserver *xserver; + GdmDisplay *display; + + xserver = l->data; + + g_debug ("Loading display for '%d' %s", xserver->number, xserver->id); + + display = gdm_static_display_new (xserver->number, + xserver->id); + + if (display == NULL) { + g_warning ("Unable to create display: %d %s", xserver->number, xserver->id); + continue; + } + + gdm_display_store_add (manager->priv->display_store, display); + } +} + +static void +load_static_servers (GdmManager *manager) +{ + + load_static_displays_from_file (manager); +} + +void +gdm_manager_start (GdmManager *manager) +{ + load_static_servers (manager); + + /* Start static X servers */ + start_unborn_local_displays (manager); + + /* Accept remote connections */ + if (manager->priv->xdmcp_enabled && ! manager->priv->wait_for_go) { + if (manager->priv->xdmcp_manager != NULL) { + g_debug ("Accepting XDMCP connections..."); + gdm_xdmcp_manager_start (manager->priv->xdmcp_manager, NULL); + } + } + +} + +void +gdm_manager_set_wait_for_go (GdmManager *manager, + gboolean wait_for_go) +{ + if (manager->priv->wait_for_go != wait_for_go) { + manager->priv->wait_for_go = wait_for_go; + + if (! wait_for_go) { + /* we got a go */ + + if (manager->priv->xdmcp_enabled && manager->priv->xdmcp_manager != NULL) { + g_debug ("Accepting XDMCP connections..."); + gdm_xdmcp_manager_start (manager->priv->xdmcp_manager, NULL); + } + } + } +} + +typedef struct { + const char *service_name; + GdmManager *manager; +} RemoveDisplayData; + +static gboolean +remove_display_for_connection (GdmDisplay *display, + RemoveDisplayData *data) +{ + g_assert (display != NULL); + g_assert (data->service_name != NULL); + + /* FIXME: compare service name to that of display */ +#if 0 + if (strcmp (info->service_name, data->service_name) == 0) { + remove_session_for_cookie (data->manager, cookie, NULL); + leader_info_cancel (info); + return TRUE; + } +#endif + + return FALSE; +} + +static void +remove_displays_for_connection (GdmManager *manager, + const char *service_name) +{ + RemoveDisplayData data; + + data.service_name = service_name; + data.manager = manager; + + g_debug ("Removing display for service name: %s", service_name); + + gdm_display_store_foreach_remove (manager->priv->display_store, + (GdmDisplayStoreFunc)remove_display_for_connection, + &data); +} + +static void +bus_name_owner_changed (DBusGProxy *bus_proxy, + const char *service_name, + const char *old_service_name, + const char *new_service_name, + GdmManager *manager) +{ + if (strlen (new_service_name) == 0) { + remove_displays_for_connection (manager, old_service_name); + } + + g_debug ("NameOwnerChanged: service_name='%s', old_service_name='%s' new_service_name='%s'", + service_name, old_service_name, new_service_name); +} + +static gboolean +register_manager (GdmManager *manager) +{ + GError *error = NULL; + + error = NULL; + manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (manager->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + exit (1); + } + + manager->priv->bus_proxy = dbus_g_proxy_new_for_name (manager->priv->connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + dbus_g_proxy_add_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (bus_name_owner_changed), + manager, + NULL); + + dbus_g_connection_register_g_object (manager->priv->connection, GDM_MANAGER_DBUS_PATH, G_OBJECT (manager)); + + return TRUE; +} + +static void +gdm_manager_class_init (GdmManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdm_manager_finalize; + + g_type_class_add_private (klass, sizeof (GdmManagerPrivate)); + + dbus_g_object_type_install_info (GDM_TYPE_MANAGER, &dbus_glib_gdm_manager_object_info); +} + +static void +gdm_manager_init (GdmManager *manager) +{ + + manager->priv = GDM_MANAGER_GET_PRIVATE (manager); + + manager->priv->daemon_config = gdm_daemon_config_new (); + + make_global_cookie (manager); + + gdm_daemon_config_get_bool_for_id (manager->priv->daemon_config, + GDM_ID_XDMCP, + &manager->priv->xdmcp_enabled); + + manager->priv->display_store = gdm_display_store_new (); + + if (manager->priv->xdmcp_enabled) { + manager->priv->xdmcp_manager = gdm_xdmcp_manager_new (manager->priv->display_store); + } +} + +static void +gdm_manager_finalize (GObject *object) +{ + GdmManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_MANAGER (object)); + + manager = GDM_MANAGER (object); + + g_return_if_fail (manager->priv != NULL); + + if (manager->priv->xdmcp_manager != NULL) { + g_object_unref (manager->priv->xdmcp_manager); + } + + gdm_display_store_clear (manager->priv->display_store); + g_object_unref (manager->priv->display_store); + + if (manager->priv->daemon_config != NULL) { + g_object_unref (manager->priv->daemon_config); + } + + G_OBJECT_CLASS (gdm_manager_parent_class)->finalize (object); +} + +GdmManager * +gdm_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + gboolean res; + + manager_object = g_object_new (GDM_TYPE_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + res = register_manager (manager_object); + if (! res) { + g_object_unref (manager_object); + return NULL; + } + } + + return GDM_MANAGER (manager_object); +} diff --git a/daemon/gdm-manager.h b/daemon/gdm-manager.h new file mode 100644 index 00000000..5dd091d9 --- /dev/null +++ b/daemon/gdm-manager.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_MANAGER_H +#define __GDM_MANAGER_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_MANAGER (gdm_manager_get_type ()) +#define GDM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_MANAGER, GdmManager)) +#define GDM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_MANAGER, GdmManagerClass)) +#define GDM_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_MANAGER)) +#define GDM_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_MANAGER)) +#define GDM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_MANAGER, GdmManagerClass)) + +typedef struct GdmManagerPrivate GdmManagerPrivate; + +typedef struct +{ + GObject parent; + GdmManagerPrivate *priv; +} GdmManager; + +typedef struct +{ + GObjectClass parent_class; +} GdmManagerClass; + +typedef enum +{ + GDM_MANAGER_ERROR_GENERAL +} GdmManagerError; + +#define GDM_MANAGER_ERROR gdm_manager_error_quark () + +GQuark gdm_manager_error_quark (void); +GType gdm_manager_get_type (void); + +GdmManager * gdm_manager_new (void); +void gdm_manager_start (GdmManager *manager); +void gdm_manager_set_wait_for_go (GdmManager *manager, + gboolean wait_for_go); + +gboolean gdm_manager_get_displays (GdmManager *manager, + GPtrArray **displays, + GError **error); + + +G_END_DECLS + +#endif /* __GDM_MANAGER_H */ diff --git a/daemon/gdm-manager.xml b/daemon/gdm-manager.xml new file mode 100644 index 00000000..c59834e7 --- /dev/null +++ b/daemon/gdm-manager.xml @@ -0,0 +1,14 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node name="/org/gnome/DisplayManager/Manager"> + <interface name="org.gnome.DisplayManager.Manager"> + <method name="GetDisplays"> + <arg name="displays" direction="out" type="ao"/> + </method> + <signal name="DisplayAdded"> + <arg name="id" type="o"/> + </signal> + <signal name="DisplayRemoved"> + <arg name="id" type="o"/> + </signal> + </interface> +</node> diff --git a/daemon/gdm-master-config.c b/daemon/gdm-master-config.c new file mode 100644 index 00000000..77278c71 --- /dev/null +++ b/daemon/gdm-master-config.c @@ -0,0 +1,941 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/resource.h> /* for PRIO_MIN */ +#include <ctype.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "gdm-config.h" +#include "gdm-master-config.h" +#include "gdm-daemon-config-entries.h" + +#define GDM_STANDARD "Standard" + +/* PRIO_MIN and PRIO_MAX are not defined on Solaris, but are -20 and 20 */ +#if sun +#ifndef PRIO_MIN +#define PRIO_MIN -20 +#endif +#ifndef PRIO_MAX +#define PRIO_MAX 20 +#endif +#endif + +#define GDM_DAEMON_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DAEMON_CONFIG, GdmDaemonConfigPrivate)) + +struct GdmDaemonConfigPrivate +{ + GdmConfig *config; + GSList *xservers; + + char *default_file; + char *custom_file; +}; + +static void gdm_daemon_config_class_init (GdmDaemonConfigClass *klass); +static void gdm_daemon_config_init (GdmDaemonConfig *daemon_config); +static void gdm_daemon_config_finalize (GObject *object); + +static gpointer daemon_config_object = NULL; + +G_DEFINE_TYPE (GdmDaemonConfig, gdm_daemon_config, G_TYPE_OBJECT) + +GQuark +gdm_daemon_config_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_daemon_config_error"); + } + + return ret; +} + +/* + * gdm_read_default + * + * This function is used to support systems that have the /etc/default/login + * interface to control programs that affect security. This is a Solaris + * thing, though some users on other systems may find it useful. + */ +static char * +gdm_read_default (char *key) +{ +#ifdef HAVE_DEFOPEN + gchar *retval = NULL; + + if (defopen ("/etc/default/login") == 0) { + int flags = defcntl (DC_GETFLAGS, 0); + + TURNOFF (flags, DC_CASE); + (void) defcntl (DC_SETFLAGS, flags); /* ignore case */ + retval = g_strdup (defread (key)); + (void) defopen ((char *)NULL); + } + return retval; +#else + return NULL; +#endif +} + +/** + * gdm_daemon_config_get_bool_for_id + * + * Gets a boolean configuration option by ID. The option must + * first be loaded, say, by calling gdm_daemon_config_parse. + */ +gboolean +gdm_daemon_config_get_bool_for_id (GdmDaemonConfig *config, + int id, + gboolean *val) +{ + return gdm_config_get_bool_for_id (config->priv->config, id, val); +} + +/** + * gdm_daemon_config_get_int_for_id + * + * Gets a integer configuration option by ID. The option must + * first be loaded, say, by calling gdm_daemon_config_parse. + */ +gboolean +gdm_daemon_config_get_int_for_id (GdmDaemonConfig *config, + int id, + int *val) +{ + return gdm_config_get_int_for_id (config->priv->config, id, val); +} + +/** + * gdm_daemon_config_get_string_for_id + * + * Gets a string configuration option by ID. The option must + * first be loaded, say, by calling gdm_daemon_config_parse. + */ +gboolean +gdm_daemon_config_get_string_for_id (GdmDaemonConfig *config, + int id, + char **val) +{ + return gdm_config_get_string_for_id (config->priv->config, id, val); +} + +static void +gdm_daemon_config_class_init (GdmDaemonConfigClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdm_daemon_config_finalize; + + g_type_class_add_private (klass, sizeof (GdmDaemonConfigPrivate)); +} + +static gboolean +validate_path (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + char *str; + + /* If the /etc/default has a PATH use that */ + str = gdm_read_default ("PATH="); + if (str != NULL) { + gdm_config_value_set_string (value, str); + g_free (str); + } + + return TRUE; +} + +static gboolean +validate_root_path (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + char *str; + + /* If the /etc/default has a PATH use that */ + str = gdm_read_default ("SUPATH="); + if (str != NULL) { + gdm_config_value_set_string (value, str); + g_free (str); + } + + return TRUE; +} + +static gboolean +validate_base_xsession (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + const char *str; + + str = gdm_config_value_get_string (value); + if (str == NULL || str[0] == '\0') { + char *path; + path = g_build_filename (GDMCONFDIR, "gdm", "Xsession", NULL); + g_debug (_("BaseXsession empty; using %s"), path); + gdm_config_value_set_string (value, path); + g_free (path); + } + + return TRUE; +} + +static gboolean +validate_power_action (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + /* FIXME: should weed out the commands that don't work */ + + return TRUE; +} + +static gboolean +validate_standard_xserver (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + gboolean res; + gboolean is_ok; + const char *str; + char *new; + + is_ok = FALSE; + new = NULL; + str = gdm_config_value_get_string (value); + + if (str != NULL) { + char **argv; + + if (g_shell_parse_argv (str, NULL, &argv, NULL)) { + if (g_access (argv[0], X_OK) == 0) { + is_ok = TRUE; + } + g_strfreev (argv); + } + } + + if G_UNLIKELY (! is_ok) { + g_debug (_("Standard X server not found; trying alternatives")); + if (g_access ("/usr/X11R6/bin/X", X_OK) == 0) { + new = g_strdup ("/usr/X11R6/bin/X"); + } else if (g_access ("/opt/X11R6/bin/X", X_OK) == 0) { + new = g_strdup ("/opt/X11R6/bin/X"); + } else if (g_access ("/usr/bin/X11/X", X_OK) == 0) { + new = g_strdup ("/usr/bin/X11/X"); + } + } + + if (new != NULL) { + gdm_config_value_set_string (value, new); + g_free (new); + } + + res = TRUE; + + return res; +} + +static gboolean +validate_graphical_theme_dir (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + const char *str; + + str = gdm_config_value_get_string (value); + + if (str == NULL || !g_file_test (str, G_FILE_TEST_IS_DIR)) { + gdm_config_value_set_string (value, GREETERTHEMEDIR); + } + + return TRUE; +} + +static gboolean +validate_graphical_theme (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + const char *str; + + str = gdm_config_value_get_string (value); + + if (str == NULL || str[0] == '\0') { + gdm_config_value_set_string (value, "circles"); + } + + return TRUE; +} + +static gboolean +validate_greeter (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + const char *str; + + str = gdm_config_value_get_string (value); + + if (str == NULL || str[0] == '\0') { + g_warning (_("%s: No greeter specified."), "gdm_config_parse"); + } + + return TRUE; +} + +static gboolean +validate_remote_greeter (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + const char *str; + + str = gdm_config_value_get_string (value); + + if (str == NULL || str[0] == '\0') { + g_warning (_("%s: No remote greeter specified."), "gdm_config_parse"); + } + + return TRUE; +} + +static gboolean +validate_session_desktop_dir (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + const char *str; + + str = gdm_config_value_get_string (value); + + if (str == NULL || str[0] == '\0') { + g_warning (_("%s: No sessions directory specified."), "gdm_config_parse"); + } + + return TRUE; +} + +static gboolean +validate_password_required (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + char *str; + + str = gdm_read_default ("PASSREQ="); + if (str != NULL && str[0] == '\0') { + gboolean val; + val = (g_ascii_strcasecmp (str, "YES") == 0); + gdm_config_value_set_bool (value, val); + } + + return TRUE; +} + +static gboolean +validate_allow_remote_root (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + char *str; + + str = gdm_read_default ("CONSOLE="); + if (str != NULL && str[0] == '\0') { + gboolean val; + val = (g_ascii_strcasecmp (str, "/dev/console") != 0); + gdm_config_value_set_bool (value, val); + } + + return TRUE; +} + +static gboolean +validate_xdmcp (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value) +{ + +#ifndef HAVE_LIBXDMCP + if (gdm_config_value_get_bool (value)) { + g_debug (_("XDMCP was enabled while there is no XDMCP support; turning it off")); + gdm_config_value_set_bool (value, FALSE); + } +#endif + + return TRUE; +} + +static gboolean +validate_at_least_int (GdmConfig *config, + GdmConfigSourceType source, + GdmConfigValue *value, + int minval, + int defval) +{ + if (gdm_config_value_get_int (value) < minval) { + gdm_config_value_set_int (value, defval); + } + + return TRUE; +} + +static gboolean +validate_cb (GdmConfig *config, + GdmConfigSourceType source, + const char *group, + const char *key, + GdmConfigValue *value, + int id, + gpointer data) +{ + gboolean res; + + res = TRUE; + + switch (id) { + case GDM_ID_PATH: + res = validate_path (config, source, value); + break; + case GDM_ID_ROOT_PATH: + res = validate_root_path (config, source, value); + break; + case GDM_ID_BASE_XSESSION: + res = validate_base_xsession (config, source, value); + break; + case GDM_ID_HALT: + case GDM_ID_REBOOT: + case GDM_ID_SUSPEND: + res = validate_power_action (config, source, value); + break; + case GDM_ID_STANDARD_XSERVER: + res = validate_standard_xserver (config, source, value); + break; + case GDM_ID_GRAPHICAL_THEME_DIR: + res = validate_graphical_theme_dir (config, source, value); + break; + case GDM_ID_GRAPHICAL_THEME: + res = validate_graphical_theme (config, source, value); + break; + case GDM_ID_GREETER: + res = validate_greeter (config, source, value); + break; + case GDM_ID_REMOTE_GREETER: + res = validate_remote_greeter (config, source, value); + break; + case GDM_ID_SESSION_DESKTOP_DIR: + res = validate_session_desktop_dir (config, source, value); + break; + case GDM_ID_PASSWORD_REQUIRED: + res = validate_password_required (config, source, value); + break; + case GDM_ID_ALLOW_REMOTE_ROOT: + res = validate_allow_remote_root (config, source, value); + break; + case GDM_ID_XDMCP: + res = validate_xdmcp (config, source, value); + break; + case GDM_ID_MAX_INDIRECT: + case GDM_ID_XINERAMA_SCREEN: + res = validate_at_least_int (config, source, value, 0, 0); + break; + case GDM_ID_TIMED_LOGIN_DELAY: + res = validate_at_least_int (config, source, value, 5, 5); + break; + case GDM_ID_MAX_ICON_WIDTH: + case GDM_ID_MAX_ICON_HEIGHT: + res = validate_at_least_int (config, source, value, 0, 128); + break; + case GDM_ID_SCAN_TIME: + res = validate_at_least_int (config, source, value, 1, 1); + break; + case GDM_ID_NONE: + case GDM_CONFIG_INVALID_ID: + break; + default: + break; + } + + return res; +} + +static const char * +source_to_name (GdmConfigSourceType source) +{ + const char *name; + + switch (source) { + case GDM_CONFIG_SOURCE_DEFAULT: + name = "default"; + break; + case GDM_CONFIG_SOURCE_MANDATORY: + name = "mandatory"; + break; + case GDM_CONFIG_SOURCE_CUSTOM: + name = "custom"; + break; + case GDM_CONFIG_SOURCE_BUILT_IN: + name = "built-in"; + break; + case GDM_CONFIG_SOURCE_RUNTIME_USER: + name = "runtime-user"; + break; + case GDM_CONFIG_SOURCE_INVALID: + name = "Invalid"; + break; + default: + name = "Unknown"; + break; + } + + return name; +} + +static gboolean +notify_cb (GdmConfig *config, + GdmConfigSourceType source, + const char *group, + const char *key, + GdmConfigValue *value, + int id, + gpointer data) +{ + char *valstr; + + /* FIXME: need a better approach */ +#if 0 + switch (id) { + case GDM_ID_GREETER: + case GDM_ID_REMOTE_GREETER: + case GDM_ID_SOUND_ON_LOGIN_FILE: + case GDM_ID_SOUND_ON_LOGIN_SUCCESS_FILE: + case GDM_ID_SOUND_ON_LOGIN_FAILURE_FILE: + case GDM_ID_GTK_MODULES_LIST: + case GDM_ID_TIMED_LOGIN: + case GDM_ID_ALLOW_ROOT: + case GDM_ID_ALLOW_REMOTE_ROOT: + case GDM_ID_ALLOW_REMOTE_AUTOLOGIN: + case GDM_ID_SYSTEM_MENU: + case GDM_ID_CONFIG_AVAILABLE: + case GDM_ID_CHOOSER_BUTTON: + case GDM_ID_DISALLOW_TCP: + case GDM_ID_ADD_GTK_MODULES: + case GDM_ID_TIMED_LOGIN_ENABLE: + case GDM_ID_RETRY_DELAY: + case GDM_ID_TIMED_LOGIN_DELAY: + notify_displays_value (config, group, key, value); + break; + case GDM_ID_NONE: + case GDM_CONFIG_INVALID_ID: + { + /* doesn't have an ID : match group/key */ + if (group != NULL) { + if (strcmp (group, GDM_CONFIG_GROUP_SERVERS) == 0) { + /* FIXME: handle this? */ + } else if (strcmp (group, GDM_CONFIG_GROUP_CUSTOM_CMD) == 0) { + notify_displays_value (config, group, key, value); + } + } + } + break; + default: + break; + } +#endif + + valstr = gdm_config_value_to_string (value); + g_debug ("Got config %s/%s=%s <%s>\n", + group, + key, + valstr, + source_to_name (source)); + g_free (valstr); + + return TRUE; +} + +/** + * gdm_daemon_config_find_xserver + * + * Return an xserver with a given ID, or NULL if not found. + */ +GdmXserver * +gdm_daemon_config_find_xserver (GdmDaemonConfig *config, + const char *id) +{ + GSList *li; + + if (config->priv->xservers == NULL) { + return NULL; + } + + if (id == NULL) { + return config->priv->xservers->data; + } + + for (li = config->priv->xservers; li != NULL; li = li->next) { + GdmXserver *svr = li->data; + if (svr->id != NULL && strcmp (svr->id, id) == 0) { + return svr; + } + } + + return NULL; +} + +/** + * gdm_daemon_config_get_xservers + * + * Prepare a string to be returned for the GET_SERVER_LIST + * sockets command. + */ +gchar * +gdm_daemon_config_get_xservers (GdmDaemonConfig *config) +{ + GSList *li; + char *retval = NULL; + + if (config->priv->xservers == NULL) { + return NULL; + } + + for (li = config->priv->xservers; li != NULL; li = li->next) { + GdmXserver *svr = li->data; + if (retval != NULL) + retval = g_strconcat (retval, ";", svr->id, NULL); + else + retval = g_strdup (svr->id); + } + + return retval; +} + +/** + * gdm_daemon_config_get_xserver_list + * + * Returns the list of xservers being used. + */ +GSList * +gdm_daemon_config_get_xserver_list (GdmDaemonConfig *config) +{ + return config->priv->xservers; +} + + +static void +gdm_daemon_config_load_xserver (GdmDaemonConfig *config, + const char *key, + const char *name) +{ + GdmXserver *svr; + int n; + char *group; + gboolean res; + GdmConfigValue *value; + + /* + * See if we already loaded a server with this id, skip if + * one already exists. + */ + if (gdm_daemon_config_find_xserver (config, name) != NULL) { + return; + } + + svr = g_new0 (GdmXserver, 1); + svr->id = g_strdup (name); + + if (isdigit (*key)) { + svr->number = atoi (key); + } + + group = g_strdup_printf ("server-%s", name); + + /* string */ + res = gdm_config_get_value (config->priv->config, group, "name", &value); + if (res) { + svr->name = g_strdup (gdm_config_value_get_string (value)); + } + res = gdm_config_get_value (config->priv->config, group, "command", &value); + if (res) { + svr->command = g_strdup (gdm_config_value_get_string (value)); + } + + g_debug ("Adding new server id=%s name=%s command=%s", name, svr->name, svr->command); + + + /* bool */ + res = gdm_config_get_value (config->priv->config, group, "flexible", &value); + if (res) { + svr->flexible = gdm_config_value_get_bool (value); + } + res = gdm_config_get_value (config->priv->config, group, "choosable", &value); + if (res) { + svr->choosable = gdm_config_value_get_bool (value); + } + res = gdm_config_get_value (config->priv->config, group, "handled", &value); + if (res) { + svr->handled = gdm_config_value_get_bool (value); + } + res = gdm_config_get_value (config->priv->config, group, "chooser", &value); + if (res) { + svr->chooser = gdm_config_value_get_bool (value); + } + + /* int */ + res = gdm_config_get_value (config->priv->config, group, "priority", &value); + if (res) { + svr->priority = gdm_config_value_get_int (value); + } + + /* do some bounds checking */ + n = svr->priority; + if (n < PRIO_MIN) { + n = PRIO_MIN; + } else if (n > PRIO_MAX) { + n = PRIO_MAX; + } + + if (n != svr->priority) { + g_warning (_("%s: Priority out of bounds; changed to %d"), + "gdm_config_parse", n); + svr->priority = n; + } + + if (svr->command == NULL || svr->command[0] == '\0') { + g_warning (_("Empty server command; using standard command.")); + g_free (svr->command); + svr->command = g_strdup (X_SERVER); + } + + config->priv->xservers = g_slist_append (config->priv->xservers, svr); +} + +static void +gdm_daemon_config_unload_xservers (GdmDaemonConfig *config) +{ + GSList *xli; + + /* Free list if already loaded */ + for (xli = config->priv->xservers; xli != NULL; xli = xli->next) { + GdmXserver *xsvr = xli->data; + + g_free (xsvr->id); + g_free (xsvr->name); + g_free (xsvr->command); + } + + if (config->priv->xservers != NULL) { + g_slist_free (config->priv->xservers); + config->priv->xservers = NULL; + } +} + +static void +gdm_daemon_config_ensure_one_xserver (GdmDaemonConfig *config) +{ + /* If no "Standard" server was created, then add it */ + if (config->priv->xservers == NULL || gdm_daemon_config_find_xserver (config, GDM_STANDARD) == NULL) { + GdmXserver *svr = g_new0 (GdmXserver, 1); + + svr->id = g_strdup (GDM_STANDARD); + svr->name = g_strdup ("Standard server"); + svr->command = g_strdup (X_SERVER); + svr->flexible = TRUE; + svr->choosable = TRUE; + svr->handled = TRUE; + svr->priority = 0; + + config->priv->xservers = g_slist_append (config->priv->xservers, svr); + } +} + +static void +load_xservers_group (GdmDaemonConfig *config) +{ + char **keys; + gsize len; + int i; + + keys = gdm_config_get_keys_for_group (config->priv->config, GDM_CONFIG_GROUP_SERVERS, &len, NULL); + + /* now construct entries for these groups */ + for (i = 0; i < len; i++) { + GdmConfigEntry entry; + GdmConfigValue *value; + char *new_group; + gboolean res; + int j; + const char *name; + + entry.group = GDM_CONFIG_GROUP_SERVERS; + entry.key = keys[i]; + entry.type = GDM_CONFIG_VALUE_STRING; + entry.default_value = NULL; + entry.id = GDM_CONFIG_INVALID_ID; + + gdm_config_add_entry (config->priv->config, &entry); + gdm_config_process_entry (config->priv->config, &entry, NULL); + + res = gdm_config_get_value (config->priv->config, entry.group, entry.key, &value); + if (! res) { + continue; + } + + name = gdm_config_value_get_string (value); + if (name == NULL || name[0] == '\0') { + gdm_config_value_free (value); + continue; + } + + /* skip servers marked as inactive */ + if (g_ascii_strcasecmp (name, "inactive") == 0) { + gdm_config_value_free (value); + continue; + } + + new_group = g_strdup_printf ("server-%s", name); + for (j = 0; j < G_N_ELEMENTS (gdm_daemon_server_config_entries); j++) { + GdmConfigEntry *srv_entry; + if (gdm_daemon_server_config_entries[j].key == NULL) { + continue; + } + srv_entry = gdm_config_entry_copy (&gdm_daemon_server_config_entries[j]); + g_free (srv_entry->group); + srv_entry->group = g_strdup (new_group); + gdm_config_process_entry (config->priv->config, srv_entry, NULL); + gdm_config_entry_free (srv_entry); + } + g_free (new_group); + + /* now we can add this server */ + gdm_daemon_config_load_xserver (config, entry.key, gdm_config_value_get_string (value)); + + gdm_config_value_free (value); + } +} + +static void +gdm_daemon_config_load_xservers (GdmDaemonConfig *config) +{ + gdm_daemon_config_unload_xservers (config); + load_xservers_group (config); + gdm_daemon_config_ensure_one_xserver (config); +} + +static void +load_config (GdmDaemonConfig *config) +{ + GError *error; + + config->priv->config = gdm_config_new (); + + gdm_config_set_notify_func (config->priv->config, notify_cb, NULL); + gdm_config_set_validate_func (config->priv->config, validate_cb, NULL); + + gdm_config_add_static_entries (config->priv->config, gdm_daemon_config_entries); + + gdm_config_set_default_file (config->priv->config, config->priv->default_file); + gdm_config_set_custom_file (config->priv->config, config->priv->custom_file); + + /* load the data files */ + error = NULL; + gdm_config_load (config->priv->config, &error); + if (error != NULL) { + g_warning ("Unable to load configuration: %s", error->message); + g_error_free (error); + } + + /* populate the database with all specified entries */ + gdm_config_process_all (config->priv->config, &error); + + gdm_daemon_config_load_xservers (config); +} + +void +gdm_daemon_config_load (GdmDaemonConfig *config) +{ + g_return_if_fail (GDM_IS_DAEMON_CONFIG (config)); + + if (config->priv->config != NULL) { + return; + } + + load_config (config); +} + +static void +gdm_daemon_config_init (GdmDaemonConfig *config) +{ + + config->priv = GDM_DAEMON_CONFIG_GET_PRIVATE (config); + + config->priv->default_file = GDM_DEFAULTS_CONF; + config->priv->custom_file = GDM_CUSTOM_CONF; +} + +static void +gdm_daemon_config_finalize (GObject *object) +{ + GdmDaemonConfig *config; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_DAEMON_CONFIG (object)); + + config = GDM_DAEMON_CONFIG (object); + + g_return_if_fail (config->priv != NULL); + + if (config->priv->xservers != NULL) { + g_slist_free (config->priv->xservers); + } + + G_OBJECT_CLASS (gdm_daemon_config_parent_class)->finalize (object); +} + +GdmDaemonConfig * +gdm_daemon_config_new (void) +{ + if (daemon_config_object != NULL) { + g_object_ref (daemon_config_object); + } else { + daemon_config_object = g_object_new (GDM_TYPE_DAEMON_CONFIG, NULL); + g_object_add_weak_pointer (daemon_config_object, + (gpointer *) &daemon_config_object); + } + + return GDM_DAEMON_CONFIG (daemon_config_object); +} diff --git a/daemon/gdm-master-config.h b/daemon/gdm-master-config.h new file mode 100644 index 00000000..54e1f8a7 --- /dev/null +++ b/daemon/gdm-master-config.h @@ -0,0 +1,93 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_DAEMON_CONFIG_H +#define __GDM_DAEMON_CONFIG_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_DAEMON_CONFIG (gdm_daemon_config_get_type ()) +#define GDM_DAEMON_CONFIG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_DAEMON_CONFIG, GdmDaemonConfig)) +#define GDM_DAEMON_CONFIG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_DAEMON_CONFIG, GdmDaemonConfigClass)) +#define GDM_IS_DAEMON_CONFIG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_DAEMON_CONFIG)) +#define GDM_IS_DAEMON_CONFIG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_DAEMON_CONFIG)) +#define GDM_DAEMON_CONFIG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_DAEMON_CONFIG, GdmDaemonConfigClass)) + +typedef struct GdmDaemonConfigPrivate GdmDaemonConfigPrivate; + +typedef struct +{ + GObject parent; + GdmDaemonConfigPrivate *priv; +} GdmDaemonConfig; + +typedef struct +{ + GObjectClass parent_class; +} GdmDaemonConfigClass; + +typedef enum +{ + GDM_DAEMON_CONFIG_ERROR_GENERAL +} GdmDaemonConfigError; + +#define GDM_DAEMON_CONFIG_ERROR gdm_daemon_config_error_quark () + +typedef struct _GdmXserver GdmXserver; + +struct _GdmXserver +{ + char *id; + char *name; + char *command; + gboolean flexible; + gboolean choosable; /* not implemented yet */ + gboolean chooser; /* instead of greeter, run chooser */ + gboolean handled; + int number; + int priority; +}; + +GQuark gdm_daemon_config_error_quark (void); +GType gdm_daemon_config_get_type (void); + +GdmDaemonConfig * gdm_daemon_config_new (void); +void gdm_daemon_config_load (GdmDaemonConfig *config); +gboolean gdm_daemon_config_get_bool_for_id (GdmDaemonConfig *config, + int id, + gboolean *value); +gboolean gdm_daemon_config_get_int_for_id (GdmDaemonConfig *config, + int id, + int *value); +gboolean gdm_daemon_config_get_string_for_id (GdmDaemonConfig *config, + int id, + char **value); + +GdmXserver * gdm_daemon_config_find_xserver (GdmDaemonConfig *config, + const char *id); +char * gdm_daemon_config_get_xservers (GdmDaemonConfig *config); +GSList * gdm_daemon_config_get_xserver_list (GdmDaemonConfig *config); + +G_END_DECLS + +#endif /* __GDM_DAEMON_CONFIG_H */ diff --git a/daemon/gdm-net.c b/daemon/gdm-net.c index 93a48dbe..304fe84e 100644 --- a/daemon/gdm-net.c +++ b/daemon/gdm-net.c @@ -109,7 +109,7 @@ struct _GdmConnection { GdmDisplay *disp; }; -int +int gdm_connection_is_server_busy (GdmConnection *conn) { int max_connections = MAX_CONNECTIONS; @@ -391,14 +391,15 @@ try_again: conn->subconnections = NULL; conn->n_subconnections = 0; + gdm_fd_set_close_on_exec (conn->fd); + unixchan = g_io_channel_unix_new (conn->fd); g_io_channel_set_encoding (unixchan, NULL, NULL); g_io_channel_set_buffered (unixchan, FALSE); - conn->source = g_io_add_watch_full - (unixchan, G_PRIORITY_DEFAULT, - G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, - gdm_socket_handler, conn, NULL); + conn->source = g_io_add_watch_full (unixchan, G_PRIORITY_DEFAULT, + G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + gdm_socket_handler, conn, NULL); g_io_channel_unref (unixchan); listen (fd, 5); @@ -428,6 +429,8 @@ gdm_connection_open_fd (int fd) conn->subconnections = NULL; conn->n_subconnections = 0; + gdm_fd_set_close_on_exec (conn->fd); + unixchan = g_io_channel_unix_new (conn->fd); g_io_channel_set_encoding (unixchan, NULL, NULL); g_io_channel_set_buffered (unixchan, FALSE); @@ -480,6 +483,8 @@ gdm_connection_open_fifo (const char *fifo, mode_t mode) conn->subconnections = NULL; conn->n_subconnections = 0; + gdm_fd_set_close_on_exec (conn->fd); + fifochan = g_io_channel_unix_new (conn->fd); g_io_channel_set_encoding (fifochan, NULL, NULL); g_io_channel_set_buffered (fifochan, FALSE); @@ -679,4 +684,3 @@ gdm_kill_subconnections_with_display (GdmConnection *conn, } } -/* EOF */ diff --git a/daemon/gdm-server.c b/daemon/gdm-server.c new file mode 100644 index 00000000..11701c7d --- /dev/null +++ b/daemon/gdm-server.c @@ -0,0 +1,708 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include <X11/Xlib.h> /* for Display */ + +#include "gdm-common.h" + +#include "gdm-server.h" + +extern char **environ; + +#define GDM_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SERVER, GdmServerPrivate)) + +/* These are the servstat values, also used as server + * process exit codes */ +#define SERVER_TIMEOUT 2 /* Server didn't start */ +#define SERVER_DEAD 250 /* Server stopped */ +#define SERVER_PENDING 251 /* Server started but not ready for connections yet */ +#define SERVER_RUNNING 252 /* Server running and ready for connections */ +#define SERVER_ABORT 253 /* Server failed badly. Suspending display. */ + +struct GdmServerPrivate +{ + char *command; + GPid pid; + + gboolean disable_tcp; + int priority; + uid_t uid; + char *session_args; + + char *log_dir; + char *display_name; + char *auth_file; + + gboolean is_parented; + char *parent_display_name; + char *parent_auth_file; + char *chosen_hostname; +}; + +enum { + PROP_0, + PROP_DISPLAY_NAME, + PROP_AUTH_FILE, + PROP_IS_PARENTED, + PROP_PARENT_DISPLAY_NAME, + PROP_PARENT_AUTH_FILE, + PROP_CHOSEN_HOSTNAME, + PROP_COMMAND, + PROP_PRIORITY, + PROP_UID, + PROP_SESSION_ARGS, + PROP_LOG_DIR, + PROP_DISABLE_TCP, +}; + +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) + +gboolean +gdm_server_stop (GdmServer *server) +{ + g_debug ("Stopping server"); + + return TRUE; +} + +/* We keep a connection (parent_dsp) open with the parent X server + * before running a proxy on it to prevent the X server resetting + * as we open and close other connections. + * Note that XDMCP servers, by default, reset when the seed X + * connection closes whereas usually the X server only quits when + * all X connections have closed. + */ +#if 0 +static gboolean +connect_to_parent (GdmServer *server) +{ + int maxtries; + int openretries; + + g_debug ("gdm_server_start: Connecting to parent display \'%s\'", + d->parent_disp); + + d->parent_dsp = NULL; + + maxtries = SERVER_IS_XDMCP (d) ? 10 : 2; + + openretries = 0; + while (openretries < maxtries && + d->parent_dsp == NULL) { + d->parent_dsp = XOpenDisplay (d->parent_disp); + + if G_UNLIKELY (d->parent_dsp == NULL) { + g_debug ("gdm_server_start: Sleeping %d on a retry", 1+openretries*2); + gdm_sleep_no_signal (1+openretries*2); + openretries++; + } + } + + if (d->parent_dsp == NULL) + gdm_error (_("%s: failed to connect to parent display \'%s\'"), + "gdm_server_start", d->parent_disp); + + return d->parent_dsp != NULL; +} +#endif + +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; + + g_shell_parse_argv (server->priv->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 + 10); + /* 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->priv->display_name); + len++; + + if (server->priv->auth_file != NULL) { + argv[len++] = g_strdup ("-auth"); + argv[len++] = g_strdup (server->priv->auth_file); + } + + if (server->priv->chosen_hostname) { + /* run just one session */ + argv[len++] = g_strdup ("-terminate"); + argv[len++] = g_strdup ("-query"); + argv[len++] = g_strdup (server->priv->chosen_hostname); + query_in_arglist = TRUE; + } + + if (server->priv->disable_tcp && ! query_in_arglist) { + argv[len++] = g_strdup ("-nolisten"); + argv[len++] = g_strdup ("tcp"); + } + + if (vtarg != NULL && ! gotvtarg) { + argv[len++] = g_strdup (vtarg); + } + + argv[len++] = NULL; + + *argvp = argv; + *argcp = len; + + return TRUE; +} + +/* somewhat safer rename (safer if the log dir is unsafe), may in fact + lose the file though, it guarantees that a is gone, but not that + b exists */ +static void +safer_rename (const char *a, const char *b) +{ + errno = 0; + if (link (a, b) < 0) { + if (errno == EEXIST) { + VE_IGNORE_EINTR (g_unlink (a)); + return; + } + VE_IGNORE_EINTR (g_unlink (b)); + /* likely this system doesn't support hard links */ + g_rename (a, b); + VE_IGNORE_EINTR (g_unlink (a)); + return; + } + VE_IGNORE_EINTR (g_unlink (a)); +} + +static void +rotate_logs (GdmServer *server) +{ + const char *dname; + const char *logdir; + + dname = server->priv->display_name; + logdir = server->priv->log_dir; + + /* I'm too lazy to write a loop */ + char *fname4 = gdm_make_filename (logdir, dname, ".log.4"); + char *fname3 = gdm_make_filename (logdir, dname, ".log.3"); + char *fname2 = gdm_make_filename (logdir, dname, ".log.2"); + char *fname1 = gdm_make_filename (logdir, dname, ".log.1"); + char *fname = gdm_make_filename (logdir, dname, ".log"); + + /* Rotate the logs (keep 4 last) */ + VE_IGNORE_EINTR (g_unlink (fname4)); + safer_rename (fname3, fname4); + safer_rename (fname2, fname3); + safer_rename (fname1, fname2); + safer_rename (fname, fname1); + + g_free (fname4); + g_free (fname3); + g_free (fname2); + g_free (fname1); + g_free (fname); +} + +static void +change_user (GdmServer *server) +{ + if (server->priv->uid != 0) { + struct passwd *pwent; + + pwent = getpwuid (server->priv->uid); + if (pwent == NULL) { + g_warning (_("%s: Server was to be spawned by uid %d but " + "that user doesn't exist"), + "gdm_server_spawn", + (int)server->priv->uid); + _exit (SERVER_ABORT); + } + + if (setgid (pwent->pw_gid) < 0) { + g_warning (_("%s: Couldn't set groupid to %d"), + "gdm_server_spawn", (int)pwent->pw_gid); + _exit (SERVER_ABORT); + } + + if (initgroups (pwent->pw_name, pwent->pw_gid) < 0) { + g_warning (_("%s: initgroups () failed for %s"), + "gdm_server_spawn", pwent->pw_name); + _exit (SERVER_ABORT); + } + + if (setuid (server->priv->uid) < 0) { + g_warning (_("%s: Couldn't set userid to %d"), + "gdm_server_spawn", (int)server->priv->uid); + _exit (SERVER_ABORT); + } + } else { + gid_t groups[1] = { 0 }; + + if (setgid (0) < 0) { + g_warning (_("%s: Couldn't set groupid to 0"), + "gdm_server_spawn"); + /* 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 void +server_child_setup (GdmServer *server) +{ + char *logfile; + int logfd; + struct sigaction ign_signal; + sigset_t mask; + + /* Rotate the X server logs */ + rotate_logs (server); + + /* Log all output from spawned programs to a file */ + logfile = gdm_make_filename (server->priv->log_dir, + server->priv->display_name, + ".log"); + VE_IGNORE_EINTR (g_unlink (logfile)); + VE_IGNORE_EINTR (logfd = open (logfile, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL, 0644)); + + if (logfd != -1) { + VE_IGNORE_EINTR (dup2 (logfd, 1)); + VE_IGNORE_EINTR (dup2 (logfd, 2)); + close (logfd); + } else { + g_warning (_("%s: Could not open logfile for display %s!"), + "gdm_server_spawn", + server->priv->display_name); + } + + /* 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 (SERVER_ABORT); + } + + if (sigaction (SIGTTIN, &ign_signal, NULL) < 0) { + g_warning (_("%s: Error setting %s to %s"), + "gdm_server_spawn", "TTIN", "SIG_IGN"); + _exit (SERVER_ABORT); + } + + if (sigaction (SIGTTOU, &ign_signal, NULL) < 0) { + g_warning (_("%s: Error setting %s to %s"), + "gdm_server_spawn", "TTOU", "SIG_IGN"); + _exit (SERVER_ABORT); + } + + /* 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); + + if (server->priv->priority != 0) { + if (setpriority (PRIO_PROCESS, 0, server->priv->priority)) { + g_warning (_("%s: Server priority couldn't be set to %d: %s"), + "gdm_server_spawn", + server->priv->priority, + g_strerror (errno)); + } + } + + setpgid (0, 0); + + change_user (server); + +#if sun + { + /* Remove old communication pipe, if present */ + char old_pipe[MAXPATHLEN]; + + sprintf (old_pipe, "%s/%d", SDTLOGIN_DIR, server->priv->display_name); + g_unlink (old_pipe); + } +#endif +} + +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]); + } + + /* modify environment here */ + if (server->priv->is_parented) { + if (server->priv->parent_auth_file != NULL) { + g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (server->priv->parent_auth_file)); + } + + if (server->priv->parent_display_name != NULL) { + g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (server->priv->parent_display_name)); + } + } else { + g_hash_table_insert (hash, g_strdup ("DISPLAY="), g_strdup (server->priv->display_name)); + } + + if (server->priv->uid != 0) { + struct passwd *pwent; + + pwent = getpwuid (server->priv->uid); + + 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->priv->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 gboolean +gdm_server_spawn (GdmServer *server, + const char *vtarg) +{ + int argc; + gchar **argv = NULL; + GError *error; + GPtrArray *env; + gboolean ret; + + ret = FALSE; + + /* Figure out the server command */ + argv = NULL; + argc = 0; + gdm_server_resolve_command_line (server, + vtarg, + &argc, + &argv); + + if (server->priv->session_args) { + server_add_xserver_args (server, &argc, &argv); + } + + if (argv[0] == NULL) { + g_warning (_("%s: Empty server command for display %s"), + "gdm_server_spawn", + server->priv->display_name); + _exit (SERVER_ABORT); + } + + env = get_server_environment (server); + + g_debug ("Starting X server process"); + error = NULL; + ret = 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->priv->pid, + NULL, + NULL, + NULL, + &error); + + if (! ret) { + g_warning ("Could not start command '%s': %s", + server->priv->command, + error->message); + g_error_free (error); + } + + g_strfreev (argv); + 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) +{ + char *vtarg = NULL; + int vtfd = -1; + int vt = -1; + gboolean res; + +#if 0 + if (d->type == TYPE_XDMCP_PROXY && + ! connect_to_parent (d)) + return FALSE; +#endif + +#if 0 + if (d->type == TYPE_STATIC || + d->type == TYPE_FLEXI) { + vtarg = gdm_get_empty_vt_argument (&vtfd, &vt); + } +#endif + + /* fork X server process */ + res = gdm_server_spawn (server, vtarg); + +#if 0 + /* If we were holding a vt open for the server, close it now as it has + * already taken the bait. */ + if (vtfd > 0) { + VE_IGNORE_EINTR (close (vtfd)); + } +#endif + + return res; +} + +static void +_gdm_server_set_display_name (GdmServer *server, + const char *name) +{ + g_free (server->priv->display_name); + server->priv->display_name = g_strdup (name); +} + +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; + 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->priv->display_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +gdm_server_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GdmServer *server; + GdmServerClass *klass; + + klass = GDM_SERVER_CLASS (g_type_class_peek (GDM_TYPE_SERVER)); + + server = GDM_SERVER (G_OBJECT_CLASS (gdm_server_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + return G_OBJECT (server); +} + +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->constructor = gdm_server_constructor; + object_class->finalize = gdm_server_finalize; + + g_type_class_add_private (klass, sizeof (GdmServerPrivate)); + + 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)); +} + +static void +gdm_server_init (GdmServer *server) +{ + + server->priv = GDM_SERVER_GET_PRIVATE (server); + + server->priv->pid = -1; +} + +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); + + g_return_if_fail (server->priv != NULL); + + G_OBJECT_CLASS (gdm_server_parent_class)->finalize (object); +} + +GdmServer * +gdm_server_new (const char *display_name) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_SERVER, + "display-name", display_name, + NULL); + + return GDM_SERVER (object); +} diff --git a/daemon/gdm-server.h b/daemon/gdm-server.h new file mode 100644 index 00000000..fdb55b29 --- /dev/null +++ b/daemon/gdm-server.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_SERVER_H +#define __GDM_SERVER_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_SERVER (gdm_server_get_type ()) +#define GDM_SERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_SERVER, GdmServer)) +#define GDM_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_SERVER, GdmServerClass)) +#define GDM_IS_SERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_SERVER)) +#define GDM_IS_SERVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_SERVER)) +#define GDM_SERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_SERVER, GdmServerClass)) + +typedef struct GdmServerPrivate GdmServerPrivate; + +typedef struct +{ + GObject parent; + GdmServerPrivate *priv; +} GdmServer; + +typedef struct +{ + GObjectClass parent_class; + +} GdmServerClass; + +GType gdm_server_get_type (void); +GdmServer * gdm_server_new (const char *display_id); +gboolean gdm_server_start (GdmServer *server); +gboolean gdm_server_stop (GdmServer *server); + +G_END_DECLS + +#endif /* __GDM_SERVER_H */ diff --git a/daemon/gdm-slave-proxy.c b/daemon/gdm-slave-proxy.c new file mode 100644 index 00000000..3b3f1dab --- /dev/null +++ b/daemon/gdm-slave-proxy.c @@ -0,0 +1,441 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gdm-slave-proxy.h" + +#define GDM_SLAVE_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SLAVE, GdmSlaveProxyPrivate)) + +#define GDM_SLAVE_PROXY_COMMAND LIBEXECDIR"/gdm-slave" + +struct GdmSlaveProxyPrivate +{ + char *display_id; + GPid pid; + guint output_watch_id; + guint error_watch_id; +}; + +enum { + PROP_0, + PROP_DISPLAY_ID, +}; + +static void gdm_slave_proxy_class_init (GdmSlaveProxyClass *klass); +static void gdm_slave_proxy_init (GdmSlaveProxy *slave); +static void gdm_slave_proxy_finalize (GObject *object); + +G_DEFINE_TYPE (GdmSlaveProxy, gdm_slave_proxy, G_TYPE_OBJECT) + +/* adapted from gspawn.c */ +static int +wait_on_child (int pid) +{ + int status; + + wait_again: + if (waitpid (pid, &status, 0) < 0) { + if (errno == EINTR) { + goto wait_again; + } else if (errno == ECHILD) { + ; /* do nothing, child already reaped */ + } else { + g_debug ("waitpid () should not fail in 'GdmSpawnProxy'"); + } + } + + return status; +} + +static void +slave_died (GdmSlaveProxy *slave) +{ + int exit_status; + + g_debug ("Waiting on process %d", slave->priv->pid); + exit_status = wait_on_child (slave->priv->pid); + + if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) { + g_debug ("Wait on child process failed"); + } else { + /* exited normally */ + } + + g_spawn_close_pid (slave->priv->pid); + slave->priv->pid = -1; + + g_debug ("Slave died"); +} + +static gboolean +output_watch (GIOChannel *source, + GIOCondition condition, + GdmSlaveProxy *slave) +{ + gboolean finished = FALSE; + + if (condition & G_IO_IN) { + GIOStatus status; + GError *error = NULL; + char *line; + + line = NULL; + status = g_io_channel_read_line (source, &line, NULL, NULL, &error); + + switch (status) { + case G_IO_STATUS_NORMAL: + { + char *p; + + g_debug ("command output: %s", line); + + if ((p = strstr (line, "ADDRESS=")) != NULL) { + char *address; + + address = g_strdup (p + strlen ("ADDRESS=")); + g_debug ("Got address %s", address); + + g_free (address); + } + } + break; + case G_IO_STATUS_EOF: + finished = TRUE; + break; + case G_IO_STATUS_ERROR: + finished = TRUE; + g_debug ("Error reading from child: %s\n", error->message); + return FALSE; + case G_IO_STATUS_AGAIN: + default: + break; + } + + g_free (line); + } else if (condition & G_IO_HUP) { + finished = TRUE; + } + + if (finished) { + slave_died (slave); + + slave->priv->output_watch_id = 0; + + return FALSE; + } + + return TRUE; +} + +/* just for debugging */ +static gboolean +error_watch (GIOChannel *source, + GIOCondition condition, + GdmSlaveProxy *slave) +{ + gboolean finished = FALSE; + + if (condition & G_IO_IN) { + GIOStatus status; + GError *error = NULL; + char *line; + + line = NULL; + status = g_io_channel_read_line (source, &line, NULL, NULL, &error); + + switch (status) { + case G_IO_STATUS_NORMAL: + g_debug ("command error output: %s", line); + break; + case G_IO_STATUS_EOF: + finished = TRUE; + break; + case G_IO_STATUS_ERROR: + finished = TRUE; + g_debug ("Error reading from child: %s\n", error->message); + return FALSE; + case G_IO_STATUS_AGAIN: + default: + break; + } + g_free (line); + } else if (condition & G_IO_HUP) { + finished = TRUE; + } + + if (finished) { + slave->priv->error_watch_id = 0; + + return FALSE; + } + + return TRUE; +} + +static gboolean +spawn_slave (GdmSlaveProxy *slave) +{ + char *command; + char **argv; + gboolean result; + GIOChannel *channel; + GError *error = NULL; + int standard_output; + int standard_error; + + + result = FALSE; + + command = g_strdup_printf ("%s --display-id %s", GDM_SLAVE_PROXY_COMMAND, slave->priv->display_id); + + if (! g_shell_parse_argv (command, NULL, &argv, &error)) { + g_warning ("Could not parse command: %s", error->message); + g_error_free (error); + goto out; + } + + g_debug ("Running command: %s", command); + + error = NULL; + result = g_spawn_async_with_pipes (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &slave->priv->pid, + NULL, + &standard_output, + &standard_error, + &error); + + if (! result) { + g_warning ("Could not start command '%s': %s", command, error->message); + g_error_free (error); + g_strfreev (argv); + goto out; + } + + g_strfreev (argv); + + /* output channel */ + channel = g_io_channel_unix_new (standard_output); + g_io_channel_set_close_on_unref (channel, TRUE); + g_io_channel_set_flags (channel, + g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK, + NULL); + slave->priv->output_watch_id = g_io_add_watch (channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc)output_watch, + slave); + g_io_channel_unref (channel); + + /* error channel */ + channel = g_io_channel_unix_new (standard_error); + g_io_channel_set_close_on_unref (channel, TRUE); + g_io_channel_set_flags (channel, + g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK, + NULL); + slave->priv->error_watch_id = g_io_add_watch (channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc)error_watch, + slave); + g_io_channel_unref (channel); + + result = TRUE; + + out: + g_free (command); + + return result; +} + +static int +signal_pid (int pid, + int signal) +{ + int status = -1; + + /* perhaps block sigchld */ + + status = kill (pid, signal); + + if (status < 0) { + if (errno == ESRCH) { + g_warning ("Child process %lu was already dead.", + (unsigned long) pid); + } else { + g_warning ("Couldn't kill child process %lu: %s", + (unsigned long) pid, + g_strerror (errno)); + } + } + + /* perhaps unblock sigchld */ + + return status; +} + +static void +kill_slave (GdmSlaveProxy *slave) +{ + if (slave->priv->pid <= 1) { + return; + } + + signal_pid (slave->priv->pid, SIGTERM); + + /* watch should call slave_died */ +} + +gboolean +gdm_slave_proxy_start (GdmSlaveProxy *slave) +{ + spawn_slave (slave); + + return TRUE; +} + +gboolean +gdm_slave_proxy_stop (GdmSlaveProxy *slave) +{ + kill_slave (slave); + + return TRUE; +} + +static void +_gdm_slave_proxy_set_display_id (GdmSlaveProxy *slave, + const char *id) +{ + g_free (slave->priv->display_id); + slave->priv->display_id = g_strdup (id); +} + +static void +gdm_slave_proxy_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmSlaveProxy *self; + + self = GDM_SLAVE_PROXY (object); + + switch (prop_id) { + case PROP_DISPLAY_ID: + _gdm_slave_proxy_set_display_id (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_slave_proxy_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmSlaveProxy *self; + + self = GDM_SLAVE_PROXY (object); + + switch (prop_id) { + case PROP_DISPLAY_ID: + g_value_set_string (value, self->priv->display_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_slave_proxy_class_init (GdmSlaveProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_slave_proxy_get_property; + object_class->set_property = gdm_slave_proxy_set_property; + object_class->finalize = gdm_slave_proxy_finalize; + + g_type_class_add_private (klass, sizeof (GdmSlaveProxyPrivate)); + + g_object_class_install_property (object_class, + PROP_DISPLAY_ID, + g_param_spec_string ("display-id", + "id", + "id", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + +} + +static void +gdm_slave_proxy_init (GdmSlaveProxy *slave) +{ + + slave->priv = GDM_SLAVE_PROXY_GET_PRIVATE (slave); + + slave->priv->pid = -1; +} + +static void +gdm_slave_proxy_finalize (GObject *object) +{ + GdmSlaveProxy *slave; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_SLAVE (object)); + + slave = GDM_SLAVE_PROXY (object); + + g_return_if_fail (slave->priv != NULL); + + G_OBJECT_CLASS (gdm_slave_proxy_parent_class)->finalize (object); +} + +GdmSlaveProxy * +gdm_slave_proxy_new (const char *id) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_SLAVE, + "display-id", id, + NULL); + + return GDM_SLAVE_PROXY (object); +} diff --git a/daemon/gdm-slave-proxy.h b/daemon/gdm-slave-proxy.h new file mode 100644 index 00000000..ff45790a --- /dev/null +++ b/daemon/gdm-slave-proxy.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_SLAVE_PROXY_H +#define __GDM_SLAVE_PROXY_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_SLAVE (gdm_slave_proxy_get_type ()) +#define GDM_SLAVE_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_SLAVE, GdmSlaveProxy)) +#define GDM_SLAVE_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_SLAVE, GdmSlaveProxyClass)) +#define GDM_IS_SLAVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_SLAVE)) +#define GDM_IS_SLAVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_SLAVE)) +#define GDM_SLAVE_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_SLAVE, GdmSlaveProxyClass)) + +typedef struct GdmSlaveProxyPrivate GdmSlaveProxyPrivate; + +typedef struct +{ + GObject parent; + GdmSlaveProxyPrivate *priv; +} GdmSlaveProxy; + +typedef struct +{ + GObjectClass parent_class; + +} GdmSlaveProxyClass; + +GType gdm_slave_proxy_get_type (void); +GdmSlaveProxy * gdm_slave_proxy_new (const char *display_id); +gboolean gdm_slave_proxy_start (GdmSlaveProxy *slave); +gboolean gdm_slave_proxy_stop (GdmSlaveProxy *slave); + +G_END_DECLS + +#endif /* __GDM_SLAVE_PROXY_H */ diff --git a/daemon/gdm-slave.c b/daemon/gdm-slave.c new file mode 100644 index 00000000..bf043383 --- /dev/null +++ b/daemon/gdm-slave.c @@ -0,0 +1,984 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <pwd.h> +#include <grp.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include <X11/Xlib.h> /* for Display */ + +#include "gdm-common.h" + +#include "gdm-slave.h" +#include "gdm-slave-glue.h" + +#include "gdm-server.h" +#include "gdm-greeter.h" + +extern char **environ; + +#define GDM_SLAVE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_SLAVE, GdmSlavePrivate)) + +#define GDM_SLAVE_COMMAND LIBEXECDIR"/gdm-slave" + +#define GDM_DBUS_NAME "org.gnome.DisplayManager" +#define GDM_DBUS_DISPLAY_INTERFACE "org.gnome.DisplayManager.Display" + +struct GdmSlavePrivate +{ + char *id; + GPid pid; + guint output_watch_id; + guint error_watch_id; + + int ping_interval; + + GPid server_pid; + Display *server_display; + + /* cached display values */ + char *display_id; + char *display_name; + int *display_number; + char *display_hostname; + gboolean display_is_local; + gboolean display_is_parented; + char *display_auth_file; + char *parent_display_name; + char *parent_display_auth_file; + + + GdmServer *server; + GdmGreeter *greeter; + DBusGProxy *display_proxy; + DBusGConnection *connection; +}; + +enum { + PROP_0, + PROP_DISPLAY_ID, +}; + +static void gdm_slave_class_init (GdmSlaveClass *klass); +static void gdm_slave_init (GdmSlave *slave); +static void gdm_slave_finalize (GObject *object); + +G_DEFINE_TYPE (GdmSlave, gdm_slave, G_TYPE_OBJECT) + +/* adapted from gspawn.c */ +static int +wait_on_child (int pid) +{ + int status; + + wait_again: + if (waitpid (pid, &status, 0) < 0) { + if (errno == EINTR) { + goto wait_again; + } else if (errno == ECHILD) { + ; /* do nothing, child already reaped */ + } else { + g_debug ("waitpid () should not fail in 'GdmSpawn'"); + } + } + + return status; +} + +static void +slave_died (GdmSlave *slave) +{ + int exit_status; + + g_debug ("Waiting on process %d", slave->priv->pid); + exit_status = wait_on_child (slave->priv->pid); + + if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0)) { + g_debug ("Wait on child process failed"); + } else { + /* exited normally */ + } + + g_spawn_close_pid (slave->priv->pid); + slave->priv->pid = -1; + + g_debug ("Slave died"); +} + +static gboolean +output_watch (GIOChannel *source, + GIOCondition condition, + GdmSlave *slave) +{ + gboolean finished = FALSE; + + if (condition & G_IO_IN) { + GIOStatus status; + GError *error = NULL; + char *line; + + line = NULL; + status = g_io_channel_read_line (source, &line, NULL, NULL, &error); + + switch (status) { + case G_IO_STATUS_NORMAL: + { + char *p; + + g_debug ("command output: %s", line); + + if ((p = strstr (line, "ADDRESS=")) != NULL) { + char *address; + + address = g_strdup (p + strlen ("ADDRESS=")); + g_debug ("Got address %s", address); + + g_free (address); + } + } + break; + case G_IO_STATUS_EOF: + finished = TRUE; + break; + case G_IO_STATUS_ERROR: + finished = TRUE; + g_debug ("Error reading from child: %s\n", error->message); + return FALSE; + case G_IO_STATUS_AGAIN: + default: + break; + } + + g_free (line); + } else if (condition & G_IO_HUP) { + finished = TRUE; + } + + if (finished) { + slave_died (slave); + + slave->priv->output_watch_id = 0; + + return FALSE; + } + + return TRUE; +} + +/* just for debugging */ +static gboolean +error_watch (GIOChannel *source, + GIOCondition condition, + GdmSlave *slave) +{ + gboolean finished = FALSE; + + if (condition & G_IO_IN) { + GIOStatus status; + GError *error = NULL; + char *line; + + line = NULL; + status = g_io_channel_read_line (source, &line, NULL, NULL, &error); + + switch (status) { + case G_IO_STATUS_NORMAL: + g_debug ("command error output: %s", line); + break; + case G_IO_STATUS_EOF: + finished = TRUE; + break; + case G_IO_STATUS_ERROR: + finished = TRUE; + g_debug ("Error reading from child: %s\n", error->message); + return FALSE; + case G_IO_STATUS_AGAIN: + default: + break; + } + g_free (line); + } else if (condition & G_IO_HUP) { + finished = TRUE; + } + + if (finished) { + slave->priv->error_watch_id = 0; + + return FALSE; + } + + return TRUE; +} + +static gboolean +spawn_slave (GdmSlave *slave) +{ + char *command; + char **argv; + gboolean result; + GIOChannel *channel; + GError *error = NULL; + int standard_output; + int standard_error; + + + result = FALSE; + + command = g_strdup_printf ("%s --id %s", GDM_SLAVE_COMMAND, slave->priv->display_id); + + if (! g_shell_parse_argv (command, NULL, &argv, &error)) { + g_warning ("Could not parse command: %s", error->message); + g_error_free (error); + goto out; + } + + error = NULL; + result = g_spawn_async_with_pipes (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &slave->priv->pid, + NULL, + &standard_output, + &standard_error, + &error); + + if (! result) { + g_warning ("Could not start command '%s': %s", command, error->message); + g_error_free (error); + g_strfreev (argv); + goto out; + } + + g_strfreev (argv); + + /* output channel */ + channel = g_io_channel_unix_new (standard_output); + g_io_channel_set_close_on_unref (channel, TRUE); + g_io_channel_set_flags (channel, + g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK, + NULL); + slave->priv->output_watch_id = g_io_add_watch (channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc)output_watch, + slave); + g_io_channel_unref (channel); + + /* error channel */ + channel = g_io_channel_unix_new (standard_error); + g_io_channel_set_close_on_unref (channel, TRUE); + g_io_channel_set_flags (channel, + g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK, + NULL); + slave->priv->error_watch_id = g_io_add_watch (channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc)error_watch, + slave); + g_io_channel_unref (channel); + + result = TRUE; + + out: + g_free (command); + + return result; +} + +static int +signal_pid (int pid, + int signal) +{ + int status = -1; + + /* perhaps block sigchld */ + + status = kill (pid, signal); + + if (status < 0) { + if (errno == ESRCH) { + g_warning ("Child process %lu was already dead.", + (unsigned long) pid); + } else { + g_warning ("Couldn't kill child process %lu: %s", + (unsigned long) pid, + g_strerror (errno)); + } + } + + /* perhaps unblock sigchld */ + + return status; +} + +static void +kill_slave (GdmSlave *slave) +{ + if (slave->priv->pid <= 1) { + return; + } + + signal_pid (slave->priv->pid, SIGTERM); + + /* watch should call slave_died */ +} + +static void +set_busy_cursor (GdmSlave *slave) +{ + if (slave->priv->server_display != NULL) { + Cursor xcursor; + + xcursor = XCreateFontCursor (slave->priv->server_display, GDK_WATCH); + XDefineCursor (slave->priv->server_display, + DefaultRootWindow (slave->priv->server_display), + xcursor); + XFreeCursor (slave->priv->server_display, xcursor); + XSync (slave->priv->server_display, False); + } +} + +static void +gdm_slave_whack_temp_auth_file (GdmSlave *slave) +{ +#if 0 + uid_t old; + + old = geteuid (); + if (old != 0) + seteuid (0); + if (d->parent_temp_auth_file != NULL) { + VE_IGNORE_EINTR (g_unlink (d->parent_temp_auth_file)); + } + g_free (d->parent_temp_auth_file); + d->parent_temp_auth_file = NULL; + if (old != 0) + seteuid (old); +#endif +} + + +static void +create_temp_auth_file (GdmSlave *slave) +{ +#if 0 + if (d->type == TYPE_FLEXI_XNEST && + d->parent_auth_file != NULL) { + if (d->parent_temp_auth_file != NULL) { + VE_IGNORE_EINTR (g_unlink (d->parent_temp_auth_file)); + } + g_free (d->parent_temp_auth_file); + d->parent_temp_auth_file = + copy_auth_file (d->server_uid, + gdm_daemon_config_get_gdmuid (), + d->parent_auth_file); + } +#endif +} + +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_script_environment (GdmSlave *slave, + const char *username) +{ + GPtrArray *env; + char **l; + GHashTable *hash; + struct passwd *pwent; + char *x_servers_file; + + 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]); + } + + /* modify environment here */ + g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup ("/")); + g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup ("/")); + g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup ("/bin/sh")); + + g_hash_table_insert (hash, g_strdup ("LOGNAME"), g_strdup (username)); + g_hash_table_insert (hash, g_strdup ("USER"), g_strdup (username)); + g_hash_table_insert (hash, g_strdup ("USERNAME"), g_strdup (username)); + + pwent = getpwnam (username); + if (pwent != NULL) { + if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') { + g_hash_table_insert (hash, g_strdup ("HOME"), g_strdup (pwent->pw_dir)); + g_hash_table_insert (hash, g_strdup ("PWD"), g_strdup (pwent->pw_dir)); + } + + g_hash_table_insert (hash, g_strdup ("SHELL"), g_strdup (pwent->pw_shell)); + } + + if (slave->priv->display_is_parented) { + g_hash_table_insert (hash, g_strdup ("GDM_PARENT_DISPLAY"), g_strdup (slave->priv->parent_display_name)); + + /*g_hash_table_insert (hash, "GDM_PARENT_XAUTHORITY"), slave->priv->parent_temp_auth_file));*/ + } + + /* some env for use with the Pre and Post scripts */ + x_servers_file = gdm_make_filename (AUTHDIR, + slave->priv->display_name, + ".Xservers"); + g_hash_table_insert (hash, g_strdup ("X_SERVERS"), x_servers_file); + + if (! slave->priv->display_is_local) { + g_hash_table_insert (hash, g_strdup ("REMOTE_HOST"), g_strdup (slave->priv->display_hostname)); + } + + /* Runs as root */ + g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (slave->priv->display_auth_file)); + g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (slave->priv->display_name)); + + /*g_setenv ("PATH", gdm_daemon_config_get_value_string (GDM_KEY_ROOT_PATH), TRUE);*/ + + g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true")); + +#if 0 + if ( ! ve_string_empty (d->theme_name)) + g_setenv ("GDM_GTK_THEME", d->theme_name, TRUE); +#endif + 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 gboolean +gdm_slave_exec_script (GdmSlave *slave, + const char *dir, + const char *login, + struct passwd *pwent, + gboolean pass_stdout) +{ + char *script; + char **argv; + gint status; + GError *error; + GPtrArray *env; + gboolean res; + gboolean ret; + + script = g_build_filename (dir, slave->priv->display_name, NULL); + if (g_access (script, R_OK|X_OK) != 0) { + g_free (script); + script = NULL; + } + + if (script == NULL && + slave->priv->display_hostname != NULL) { + script = g_build_filename (dir, slave->priv->display_hostname, NULL); + if (g_access (script, R_OK|X_OK) != 0) { + g_free (script); + script = NULL; + } + } + +#if 0 + if (script == NULL && + SERVER_IS_XDMCP (d)) { + script = g_build_filename (dir, "XDMCP", NULL); + if (g_access (script, R_OK|X_OK) != 0) { + g_free (script); + script = NULL; + } + } + if (script == NULL && + SERVER_IS_FLEXI (d)) { + script = g_build_filename (dir, "Flexi", NULL); + if (g_access (script, R_OK|X_OK) != 0) { + g_free (script); + script = NULL; + } + } +#endif + + if (script == NULL) { + script = g_build_filename (dir, "Default", NULL); + if (g_access (script, R_OK|X_OK) != 0) { + g_free (script); + script = NULL; + } + } + + if (script == NULL) { + return TRUE; + } + + create_temp_auth_file (slave); + + g_debug ("Running process: %s", script); + error = NULL; + if (! g_shell_parse_argv (script, NULL, &argv, &error)) { + g_warning ("Could not parse command: %s", error->message); + g_error_free (error); + goto out; + } + + env = get_script_environment (slave, login); + + res = g_spawn_sync (NULL, + argv, + (char **)env->pdata, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + NULL, + NULL, + &status, + &error); + + g_ptr_array_foreach (env, (GFunc)g_free, NULL); + g_ptr_array_free (env, TRUE); + + gdm_slave_whack_temp_auth_file (slave); + + if (WIFEXITED (status)) { + ret = WEXITSTATUS (status) != 0; + } else { + ret = TRUE; + } + + out: + g_free (script); + + return ret; +} + +static gboolean +gdm_slave_run (GdmSlave *slave) +{ + /* if this is local display start a server if one doesn't + * exist */ + if (slave->priv->display_is_local) { + gboolean res; + + slave->priv->server = gdm_server_new (slave->priv->display_name); + + res = gdm_server_start (slave->priv->server); + if (! res) { + g_warning (_("Could not start the X " + "server (your graphical environment) " + "due to some internal error. " + "Please contact your system administrator " + "or check your syslog to diagnose. " + "In the meantime this display will be " + "disabled. Please restart GDM when " + "the problem is corrected.")); + exit (1); + } + } + + /* We can use d->handled from now on on this display, + * since the lookup was done in server start */ + + g_setenv ("DISPLAY", slave->priv->display_name, TRUE); + g_unsetenv ("XAUTHORITY"); /* just in case it's set */ + +#if 0 + gdm_auth_set_local_auth (d); +#endif + +#if 0 + /* X error handlers to avoid the default one (i.e. exit (1)) */ + do_xfailed_on_xio_error = TRUE; + XSetErrorHandler (gdm_slave_xerror_handler); + XSetIOErrorHandler (gdm_slave_xioerror_handler); +#endif + + /* We keep our own (windowless) connection (dsp) open to avoid the + * X server resetting due to lack of active connections. */ + + g_debug ("gdm_slave_run: Opening display %s", slave->priv->display_name); + + gdm_sigchld_block_push (); + slave->priv->server_display = XOpenDisplay (slave->priv->display_name); + gdm_sigchld_block_pop (); + + if (slave->priv->server_display == NULL) { + return FALSE; + } + + + /* FIXME: handle wait for go */ + + + /* Set the busy cursor */ + set_busy_cursor (slave); + + + /* FIXME: send a signal back to the master */ + +#if 0 + + /* OK from now on it's really the user whacking us most likely, + * we have already started up well */ + do_xfailed_on_xio_error = FALSE; +#endif + + /* If XDMCP setup pinging */ + if ( ! slave->priv->display_is_local && slave->priv->ping_interval > 0) { + alarm (slave->priv->ping_interval); + } + +#if 0 + /* checkout xinerama */ + gdm_screen_init (slave); +#endif + +#ifdef HAVE_TSOL + /* Check out Solaris Trusted Xserver extension */ + gdm_tsol_init (d); +#endif + + /* Run the init script. gdmslave suspends until script has terminated */ + gdm_slave_exec_script (slave, + GDMCONFDIR"/Init", + NULL, + NULL, + FALSE /* pass_stdout */); + + slave->priv->greeter = gdm_greeter_new (slave->priv->display_name); + gdm_greeter_start (slave->priv->greeter); + + /* If XDMCP stop pinging */ + if ( ! slave->priv->display_is_local) { + alarm (0); + } +} + +gboolean +gdm_slave_start (GdmSlave *slave) +{ + gboolean res; + char *id; + GError *error; + + g_debug ("Starting slave"); + + g_assert (slave->priv->display_proxy == NULL); + + g_debug ("Creating proxy for %s", slave->priv->display_id); + slave->priv->display_proxy = dbus_g_proxy_new_for_name (slave->priv->connection, + GDM_DBUS_NAME, + slave->priv->display_id, + GDM_DBUS_DISPLAY_INTERFACE); + if (slave->priv->display_proxy == NULL) { + g_warning ("Unable to create display proxy"); + return FALSE; + } + + /* Make sure display ID works */ + error = NULL; + res = dbus_g_proxy_call (slave->priv->display_proxy, + "GetId", + &error, + G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &id, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to get display id %s: %s", slave->priv->display_id, error->message); + g_error_free (error); + } else { + g_warning ("Failed to get display id %s", slave->priv->display_id); + } + + return FALSE; + } + + g_debug ("Got display id: %s", id); + + if (strcmp (id, slave->priv->display_id) != 0) { + g_critical ("Display ID doesn't match"); + exit (1); + } + + /* cache some values up front */ + error = NULL; + res = dbus_g_proxy_call (slave->priv->display_proxy, + "IsLocal", + &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &slave->priv->display_is_local, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to get value: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to get value"); + } + + return FALSE; + } + + error = NULL; + res = dbus_g_proxy_call (slave->priv->display_proxy, + "GetName", + &error, + G_TYPE_INVALID, + G_TYPE_STRING, &slave->priv->display_name, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to get value: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to get value"); + } + + return FALSE; + } + + error = NULL; + res = dbus_g_proxy_call (slave->priv->display_proxy, + "GetNumber", + &error, + G_TYPE_INVALID, + G_TYPE_STRING, &slave->priv->display_number, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to get value: %s", error->message); + g_error_free (error); + } else { + g_warning ("Failed to get value"); + } + + return FALSE; + } + + gdm_slave_run (slave); + + return TRUE; +} + +static gboolean +gdm_slave_stop (GdmSlave *slave) +{ + g_debug ("Stopping slave"); + + if (slave->priv->display_proxy != NULL) { + g_object_unref (slave->priv->display_proxy); + } + + return TRUE; +} + +static void +_gdm_slave_set_display_id (GdmSlave *slave, + const char *id) +{ + g_free (slave->priv->display_id); + slave->priv->display_id = g_strdup (id); +} + +static void +gdm_slave_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmSlave *self; + + self = GDM_SLAVE (object); + + switch (prop_id) { + case PROP_DISPLAY_ID: + _gdm_slave_set_display_id (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_slave_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmSlave *self; + + self = GDM_SLAVE (object); + + switch (prop_id) { + case PROP_DISPLAY_ID: + g_value_set_string (value, self->priv->display_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +register_slave (GdmSlave *slave) +{ + GError *error = NULL; + + error = NULL; + slave->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (slave->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + exit (1); + } + + dbus_g_connection_register_g_object (slave->priv->connection, slave->priv->id, G_OBJECT (slave)); + + return TRUE; +} + + +static GObject * +gdm_slave_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GdmSlave *slave; + GdmSlaveClass *klass; + gboolean res; + const char *id; + + klass = GDM_SLAVE_CLASS (g_type_class_peek (GDM_TYPE_SLAVE)); + + slave = GDM_SLAVE (G_OBJECT_CLASS (gdm_slave_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + id = NULL; + if (g_str_has_prefix (slave->priv->display_id, "/org/gnome/DisplayManager/Display")) { + id = slave->priv->display_id + strlen ("/org/gnome/DisplayManager/Display"); + } + + slave->priv->id = g_strdup_printf ("/org/gnome/DisplayManager/Slave%s", id); + g_debug ("Registering %s", slave->priv->id); + + res = register_slave (slave); + if (! res) { + g_warning ("Unable to register slave with system bus"); + } + + return G_OBJECT (slave); +} + +static void +gdm_slave_class_init (GdmSlaveClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_slave_get_property; + object_class->set_property = gdm_slave_set_property; + object_class->constructor = gdm_slave_constructor; + object_class->finalize = gdm_slave_finalize; + + g_type_class_add_private (klass, sizeof (GdmSlavePrivate)); + + g_object_class_install_property (object_class, + PROP_DISPLAY_ID, + g_param_spec_string ("display-id", + "id", + "id", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + dbus_g_object_type_install_info (GDM_TYPE_SLAVE, &dbus_glib_gdm_slave_object_info); +} + +static void +gdm_slave_init (GdmSlave *slave) +{ + + slave->priv = GDM_SLAVE_GET_PRIVATE (slave); + + slave->priv->pid = -1; +} + +static void +gdm_slave_finalize (GObject *object) +{ + GdmSlave *slave; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_SLAVE (object)); + + slave = GDM_SLAVE (object); + + g_return_if_fail (slave->priv != NULL); + + G_OBJECT_CLASS (gdm_slave_parent_class)->finalize (object); +} + +GdmSlave * +gdm_slave_new (const char *id) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_SLAVE, + "display-id", id, + NULL); + + return GDM_SLAVE (object); +} diff --git a/daemon/gdm-slave.h b/daemon/gdm-slave.h new file mode 100644 index 00000000..ffc198ae --- /dev/null +++ b/daemon/gdm-slave.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_SLAVE_H +#define __GDM_SLAVE_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_SLAVE (gdm_slave_get_type ()) +#define GDM_SLAVE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_SLAVE, GdmSlave)) +#define GDM_SLAVE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_SLAVE, GdmSlaveClass)) +#define GDM_IS_SLAVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_SLAVE)) +#define GDM_IS_SLAVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_SLAVE)) +#define GDM_SLAVE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_SLAVE, GdmSlaveClass)) + +typedef struct GdmSlavePrivate GdmSlavePrivate; + +typedef struct +{ + GObject parent; + GdmSlavePrivate *priv; +} GdmSlave; + +typedef struct +{ + GObjectClass parent_class; + +} GdmSlaveClass; + +GType gdm_slave_get_type (void); +GdmSlave * gdm_slave_new (const char *display_id); +gboolean gdm_slave_start (GdmSlave *slave); + +G_END_DECLS + +#endif /* __GDM_SLAVE_H */ diff --git a/daemon/gdm-slave.xml b/daemon/gdm-slave.xml new file mode 100644 index 00000000..65665949 --- /dev/null +++ b/daemon/gdm-slave.xml @@ -0,0 +1,5 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.gnome.DisplayManager.Slave"> + </interface> +</node> diff --git a/daemon/gdm-static-display.c b/daemon/gdm-static-display.c new file mode 100644 index 00000000..f02f40c4 --- /dev/null +++ b/daemon/gdm-static-display.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gdm-display.h" +#include "gdm-static-display.h" +#include "gdm-static-display-glue.h" + +#define GDM_STATIC_DISPLAY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_STATIC_DISPLAY, GdmStaticDisplayPrivate)) + +struct GdmStaticDisplayPrivate +{ + struct sockaddr_storage addr; +}; + +enum { + PROP_0, +}; + +static void gdm_static_display_class_init (GdmStaticDisplayClass *klass); +static void gdm_static_display_init (GdmStaticDisplay *static_display); +static void gdm_static_display_finalize (GObject *object); + +G_DEFINE_TYPE (GdmStaticDisplay, gdm_static_display, GDM_TYPE_DISPLAY) + +static gboolean +gdm_static_display_create_authority (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + GDM_DISPLAY_CLASS (gdm_static_display_parent_class)->create_authority (display); + + return TRUE; +} + +static gboolean +gdm_static_display_manage (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + GDM_DISPLAY_CLASS (gdm_static_display_parent_class)->manage (display); + + return TRUE; +} + +static gboolean +gdm_static_display_unmanage (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + GDM_DISPLAY_CLASS (gdm_static_display_parent_class)->unmanage (display); + + return TRUE; +} + +static void +gdm_static_display_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmStaticDisplay *self; + + self = GDM_STATIC_DISPLAY (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_static_display_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmStaticDisplay *self; + + self = GDM_STATIC_DISPLAY (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_static_display_class_init (GdmStaticDisplayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass); + + object_class->get_property = gdm_static_display_get_property; + object_class->set_property = gdm_static_display_set_property; + object_class->finalize = gdm_static_display_finalize; + + display_class->create_authority = gdm_static_display_create_authority; + display_class->manage = gdm_static_display_manage; + display_class->unmanage = gdm_static_display_unmanage; + + g_type_class_add_private (klass, sizeof (GdmStaticDisplayPrivate)); + + dbus_g_object_type_install_info (GDM_TYPE_STATIC_DISPLAY, &dbus_glib_gdm_static_display_object_info); +} + +static void +gdm_static_display_init (GdmStaticDisplay *static_display) +{ + + static_display->priv = GDM_STATIC_DISPLAY_GET_PRIVATE (static_display); +} + +static void +gdm_static_display_finalize (GObject *object) +{ + GdmStaticDisplay *static_display; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_STATIC_DISPLAY (object)); + + static_display = GDM_STATIC_DISPLAY (object); + + g_return_if_fail (static_display->priv != NULL); + + G_OBJECT_CLASS (gdm_static_display_parent_class)->finalize (object); +} + +GdmDisplay * +gdm_static_display_new (int number, + const char *name) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_STATIC_DISPLAY, + "number", number, + "name", name, + NULL); + + return GDM_DISPLAY (object); +} diff --git a/daemon/gdm-static-display.h b/daemon/gdm-static-display.h new file mode 100644 index 00000000..30b94fcd --- /dev/null +++ b/daemon/gdm-static-display.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_STATIC_DISPLAY_H +#define __GDM_STATIC_DISPLAY_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> +#include "gdm-display.h" +#include <sys/types.h> +#include <sys/socket.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_STATIC_DISPLAY (gdm_static_display_get_type ()) +#define GDM_STATIC_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_STATIC_DISPLAY, GdmStaticDisplay)) +#define GDM_STATIC_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_STATIC_DISPLAY, GdmStaticDisplayClass)) +#define GDM_IS_STATIC_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_STATIC_DISPLAY)) +#define GDM_IS_STATIC_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_STATIC_DISPLAY)) +#define GDM_STATIC_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_STATIC_DISPLAY, GdmStaticDisplayClass)) + +typedef struct GdmStaticDisplayPrivate GdmStaticDisplayPrivate; + +typedef struct +{ + GdmDisplay parent; + GdmStaticDisplayPrivate *priv; +} GdmStaticDisplay; + +typedef struct +{ + GdmDisplayClass parent_class; + +} GdmStaticDisplayClass; + +GType gdm_static_display_get_type (void); +GdmDisplay * gdm_static_display_new (int number, + const char *name); + + +G_END_DECLS + +#endif /* __GDM_STATIC_DISPLAY_H */ diff --git a/daemon/gdm-static-display.xml b/daemon/gdm-static-display.xml new file mode 100644 index 00000000..bd3670b8 --- /dev/null +++ b/daemon/gdm-static-display.xml @@ -0,0 +1,5 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.gnome.DisplayManager.StaticDisplay"> + </interface> +</node> diff --git a/daemon/gdm-xdmcp-display.c b/daemon/gdm-xdmcp-display.c new file mode 100644 index 00000000..df2342a9 --- /dev/null +++ b/daemon/gdm-xdmcp-display.c @@ -0,0 +1,329 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gdm-display.h" +#include "gdm-xdmcp-display.h" +#include "gdm-xdmcp-display-glue.h" + +#include "gdm-common.h" +#include "gdm-address.h" + +#include "cookie.h" +#include "auth.h" + +#define GDM_XDMCP_DISPLAY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_XDMCP_DISPLAY, GdmXdmcpDisplayPrivate)) + +struct GdmXdmcpDisplayPrivate +{ + char *remote_hostname; + GdmAddress *remote_address; + gint32 session_number; +}; + +enum { + PROP_0, + PROP_REMOTE_HOSTNAME, + PROP_REMOTE_ADDRESS, + PROP_SESSION_NUMBER, +}; + +static void gdm_xdmcp_display_class_init (GdmXdmcpDisplayClass *klass); +static void gdm_xdmcp_display_init (GdmXdmcpDisplay *xdmcp_display); +static void gdm_xdmcp_display_finalize (GObject *object); + +G_DEFINE_TYPE (GdmXdmcpDisplay, gdm_xdmcp_display, GDM_TYPE_DISPLAY) + +gint32 +gdm_xdmcp_display_get_session_number (GdmXdmcpDisplay *display) +{ + g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY (display), 0); + + return display->priv->session_number; +} + +gboolean +gdm_xdmcp_display_get_remote_hostname (GdmXdmcpDisplay *display, + char **hostname, + GError **error) +{ + g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY (display), FALSE); + + if (hostname != NULL) { + *hostname = g_strdup (display->priv->remote_hostname); + } + + return TRUE; +} + +GdmAddress * +gdm_xdmcp_display_get_remote_address (GdmXdmcpDisplay *display) +{ + g_return_val_if_fail (GDM_IS_XDMCP_DISPLAY (display), NULL); + + return display->priv->remote_address; +} + +static gboolean +gdm_xdmcp_display_create_authority (GdmDisplay *display) +{ + FILE *af; + int closeret; + gboolean ret; + char *authfile; + int display_num; + char *name; + char *cookie; + char *bcookie; + GSList *authlist; + char *basename; + + ret = FALSE; + name = NULL; + + g_object_get (display, + "name", &name, + "number", &display_num, + NULL); + + g_debug ("Setting up access for %s", name); + + /* gdm and xserver authfile can be the same, server will run as root */ + basename = g_strconcat (name, ".Xauth", NULL); + authfile = g_build_filename (AUTHDIR, basename, NULL); + g_free (basename); + + af = gdm_safe_fopen_w (authfile, 0644); + if (af == NULL) { + g_warning (_("Cannot safely open %s"), authfile); + g_free (authfile); + goto out; + } + + /* Create new random cookie */ + gdm_cookie_generate (&cookie, &bcookie); + authlist = NULL; + if (! gdm_auth_add_entry_for_display (display_num, bcookie, &authlist, af)) { + goto out; + } + + g_debug ("gdm_auth_secure_display: Setting up access"); + + VE_IGNORE_EINTR (closeret = fclose (af)); + if (closeret < 0) { + g_warning (_("Could not write new authorization entry: %s"), + g_strerror (errno)); + goto out; + } + + g_debug ("Set up access for %s - %d entries", + name, + g_slist_length (authlist)); + + /* FIXME: save authlist */ + + g_object_set (display, + "authority-file", authfile, + "cookie", cookie, + "binary-cookie", bcookie, + NULL); + + ret = TRUE; + + out: + g_free (name); + + return ret; +} + +static gboolean +gdm_xdmcp_display_manage (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + GDM_DISPLAY_CLASS (gdm_xdmcp_display_parent_class)->manage (display); + + return TRUE; +} + +static gboolean +gdm_xdmcp_display_unmanage (GdmDisplay *display) +{ + g_return_val_if_fail (GDM_IS_DISPLAY (display), FALSE); + + GDM_DISPLAY_CLASS (gdm_xdmcp_display_parent_class)->unmanage (display); + + return TRUE; +} + +static void +gdm_xdmcp_display_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmXdmcpDisplay *self; + + self = GDM_XDMCP_DISPLAY (object); + + switch (prop_id) { + case PROP_REMOTE_HOSTNAME: + self->priv->remote_hostname = g_value_dup_string (value); + break; + case PROP_REMOTE_ADDRESS: + self->priv->remote_address = g_value_get_boxed (value); + break; + case PROP_SESSION_NUMBER: + self->priv->session_number = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_xdmcp_display_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmXdmcpDisplay *self; + + self = GDM_XDMCP_DISPLAY (object); + + switch (prop_id) { + case PROP_REMOTE_HOSTNAME: + g_value_set_string (value, self->priv->remote_hostname); + break; + case PROP_REMOTE_ADDRESS: + g_value_set_boxed (value, self->priv->remote_address); + break; + case PROP_SESSION_NUMBER: + g_value_set_int (value, self->priv->session_number); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_xdmcp_display_class_init (GdmXdmcpDisplayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdmDisplayClass *display_class = GDM_DISPLAY_CLASS (klass); + + object_class->get_property = gdm_xdmcp_display_get_property; + object_class->set_property = gdm_xdmcp_display_set_property; + object_class->finalize = gdm_xdmcp_display_finalize; + + display_class->create_authority = gdm_xdmcp_display_create_authority; + display_class->manage = gdm_xdmcp_display_manage; + display_class->unmanage = gdm_xdmcp_display_unmanage; + + g_type_class_add_private (klass, sizeof (GdmXdmcpDisplayPrivate)); + + g_object_class_install_property (object_class, + PROP_REMOTE_HOSTNAME, + g_param_spec_string ("remote-hostname", + "remote-hostname", + "remote-hostname", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_REMOTE_ADDRESS, + g_param_spec_boxed ("remote-address", + "Remote address", + "Remote address", + GDM_TYPE_ADDRESS, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_SESSION_NUMBER, + g_param_spec_int ("session-number", + "session-number", + "session-number", + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + dbus_g_object_type_install_info (GDM_TYPE_XDMCP_DISPLAY, &dbus_glib_gdm_xdmcp_display_object_info); +} + +static void +gdm_xdmcp_display_init (GdmXdmcpDisplay *xdmcp_display) +{ + + xdmcp_display->priv = GDM_XDMCP_DISPLAY_GET_PRIVATE (xdmcp_display); +} + +static void +gdm_xdmcp_display_finalize (GObject *object) +{ + GdmXdmcpDisplay *xdmcp_display; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_XDMCP_DISPLAY (object)); + + xdmcp_display = GDM_XDMCP_DISPLAY (object); + + g_return_if_fail (xdmcp_display->priv != NULL); + + G_OBJECT_CLASS (gdm_xdmcp_display_parent_class)->finalize (object); +} + +GdmDisplay * +gdm_xdmcp_display_new (int number, + const char *name, + const char *hostname, + GdmAddress *address, + gint32 session_number) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_XDMCP_DISPLAY, + "is-local", FALSE, + "number", number, + "name", name, + "remote-hostname", hostname, + "remote-address", address, + "session-number", session_number, + NULL); + + return GDM_DISPLAY (object); +} diff --git a/daemon/gdm-xdmcp-display.h b/daemon/gdm-xdmcp-display.h new file mode 100644 index 00000000..3a132588 --- /dev/null +++ b/daemon/gdm-xdmcp-display.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef __GDM_XDMCP_DISPLAY_H +#define __GDM_XDMCP_DISPLAY_H + +#include <sys/types.h> +#include <sys/socket.h> +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +#include "gdm-display.h" +#include "gdm-address.h" + +G_BEGIN_DECLS + +#define GDM_TYPE_XDMCP_DISPLAY (gdm_xdmcp_display_get_type ()) +#define GDM_XDMCP_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_XDMCP_DISPLAY, GdmXdmcpDisplay)) +#define GDM_XDMCP_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_XDMCP_DISPLAY, GdmXdmcpDisplayClass)) +#define GDM_IS_XDMCP_DISPLAY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_XDMCP_DISPLAY)) +#define GDM_IS_XDMCP_DISPLAY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_XDMCP_DISPLAY)) +#define GDM_XDMCP_DISPLAY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_XDMCP_DISPLAY, GdmXdmcpDisplayClass)) + +typedef struct GdmXdmcpDisplayPrivate GdmXdmcpDisplayPrivate; + +typedef struct +{ + GdmDisplay parent; + GdmXdmcpDisplayPrivate *priv; +} GdmXdmcpDisplay; + +typedef struct +{ + GdmDisplayClass parent_class; + +} GdmXdmcpDisplayClass; + +GType gdm_xdmcp_display_get_type (void); + + +GdmDisplay * gdm_xdmcp_display_new (int number, + const char *name, + const char *hostname, + GdmAddress *addr, + gint32 serial_number); + +gint32 gdm_xdmcp_display_get_session_number (GdmXdmcpDisplay *display); +GdmAddress * gdm_xdmcp_display_get_remote_address (GdmXdmcpDisplay *display); + + +/* exported */ +gboolean gdm_xdmcp_display_get_remote_hostname (GdmXdmcpDisplay *display, + char **hostname, + GError **error); + + +G_END_DECLS + +#endif /* __GDM_XDMCP_DISPLAY_H */ diff --git a/daemon/gdm-xdmcp-display.xml b/daemon/gdm-xdmcp-display.xml new file mode 100644 index 00000000..512019ce --- /dev/null +++ b/daemon/gdm-xdmcp-display.xml @@ -0,0 +1,5 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.gnome.DisplayManager.XdmcpDisplay"> + </interface> +</node> diff --git a/daemon/gdm-xdmcp-manager.c b/daemon/gdm-xdmcp-manager.c index ad377024..297dfc89 100644 --- a/daemon/gdm-xdmcp-manager.c +++ b/daemon/gdm-xdmcp-manager.c @@ -51,13 +51,15 @@ #include <X11/Xdmcp.h> #include "gdm-common.h" +#include "gdm-xdmcp-display.h" #include "gdm-xdmcp-manager.h" +#include "gdm-display-store.h" -#include "misc.h" #include "auth.h" #include "cookie.h" #include "choose.h" -#include "gdm-daemon-config.h" +#include "gdm-master-config.h" +#include "gdm-daemon-config-entries.h" /* * On Sun, we need to define allow_severity and deny_severity to link @@ -138,21 +140,23 @@ static XdmAuthRec serv_authlist = { /* NOTE: Timeout and max are hardcoded */ typedef struct _GdmForwardQuery { - time_t acctime; - struct sockaddr_storage *dsp_sa; - struct sockaddr_storage *from_sa; + time_t acctime; + GdmAddress *dsp_address; + GdmAddress *from_address; } GdmForwardQuery; typedef struct { - int times; - guint handler; - struct sockaddr_storage manager; - struct sockaddr_storage origin; - GdmXdmcpManager *xdmcp_manager; + int times; + guint handler; + GdmAddress *manager; + GdmAddress *origin; + GdmXdmcpManager *xdmcp_manager; } ManagedForward; struct GdmXdmcpManagerPrivate { + GdmDisplayStore *display_store; + GSList *forward_queries; GSList *managed_forwards; @@ -188,6 +192,7 @@ enum { enum { PROP_0, + PROP_DISPLAY_STORE, PROP_PORT, PROP_USE_MULTICAST, PROP_MULTICAST_ADDRESS, @@ -449,13 +454,17 @@ do_bind (guint port, debug_addrinfo (ai); if (sock < 0) { - char *host; - char *serv; + char *host; + char *serv; + GdmAddress *addr; - gdm_address_get_info ((struct sockaddr_storage *)ai->ai_addr, &host, &serv); + addr = gdm_address_new_from_sockaddr_storage ((struct sockaddr_storage *)ai->ai_addr); + gdm_address_get_numeric_info (addr, &host, &serv); g_debug ("XDMCP: Attempting to bind to host %s port %s", host, serv); g_free (host); g_free (serv); + gdm_address_free (addr); + sock = create_socket (ai); if (sock >= 0) { if (hostaddr != NULL) { @@ -568,6 +577,8 @@ open_port (GdmXdmcpManager *manager) return FALSE; } + gdm_fd_set_close_on_exec (manager->priv->socket_fd); + if (manager->priv->use_multicast) { setup_multicast (manager); } @@ -576,7 +587,7 @@ open_port (GdmXdmcpManager *manager) } static gboolean -gdm_xdmcp_host_allow (struct sockaddr_storage *clnt_sa) +gdm_xdmcp_host_allow (GdmAddress *address) { #ifdef HAVE_TCPWRAPPERS @@ -589,29 +600,21 @@ gdm_xdmcp_host_allow (struct sockaddr_storage *clnt_sa) char *client_addr, char *client_user); - GdmHostent *client_he; char *client; - gboolean ret; char *host; + gboolean ret; - /* Find client hostname */ - client_he = gdm_gethostbyaddr (clnt_sa); + host = NULL; - if (client_he->not_found) { - client = "unknown"; - } else { - g_debug ("gdm_xdmcp_host_allow: client->hostname is %s\n", - client_he->hostname); - client = client_he->hostname; - } + /* Find client hostname */ + client = gdm_address_get_hostname (address); + gdm_address_get_numeric_info (address, &host, NULL); /* Check with tcp_wrappers if client is allowed to access */ - host = NULL; - gdm_address_get_info (clnt_sa, &host, NULL); ret = hosts_ctl ("gdm", client, host, ""); - g_free (host); - gdm_hostent_free (client_he); + g_free (host); + g_free (client); return ret; #else /* HAVE_TCPWRAPPERS */ @@ -619,49 +622,91 @@ gdm_xdmcp_host_allow (struct sockaddr_storage *clnt_sa) #endif /* HAVE_TCPWRAPPERS */ } -static int -gdm_xdmcp_num_displays_from_host (GdmXdmcpManager *manager, - struct sockaddr_storage *addr) +typedef struct { + GdmAddress *address; + int count; +} CountDisplayData; + +static gboolean +count_displays_from_host (const char *id, + GdmDisplay *display, + CountDisplayData *data) { - GSList *li; - int count = 0; - GSList *displays; + GdmAddress *address; - displays = gdm_daemon_config_get_display_list (); + if (GDM_IS_XDMCP_DISPLAY (display)) { + address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display)); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (SERVER_IS_XDMCP (disp)) { - if (gdm_address_equal (&disp->addr, addr)) { - count++; - } + if (gdm_address_equal (address, data->address)) { + data->count++; } } - return count; + + return TRUE; } -static GdmDisplay * -gdm_xdmcp_display_lookup_by_host (GdmXdmcpManager *manager, - struct sockaddr_storage *addr, - int dspnum) +static int +gdm_xdmcp_num_displays_from_host (GdmXdmcpManager *manager, + GdmAddress *address) { - GSList *li; - GSList *displays; + CountDisplayData data; - displays = gdm_daemon_config_get_display_list (); + data.count = 0; + data.address = address; - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (SERVER_IS_XDMCP (disp)) { + gdm_display_store_foreach (manager->priv->display_store, + (GdmDisplayStoreFunc)count_displays_from_host, + &data); - if (gdm_address_equal (&disp->addr, addr) - && disp->xdmcp_dispnum == dspnum) { - return disp; - } - } + return data.count; +} + +typedef struct { + GdmAddress *address; + int display_num; +} LookupHostData; + +static gboolean +lookup_by_host (const char *id, + GdmDisplay *display, + LookupHostData *data) +{ + GdmAddress *this_address; + int disp_num; + + if (! GDM_IS_XDMCP_DISPLAY (display)) { + return FALSE; + } + + this_address = gdm_xdmcp_display_get_remote_address (GDM_XDMCP_DISPLAY (display)); + gdm_display_get_number (display, &disp_num, NULL); + + if (gdm_address_equal (this_address, data->address) + && disp_num == data->display_num) { + return TRUE; } - return NULL; + return FALSE; +} + +static GdmDisplay * +gdm_xdmcp_display_lookup_by_host (GdmXdmcpManager *manager, + GdmAddress *address, + int display_num) +{ + GdmDisplay *display; + LookupHostData *data; + + data = g_new0 (LookupHostData, 1); + data->address = address; + data->display_num = display_num; + + display = gdm_display_store_find (manager->priv->display_store, + (GdmDisplayStoreFunc)lookup_by_host, + data); + g_free (data); + + return display; } static char * @@ -709,8 +754,8 @@ get_willing_output (GdmXdmcpManager *manager) } static void -gdm_xdmcp_send_willing (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa) +gdm_xdmcp_send_willing (GdmXdmcpManager *manager, + GdmAddress *address) { ARRAY8 status; XdmcpHeader header; @@ -718,7 +763,7 @@ gdm_xdmcp_send_willing (GdmXdmcpManager *manager, static time_t last_willing = 0; char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XDMCP: Sending WILLING to %s", host); g_free (host); @@ -736,8 +781,8 @@ gdm_xdmcp_send_willing (GdmXdmcpManager *manager, } } - if (! gdm_address_is_local (clnt_sa) && - gdm_xdmcp_num_displays_from_host (manager, clnt_sa) >= manager->priv->max_displays_per_host) { + if (! gdm_address_is_local (address) && + gdm_xdmcp_num_displays_from_host (manager, address) >= manager->priv->max_displays_per_host) { /* * Don't translate, this goes over the wire to servers where we * don't know the charset or language, so it must be ascii @@ -763,16 +808,16 @@ gdm_xdmcp_send_willing (GdmXdmcpManager *manager, XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); g_free (status.data); } static void -gdm_xdmcp_send_unwilling (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int type) +gdm_xdmcp_send_unwilling (GdmXdmcpManager *manager, + GdmAddress *address, + int type) { ARRAY8 status; XdmcpHeader header; @@ -785,7 +830,7 @@ gdm_xdmcp_send_unwilling (GdmXdmcpManager *manager, return; } - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XDMCP: Sending UNWILLING to %s", host); g_warning (_("Denied XDMCP query from host %s"), host); g_free (host); @@ -806,7 +851,7 @@ gdm_xdmcp_send_unwilling (GdmXdmcpManager *manager, XdmcpWriteARRAY8 (&manager->priv->buf, &status); XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); last_time = time (NULL); @@ -816,9 +861,13 @@ gdm_xdmcp_send_unwilling (GdmXdmcpManager *manager, #define SIN6(__s) ((struct sockaddr_in6 *) __s) static void -set_port_for_request (struct sockaddr_storage *ss, - ARRAY8 *port) +set_port_for_request (GdmAddress *address, + ARRAY8 *port) { + struct sockaddr_storage *ss; + + ss = gdm_address_peek_sockaddr_storage (address); + /* we depend on this being 2 elsewhere as well */ port->length = 2; @@ -836,22 +885,25 @@ set_port_for_request (struct sockaddr_storage *ss, } static void -set_address_for_request (struct sockaddr_storage *ss, - ARRAY8 *address) +set_address_for_request (GdmAddress *address, + ARRAY8 *addr) { + struct sockaddr_storage *ss; + + ss = gdm_address_peek_sockaddr_storage (address); switch (ss->ss_family) { case AF_INET: - address->length = sizeof (struct in_addr); - address->data = g_memdup (&SIN (ss)->sin_addr, address->length); + addr->length = sizeof (struct in_addr); + addr->data = g_memdup (&SIN (ss)->sin_addr, addr->length); break; case AF_INET6: - address->length = sizeof (struct in6_addr); - address->data = g_memdup (&SIN6 (ss)->sin6_addr, address->length); + addr->length = sizeof (struct in6_addr); + addr->data = g_memdup (&SIN6 (ss)->sin6_addr, addr->length); break; default: - address->length = 0; - address->data = NULL; + addr->length = 0; + addr->data = NULL; break; } @@ -860,14 +912,13 @@ set_address_for_request (struct sockaddr_storage *ss, static void gdm_xdmcp_send_forward_query (GdmXdmcpManager *manager, GdmIndirectDisplay *id, - struct sockaddr_storage *clnt_sa, - struct sockaddr_storage *display_addr, + GdmAddress *address, + GdmAddress *display_address, ARRAYofARRAY8Ptr authlist) { - struct sockaddr_storage *sa; XdmcpHeader header; int i; - ARRAY8 address; + ARRAY8 addr; ARRAY8 port; char *host; char *serv; @@ -875,26 +926,24 @@ gdm_xdmcp_send_forward_query (GdmXdmcpManager *manager, g_assert (id != NULL); g_assert (id->chosen_host != NULL); - gdm_address_get_info (id->chosen_host, &host, NULL); + gdm_address_get_numeric_info (id->chosen_host, &host, NULL); g_debug ("XDMCP: Sending forward query to %s", host); g_free (host); - gdm_address_get_info (display_addr, &host, &serv); + gdm_address_get_numeric_info (display_address, &host, &serv); g_debug ("gdm_xdmcp_send_forward_query: Query contains %s:%s", host, serv); g_free (host); g_free (serv); - set_port_for_request (clnt_sa, &port); - set_address_for_request (display_addr, &address); - - sa = g_memdup (id->chosen_host, sizeof (id->chosen_host)); + set_port_for_request (address, &port); + set_address_for_request (display_address, &addr); header.version = XDM_PROTOCOL_VERSION; header.opcode = (CARD16) FORWARD_QUERY; header.length = 0; - header.length += 2 + address.length; + header.length += 2 + addr.length; header.length += 2 + port.length; header.length += 1; for (i = 0; i < authlist->length; i++) { @@ -902,32 +951,31 @@ gdm_xdmcp_send_forward_query (GdmXdmcpManager *manager, } XdmcpWriteHeader (&manager->priv->buf, &header); - XdmcpWriteARRAY8 (&manager->priv->buf, &address); + XdmcpWriteARRAY8 (&manager->priv->buf, &addr); XdmcpWriteARRAY8 (&manager->priv->buf, &port); XdmcpWriteARRAYofARRAY8 (&manager->priv->buf, authlist); XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr) sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (id->chosen_host), (int)sizeof (struct sockaddr_storage)); g_free (port.data); - g_free (address.data); - g_free (sa); + g_free (addr.data); } static void handle_any_query (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, + GdmAddress *address, ARRAYofARRAY8Ptr authentication_names, int type) { - gdm_xdmcp_send_willing (manager, clnt_sa); + gdm_xdmcp_send_willing (manager, address); } static void handle_direct_query (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, + GdmAddress *address, int len, int type) { @@ -949,7 +997,7 @@ handle_direct_query (GdmXdmcpManager *manager, } if (len == expected_len) { - handle_any_query (manager, clnt_sa, &clnt_authlist, type); + handle_any_query (manager, address, &clnt_authlist, type); } else { g_warning (_("Error in checksum")); } @@ -958,40 +1006,41 @@ handle_direct_query (GdmXdmcpManager *manager, } static void -gdm_xdmcp_handle_broadcast_query (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_broadcast_query (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { - if (gdm_xdmcp_host_allow (clnt_sa)) { - handle_direct_query (manager, clnt_sa, len, BROADCAST_QUERY); + if (gdm_xdmcp_host_allow (address)) { + handle_direct_query (manager, address, len, BROADCAST_QUERY); } else { /* just ignore it */ } } static void -gdm_xdmcp_handle_query (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_query (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { - if (gdm_xdmcp_host_allow (clnt_sa)) { - handle_direct_query (manager, clnt_sa, len, QUERY); + if (gdm_xdmcp_host_allow (address)) { + handle_direct_query (manager, address, len, QUERY); } else { - gdm_xdmcp_send_unwilling (manager, clnt_sa, QUERY); + gdm_xdmcp_send_unwilling (manager, address, QUERY); } } static void -gdm_xdmcp_handle_indirect_query (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) -{ - ARRAYofARRAY8 clnt_authlist; - int expected_len; - int i; - int res; +gdm_xdmcp_handle_indirect_query (GdmXdmcpManager *manager, + GdmAddress *address, + int len) +{ + ARRAYofARRAY8 clnt_authlist; + int expected_len; + int i; + int res; + GdmIndirectDisplay *id; - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { /* ignore the request */ return; } @@ -1017,59 +1066,59 @@ gdm_xdmcp_handle_indirect_query (GdmXdmcpManager *manager, * the pending list. If found send a FORWARD_QUERY to the * chosen manager. Otherwise alloc a new indirect display. */ - if (len == expected_len) { - GdmIndirectDisplay *id; - - id = gdm_choose_indirect_lookup (clnt_sa); - - if (id != NULL && id->chosen_host != NULL) { - /* if user chose us, then just send willing */ - if (gdm_address_is_local (id->chosen_host)) { - /* get rid of indirect, so that we don't get - * the chooser */ - gdm_choose_indirect_dispose (id); - gdm_xdmcp_send_willing (manager, clnt_sa); - } else if (gdm_address_is_loopback (clnt_sa)) { - /* woohoo! fun, I have no clue how to get - * the correct ip, SO I just send forward - * queries with all the different IPs */ - const GList *list = gdm_address_peek_local_list (); - - while (list != NULL) { - struct sockaddr_storage *saddr = list->data; - - if (! gdm_address_is_loopback (saddr)) { - /* forward query to * chosen host */ - gdm_xdmcp_send_forward_query (manager, - id, - clnt_sa, - saddr, - &clnt_authlist); - } - - list = list->next; + if (len != expected_len) { + g_warning (_("Error in checksum")); + goto out; + } + + + id = gdm_choose_indirect_lookup (address); + + if (id != NULL && id->chosen_host != NULL) { + /* if user chose us, then just send willing */ + if (gdm_address_is_local (id->chosen_host)) { + /* get rid of indirect, so that we don't get + * the chooser */ + gdm_choose_indirect_dispose (id); + gdm_xdmcp_send_willing (manager, address); + } else if (gdm_address_is_loopback (address)) { + /* woohoo! fun, I have no clue how to get + * the correct ip, SO I just send forward + * queries with all the different IPs */ + const GList *list = gdm_address_peek_local_list (); + + while (list != NULL) { + GdmAddress *saddr = list->data; + + if (! gdm_address_is_loopback (saddr)) { + /* forward query to * chosen host */ + gdm_xdmcp_send_forward_query (manager, + id, + address, + saddr, + &clnt_authlist); } - } else { - /* or send forward query to chosen host */ - gdm_xdmcp_send_forward_query (manager, - id, - clnt_sa, - clnt_sa, - &clnt_authlist); - } - } else if (id == NULL) { - id = gdm_choose_indirect_alloc (clnt_sa); - if (id != NULL) { - gdm_xdmcp_send_willing (manager, clnt_sa); + + list = list->next; } - } else { - gdm_xdmcp_send_willing (manager, clnt_sa); + } else { + /* or send forward query to chosen host */ + gdm_xdmcp_send_forward_query (manager, + id, + address, + address, + &clnt_authlist); } - - } else { - g_warning (_("Error in checksum")); + } else if (id == NULL) { + id = gdm_choose_indirect_alloc (address); + if (id != NULL) { + gdm_xdmcp_send_willing (manager, address); + } + } else { + gdm_xdmcp_send_willing (manager, address); } +out: XdmcpDisposeARRAYofARRAY8 (&clnt_authlist); } @@ -1088,15 +1137,15 @@ gdm_forward_query_dispose (GdmXdmcpManager *manager, { char *host; - gdm_address_get_info (q->dsp_sa, &host, NULL); + gdm_address_get_numeric_info (q->dsp_address, &host, NULL); g_debug ("gdm_forward_query_dispose: Disposing %s", host); g_free (host); } - g_free (q->dsp_sa); - q->dsp_sa = NULL; - g_free (q->from_sa); - q->from_sa = NULL; + g_free (q->dsp_address); + q->dsp_address = NULL; + g_free (q->from_address); + q->from_address = NULL; g_free (q); } @@ -1124,9 +1173,9 @@ remove_oldest_forward (GdmXdmcpManager *manager) } static GdmForwardQuery * -gdm_forward_query_alloc (GdmXdmcpManager *manager, - struct sockaddr_storage *mgr_sa, - struct sockaddr_storage *dsp_sa) +gdm_forward_query_alloc (GdmXdmcpManager *manager, + GdmAddress *mgr_address, + GdmAddress *dsp_address) { GdmForwardQuery *q; int count; @@ -1138,8 +1187,8 @@ gdm_forward_query_alloc (GdmXdmcpManager *manager, } q = g_new0 (GdmForwardQuery, 1); - q->dsp_sa = g_memdup (dsp_sa, sizeof (struct sockaddr_storage)); - q->from_sa = g_memdup (mgr_sa, sizeof (struct sockaddr_storage)); + q->dsp_address = gdm_address_copy (dsp_address); + q->from_address = gdm_address_copy (mgr_address); manager->priv->forward_queries = g_slist_prepend (manager->priv->forward_queries, q); @@ -1147,64 +1196,70 @@ gdm_forward_query_alloc (GdmXdmcpManager *manager, } static GdmForwardQuery * -gdm_forward_query_lookup (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa) +gdm_forward_query_lookup (GdmXdmcpManager *manager, + GdmAddress *address) { GSList *li; GSList *qlist; - GdmForwardQuery *q; + GdmForwardQuery *ret; time_t curtime; curtime = time (NULL); + ret = NULL; qlist = g_slist_copy (manager->priv->forward_queries); for (li = qlist; li != NULL; li = li->next) { + GdmForwardQuery *q; + char *host; + char *serv; + q = (GdmForwardQuery *) li->data; - if (q == NULL) + if (q == NULL) { continue; - - if (gdm_address_equal (q->dsp_sa, clnt_sa)) { - g_slist_free (qlist); - return q; } - if (q->acctime > 0 && curtime > q->acctime + GDM_FORWARD_QUERY_TIMEOUT) { - char *host; - char *serv; + gdm_address_get_numeric_info (q->dsp_address, &host, &serv); - gdm_address_get_info (q->dsp_sa, &host, &serv); + g_debug ("gdm_forward_query_lookup: comparing %s:%s", host, serv); + if (gdm_address_equal (q->dsp_address, address)) { + ret = q; + g_free (host); + g_free (serv); + break; + } + if (q->acctime > 0 && curtime > q->acctime + GDM_FORWARD_QUERY_TIMEOUT) { g_debug ("gdm_forward_query_lookup: Disposing stale forward query from %s:%s", host, serv); - g_free (host); - g_free (serv); gdm_forward_query_dispose (manager, q); - continue; } + + g_free (host); + g_free (serv); } g_slist_free (qlist); - { + if (ret == NULL) { char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("gdm_forward_query_lookup: Host %s not found", host); g_free (host); } - return NULL; + return ret; } static gboolean -create_sa_from_request (ARRAY8 *req_addr, - ARRAY8 *req_port, - int family, - struct sockaddr_storage **sap) +create_address_from_request (ARRAY8 *req_addr, + ARRAY8 *req_port, + int family, + GdmAddress **address) { uint16_t port; char host_buf [NI_MAXHOST]; @@ -1217,8 +1272,8 @@ create_sa_from_request (ARRAY8 *req_addr, int gaierr; gboolean found; - if (sap != NULL) { - *sap = NULL; + if (address != NULL) { + *address = NULL; } if (req_addr == NULL) { @@ -1273,8 +1328,8 @@ create_sa_from_request (ARRAY8 *req_addr, found = FALSE; if (ai != NULL) { found = TRUE; - if (sap != NULL) { - *sap = g_memdup (ai->ai_addr, ai->ai_addrlen); + if (address != NULL) { + *address = gdm_address_new_from_sockaddr_storage ((struct sockaddr_storage *)ai->ai_addr); } } @@ -1284,17 +1339,17 @@ create_sa_from_request (ARRAY8 *req_addr, } static void -gdm_xdmcp_whack_queued_managed_forwards (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - struct sockaddr_storage *origin) +gdm_xdmcp_whack_queued_managed_forwards (GdmXdmcpManager *manager, + GdmAddress *address, + GdmAddress *origin) { GSList *li; for (li = manager->priv->managed_forwards; li != NULL; li = li->next) { ManagedForward *mf = li->data; - if (gdm_address_equal (&mf->manager, clnt_sa) && - gdm_address_equal (&mf->origin, origin)) { + if (gdm_address_equal (mf->manager, address) && + gdm_address_equal (mf->origin, origin)) { manager->priv->managed_forwards = g_slist_remove_link (manager->priv->managed_forwards, li); g_slist_free_1 (li); g_source_remove (mf->handler); @@ -1305,26 +1360,26 @@ gdm_xdmcp_whack_queued_managed_forwards (GdmXdmcpManager *manager, } static void -gdm_xdmcp_handle_forward_query (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_forward_query (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { ARRAY8 clnt_addr; ARRAY8 clnt_port; ARRAYofARRAY8 clnt_authlist; int i; int explen; - struct sockaddr_storage *disp_sa; + GdmAddress *disp_address; char *host; char *serv; - disp_sa = NULL; + disp_address = NULL; /* Check with tcp_wrappers if client is allowed to access */ - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_warning ("%s: Got FORWARD_QUERY from banned host %s", "gdm_xdmcp_handle_forward query", @@ -1377,69 +1432,71 @@ gdm_xdmcp_handle_forward_query (GdmXdmcpManager *manager, goto out; } - if (! create_sa_from_request (&clnt_addr, &clnt_port, clnt_sa->ss_family, &disp_sa)) { + if (! create_address_from_request (&clnt_addr, &clnt_port, gdm_address_get_family_type (address), &disp_address)) { g_warning ("Unable to parse address for request"); goto out; } gdm_xdmcp_whack_queued_managed_forwards (manager, - clnt_sa, - disp_sa); + address, + disp_address); - gdm_address_get_info (disp_sa, &host, &serv); + gdm_address_get_numeric_info (disp_address, &host, &serv); g_debug ("gdm_xdmcp_handle_forward_query: Got FORWARD_QUERY for display: %s, port %s", host, serv); g_free (host); g_free (serv); /* Check with tcp_wrappers if display is allowed to access */ - if (gdm_xdmcp_host_allow (disp_sa)) { + if (gdm_xdmcp_host_allow (disp_address)) { GdmForwardQuery *q; - q = gdm_forward_query_lookup (manager, disp_sa); - if (q != NULL) + q = gdm_forward_query_lookup (manager, disp_address); + if (q != NULL) { gdm_forward_query_dispose (manager, q); + } - gdm_forward_query_alloc (manager, clnt_sa, disp_sa); + gdm_forward_query_alloc (manager, address, disp_address); - gdm_xdmcp_send_willing (manager, disp_sa); + gdm_xdmcp_send_willing (manager, disp_address); } out: - g_free (disp_sa); + gdm_address_free (disp_address); + XdmcpDisposeARRAYofARRAY8 (&clnt_authlist); XdmcpDisposeARRAY8 (&clnt_port); XdmcpDisposeARRAY8 (&clnt_addr); } static void -gdm_xdmcp_really_send_managed_forward (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - struct sockaddr_storage *origin) +gdm_xdmcp_really_send_managed_forward (GdmXdmcpManager *manager, + GdmAddress *address, + GdmAddress *origin) { - ARRAY8 address; + ARRAY8 addr; XdmcpHeader header; char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XDMCP: Sending MANAGED_FORWARD to %s", host); g_free (host); - set_address_for_request (origin, &address); + set_address_for_request (origin, &addr); header.opcode = (CARD16) GDM_XDMCP_MANAGED_FORWARD; - header.length = 4 + address.length; + header.length = 4 + addr.length; header.version = GDM_XDMCP_PROTOCOL_VERSION; XdmcpWriteHeader (&manager->priv->buf, &header); - XdmcpWriteARRAY8 (&manager->priv->buf, &address); + XdmcpWriteARRAY8 (&manager->priv->buf, &addr); XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); - g_free (address.data); + g_free (addr.data); } static gboolean @@ -1447,8 +1504,8 @@ managed_forward_handler (ManagedForward *mf) { if (mf->xdmcp_manager->priv->socket_fd > 0) { gdm_xdmcp_really_send_managed_forward (mf->xdmcp_manager, - &(mf->manager), - &(mf->origin)); + mf->manager, + mf->origin); } mf->times++; @@ -1462,161 +1519,188 @@ managed_forward_handler (ManagedForward *mf) } static void -gdm_xdmcp_send_managed_forward (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - struct sockaddr_storage *origin) +managed_forward_free (ManagedForward *mf) +{ + gdm_address_free (mf->origin); + gdm_address_free (mf->manager); + g_free (mf); +} + +static void +gdm_xdmcp_send_managed_forward (GdmXdmcpManager *manager, + GdmAddress *address, + GdmAddress *origin) { ManagedForward *mf; - gdm_xdmcp_really_send_managed_forward (manager, clnt_sa, origin); + gdm_xdmcp_really_send_managed_forward (manager, address, origin); mf = g_new0 (ManagedForward, 1); mf->times = 0; mf->xdmcp_manager = manager; - memcpy (&(mf->manager), clnt_sa, sizeof (struct sockaddr_storage)); - memcpy (&(mf->origin), origin, sizeof (struct sockaddr_storage)); + mf->manager = gdm_address_copy (address); + mf->origin = gdm_address_copy (origin); mf->handler = g_timeout_add_full (G_PRIORITY_DEFAULT, MANAGED_FORWARD_INTERVAL, (GSourceFunc)managed_forward_handler, mf, - (GDestroyNotify) g_free); + (GDestroyNotify)managed_forward_free); manager->priv->managed_forwards = g_slist_prepend (manager->priv->managed_forwards, mf); } static void -gdm_xdmcp_send_got_managed_forward (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - struct sockaddr_storage *origin) +gdm_xdmcp_send_got_managed_forward (GdmXdmcpManager *manager, + GdmAddress *address, + GdmAddress *origin) { - ARRAY8 address; + ARRAY8 addr; XdmcpHeader header; char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XDMCP: Sending GOT_MANAGED_FORWARD to %s", host); g_free (host); - set_address_for_request (origin, &address); + set_address_for_request (origin, &addr); header.opcode = (CARD16) GDM_XDMCP_GOT_MANAGED_FORWARD; - header.length = 4 + address.length; + header.length = 4 + addr.length; header.version = GDM_XDMCP_PROTOCOL_VERSION; XdmcpWriteHeader (&manager->priv->buf, &header); - XdmcpWriteARRAY8 (&manager->priv->buf, &address); + XdmcpWriteARRAY8 (&manager->priv->buf, &addr); XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); } -static void -gdm_xdmcp_recount_sessions (GdmXdmcpManager *manager) +static gboolean +count_sessions (const char *id, + GdmDisplay *display, + GdmXdmcpManager *manager) { - GSList *li; - GSList *displays; + if (GDM_IS_XDMCP_DISPLAY (display)) { + int status; - displays = gdm_daemon_config_get_display_list (); + status = gdm_display_get_status (display); - manager->priv->num_sessions = 0; - manager->priv->num_pending_sessions = 0; + if (status == GDM_DISPLAY_MANAGED) { + manager->priv->num_sessions++; + } else if (status == GDM_DISPLAY_UNMANAGED) { + manager->priv->num_pending_sessions++; + } + } - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - if (SERVER_IS_XDMCP (d)) { - if (d->dispstat == XDMCP_MANAGED) - manager->priv->num_sessions++; - else if (d->dispstat == XDMCP_PENDING) - manager->priv->num_pending_sessions++; - } - } + return TRUE; } static void -do_dispose (GdmXdmcpManager *manager, - GdmDisplay *d) +gdm_xdmcp_recount_sessions (GdmXdmcpManager *manager) { + manager->priv->num_sessions = 0; + manager->priv->num_pending_sessions = 0; - gdm_display_dispose (d); - gdm_xdmcp_recount_sessions (manager); + gdm_display_store_foreach (manager->priv->display_store, + (GdmDisplayStoreFunc)count_sessions, + manager); +} + +static gboolean +purge_displays (const char *id, + GdmDisplay *display, + GdmXdmcpManager *manager) +{ + if (GDM_IS_XDMCP_DISPLAY (display)) { + int status; + time_t currtime; + time_t acctime; + + currtime = time (NULL); + status = gdm_display_get_status (display); + acctime = gdm_display_get_creation_time (display); + + if (status == GDM_DISPLAY_UNMANAGED && + currtime > acctime + manager->priv->max_wait) { + /* return TRUE to remove display */ + return TRUE; + } + } + + return FALSE; } static void gdm_xdmcp_displays_purge (GdmXdmcpManager *manager) { - GSList *dlist; - time_t curtime = time (NULL); - GSList *displays; + gdm_display_store_foreach_remove (manager->priv->display_store, + (GdmDisplayStoreFunc)purge_displays, + manager); + + gdm_xdmcp_recount_sessions (manager); +} - displays = gdm_daemon_config_get_display_list (); +typedef struct { + const char *hostname; + int display_num; +} RemoveHostData; - dlist = displays; - while (dlist != NULL) { - GdmDisplay *d = dlist->data; +static gboolean +remove_host (const char *id, + GdmDisplay *display, + RemoveHostData *data) +{ + char *hostname; + int disp_num; - if (d != NULL && - SERVER_IS_XDMCP (d) && - d->dispstat == XDMCP_PENDING && - curtime > d->acctime + manager->priv->max_wait) { - g_debug ("gdm_xdmcp_displays_purge: Disposing session id %ld", - (long)d->sessionid); - do_dispose (manager, d); + if (! GDM_IS_XDMCP_DISPLAY (display)) { + return FALSE; + } - /* restart as the list is now broken */ - dlist = displays; - } else { - /* just go on */ - dlist = dlist->next; - } + gdm_xdmcp_display_get_remote_hostname (GDM_XDMCP_DISPLAY (display), &hostname, NULL); + gdm_display_get_number (display, &disp_num, NULL); + + if (disp_num == data->display_num && + hostname != NULL && + data->hostname != NULL && + strcmp (hostname, data->hostname) == 0) { + /* return TRUE to remove */ + return TRUE; } + + return FALSE; } static void gdm_xdmcp_display_dispose_check (GdmXdmcpManager *manager, const char *hostname, - int dspnum) + int display_num) { - GSList *dlist; - GSList *displays; + RemoveHostData *data; if (hostname == NULL) { return; } - g_debug ("gdm_xdmcp_display_dispose_check (%s:%d)", hostname, dspnum); - - displays = gdm_daemon_config_get_display_list (); - - dlist = displays; - while (dlist != NULL) { - GdmDisplay *d = dlist->data; - - if (d != NULL && - SERVER_IS_XDMCP (d) && - d->xdmcp_dispnum == dspnum && - strcmp (d->hostname, hostname) == 0) { + g_debug ("gdm_xdmcp_display_dispose_check (%s:%d)", hostname, display_num); - if (d->dispstat == XDMCP_MANAGED) { - gdm_display_unmanage (d); - } else { - do_dispose (manager, d); - } + data = g_new0 (RemoveHostData, 1); + data->hostname = hostname; + data->display_num = display_num; + gdm_display_store_foreach_remove (manager->priv->display_store, + (GdmDisplayStoreFunc)remove_host, + data); + g_free (data); - /* restart as the list is now broken */ - dlist = displays; - } else { - /* just go on */ - dlist = dlist->next; - } - } + gdm_xdmcp_recount_sessions (manager); } static void -gdm_xdmcp_send_decline (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - const char *reason) +gdm_xdmcp_send_decline (GdmXdmcpManager *manager, + GdmAddress *address, + const char *reason) { XdmcpHeader header; ARRAY8 authentype; @@ -1625,7 +1709,7 @@ gdm_xdmcp_send_decline (GdmXdmcpManager *manager, GdmForwardQuery *fq; char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XMDCP: Sending DECLINE to %s", host); g_free (host); @@ -1651,133 +1735,63 @@ gdm_xdmcp_send_decline (GdmXdmcpManager *manager, XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); /* Send MANAGED_FORWARD to indicate that the connection * reached some sort of resolution */ - fq = gdm_forward_query_lookup (manager, clnt_sa); + fq = gdm_forward_query_lookup (manager, address); if (fq != NULL) { - gdm_xdmcp_send_managed_forward (manager, fq->from_sa, clnt_sa); + gdm_xdmcp_send_managed_forward (manager, fq->from_address, address); gdm_forward_query_dispose (manager, fq); } } static GdmDisplay * gdm_xdmcp_display_alloc (GdmXdmcpManager *manager, - struct sockaddr_storage *addr, - GdmHostent *he /* eaten and freed */, + const char *hostname, + GdmAddress *address, int displaynum) { - GdmDisplay *d = NULL; - const char *proxycmd; - gboolean use_proxy = FALSE; - - proxycmd = NULL; - use_proxy = FALSE; -#if 0 - proxycmd = gdm_daemon_config_get_value_string (GDM_KEY_XDMCP_PROXY_XSERVER); - use_proxy = FALSE; -#endif - d = g_new0 (GdmDisplay, 1); - - if (use_proxy && proxycmd != NULL) { - d->type = TYPE_XDMCP_PROXY; - d->command = g_strdup (proxycmd); - g_debug ("Using proxy server for XDMCP: %s\n", d->command); - } else { - d->type = TYPE_XDMCP; - } - - d->logout_action = GDM_LOGOUT_ACTION_NONE; - d->authfile = NULL; - d->auths = NULL; - d->userauth = NULL; - d->greetpid = 0; - d->servpid = 0; - d->servstat = 0; - d->sesspid = 0; - d->slavepid = 0; - d->attached = FALSE; - d->dispstat = XDMCP_PENDING; - d->sessionid = get_next_session_serial (manager); - - d->acctime = time (NULL); - d->dispnum = displaynum; - d->xdmcp_dispnum = displaynum; - - d->handled = TRUE; - d->tcp_disallowed = FALSE; - d->vt = -1; - d->x_servers_order = -1; - d->logged_in = FALSE; - d->login = NULL; - d->sleep_before_run = 0; - - if (gdm_daemon_config_get_value_bool (GDM_KEY_ALLOW_REMOTE_AUTOLOGIN) && - ! ve_string_empty (gdm_daemon_config_get_value_string (GDM_KEY_TIMED_LOGIN))) { - d->timed_login_ok = TRUE; - } else { - d->timed_login_ok = FALSE; - } - - d->name = g_strdup_printf ("%s:%d", - he->hostname, - displaynum); - - memcpy (&d->addr, addr, sizeof (struct sockaddr_storage)); + GdmDisplay *display; + char *name; - d->hostname = he->hostname; - he->hostname = NULL; - d->addrs = he->addrs; - he->addrs = NULL; - d->addr_count = he->addr_count; - he->addr_count = 0; + name = g_strdup_printf ("%s:%d", + hostname, + displaynum); - gdm_hostent_free (he); - - d->slave_notify_fd = -1; - d->master_notify_fd = -1; - d->xsession_errors_bytes = 0; - d->xsession_errors_fd = -1; - d->session_output_fd = -1; - d->chooser_output_fd = -1; - d->chooser_last_line = NULL; - d->theme_name = NULL; - - /* Secure display with cookie */ - if G_UNLIKELY (! gdm_auth_secure_display (d)) { - g_warning ("gdm_xdmcp_display_alloc: Error setting up cookies for %s", - d->name); + display = gdm_xdmcp_display_new (displaynum, + name, + hostname, + address, + get_next_session_serial (manager)); + if (display == NULL) { + goto out; } - if (d->type == TYPE_XDMCP_PROXY) { - d->parent_disp = d->name; - d->name = g_strdup (":-1"); - d->dispnum = -1; - d->server_uid = gdm_daemon_config_get_gdmuid (); - d->parent_auth_file = d->authfile; - d->authfile = NULL; + if (! gdm_display_create_authority (display)) { + g_object_unref (display); + display = NULL; + goto out; } - gdm_daemon_config_display_list_append (d); + gdm_display_store_add (manager->priv->display_store, display); manager->priv->num_pending_sessions++; + out: + g_free (name); - g_debug ("gdm_xdmcp_display_alloc: display=%s, session id=%ld, xdmcp_pending=%d", - d->name, (long)d->sessionid, manager->priv->num_pending_sessions); - - return d; + return display; } static void -gdm_xdmcp_send_accept (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - CARD32 session_id, - ARRAY8Ptr authentication_name, - ARRAY8Ptr authentication_data, - ARRAY8Ptr authorization_name, - ARRAY8Ptr authorization_data) +gdm_xdmcp_send_accept (GdmXdmcpManager *manager, + GdmAddress *address, + CARD32 session_id, + ARRAY8Ptr authentication_name, + ARRAY8Ptr authentication_data, + ARRAY8Ptr authorization_name, + ARRAY8Ptr authorization_data) { XdmcpHeader header; char *host; @@ -1799,10 +1813,10 @@ gdm_xdmcp_send_accept (GdmXdmcpManager *manager, XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XDMCP: Sending ACCEPT to %s with SessionID=%ld", host, (long)session_id); @@ -1810,9 +1824,9 @@ gdm_xdmcp_send_accept (GdmXdmcpManager *manager, } static void -gdm_xdmcp_handle_request (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_request (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { CARD16 clnt_dspnum; ARRAY16 clnt_conntyp; @@ -1825,23 +1839,22 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, int i; gboolean mitauth; gboolean entered; - char *host; + char *hostname; mitauth = FALSE; entered = FALSE; - gdm_address_get_info (clnt_sa, &host, NULL); - g_debug ("gdm_xdmcp_handle_request: Got REQUEST from %s", host); + hostname = NULL; + gdm_address_get_numeric_info (address, &hostname, NULL); + g_debug ("gdm_xdmcp_handle_request: Got REQUEST from %s", hostname); /* Check with tcp_wrappers if client is allowed to access */ - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { g_warning (_("%s: Got REQUEST from banned host %s"), "gdm_xdmcp_handle_request", - host); - g_free (host); - return; + hostname); + goto out; } - g_free (host); gdm_xdmcp_displays_purge (manager); /* Purge pending displays */ @@ -1849,14 +1862,14 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, if G_UNLIKELY (! XdmcpReadCARD16 (&manager->priv->buf, &clnt_dspnum)) { g_warning (_("%s: Could not read Display Number"), "gdm_xdmcp_handle_request"); - return; + goto out; } /* We don't care about connection type. Address says it all */ if G_UNLIKELY (! XdmcpReadARRAY16 (&manager->priv->buf, &clnt_conntyp)) { g_warning (_("%s: Could not read Connection Type"), "gdm_xdmcp_handle_request"); - return; + goto out; } /* This is TCP/IP - we don't care */ @@ -1864,7 +1877,7 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, g_warning (_("%s: Could not read Client Address"), "gdm_xdmcp_handle_request"); XdmcpDisposeARRAY16 (&clnt_conntyp); - return; + goto out; } /* Read authentication type */ @@ -1873,7 +1886,7 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, "gdm_xdmcp_handle_request"); XdmcpDisposeARRAYofARRAY8 (&clnt_addr); XdmcpDisposeARRAY16 (&clnt_conntyp); - return; + goto out; } /* Read authentication data */ @@ -1883,7 +1896,7 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, XdmcpDisposeARRAYofARRAY8 (&clnt_addr); XdmcpDisposeARRAY16 (&clnt_conntyp); XdmcpDisposeARRAY8 (&clnt_authname); - return; + goto out; } /* Read and select from supported authorization list */ @@ -1894,7 +1907,7 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, XdmcpDisposeARRAYofARRAY8 (&clnt_addr); XdmcpDisposeARRAY16 (&clnt_conntyp); XdmcpDisposeARRAY8 (&clnt_authname); - return; + goto out; } /* libXdmcp doesn't terminate strings properly so we cheat and use strncmp () */ @@ -1913,7 +1926,7 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, XdmcpDisposeARRAYofARRAY8 (&clnt_addr); XdmcpDisposeARRAYofARRAY8 (&clnt_authorization); XdmcpDisposeARRAY16 (&clnt_conntyp); - return; + goto out; } /* Crude checksumming */ @@ -1930,11 +1943,9 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, explen += 2 + clnt_manufacturer.length; if G_UNLIKELY (explen != len) { - gdm_address_get_info (clnt_sa, &host, NULL); g_warning (_("%s: Failed checksum from %s"), "gdm_xdmcp_handle_request", - host); - g_free (host); + hostname); XdmcpDisposeARRAY8 (&clnt_authname); XdmcpDisposeARRAY8 (&clnt_authdata); @@ -1942,7 +1953,7 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, XdmcpDisposeARRAYofARRAY8 (&clnt_addr); XdmcpDisposeARRAYofARRAY8 (&clnt_authorization); XdmcpDisposeARRAY16 (&clnt_conntyp); - return; + goto out; } { @@ -1959,36 +1970,42 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, /* Check if ok to manage display */ if (mitauth && manager->priv->num_sessions < manager->priv->max_displays && - (gdm_address_is_local (clnt_sa) || - gdm_xdmcp_num_displays_from_host (manager, clnt_sa) < manager->priv->max_displays_per_host)) { + (gdm_address_is_local (address) || + gdm_xdmcp_num_displays_from_host (manager, address) < manager->priv->max_displays_per_host)) { entered = TRUE; } if (entered) { - GdmHostent *he; - he = gdm_gethostbyaddr (clnt_sa); /* Check if we are already talking to this host */ - gdm_xdmcp_display_dispose_check (manager, he->hostname, clnt_dspnum); + gdm_xdmcp_display_dispose_check (manager, hostname, clnt_dspnum); if (manager->priv->num_pending_sessions >= manager->priv->max_pending_displays) { g_debug ("gdm_xdmcp_handle_request: maximum pending"); /* Don't translate, this goes over the wire to servers where we * don't know the charset or language, so it must be ascii */ - gdm_xdmcp_send_decline (manager, clnt_sa, "Maximum pending servers"); - gdm_hostent_free (he); + gdm_xdmcp_send_decline (manager, address, "Maximum pending servers"); } else { - GdmDisplay *d; + GdmDisplay *display; - d = gdm_xdmcp_display_alloc (manager, - clnt_sa, - he /* eaten and freed */, - clnt_dspnum); - if (d != NULL) { + display = gdm_xdmcp_display_alloc (manager, + hostname, + address, + clnt_dspnum); + + if (display != NULL) { ARRAY8 authentication_name; ARRAY8 authentication_data; ARRAY8 authorization_name; ARRAY8 authorization_data; + char *binary_cookie; + gint32 session_number; + + binary_cookie = gdm_display_get_binary_cookie (display); + session_number = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display)); + + /* the send accept will fail if cookie is null */ + g_assert (binary_cookie != NULL); authentication_name.data = NULL; authentication_name.length = 0; @@ -1998,13 +2015,15 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, authorization_name.data = (CARD8 *) "MIT-MAGIC-COOKIE-1"; authorization_name.length = strlen ((char *) authorization_name.data); - authorization_data.data = (CARD8 *) d->bcookie; + authorization_data.data = (CARD8 *) binary_cookie; authorization_data.length = 16; + g_free (binary_cookie); + /* the addrs are NOT copied */ gdm_xdmcp_send_accept (manager, - clnt_sa, - d->sessionid, + address, + session_number, &authentication_name, &authentication_data, &authorization_name, @@ -2016,18 +2035,18 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, * don't know the charset or language, so it must be ascii */ if ( ! mitauth) { gdm_xdmcp_send_decline (manager, - clnt_sa, + address, "Only MIT-MAGIC-COOKIE-1 supported"); } else if (manager->priv->num_sessions >= manager->priv->max_displays) { g_warning ("Maximum number of open XDMCP sessions reached"); gdm_xdmcp_send_decline (manager, - clnt_sa, + address, "Maximum number of open sessions reached"); } else { g_debug ("Maximum number of open XDMCP sessions from host %s reached", - host); + hostname); gdm_xdmcp_send_decline (manager, - clnt_sa, + address, "Maximum number of open sessions from your host reached"); } } @@ -2038,40 +2057,54 @@ gdm_xdmcp_handle_request (GdmXdmcpManager *manager, XdmcpDisposeARRAYofARRAY8 (&clnt_addr); XdmcpDisposeARRAYofARRAY8 (&clnt_authorization); XdmcpDisposeARRAY16 (&clnt_conntyp); + out: + g_free (hostname); } -static GdmDisplay * -gdm_xdmcp_display_lookup (GdmXdmcpManager *manager, - CARD32 sessid) +static gboolean +lookup_by_session_id (const char *id, + GdmDisplay *display, + gpointer data) { - GSList *l; - GdmDisplay *d; - GSList *displays; + CARD32 sessid; + CARD32 session_id; - if (sessid == 0) { - return (NULL); + sessid = GPOINTER_TO_INT (data); + + if (! GDM_IS_XDMCP_DISPLAY (display)) { + return FALSE; } - displays = gdm_daemon_config_get_display_list (); + session_id = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display)); - l = displays; - while (l != NULL) { - d = (GdmDisplay *) l->data; + if (session_id == sessid) { + return TRUE; + } - if (d && d->sessionid == sessid) { - return (d); - } + return FALSE; +} + +static GdmDisplay * +gdm_xdmcp_display_lookup (GdmXdmcpManager *manager, + CARD32 sessid) +{ + GdmDisplay *display; - l = l->next; + if (sessid == 0) { + return NULL; } - return (NULL); + display = gdm_display_store_find (manager->priv->display_store, + (GdmDisplayStoreFunc)lookup_by_session_id, + GINT_TO_POINTER (sessid)); + + return display; } static void -gdm_xdmcp_send_failed (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - CARD32 sessid) +gdm_xdmcp_send_failed (GdmXdmcpManager *manager, + GdmAddress *address, + CARD32 sessid) { XdmcpHeader header; ARRAY8 status; @@ -2095,14 +2128,14 @@ gdm_xdmcp_send_failed (GdmXdmcpManager *manager, XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); } static void -gdm_xdmcp_send_refuse (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - CARD32 sessid) +gdm_xdmcp_send_refuse (GdmXdmcpManager *manager, + GdmAddress *address, + CARD32 sessid) { XdmcpHeader header; GdmForwardQuery *fq; @@ -2119,38 +2152,37 @@ gdm_xdmcp_send_refuse (GdmXdmcpManager *manager, XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); /* * This was from a forwarded query quite apparently so * send MANAGED_FORWARD */ - fq = gdm_forward_query_lookup (manager, clnt_sa); + fq = gdm_forward_query_lookup (manager, address); if (fq != NULL) { - gdm_xdmcp_send_managed_forward (manager, fq->from_sa, clnt_sa); + gdm_xdmcp_send_managed_forward (manager, fq->from_address, address); gdm_forward_query_dispose (manager, fq); } } static void -gdm_xdmcp_handle_manage (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_manage (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { CARD32 clnt_sessid; CARD16 clnt_dspnum; ARRAY8 clnt_dspclass; - GdmDisplay *d; - GdmIndirectDisplay *id; + GdmDisplay *display; GdmForwardQuery *fq; char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("gdm_xdmcp_handle_manage: Got MANAGE from %s", host); /* Check with tcp_wrappers if client is allowed to access */ - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { g_warning (_("%s: Got Manage from banned host %s"), "gdm_xdmcp_handle_manage", host); @@ -2180,7 +2212,7 @@ gdm_xdmcp_handle_manage (GdmXdmcpManager *manager, return; } - if G_UNLIKELY (gdm_daemon_config_get_value_bool (GDM_KEY_DEBUG)) { + { char *s = g_strndup ((char *) clnt_dspclass.data, clnt_dspclass.length); g_debug ("gdm_xdmcp-handle_manage: Got display=%d, SessionID=%ld Class=%s from %s", (int)clnt_dspnum, (long)clnt_sessid, ve_sure_string (s), host); @@ -2188,79 +2220,84 @@ gdm_xdmcp_handle_manage (GdmXdmcpManager *manager, g_free (s); } - d = gdm_xdmcp_display_lookup (manager, clnt_sessid); - if (d != NULL && - d->dispstat == XDMCP_PENDING) { + display = gdm_xdmcp_display_lookup (manager, clnt_sessid); + if (display != NULL && + gdm_display_get_status (display) == GDM_DISPLAY_UNMANAGED) { + char *name; + + name = NULL; + gdm_display_get_name (display, &name, NULL); + g_debug ("gdm_xdmcp_handle_manage: Looked up %s", name); + g_free (name); - g_debug ("gdm_xdmcp_handle_manage: Looked up %s", d->name); +#if 0 /* FIXME: */ + if (manager->priv->honor_indirect) { + GdmIndirectDisplay *id; - if (gdm_daemon_config_get_value_bool (GDM_KEY_INDIRECT)) { - id = gdm_choose_indirect_lookup (clnt_sa); + id = gdm_choose_indirect_lookup (address); /* This was an indirect thingie and nothing was yet chosen, * use a chooser */ - if (d->dispstat == XDMCP_PENDING && - id != NULL && + if (id != NULL && id->chosen_host == NULL) { d->use_chooser = TRUE; d->indirect_id = id->id; } else { d->indirect_id = 0; d->use_chooser = FALSE; - if (id != NULL) + if (id != NULL) { gdm_choose_indirect_dispose (id); + } } } else { - d->indirect_id = 0; - d->use_chooser = FALSE; - } + } +#endif /* this was from a forwarded query quite apparently so * send MANAGED_FORWARD */ - fq = gdm_forward_query_lookup (manager, clnt_sa); + fq = gdm_forward_query_lookup (manager, address); if (fq != NULL) { - gdm_xdmcp_send_managed_forward (manager, fq->from_sa, clnt_sa); + gdm_xdmcp_send_managed_forward (manager, fq->from_address, address); gdm_forward_query_dispose (manager, fq); } - d->dispstat = XDMCP_MANAGED; manager->priv->num_sessions++; manager->priv->num_pending_sessions--; /* Start greeter/session */ - if G_UNLIKELY (!gdm_display_manage (d)) { - gdm_xdmcp_send_failed (manager, clnt_sa, clnt_sessid); - XdmcpDisposeARRAY8 (&clnt_dspclass); - return; + if (! gdm_display_manage (display)) { + gdm_xdmcp_send_failed (manager, address, clnt_sessid); + g_debug ("Failed to manage display"); } - } else if G_UNLIKELY (d != NULL && d->dispstat == XDMCP_MANAGED) { + } else if (display != NULL && + gdm_display_get_status (display) == GDM_DISPLAY_MANAGED) { g_debug ("gdm_xdmcp_handle_manage: Session id %ld already managed", - (long)clnt_sessid); + (long)clnt_sessid); } else { g_warning ("gdm_xdmcp_handle_manage: Failed to look up session id %ld", (long)clnt_sessid); - gdm_xdmcp_send_refuse (manager, clnt_sa, clnt_sessid); + gdm_xdmcp_send_refuse (manager, address, clnt_sessid); } XdmcpDisposeARRAY8 (&clnt_dspclass); } static void -gdm_xdmcp_handle_managed_forward (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_managed_forward (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { ARRAY8 clnt_address; GdmIndirectDisplay *id; char *host; - struct sockaddr_storage *disp_sa; + GdmAddress *disp_address; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("gdm_xdmcp_handle_managed_forward: Got MANAGED_FORWARD from %s", host); /* Check with tcp_wrappers if client is allowed to access */ - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { g_warning ("%s: Got MANAGED_FORWARD from banned host %s", "gdm_xdmcp_handle_request", host); g_free (host); @@ -2275,39 +2312,41 @@ gdm_xdmcp_handle_managed_forward (GdmXdmcpManager *manager, return; } - disp_sa = NULL; - if (! create_sa_from_request (&clnt_address, NULL, clnt_sa->ss_family, &disp_sa)) { + disp_address = NULL; + if (! create_address_from_request (&clnt_address, NULL, gdm_address_get_family_type (address), &disp_address)) { g_warning ("Unable to parse address for request"); XdmcpDisposeARRAY8 (&clnt_address); return; } - id = gdm_choose_indirect_lookup_by_chosen (clnt_sa, disp_sa); + id = gdm_choose_indirect_lookup_by_chosen (address, disp_address); if (id != NULL) { gdm_choose_indirect_dispose (id); } /* Note: we send GOT even on not found, just in case our previous * didn't get through and this was a second managed forward */ - gdm_xdmcp_send_got_managed_forward (manager, clnt_sa, disp_sa); + gdm_xdmcp_send_got_managed_forward (manager, address, disp_address); + + gdm_address_free (disp_address); XdmcpDisposeARRAY8 (&clnt_address); } static void -gdm_xdmcp_handle_got_managed_forward (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_got_managed_forward (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { - struct sockaddr_storage *disp_sa; - ARRAY8 clnt_address; - char *host; + GdmAddress *disp_address; + ARRAY8 clnt_address; + char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("gdm_xdmcp_handle_got_managed_forward: Got MANAGED_FORWARD from %s", host); - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { g_warning ("%s: Got GOT_MANAGED_FORWARD from banned host %s", "gdm_xdmcp_handle_request", host); g_free (host); @@ -2322,37 +2361,43 @@ gdm_xdmcp_handle_got_managed_forward (GdmXdmcpManager *manager, return; } - if (! create_sa_from_request (&clnt_address, NULL, clnt_sa->ss_family, &disp_sa)) { + if (! create_address_from_request (&clnt_address, NULL, gdm_address_get_family_type (address), &disp_address)) { g_warning (_("%s: Could not read address"), "gdm_xdmcp_handle_got_managed_forward"); XdmcpDisposeARRAY8 (&clnt_address); return; } - gdm_xdmcp_whack_queued_managed_forwards (manager, clnt_sa, disp_sa); + gdm_xdmcp_whack_queued_managed_forwards (manager, address, disp_address); + + gdm_address_free (disp_address); XdmcpDisposeARRAY8 (&clnt_address); } static void -gdm_xdmcp_send_alive (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - CARD16 dspnum, - CARD32 sessid) +gdm_xdmcp_send_alive (GdmXdmcpManager *manager, + GdmAddress *address, + CARD16 dspnum, + CARD32 sessid) { XdmcpHeader header; - GdmDisplay *d; - int send_running = 0; - CARD32 send_sessid = 0; + GdmDisplay *display; + int send_running = 0; + CARD32 send_sessid = 0; - d = gdm_xdmcp_display_lookup (manager, sessid); - if (d == NULL) { - d = gdm_xdmcp_display_lookup_by_host (manager, clnt_sa, dspnum); + display = gdm_xdmcp_display_lookup (manager, sessid); + if (display == NULL) { + display = gdm_xdmcp_display_lookup_by_host (manager, address, dspnum); } - if (d != NULL) { - send_sessid = d->sessionid; - if (d->dispstat == XDMCP_MANAGED) { + if (display != NULL) { + int status; + + send_sessid = gdm_xdmcp_display_get_session_number (GDM_XDMCP_DISPLAY (display)); + status = gdm_display_get_status (display); + + if (status == GDM_DISPLAY_MANAGED) { send_running = 1; } } @@ -2372,24 +2417,24 @@ gdm_xdmcp_send_alive (GdmXdmcpManager *manager, XdmcpFlush (manager->priv->socket_fd, &manager->priv->buf, - (XdmcpNetaddr)clnt_sa, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), (int)sizeof (struct sockaddr_storage)); } static void -gdm_xdmcp_handle_keepalive (GdmXdmcpManager *manager, - struct sockaddr_storage *clnt_sa, - int len) +gdm_xdmcp_handle_keepalive (GdmXdmcpManager *manager, + GdmAddress *address, + int len) { CARD16 clnt_dspnum; CARD32 clnt_sessid; char *host; - gdm_address_get_info (clnt_sa, &host, NULL); + gdm_address_get_numeric_info (address, &host, NULL); g_debug ("XDMCP: Got KEEPALIVE from %s", host); /* Check with tcp_wrappers if client is allowed to access */ - if (! gdm_xdmcp_host_allow (clnt_sa)) { + if (! gdm_xdmcp_host_allow (address)) { g_warning (_("%s: Got KEEPALIVE from banned host %s"), "gdm_xdmcp_handle_keepalive", host); @@ -2412,7 +2457,7 @@ gdm_xdmcp_handle_keepalive (GdmXdmcpManager *manager, return; } - gdm_xdmcp_send_alive (manager, clnt_sa, clnt_dspnum, clnt_sessid); + gdm_xdmcp_send_alive (manager, address, clnt_dspnum, clnt_sessid); } static const char * @@ -2456,22 +2501,22 @@ decode_packet (GIOChannel *source, GIOCondition cond, GdmXdmcpManager *manager) { - struct sockaddr_storage clnt_sa; - gint sa_len; + struct sockaddr_storage clnt_ss; + GdmAddress *address; + gint ss_len; XdmcpHeader header; char *host; char *port; int res; - sa_len = sizeof (clnt_sa); - g_debug ("decode_packet: GIOCondition %d", (int)cond); if ( ! (cond & G_IO_IN)) { return TRUE; } - res = XdmcpFill (manager->priv->socket_fd, &manager->priv->buf, (XdmcpNetaddr)&clnt_sa, &sa_len); + ss_len = sizeof (clnt_ss); + res = XdmcpFill (manager->priv->socket_fd, &manager->priv->buf, (XdmcpNetaddr)&clnt_ss, &ss_len); if G_UNLIKELY (! res) { g_debug (_("XMCP: Could not create XDMCP buffer!")); return TRUE; @@ -2489,7 +2534,13 @@ decode_packet (GIOChannel *source, return TRUE; } - gdm_address_get_info (&clnt_sa, &host, &port); + address = gdm_address_new_from_sockaddr_storage (&clnt_ss); + if (address == NULL) { + g_warning (_("XMDCP: Unable to parse address")); + return TRUE; + } + + gdm_address_get_numeric_info (address, &host, &port); g_debug ("XDMCP: Received opcode %s from client %s : %s", opcode_string (header.opcode), @@ -2498,39 +2549,39 @@ decode_packet (GIOChannel *source, switch (header.opcode) { case BROADCAST_QUERY: - gdm_xdmcp_handle_broadcast_query (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_broadcast_query (manager, address, header.length); break; case QUERY: - gdm_xdmcp_handle_query (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_query (manager, address, header.length); break; case INDIRECT_QUERY: - gdm_xdmcp_handle_indirect_query (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_indirect_query (manager, address, header.length); break; case FORWARD_QUERY: - gdm_xdmcp_handle_forward_query (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_forward_query (manager, address, header.length); break; case REQUEST: - gdm_xdmcp_handle_request (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_request (manager, address, header.length); break; case MANAGE: - gdm_xdmcp_handle_manage (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_manage (manager, address, header.length); break; case KEEPALIVE: - gdm_xdmcp_handle_keepalive (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_keepalive (manager, address, header.length); break; case GDM_XDMCP_MANAGED_FORWARD: - gdm_xdmcp_handle_managed_forward (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_managed_forward (manager, address, header.length); break; case GDM_XDMCP_GOT_MANAGED_FORWARD: - gdm_xdmcp_handle_got_managed_forward (manager, &clnt_sa, header.length); + gdm_xdmcp_handle_got_managed_forward (manager, address, header.length); break; default: @@ -2544,6 +2595,8 @@ decode_packet (GIOChannel *source, g_free (host); g_free (port); + gdm_address_free (address); + return TRUE; } @@ -2684,6 +2737,20 @@ gdm_xdmcp_manager_set_willing_script (GdmXdmcpManager *manager, } static void +gdm_xdmcp_manager_set_display_store (GdmXdmcpManager *manager, + GdmDisplayStore *display_store) +{ + if (manager->priv->display_store != NULL) { + g_object_unref (manager->priv->display_store); + manager->priv->display_store = NULL; + } + + if (display_store != NULL) { + manager->priv->display_store = g_object_ref (display_store); + } +} + +static void gdm_xdmcp_manager_set_property (GObject *object, guint prop_id, const GValue *value, @@ -2694,6 +2761,9 @@ gdm_xdmcp_manager_set_property (GObject *object, self = GDM_XDMCP_MANAGER (object); switch (prop_id) { + case PROP_DISPLAY_STORE: + gdm_xdmcp_manager_set_display_store (self, g_value_get_object (value)); + break; case PROP_PORT: gdm_xdmcp_manager_set_port (self, g_value_get_uint (value)); break; @@ -2738,6 +2808,9 @@ gdm_xdmcp_manager_get_property (GObject *object, self = GDM_XDMCP_MANAGER (object); switch (prop_id) { + case PROP_DISPLAY_STORE: + g_value_set_object (value, self->priv->display_store); + break; case PROP_PORT: g_value_set_uint (value, self->priv->port); break; @@ -2781,6 +2854,13 @@ gdm_xdmcp_manager_class_init (GdmXdmcpManagerClass *klass) object_class->finalize = gdm_xdmcp_manager_finalize; g_object_class_install_property (object_class, + PROP_DISPLAY_STORE, + g_param_spec_object ("display-store", + "display store", + "display store", + GDM_TYPE_DISPLAY_STORE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_PORT, g_param_spec_uint ("port", "UDP port", @@ -2918,12 +2998,14 @@ gdm_xdmcp_manager_finalize (GObject *object) } GdmXdmcpManager * -gdm_xdmcp_manager_new (void) +gdm_xdmcp_manager_new (GdmDisplayStore *store) { if (xdmcp_manager_object != NULL) { g_object_ref (xdmcp_manager_object); } else { - xdmcp_manager_object = g_object_new (GDM_TYPE_XDMCP_MANAGER, NULL); + xdmcp_manager_object = g_object_new (GDM_TYPE_XDMCP_MANAGER, + "display-store", store, + NULL); g_object_add_weak_pointer (xdmcp_manager_object, (gpointer *) &xdmcp_manager_object); } diff --git a/daemon/gdm-xdmcp-manager.h b/daemon/gdm-xdmcp-manager.h index fe12ddc5..ffa1c57a 100644 --- a/daemon/gdm-xdmcp-manager.h +++ b/daemon/gdm-xdmcp-manager.h @@ -24,6 +24,8 @@ #include <glib-object.h> +#include "gdm-display-store.h" + G_BEGIN_DECLS #define GDM_TYPE_XDMCP_MANAGER (gdm_xdmcp_manager_get_type ()) @@ -56,7 +58,7 @@ typedef enum GQuark gdm_xdmcp_manager_error_quark (void); GType gdm_xdmcp_manager_get_type (void); -GdmXdmcpManager * gdm_xdmcp_manager_new (void); +GdmXdmcpManager * gdm_xdmcp_manager_new (GdmDisplayStore *display_store); void gdm_xdmcp_manager_set_port (GdmXdmcpManager *manager, guint port); diff --git a/daemon/gdm.c b/daemon/gdm.c deleted file mode 100644 index 19e382b4..00000000 --- a/daemon/gdm.c +++ /dev/null @@ -1,4334 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * GDM - The GNOME Display Manager - * Copyright (C) 1998, 1999, 2000 Martin K. Petersen <mkp@mkp.net> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "config.h" - -#include <signal.h> -#include <limits.h> -#include <stdlib.h> -#include <unistd.h> -#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) -#include <sched.h> -#endif -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <sys/socket.h> -#include <sys/resource.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <netdb.h> -#include <ctype.h> -#include <pwd.h> -#include <grp.h> -#include <fcntl.h> -#include <errno.h> -#include <locale.h> -#include <dirent.h> - -#ifdef HAVE_CHKAUTHATTR -#include <auth_attr.h> -#include <secdb.h> -#endif - -/* This should be moved to auth.c I suppose */ - -#include <X11/Xauth.h> -#include <glib/gi18n.h> -#include <glib-object.h> - -#include <gtk/gtk.h> - -/* Needed for signal handling */ -#include "gdm-common.h" - -#include "gdm.h" -#include "misc.h" -#include "slave.h" -#include "server.h" -#include "verify.h" -#include "display.h" -#include "choose.h" -#include "getvt.h" -#include "gdm-net.h" -#include "cookie.h" -#include "filecheck.h" -#include "errorgui.h" - -#include "gdm-socket-protocol.h" -#include "gdm-daemon-config.h" -#include "gdm-log.h" - -#include "xdmcp.h" - -#define DYNAMIC_ADD 0 -#define DYNAMIC_RELEASE 1 -#define DYNAMIC_REMOVE 2 - -#ifdef HAVE_LOGINDEVPERM -#include <libdevinfo.h> -#endif /* HAVE_LOGINDEVPERM */ - -/* Local functions */ -static void gdm_handle_message (GdmConnection *conn, - const gchar *msg, - gpointer data); -static void gdm_handle_user_message (GdmConnection *conn, - const gchar *msg, - gpointer data); -static void gdm_daemonify (void); -static void gdm_safe_restart (void); -static void gdm_try_logout_action (GdmDisplay *disp); -static void gdm_restart_now (void); -static void handle_flexi_server (GdmConnection *conn, - int type, - const gchar *server, - gboolean handled, - gboolean chooser, - const gchar *xnest_disp, - uid_t xnest_uid, - const gchar *xnest_auth_file, - const gchar *xnest_cookie, - const gchar *username); -static void custom_cmd_restart (long cmd_id); -static void custom_cmd_no_restart (long cmd_id); - -/* Global vars */ - -gint flexi_servers = 0; /* Number of flexi servers */ -static pid_t extra_process = 0; /* An extra process. Used for quickie - processes, so that they also get whacked */ -static int extra_status = 0; /* Last status from the last extra process */ - -gboolean gdm_wait_for_go = FALSE; /* wait for a GO in the fifo */ -static gboolean print_version = FALSE; /* print version number and quit */ -static gboolean preserve_ld_vars = FALSE; /* Preserve the ld environment - variables */ -static gboolean no_daemon = FALSE; /* Do not daemonize */ -static gboolean no_console = FALSE; /* There are no static servers, this - means, don't run static servers - and second, don't display info on - the console */ - -GdmConnection *fifoconn = NULL; /* Fifo connection */ -GdmConnection *pipeconn = NULL; /* slavepipe (handled just like Fifo for - compatibility) connection */ -GdmConnection *unixconn = NULL; /* UNIX Socket connection */ -int slave_fifo_pipe_fd = -1; /* The slavepipe connection */ - -unsigned char *gdm_global_cookie = NULL; -unsigned char *gdm_global_bcookie = NULL; - -gboolean gdm_first_login = TRUE; - -static GdmLogoutAction safe_logout_action = GDM_LOGOUT_ACTION_NONE; - -/* set in the main function */ -gchar **stored_argv = NULL; -int stored_argc = 0; - -static GMainLoop *main_loop = NULL; -static gchar *config_file = NULL; -static gboolean gdm_restart_mode = FALSE; -static gboolean monte_carlo_sqrt2 = FALSE; - -/* - * Lookup display number if the display number is - * exists then clear the remove flag and return TRUE - * otherwise return FALSE. - */ -static gboolean -mark_display_exists (int num) -{ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (disp->dispnum == num) { - disp->removeconf = FALSE; - return TRUE; - } - } - return FALSE; -} - -/** - * gdm_daemonify: - * - * Detach gdm daemon from the controlling terminal - */ -static void -gdm_daemonify (void) -{ - FILE *pf; - pid_t pid; - - pid = fork (); - if (pid > 0) { - const char *pidfile = GDM_PID_FILE; - - errno = 0; - if ((pf = gdm_safe_fopen_w (pidfile, 0644)) != NULL) { - errno = 0; - VE_IGNORE_EINTR (fprintf (pf, "%d\n", (int)pid)); - VE_IGNORE_EINTR (fclose (pf)); - if G_UNLIKELY (errno != 0) { - /* FIXME: how to handle this? */ - gdm_fdprintf (2, _("Cannot write PID file %s: possibly out of diskspace. Error: %s\n"), - pidfile, strerror (errno)); - gdm_error (_("Cannot write PID file %s: possibly out of diskspace. Error: %s"), - pidfile, strerror (errno)); - - } - } else if G_UNLIKELY (errno != 0) { - /* FIXME: how to handle this? */ - gdm_fdprintf (2, _("Cannot write PID file %s: possibly out of diskspace. Error: %s\n"), - pidfile, strerror (errno)); - gdm_error (_("Cannot write PID file %s: possibly out of diskspace. Error: %s"), - pidfile, strerror (errno)); - - } - - exit (EXIT_SUCCESS); - } - - if G_UNLIKELY (pid < 0) - gdm_fail (_("%s: fork () failed!"), "gdm_daemonify"); - - if G_UNLIKELY (setsid () < 0) - gdm_fail (_("%s: setsid () failed: %s!"), "gdm_daemonify", - strerror (errno)); - - VE_IGNORE_EINTR (g_chdir (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR))); - umask (022); - - VE_IGNORE_EINTR (close (0)); - VE_IGNORE_EINTR (close (1)); - VE_IGNORE_EINTR (close (2)); - - gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */ - gdm_open_dev_null (O_RDWR); /* open stdout - fd 1 */ - gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ -} - -static void -gdm_start_first_unborn_local (int delay) -{ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - /* tickle the random stuff */ - gdm_random_tick (); - - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - - if (d != NULL && - d->type == TYPE_STATIC && - d->dispstat == DISPLAY_UNBORN) { - GdmXserver *svr; - g_debug ("gdm_start_first_unborn_local: " - "Starting %s", d->name); - - /* well sleep at least 'delay' seconds - * before starting */ - d->sleep_before_run = delay; - - /* only the first static display has - * timed login going on */ - if (gdm_first_login) - d->timed_login_ok = TRUE; - - svr = gdm_server_resolve (d); - - if ( ! gdm_display_manage (d)) { - gdm_display_unmanage (d); - /* only the first static display where - we actually log in gets - autologged in */ - if (svr != NULL && - svr->handled && - ! svr->chooser) - gdm_first_login = FALSE; - } else { - /* only the first static display where - we actually log in gets - autologged in */ - if (svr != NULL && - svr->handled && - ! svr->chooser) - gdm_first_login = FALSE; - break; - } - } - } -} - -void -gdm_final_cleanup (void) -{ - GSList *list, *li; - const char *pidfile; - gboolean first; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - g_debug ("gdm_final_cleanup"); - - if (extra_process > 1) { - /* we sigterm extra processes, and we - * don't wait */ - kill (-(extra_process), SIGTERM); - extra_process = 0; - } - - /* First off whack all XDMCP and FLEXI_XNEST - slaves, we'll wait for them later */ - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - if (SERVER_IS_XDMCP (d) || - SERVER_IS_PROXY (d)) { - /* set to DEAD so that we won't kill it again */ - d->dispstat = DISPLAY_DEAD; - if (d->slavepid > 1) - kill (d->slavepid, SIGTERM); - } - } - - /* Now completely unmanage the static servers */ - first = TRUE; - list = g_slist_copy (displays); - /* somewhat of a hack to kill last server - * started first. This mostly makes things end up on - * the right vt */ - list = g_slist_reverse (list); - for (li = list; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - if (SERVER_IS_XDMCP (d) || - SERVER_IS_PROXY (d)) - continue; - /* HACK! Wait 2 seconds between killing of static servers - * because X is stupid and full of races and will otherwise - * hang my keyboard */ - if ( ! first) { - /* there could be signals happening - here */ - gdm_sleep_no_signal (2); - } - first = FALSE; - gdm_display_unmanage (d); - } - g_slist_free (list); - - /* and now kill and wait for the XDMCP and FLEXI_XNEST - slaves. unmanage will not kill slaves we have already - killed unless a SIGTERM was sent in the meantime */ - - list = g_slist_copy (displays); - for (li = list; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - if (SERVER_IS_XDMCP (d) || - SERVER_IS_PROXY (d)) - gdm_display_unmanage (d); - } - g_slist_free (list); - - /* Close stuff */ - - if (gdm_daemon_config_get_value_bool (GDM_KEY_XDMCP)) - gdm_xdmcp_close (); - - if (fifoconn != NULL) { - char *path; - gdm_connection_close (fifoconn); - path = g_build_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), ".gdmfifo", NULL); - VE_IGNORE_EINTR (g_unlink (path)); - g_free (path); - fifoconn = NULL; - } - - if (pipeconn != NULL) { - gdm_connection_close (pipeconn); - pipeconn = NULL; - } - - if (slave_fifo_pipe_fd >= 0) { - VE_IGNORE_EINTR (close (slave_fifo_pipe_fd)); - slave_fifo_pipe_fd = -1; - } - - if (unixconn != NULL) { - gdm_connection_close (unixconn); - VE_IGNORE_EINTR (g_unlink (GDM_SUP_SOCKET)); - unixconn = NULL; - } - - pidfile = GDM_PID_FILE; - if (pidfile != NULL) { - VE_IGNORE_EINTR (g_unlink (pidfile)); - } - -#ifdef HAVE_LOGINDEVPERM - (void) di_devperm_logout ("/dev/console"); -#endif /* HAVE_LOGINDEVPERM */ -} - -#ifdef __sun -void -gdm_rmdir (char *thedir) -{ - DIR *odir; - struct stat buf; - struct dirent *dp; - char thefile[FILENAME_MAX]; - - if ((stat(thedir, &buf) == -1) || ! S_ISDIR(buf.st_mode)) - return ; - - if ((rmdir (thedir) == -1) && (errno == EEXIST)) - { - odir = opendir (thedir); - do { - errno = 0; - if ((dp = readdir (odir)) != NULL) - { - if (strcmp (dp->d_name, ".") == 0 || - strcmp (dp->d_name, "..") == 0) - continue ; - snprintf (thefile, FILENAME_MAX, "%s/%s", thedir, dp->d_name); - if (stat (thefile, &buf) == -1) - continue ; - if (S_ISDIR(buf.st_mode)) - gdm_rmdir (thefile); - else - g_unlink (thefile); - } - } while (dp != NULL); - closedir (odir); - rmdir (thedir); - } -} -#endif - -static gboolean -deal_with_x_crashes (GdmDisplay *d) -{ - gboolean just_abort = FALSE; - const char *failsafe = gdm_daemon_config_get_value_string (GDM_KEY_FAILSAFE_XSERVER); - const char *keepscrashing = gdm_daemon_config_get_value_string (GDM_KEY_X_KEEPS_CRASHING); - - if ( ! d->failsafe_xserver && - ! ve_string_empty (failsafe)) { - char *bin = ve_first_word (failsafe); - /* Yay we have a failsafe */ - if ( ! ve_string_empty (bin) && - g_access (bin, X_OK) == 0) { - gdm_info (_("%s: Trying failsafe X " - "server %s"), - "deal_with_x_crashes", - failsafe); - g_free (bin); - g_free (d->command); - d->command = g_strdup (failsafe); - d->failsafe_xserver = TRUE; - return TRUE; - } - g_free (bin); - } - - /* Eeek X keeps crashing, let's try the XKeepsCrashing script */ - if ( ! ve_string_empty (keepscrashing) && - g_access (keepscrashing, X_OK|R_OK) == 0) { - pid_t pid; - - gdm_info (_("%s: Running the " - "XKeepsCrashing script"), - "deal_with_x_crashes"); - - extra_process = pid = fork (); - if (pid < 0) - extra_process = 0; - - if (pid == 0) { - char *argv[2]; - char *xlog = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_LOG_DIR), d->name, ".log"); - - gdm_unset_signals (); - - /* Also make a new process group so that we may use - * kill -(extra_process) to kill extra process and all its - * possible children */ - setsid (); - - if (gdm_daemon_config_get_value_bool (GDM_KEY_XDMCP)) - gdm_xdmcp_close (); - - gdm_close_all_descriptors (0 /* from */, -1 /* except */, -1 /* except2 */); - - /* No error checking here - if it's messed the best response - * is to ignore & try to continue */ - gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */ - gdm_open_dev_null (O_RDWR); /* open stdout - fd 1 */ - gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ - - argv[0] = (char *)gdm_daemon_config_get_value_string (GDM_KEY_X_KEEPS_CRASHING); - argv[1] = NULL; - - gdm_restoreenv (); - - /* unset DISPLAY and XAUTHORITY if they exist - * so that gdialog (if used) doesn't get confused */ - g_unsetenv ("DISPLAY"); - g_unsetenv ("XAUTHORITY"); - - /* some promised variables */ - g_setenv ("XLOG", xlog, TRUE); - g_setenv ("BINDIR", BINDIR, TRUE); - g_setenv ("SBINDIR", SBINDIR, TRUE); - g_setenv ("LIBEXECDIR", LIBEXECDIR, TRUE); - g_setenv ("SYSCONFDIR", GDMCONFDIR, TRUE); - - /* To enable gettext stuff in the script */ - g_setenv ("TEXTDOMAIN", GETTEXT_PACKAGE, TRUE); - g_setenv ("TEXTDOMAINDIR", GNOMELOCALEDIR, TRUE); - - if ( ! gdm_ok_console_language ()) { - g_unsetenv ("LANG"); - g_unsetenv ("LC_ALL"); - g_unsetenv ("LC_MESSAGES"); - g_setenv ("LANG", "C", TRUE); - g_setenv ("UNSAFE_TO_TRANSLATE", "yes", TRUE); - } - - VE_IGNORE_EINTR (execv (argv[0], argv)); - - /* yaikes! */ - _exit (32); - } else if (pid > 0) { - int status; - - if (extra_process > 1) { - int ret; - int killsignal = SIGTERM; - int storeerrno; - errno = 0; - ret = waitpid (extra_process, &status, WNOHANG); - do { - /* wait for some signal, yes this is a race */ - if (ret <= 0) - sleep (10); - errno = 0; - ret = waitpid (extra_process, &status, WNOHANG); - storeerrno = errno; - if ((ret <= 0) && gdm_daemon_config_signal_terminthup_was_notified ()) { - kill (-(extra_process), killsignal); - killsignal = SIGKILL; - } - } while (ret == 0 || (ret < 0 && storeerrno == EINTR)); - } - extra_process = 0; - - if (WIFEXITED (status) && - WEXITSTATUS (status) == 0) { - /* Yay, the user wants to try again, so - * here we go */ - return TRUE; - } else if (WIFEXITED (status) && - WEXITSTATUS (status) == 32) { - /* We couldn't run the script, just drop through */ - ; - } else { - /* Things went wrong. */ - just_abort = TRUE; - } - } - - /* if we failed to fork, or something else has happened, - * we fall through to the other options below */ - } - - /* if we have "open" we can talk to the user, not as user - * friendly as the above script, but getting there */ - if ( ! just_abort && - g_access (LIBEXECDIR "/gdmopen", X_OK) == 0) { - /* Shit if we knew what the program was to tell the user, - * the above script would have been defined and we'd run - * it for them */ - const char *error = - C_(N_("The X server (your graphical interface) " - "cannot be started. It is likely that it is not " - "set up correctly. You will need to log in on a " - "console and rerun the X configuration " - "application, then restart GDM.")); - gdm_text_message_dialog (error); - } /* else { - * At this point .... screw the user, we don't know how to - * talk to him. He's on some 'l33t system anyway, so syslog - * reading will do him good - * } */ - - gdm_error (_("Failed to start X server several times in a short time period; disabling display %s"), d->name); - - return FALSE; -} - -static gboolean -try_command (const char *command, - int *statusp) -{ - GError *error; - gboolean res; - int status; - - g_debug ("Running %s", command); - - error = NULL; - res = g_spawn_command_line_sync (command, NULL, NULL, &status, &error); - if (error != NULL) { - g_warning ("Command failed %s: %s", command, error->message); - g_error_free (error); - } - - *statusp = 0; - - if (WIFEXITED (status)) { - *statusp = WEXITSTATUS (status); - } - - return res; -} - -static gboolean -try_commands (const char **array) -{ - int i; - int status; - gboolean ret; - - ret = FALSE; - - /* the idea here is to try the first available command and return if it succeeded */ - - for (i = 0; array[i] != NULL; i++) { - if (try_command (array[i], &status)) { - ret = (status == 0); - - if (! ret) { - gdm_error (_("command failed %s: %d"), array[i], status); - } - - break; - } - } - - return ret; -} - -static void -suspend_machine (void) -{ - const gchar **suspend; - - suspend = gdm_daemon_config_get_value_string_array (GDM_KEY_SUSPEND); - - gdm_info (_("Master suspending...")); - - if (suspend == NULL) { - return; - } - - try_commands (suspend); -} - -#ifdef __linux__ -static void -change_to_first_and_clear (gboolean restart) -{ - gdm_change_vt (1); - VE_IGNORE_EINTR (close (0)); - VE_IGNORE_EINTR (close (1)); - VE_IGNORE_EINTR (close (2)); - VE_IGNORE_EINTR (open ("/dev/tty1", O_WRONLY)); - VE_IGNORE_EINTR (open ("/dev/tty1", O_WRONLY)); - VE_IGNORE_EINTR (open ("/dev/tty1", O_WRONLY)); - - g_setenv ("TERM", "linux", TRUE); - - /* evil hack that will get the fonts right */ - if (g_access ("/bin/bash", X_OK) == 0) - system ("/bin/bash -l -c /bin/true"); - - /* clear screen and set to red */ - printf ("\033[H\033[J\n\n\033[1m---\n\033[1;31m "); - - if (restart) - printf (_("System is restarting, please wait ...")); - else - printf (_("System is shutting down, please wait ...")); - /* set to black */ - printf ("\033[0m\n\033[1m---\033[0m\n\n"); -} -#endif /* __linux__ */ - -static void -halt_machine (void) -{ - const char **s; - - g_debug (_("Master halting...")); - - s = gdm_daemon_config_get_value_string_array (GDM_KEY_HALT); - - if (try_commands (s)) { - /* maybe these don't run but oh well - there isn't - really a good way to know a priori if the command - will succeed. */ - gdm_final_cleanup (); - VE_IGNORE_EINTR (g_chdir ("/")); -#ifdef __linux__ - change_to_first_and_clear (FALSE); -#endif /* __linux */ - - } -} - -static void -restart_machine (void) -{ - const char **s; - - g_debug (_("Restarting computer...")); - - s = gdm_daemon_config_get_value_string_array (GDM_KEY_REBOOT); - - if (try_commands (s)) { - gdm_final_cleanup (); - VE_IGNORE_EINTR (g_chdir ("/")); - -#ifdef __linux__ - change_to_first_and_clear (TRUE); -#endif /* __linux */ - - } -} - -static void -custom_cmd (long cmd_id) -{ - gchar * key_string; - - if (cmd_id < 0 || cmd_id >= GDM_CUSTOM_COMMAND_MAX) { - /* We are just feeling very paranoid */ - gdm_error (_("custom_cmd: Custom command index %ld outside permitted range [0,%d)"), - cmd_id, GDM_CUSTOM_COMMAND_MAX); - return; - } - - key_string = g_strdup_printf ("%s%ld=", GDM_KEY_CUSTOM_CMD_NO_RESTART_TEMPLATE, cmd_id); - if (gdm_daemon_config_get_value_bool (key_string)) - custom_cmd_no_restart (cmd_id); - else - custom_cmd_restart (cmd_id); - - g_free(key_string); -} - -static void -custom_cmd_restart (long cmd_id) -{ - gchar * key_string; - char **argv; - const char *s; - - g_debug (_("Executing custom command %ld with restart option..."), cmd_id); - - gdm_final_cleanup (); - VE_IGNORE_EINTR (g_chdir ("/")); - -#ifdef __linux__ - change_to_first_and_clear (TRUE); -#endif /* __linux */ - - key_string = g_strdup_printf ("%s%ld=", GDM_KEY_CUSTOM_CMD_TEMPLATE, cmd_id); - - argv = NULL; - s = gdm_daemon_config_get_value_string (key_string); - g_free (key_string); - if (s != NULL) { - g_shell_parse_argv (s, NULL, &argv, NULL); - } - - if (argv != NULL && argv[0] != NULL) - VE_IGNORE_EINTR (execv (argv[0], argv)); - - g_strfreev (argv); - - gdm_error (_("%s: Execution of custom command failed: %s"), - "gdm_child_action", strerror (errno)); -} - -static void -custom_cmd_no_restart (long cmd_id) -{ - pid_t pid; - - g_debug (_("Executing custom command %ld with no restart option ..."), cmd_id); - - pid = fork (); - - if (pid < 0) { - /*failed fork*/ - gdm_error (_("custom_cmd: forking process for custom command %ld failed"), cmd_id); - return; - } - else if (pid == 0) { - /* child */ - char **argv; - const char *s; - gchar *key_string; - - key_string = g_strdup_printf ("%s%ld=", GDM_KEY_CUSTOM_CMD_TEMPLATE, cmd_id); - - argv = NULL; - s = gdm_daemon_config_get_value_string (key_string); - g_free (key_string); - if (s != NULL) { - g_shell_parse_argv (s, NULL, &argv, NULL); - } - - if (argv != NULL && argv[0] != NULL) - VE_IGNORE_EINTR (execv (argv[0], argv)); - - g_strfreev (argv); - - gdm_error (_("%s: Execution of custom command failed: %s"), - "gdm_child_action", strerror (errno)); - _exit (0); - } - else { - /* parent */ - gint exitstatus = 0, status; - pid_t p_stat = waitpid (1, &exitstatus, WNOHANG); - if (p_stat > 0) { - if G_LIKELY (WIFEXITED (exitstatus)){ - status = WEXITSTATUS (exitstatus); - g_debug (_("custom_cmd: child %d returned %d"), p_stat, status); - } - return; - } - } -} - -static gboolean -gdm_cleanup_children (void) -{ - pid_t pid; - gint exitstatus = 0, status; - GdmDisplay *d = NULL; - gboolean crashed; - gboolean sysmenu; - - /* Pid and exit status of slave that died */ - pid = waitpid (-1, &exitstatus, WNOHANG); - - if (pid <= 0) - return FALSE; - - if G_LIKELY (WIFEXITED (exitstatus)) { - status = WEXITSTATUS (exitstatus); - crashed = FALSE; - g_debug ("gdm_cleanup_children: child %d returned %d", pid, status); - } else { - status = EXIT_SUCCESS; - crashed = TRUE; - if (WIFSIGNALED (exitstatus)) { - if (WTERMSIG (exitstatus) == SIGTERM || - WTERMSIG (exitstatus) == SIGINT) { - /* we send these signals, sometimes children don't handle them */ - g_debug ("gdm_cleanup_children: child %d died of signal %d (TERM/INT)", pid, - (int)WTERMSIG (exitstatus)); - } else { - gdm_error ("gdm_cleanup_children: child %d crashed of signal %d", pid, - (int)WTERMSIG (exitstatus)); - } - } else { - gdm_error ("gdm_cleanup_children: child %d crashed", pid); - } - } - - if (pid == extra_process) { - /* an extra process died, yay! */ - extra_process = 0; - extra_status = exitstatus; - return TRUE; - } - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (pid); - - if (d == NULL) - return TRUE; - - /* whack connections about this display */ - if (unixconn != NULL) - gdm_kill_subconnections_with_display (unixconn, d); - - if G_UNLIKELY (crashed) { - gdm_error ("gdm_cleanup_children: Slave crashed, killing its " - "children"); - - if (d->sesspid > 1) - kill (-(d->sesspid), SIGTERM); - d->sesspid = 0; - if (d->greetpid > 1) - kill (-(d->greetpid), SIGTERM); - d->greetpid = 0; - if (d->chooserpid > 1) - kill (-(d->chooserpid), SIGTERM); - d->chooserpid = 0; - if (d->servpid > 1) - kill (d->servpid, SIGTERM); - d->servpid = 0; - - if (gdm_daemon_config_get_value_bool (GDM_KEY_DYNAMIC_XSERVERS)) { - /* XXX - This needs to be handled better */ - gdm_server_whack_lockfile (d); - } - - /* race avoider */ - gdm_sleep_no_signal (1); - } - - /* null all these, they are not valid most definately */ - d->servpid = 0; - d->sesspid = 0; - d->greetpid = 0; - d->chooserpid = 0; - - /* definately not logged in now */ - d->logged_in = FALSE; - g_free (d->login); - d->login = NULL; - - /* Declare the display dead */ - d->slavepid = 0; - d->dispstat = DISPLAY_DEAD; - - sysmenu = gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, d->name); - - if ( ! sysmenu && - (status == DISPLAY_RESTARTGDM || - status == DISPLAY_REBOOT || - status == DISPLAY_SUSPEND || - status == DISPLAY_HALT)) { - gdm_info (_("Restart GDM, Restart machine, Suspend, or Halt request when there is no system menu from display %s"), d->name); - status = DISPLAY_REMANAGE; - } - - if ( ! d->attached && - (status == DISPLAY_RESTARTGDM || - status == DISPLAY_REBOOT || - status == DISPLAY_SUSPEND || - status == DISPLAY_HALT)) { - gdm_info (_("Restart GDM, Restart machine, Suspend or Halt request from a non-static display %s"), d->name); - status = DISPLAY_REMANAGE; - } - - if (status == DISPLAY_RUN_CHOOSER) { - /* use the chooser on the next run (but only if allowed) */ - if (sysmenu && - gdm_daemon_config_get_value_bool_per_display (GDM_KEY_CHOOSER_BUTTON, d->name)) - d->use_chooser = TRUE; - status = DISPLAY_REMANAGE; - /* go around the display loop detection, these are short - * sessions, so this decreases the chances of the loop - * detection being hit */ - d->last_loop_start_time = 0; - } - - if (status == DISPLAY_CHOSEN) { - /* forget about this indirect id, since this - * display will be dead very soon, and we don't want it - * to take the indirect display with it */ - d->indirect_id = 0; - status = DISPLAY_REMANAGE; - } - - if (status == DISPLAY_GREETERFAILED) { - if (d->managetime + 10 >= time (NULL)) { - d->try_different_greeter = TRUE; - } else { - d->try_different_greeter = FALSE; - } - /* now just remanage */ - status = DISPLAY_REMANAGE; - } else { - d->try_different_greeter = FALSE; - } - - /* checkout if we can actually do stuff */ - switch (status) { - case DISPLAY_REBOOT: - if (gdm_daemon_config_get_value_string_array (GDM_KEY_REBOOT) == NULL) - status = DISPLAY_REMANAGE; - break; - case DISPLAY_HALT: - if (gdm_daemon_config_get_value_string_array (GDM_KEY_HALT) == NULL) - status = DISPLAY_REMANAGE; - break; - case DISPLAY_SUSPEND: - if (gdm_daemon_config_get_value_string_array (GDM_KEY_SUSPEND) == NULL) - status = DISPLAY_REMANAGE; - break; - default: - break; - } - - /* if we crashed clear the theme */ - if (crashed) { - g_free (d->theme_name); - d->theme_name = NULL; - } - - start_autopsy: - - /* Autopsy */ - switch (status) { - - case DISPLAY_ABORT: /* Bury this display for good */ - gdm_info (_("%s: Aborting display %s"), - "gdm_child_action", d->name); - - gdm_try_logout_action (d); - gdm_safe_restart (); - - gdm_display_unmanage (d); - - /* If there are some pending statics, start them now */ - gdm_start_first_unborn_local (3 /* delay */); - break; - - case DISPLAY_REBOOT: /* Restart machine */ - restart_machine (); - - status = DISPLAY_REMANAGE; - goto start_autopsy; - break; - - case DISPLAY_HALT: /* Halt machine */ - halt_machine (); - - status = DISPLAY_REMANAGE; - goto start_autopsy; - break; - - case DISPLAY_SUSPEND: /* Suspend machine */ - /* XXX: this is ugly, why should there be a suspend like this, - * see GDM_SOP_SUSPEND_MACHINE */ - suspend_machine (); - - status = DISPLAY_REMANAGE; - goto start_autopsy; - break; - - case DISPLAY_RESTARTGDM: - gdm_restart_now (); - break; - - case DISPLAY_XFAILED: /* X sucks */ - g_debug ("X failed!"); - /* inform about error if needed */ - if (d->socket_conn != NULL) { - GdmConnection *conn = d->socket_conn; - d->socket_conn = NULL; - gdm_connection_set_close_notify (conn, NULL, NULL); - gdm_connection_write (conn, "ERROR 3 X failed\n"); - } - - gdm_try_logout_action (d); - gdm_safe_restart (); - - /* in remote/flexi case just drop to _REMANAGE */ - if (d->type == TYPE_STATIC) { - time_t now = time (NULL); - d->x_faileds++; - /* This really is likely the first time if it's been, - some time, say 5 minutes */ - if (now - d->last_x_failed > (5*60)) { - /* reset */ - d->x_faileds = 1; - d->last_x_failed = now; - /* well sleep at least 3 seconds before starting */ - d->sleep_before_run = 3; - } else if (d->x_faileds >= 3) { - g_debug ("gdm_child_action: dealing with X crashes"); - if ( ! deal_with_x_crashes (d)) { - g_debug ("gdm_child_action: Aborting display"); - /* an original way to deal with these things: - * "Screw you guys, I'm going home!" */ - gdm_display_unmanage (d); - - /* If there are some pending statics, - * start them now */ - gdm_start_first_unborn_local (3 /* delay */); - break; - } - g_debug ("gdm_child_action: Trying again"); - - /* reset */ - d->x_faileds = 0; - d->last_x_failed = 0; - } else { - /* well sleep at least 3 seconds before starting */ - d->sleep_before_run = 3; - } - /* go around the display loop detection, we're doing - * our own here */ - d->last_loop_start_time = 0; - } - /* fall through */ - - case DISPLAY_REMANAGE: /* Remanage display */ - default: - g_debug ("gdm_child_action: In remanage"); - - /* if we did REMANAGE, that means that we're no longer failing */ - if (status == DISPLAY_REMANAGE) { - /* reset */ - d->x_faileds = 0; - d->last_x_failed = 0; - } - - /* inform about error if needed */ - if (d->socket_conn != NULL) { - GdmConnection *conn = d->socket_conn; - d->socket_conn = NULL; - gdm_connection_set_close_notify (conn, NULL, NULL); - gdm_connection_write (conn, "ERROR 2 Startup errors\n"); - } - - gdm_try_logout_action (d); - gdm_safe_restart (); - - /* This is a static server so we start a new slave */ - if (d->type == TYPE_STATIC) { - if ( ! gdm_display_manage (d)) { - gdm_display_unmanage (d); - /* If there are some pending statics, - * start them now */ - gdm_start_first_unborn_local (3 /* delay */); - } - } else if (d->type == TYPE_FLEXI || d->type == TYPE_FLEXI_XNEST) { - /* if this was a chooser session and we have chosen a host, - then we don't want to unmanage, we want to manage and - choose that host */ - if (d->chosen_hostname != NULL || d->use_chooser) { - if ( ! gdm_display_manage (d)) { - gdm_display_unmanage (d); - } - } else { - /* else, this is a one time thing */ - gdm_display_unmanage (d); - } - /* Remote displays will send a request to be managed */ - } else /* TYPE_XDMCP */ { - gdm_display_unmanage (d); - } - - break; - } - - gdm_try_logout_action (d); - gdm_safe_restart (); - - return TRUE; -} - -static void -gdm_restart_now (void) -{ - gdm_info (_("GDM restarting ...")); - gdm_final_cleanup (); - gdm_restoreenv (); - VE_IGNORE_EINTR (execvp (stored_argv[0], stored_argv)); - gdm_error (_("Failed to restart self")); - _exit (1); -} - -static void -gdm_safe_restart (void) -{ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - if ( ! gdm_restart_mode) - return; - - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - - if (d->logged_in) - return; - } - - gdm_restart_now (); -} - -static void -gdm_do_logout_action (GdmLogoutAction logout_action) -{ - switch (logout_action) { - case GDM_LOGOUT_ACTION_HALT: - halt_machine (); - break; - - case GDM_LOGOUT_ACTION_REBOOT: - restart_machine (); - break; - - case GDM_LOGOUT_ACTION_SUSPEND: - suspend_machine (); - break; - - default: - /* This is a bit ugly but its the only place we can - check for the range of values */ - if (logout_action >= GDM_LOGOUT_ACTION_CUSTOM_CMD_FIRST && - logout_action <= GDM_LOGOUT_ACTION_CUSTOM_CMD_FIRST) - custom_cmd (logout_action - GDM_LOGOUT_ACTION_CUSTOM_CMD_FIRST); - break; - } -} - -static void -gdm_try_logout_action (GdmDisplay *disp) -{ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - if (disp != NULL && - disp->logout_action != GDM_LOGOUT_ACTION_NONE && - ! disp->logged_in) { - gdm_do_logout_action (disp->logout_action); - disp->logout_action = GDM_LOGOUT_ACTION_NONE; - return; - } - - if (safe_logout_action == GDM_LOGOUT_ACTION_NONE) - return; - - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - - if (d->logged_in) - return; - } - - gdm_do_logout_action (safe_logout_action); - safe_logout_action = GDM_LOGOUT_ACTION_NONE; -} - -static void -main_daemon_abrt (int sig) -{ - /* FIXME: note that this could mean out of memory */ - gdm_error (_("main daemon: Got SIGABRT. Something went very wrong. Going down!")); - gdm_final_cleanup (); - exit (EXIT_FAILURE); -} - -static gboolean -mainloop_sig_callback (int sig, gpointer data) -{ - /* signals are at somewhat random times aren't they? */ - gdm_random_tick (); - - g_debug ("mainloop_sig_callback: Got signal %d", (int)sig); - switch (sig) - { - case SIGCHLD: - while (gdm_cleanup_children ()) - ; - break; - - case SIGINT: - case SIGTERM: - g_debug ("mainloop_sig_callback: Got TERM/INT. Going down!"); - gdm_final_cleanup (); - exit (EXIT_SUCCESS); - break; - -#ifdef SIGXFSZ - case SIGXFSZ: - gdm_error ("main daemon: Hit file size rlimit, restarting!"); - gdm_restart_now (); - break; -#endif - -#ifdef SIGXCPU - case SIGXCPU: - gdm_error ("main daemon: Hit CPU rlimit, restarting!"); - gdm_restart_now (); - break; -#endif - - case SIGHUP: - gdm_restart_now (); - break; - - case SIGUSR1: - gdm_restart_mode = TRUE; - gdm_safe_restart (); - break; - - default: - break; - } - - return TRUE; -} - -/* - * main: The main daemon control - */ - -static void -store_argv (int argc, char *argv[]) -{ - int i; - - stored_argv = g_new0 (char *, argc + 1); - for (i = 0; i < argc; i++) - stored_argv[i] = g_strdup (argv[i]); - stored_argv[i] = NULL; - stored_argc = argc; -} - -static void -close_notify (gpointer data) -{ - GdmConnection **conn = data; - * conn = NULL; -} - -static void -create_connections (void) -{ - int p[2]; - gchar *path; - - path = g_build_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), ".gdmfifo", NULL); - fifoconn = gdm_connection_open_fifo (path, 0660); - g_free (path); - - if G_LIKELY (fifoconn != NULL) { - gdm_connection_set_handler (fifoconn, - gdm_handle_message, - NULL /* data */, - NULL /* destroy_notify */); - gdm_connection_set_close_notify (fifoconn, - &fifoconn, - close_notify); - } - - if G_UNLIKELY (pipe (p) < 0) { - slave_fifo_pipe_fd = -1; - pipeconn = NULL; - } else { - slave_fifo_pipe_fd = p[1]; - pipeconn = gdm_connection_open_fd (p[0]); - } - - if G_LIKELY (pipeconn != NULL) { - gdm_connection_set_handler (pipeconn, - gdm_handle_message, - NULL /* data */, - NULL /* destroy_notify */); - gdm_connection_set_close_notify (pipeconn, - &pipeconn, - close_notify); - } else { - VE_IGNORE_EINTR (close (p[0])); - VE_IGNORE_EINTR (close (p[1])); - slave_fifo_pipe_fd = -1; - } - - unixconn = gdm_connection_open_unix (GDM_SUP_SOCKET, 0666); - - if G_LIKELY (unixconn != NULL) { - gdm_connection_set_handler (unixconn, - gdm_handle_user_message, - NULL /* data */, - NULL /* destroy_notify */); - gdm_connection_set_nonblock (unixconn, TRUE); - gdm_connection_set_close_notify (unixconn, - &unixconn, - close_notify); - } -} - -static void -calc_sqrt2 (void) -{ - unsigned long n = 0, h = 0; - double x; - printf ("\n"); - for (;;) { - x = g_random_double_range (1.0, 2.0); - if (x*x <= 2.0) - h++; - n++; - if ( ! (n & 0xfff)) { - double sqrttwo = 1.0 + ((double)h)/(double)n; - printf ("sqrt(2) ~~ %1.10f\t(1 + %lu/%lu) " - "iteration: %lu \r", - sqrttwo, h, n, n); - } - } -} - -GOptionEntry options [] = { - { "nodaemon", '\0', 0, G_OPTION_ARG_NONE, - &no_daemon, N_("Do not fork into the background"), NULL }, - { "no-console", '\0', 0, G_OPTION_ARG_NONE, - &no_console, N_("No console (static) servers to be run"), NULL }, - { "config", '\0', 0, G_OPTION_ARG_STRING, - &config_file, N_("Alternative GDM System Defaults configuration file"), N_("CONFIGFILE") }, - { "preserve-ld-vars", '\0', 0, G_OPTION_ARG_NONE, - &preserve_ld_vars, N_("Preserve LD_* variables"), NULL }, - { "version", '\0', 0, G_OPTION_ARG_NONE, - &print_version, N_("Print GDM version"), NULL }, - { "wait-for-go", '\0', 0, G_OPTION_ARG_NONE, - &gdm_wait_for_go, N_("Start the first X server but then halt until we get a GO in the fifo"), NULL }, - { "monte-carlo-sqrt2", 0, 0, G_OPTION_ARG_NONE, - &monte_carlo_sqrt2, NULL, NULL }, - { NULL } -}; - -static gboolean -linux_only_is_running (pid_t pid) -{ - char resolved_self[PATH_MAX]; - char resolved_running[PATH_MAX]; - - char *running = g_strdup_printf ("/proc/%lu/exe", (gulong)pid); - - if (realpath ("/proc/self/exe", resolved_self) == NULL) { - g_free (running); - /* probably not a linux system */ - return TRUE; - } - - if (realpath (running, resolved_running) == NULL) { - g_free (running); - /* probably not a linux system */ - return TRUE; - } - - g_free (running); - - if (strcmp (resolved_running, resolved_self) == 0) - return TRUE; - return FALSE; -} - -static void -ensure_desc_012 (void) -{ - int fd; - /* We here ensure descriptors 0, 1 and 2 - * we of course count on the fact that open - * opens the lowest available descriptor */ - for (;;) { - fd = gdm_open_dev_null (O_RDWR); - /* Once we are up to 3, we're beyond stdin, - * stdout and stderr */ - if (fd >= 3) { - VE_IGNORE_EINTR (close (fd)); - break; - } - } -} - -/* initially if we get a TERM or INT we just want to die, - but we want to also kill an extra process if it exists */ -static void -initial_term_int (int signal) -{ - if (extra_process > 1) - kill (-(extra_process), SIGTERM); - _exit (EXIT_FAILURE); -} - -static void -gdm_make_global_cookie (void) -{ - FILE *fp; - char *file; - - gdm_cookie_generate ((char **)&gdm_global_cookie, (char **)&gdm_global_bcookie); - - file = g_build_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), ".cookie", NULL); - VE_IGNORE_EINTR (g_unlink (file)); - - fp = gdm_safe_fopen_w (file, 0600); - if G_UNLIKELY (fp == NULL) { - gdm_error (_("Can't open %s for writing"), file); - g_free (file); - return; - } - - VE_IGNORE_EINTR (fprintf (fp, "%s\n", gdm_global_cookie)); - - /* FIXME: What about out of disk space errors? */ - errno = 0; - VE_IGNORE_EINTR (fclose (fp)); - if G_UNLIKELY (errno != 0) { - gdm_error (_("Can't write to %s: %s"), file, - strerror (errno)); - } - - g_free (file); -} - -int -main (int argc, char *argv[]) -{ - FILE *pf; - sigset_t mask; - struct sigaction sig, child, abrt; - GOptionContext *ctx; - const char *pidfile; - int i; - - /* semi init pseudorandomness */ - gdm_random_tick (); - - /* We here ensure descriptors 0, 1 and 2 */ - ensure_desc_012 (); - - /* store all initial stuff, args, env, rlimits, runlevel */ - store_argv (argc, argv); - gdm_saveenv (); - gdm_get_initial_limits (); - - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - setlocale (LC_ALL, ""); - - /* Initialize runtime environment */ - umask (022); - - g_type_init (); - - ctx = g_option_context_new (_("- The GNOME login manager")); - g_option_context_add_main_entries (ctx, options, _("main options")); - - /* preprocess the arguments to support the xdm style -nodaemon - * option - */ - for (i = 0; i < argc; i++) { - if (strcmp (argv[i], "-nodaemon") == 0) - argv[i] = (char *) "--nodaemon"; - } - - g_option_context_parse (ctx, &argc, &argv, NULL); - g_option_context_free (ctx); - - if (monte_carlo_sqrt2) { - calc_sqrt2 (); - return 0; - } - - if (print_version) { - printf ("GDM %s\n", VERSION); - fflush (stdout); - exit (0); - } - - gdm_log_init (); - /* Parse configuration file */ - gdm_daemon_config_parse (config_file, no_console); - gdm_log_set_debug (gdm_daemon_config_get_bool_for_id (GDM_ID_DEBUG)); - - /* XDM compliant error message */ - if G_UNLIKELY (getuid () != 0) { - /* make sure the pid file doesn't get wiped */ - gdm_error (_("Only root wants to run GDM\n")); - exit (-1); - } - - main_loop = g_main_loop_new (NULL, FALSE); - - /* initial TERM/INT handler */ - sig.sa_handler = initial_term_int; - sig.sa_flags = SA_RESTART; - sigemptyset (&sig.sa_mask); - - /* - * Do not call gdm_fail before calling gdm_config_parse () - * since the gdm_fail function uses config data - */ - if G_UNLIKELY (sigaction (SIGTERM, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "TERM", strerror (errno)); - - if G_UNLIKELY (sigaction (SIGINT, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "INT", strerror (errno)); - - /* get the name of the root user */ - gdm_root_user (); - - pidfile = GDM_PID_FILE; - - /* Check if another gdm process is already running */ - if (g_access (pidfile, R_OK) == 0) { - - /* Check if the existing process is still alive. */ - gint pidv; - - pf = fopen (pidfile, "r"); - - if (pf != NULL && - fscanf (pf, "%d", &pidv) == 1 && - kill (pidv, 0) == 0 && - linux_only_is_running (pidv)) { - /* make sure the pid file doesn't get wiped */ - VE_IGNORE_EINTR (fclose (pf)); - gdm_fail (_("GDM already running. Aborting!")); - } - - if (pf != NULL) - VE_IGNORE_EINTR (fclose (pf)); - } - - /* Become daemon unless started in -nodaemon mode or child of init */ - if (no_daemon || getppid () == 1) { - - /* Write pid to pidfile */ - errno = 0; - if ((pf = gdm_safe_fopen_w (pidfile, 0644)) != NULL) { - errno = 0; - VE_IGNORE_EINTR (fprintf (pf, "%d\n", (int)getpid ())); - VE_IGNORE_EINTR (fclose (pf)); - if (errno != 0) { - /* FIXME: how to handle this? */ - gdm_fdprintf (2, _("Cannot write PID file %s: possibly out of diskspace. Error: %s\n"), - pidfile, strerror (errno)); - gdm_error (_("Cannot write PID file %s: possibly out of diskspace. Error: %s"), - pidfile, strerror (errno)); - - } - } else if (errno != 0) { - /* FIXME: how to handle this? */ - gdm_fdprintf (2, _("Cannot write PID file %s: possibly out of diskspace. Error: %s\n"), - pidfile, strerror (errno)); - gdm_error (_("Cannot write PID file %s: possibly out of diskspace. Error: %s"), - pidfile, strerror (errno)); - - } - - VE_IGNORE_EINTR (g_chdir (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR))); - umask (022); - } - else - gdm_daemonify (); - -#ifdef __sun - g_unlink (SDTLOGIN_DIR); - g_mkdir (SDTLOGIN_DIR, 0700); -#endif - - /* Signal handling */ - ve_signal_add (SIGCHLD, mainloop_sig_callback, NULL); - ve_signal_add (SIGTERM, mainloop_sig_callback, NULL); - ve_signal_add (SIGINT, mainloop_sig_callback, NULL); - ve_signal_add (SIGHUP, mainloop_sig_callback, NULL); - ve_signal_add (SIGUSR1, mainloop_sig_callback, NULL); - - sig.sa_handler = ve_signal_notify; - sig.sa_flags = SA_RESTART; - sigemptyset (&sig.sa_mask); - - if G_UNLIKELY (sigaction (SIGTERM, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "TERM", strerror (errno)); - - if G_UNLIKELY (sigaction (SIGINT, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "INT", strerror (errno)); - - if G_UNLIKELY (sigaction (SIGHUP, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "HUP", strerror (errno)); - - if G_UNLIKELY (sigaction (SIGUSR1, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "USR1", strerror (errno)); - - /* some process limit signals we catch and restart on, - note that we don't catch these in the slave, but then - we catch those in the main daemon as slave crashing - (terminated by signal), and we clean up appropriately */ -#ifdef SIGXCPU - ve_signal_add (SIGXCPU, mainloop_sig_callback, NULL); - if G_UNLIKELY (sigaction (SIGXCPU, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "XCPU", strerror (errno)); -#endif -#ifdef SIGXFSZ - ve_signal_add (SIGXFSZ, mainloop_sig_callback, NULL); - if G_UNLIKELY (sigaction (SIGXFSZ, &sig, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "XFSZ", strerror (errno)); -#endif - - /* cannot use mainloop for SIGABRT, the handler can never - return */ - abrt.sa_handler = main_daemon_abrt; - abrt.sa_flags = SA_RESTART; - sigemptyset (&abrt.sa_mask); - - if G_UNLIKELY (sigaction (SIGABRT, &abrt, NULL) < 0) - gdm_fail (_("%s: Error setting up %s signal handler: %s"), - "main", "ABRT", strerror (errno)); - - child.sa_handler = ve_signal_notify; - child.sa_flags = SA_RESTART|SA_NOCLDSTOP; - sigemptyset (&child.sa_mask); - sigaddset (&child.sa_mask, SIGCHLD); - - if G_UNLIKELY (sigaction (SIGCHLD, &child, NULL) < 0) - gdm_fail (_("%s: Error setting up CHLD signal handler"), "gdm_main"); - - sigemptyset (&mask); - sigaddset (&mask, SIGINT); - sigaddset (&mask, SIGTERM); - sigaddset (&mask, SIGCHLD); - sigaddset (&mask, SIGHUP); - sigaddset (&mask, SIGUSR1); - sigaddset (&mask, SIGABRT); -#ifdef SIGXCPU - sigaddset (&mask, SIGXCPU); -#endif -#ifdef SIGXFSZ - sigaddset (&mask, SIGXFSZ); -#endif - sigprocmask (SIG_UNBLOCK, &mask, NULL); - - gdm_signal_ignore (SIGUSR2); - gdm_signal_ignore (SIGPIPE); - - /* ignore power failures, up to user processes to - * handle things correctly */ -#ifdef SIGPWR - gdm_signal_ignore (SIGPWR); -#endif - /* can we ever even get this one? */ -#ifdef SIGLOST - gdm_signal_ignore (SIGLOST); -#endif - - g_debug ("gdm_main: Here we go..."); - -#ifdef HAVE_LOGINDEVPERM - di_devperm_login ("/dev/console", gdm_daemon_config_get_gdmuid (), gdm_daemon_config_get_gdmgid (), NULL); -#endif /* HAVE_LOGINDEVPERM */ - - /* Init XDMCP if applicable */ - if (gdm_daemon_config_get_value_bool (GDM_KEY_XDMCP) && ! gdm_wait_for_go) { - gdm_xdmcp_init (); - } - - create_connections (); - - /* make sure things (currently /tmp/.ICE-unix and /tmp/.X11-unix) - * are sane */ - gdm_ensure_sanity () ; - - /* Make us a unique global cookie to authenticate */ - gdm_make_global_cookie (); - - /* Start static X servers */ - gdm_start_first_unborn_local (0 /* delay */); - - /* Accept remote connections */ - if (gdm_daemon_config_get_value_bool (GDM_KEY_XDMCP) && ! gdm_wait_for_go) { - g_debug ("Accepting XDMCP connections..."); - gdm_xdmcp_run (); - } - - /* We always exit via exit (), and sadly we need to g_main_quit () - * at times not knowing if it's this main or a recursive one we're - * quitting. - */ - while (1) - { - g_main_loop_run (main_loop); - g_debug ("main: Exited main loop"); - } - - return EXIT_SUCCESS; /* Not reached */ -} - -static gboolean -order_exists (int order) -{ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - if (d->x_servers_order == order) - return TRUE; - } - return FALSE; -} - -static int -get_new_order (GdmDisplay *d) -{ - int order; - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - /* first try the position in the 'displays' list as - * our order */ - for (order = 0, li = displays; li != NULL; order++, li = li->next) { - if (li->data == d) - break; - } - /* next make sure it's unique */ - while (order_exists (order)) - order++; - return order; -} - -static void -write_x_servers (GdmDisplay *d) -{ - FILE *fp; - char *file = gdm_make_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), d->name, ".Xservers"); - int i; - int bogusname; - - if (d->x_servers_order < 0) - d->x_servers_order = get_new_order (d); - - fp = gdm_safe_fopen_w (file, 0644); - if G_UNLIKELY (fp == NULL) { - gdm_error (_("Can't open %s for writing"), file); - g_free (file); - return; - } - - for (bogusname = 0, i = 0; i < d->x_servers_order; bogusname++, i++) { - char buf[32]; - g_snprintf (buf, sizeof (buf), ":%d", bogusname); - if (strcmp (buf, d->name) == 0) - g_snprintf (buf, sizeof (buf), ":%d", ++bogusname); - VE_IGNORE_EINTR (fprintf (fp, "%s local /usr/X11R6/bin/Xbogus\n", buf)); - } - - if (SERVER_IS_LOCAL (d)) { - char **argv; - char *command; - int argc; - argc = 0; - argv = NULL; - gdm_server_resolve_command_line (d, - FALSE, /* resolve_flags */ - NULL, /* vtarg */ - &argc, - &argv); - command = g_strjoinv (" ", argv); - g_strfreev (argv); - VE_IGNORE_EINTR (fprintf (fp, "%s local %s\n", d->name, command)); - g_free (command); - } else { - VE_IGNORE_EINTR (fprintf (fp, "%s foreign\n", d->name)); - } - - /* FIXME: What about out of disk space errors? */ - errno = 0; - VE_IGNORE_EINTR (fclose (fp)); - if G_UNLIKELY (errno != 0) { - gdm_error (_("Can't write to %s: %s"), file, - strerror (errno)); - } - - g_free (file); -} - -static void -send_slave_ack_dialog_int (GdmDisplay *d, int type, int response) -{ - if (d->master_notify_fd >= 0) { - char *not; - - not = g_strdup_printf ("%c%c%d\n", GDM_SLAVE_NOTIFY_RESPONSE, type, response); - VE_IGNORE_EINTR (write (d->master_notify_fd, not, strlen (not))); - - g_free (not); - } - if (d->slavepid > 1) { - /* now yield the CPU as the other process has more - useful work to do then we do */ -#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) - sched_yield (); -#endif - } -} - -static void -send_slave_ack_dialog_char (GdmDisplay *d, int type, const char *resp) -{ - if (d->master_notify_fd >= 0) { - if (resp == NULL) { - char not[3]; - - not[0] = GDM_SLAVE_NOTIFY_RESPONSE; - not[1] = type; - not[2] = '\n'; - VE_IGNORE_EINTR (write (d->master_notify_fd, not, 3)); - } else { - char *not = g_strdup_printf ("%c%c%s\n", - GDM_SLAVE_NOTIFY_RESPONSE, - type, - resp); - VE_IGNORE_EINTR (write (d->master_notify_fd, not, strlen (not))); - g_free (not); - } - } - if (d->slavepid > 1) { - /* now yield the CPU as the other process has more - useful work to do then we do */ -#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) - sched_yield (); -#endif - } -} - -static void -send_slave_ack (GdmDisplay *d, const char *resp) -{ - if (d->master_notify_fd >= 0) { - if (resp == NULL) { - char not[2]; - not[0] = GDM_SLAVE_NOTIFY_ACK; - not[1] = '\n'; - VE_IGNORE_EINTR (write (d->master_notify_fd, not, 2)); - } else { - char *not = g_strdup_printf ("%c%s\n", - GDM_SLAVE_NOTIFY_ACK, - resp); - VE_IGNORE_EINTR (write (d->master_notify_fd, not, strlen (not))); - g_free (not); - } - } - if (d->slavepid > 1) { - kill (d->slavepid, SIGUSR2); - /* now yield the CPU as the other process has more - useful work to do then we do */ -#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) - sched_yield (); -#endif - } -} - -static void -send_slave_command (GdmDisplay *d, const char *command) -{ - if (d->master_notify_fd >= 0) { - char *cmd = g_strdup_printf ("%c%s\n", - GDM_SLAVE_NOTIFY_COMMAND, - command); - VE_IGNORE_EINTR (write (d->master_notify_fd, cmd, strlen (cmd))); - g_free (cmd); - } - if (d->slavepid > 1) { - kill (d->slavepid, SIGUSR2); - /* now yield the CPU as the other process has more - useful work to do then we do */ -#if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) - sched_yield (); -#endif - } -} - -static void -gdm_handle_message (GdmConnection *conn, const char *msg, gpointer data) -{ - /* Evil!, all this for debugging? */ - if G_UNLIKELY (gdm_daemon_config_get_value_bool (GDM_KEY_DEBUG)) { - if (strncmp (msg, GDM_SOP_COOKIE " ", - strlen (GDM_SOP_COOKIE " ")) == 0) { - char *s = g_strndup - (msg, strlen (GDM_SOP_COOKIE " XXXX XX")); - /* cut off most of the cookie for "security" */ - g_debug ("Handling message: '%s...'", s); - g_free (s); - } else if (strncmp (msg, GDM_SOP_SYSLOG " ", - strlen (GDM_SOP_SYSLOG " ")) != 0) { - /* Don't print out the syslog message as it will - * be printed out anyway as that's the whole point - * of the message. */ - g_debug ("Handling message: '%s'", msg); - } - } - - if (strncmp (msg, GDM_SOP_CHOSEN " ", - strlen (GDM_SOP_CHOSEN " ")) == 0) { - gdm_choose_data (msg); - } else if (strncmp (msg, GDM_SOP_CHOSEN_LOCAL " ", - strlen (GDM_SOP_CHOSEN_LOCAL " ")) == 0) { - GdmDisplay *d; - long slave_pid; - char *p; - - if (sscanf (msg, GDM_SOP_CHOSEN_LOCAL " %ld", &slave_pid) - != 1) - return; - p = strchr (msg, ' '); - if (p != NULL) - p = strchr (p+1, ' '); - if (p == NULL) - return; - p++; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - g_free (d->chosen_hostname); - d->chosen_hostname = g_strdup (p); - g_debug ("Got CHOSEN_LOCAL == %s", p); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_XPID " ", - strlen (GDM_SOP_XPID " ")) == 0) { - GdmDisplay *d; - long slave_pid, pid; - - if (sscanf (msg, GDM_SOP_XPID " %ld %ld", &slave_pid, &pid) - != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - d->servpid = pid; - g_debug ("Got XPID == %ld", (long)pid); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_SESSPID " ", - strlen (GDM_SOP_SESSPID " ")) == 0) { - GdmDisplay *d; - long slave_pid, pid; - - if (sscanf (msg, GDM_SOP_SESSPID " %ld %ld", &slave_pid, &pid) - != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - d->sesspid = pid; - g_debug ("Got SESSPID == %ld", (long)pid); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_GREETPID " ", - strlen (GDM_SOP_GREETPID " ")) == 0) { - GdmDisplay *d; - long slave_pid, pid; - - if (sscanf (msg, GDM_SOP_GREETPID " %ld %ld", &slave_pid, &pid) - != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - d->greetpid = pid; - g_debug ("Got GREETPID == %ld", (long)pid); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_CHOOSERPID " ", - strlen (GDM_SOP_CHOOSERPID " ")) == 0) { - GdmDisplay *d; - long slave_pid, pid; - - if (sscanf (msg, GDM_SOP_CHOOSERPID " %ld %ld", - &slave_pid, &pid) != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - d->chooserpid = pid; - g_debug ("Got CHOOSERPID == %ld", (long)pid); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_LOGGED_IN " ", - strlen (GDM_SOP_LOGGED_IN " ")) == 0) { - GdmDisplay *d; - long slave_pid; - int logged_in; - if (sscanf (msg, GDM_SOP_LOGGED_IN " %ld %d", &slave_pid, - &logged_in) != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - d->logged_in = logged_in ? TRUE : FALSE; - g_debug ("Got logged in == %s", - d->logged_in ? "TRUE" : "FALSE"); - - /* whack connections about this display if a user - * just logged out since we don't want such - * connections persisting to be authenticated */ - if ( ! logged_in && unixconn != NULL) - gdm_kill_subconnections_with_display (unixconn, d); - - /* if the user just logged out, - * let's see if it's safe to restart */ - if ( ! d->logged_in) { - gdm_try_logout_action (d); - gdm_safe_restart (); - } - - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_DISP_NUM " ", - strlen (GDM_SOP_DISP_NUM " ")) == 0) { - GdmDisplay *d; - long slave_pid; - int disp_num; - - if (sscanf (msg, GDM_SOP_DISP_NUM " %ld %d", - &slave_pid, &disp_num) != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - g_free (d->name); - d->name = g_strdup_printf (":%d", disp_num); - d->dispnum = disp_num; - g_debug ("Got DISP_NUM == %d", disp_num); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_VT_NUM " ", - strlen (GDM_SOP_VT_NUM " ")) == 0) { - GdmDisplay *d; - long slave_pid; - int vt_num; - - if (sscanf (msg, GDM_SOP_VT_NUM " %ld %d", - &slave_pid, &vt_num) != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - d->vt = vt_num; - g_debug ("Got VT_NUM == %d", vt_num); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_LOGIN " ", - strlen (GDM_SOP_LOGIN " ")) == 0) { - GdmDisplay *d; - long slave_pid; - char *p; - - if (sscanf (msg, GDM_SOP_LOGIN " %ld", - &slave_pid) != 1) - return; - p = strchr (msg, ' '); - if (p != NULL) - p = strchr (p+1, ' '); - if (p == NULL) - return; - - p++; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - g_free (d->login); - d->login = g_strdup (p); - g_debug ("Got LOGIN == %s", p); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_QUERYLOGIN " ", - strlen (GDM_SOP_QUERYLOGIN " ")) == 0) { - GdmDisplay *d; - long slave_pid; - char *p; - - if (sscanf (msg, GDM_SOP_QUERYLOGIN " %ld", - &slave_pid) != 1) - return; - p = strchr (msg, ' '); - if (p != NULL) - p = strchr (p+1, ' '); - if (p == NULL) - return; - - p++; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - GString *resp = NULL; - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - g_debug ("Got QUERYLOGIN %s", p); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *di = li->data; - if (di->logged_in && - di->login != NULL && - strcmp (di->login, p) == 0) { - gboolean migratable = FALSE; - - if (resp == NULL) - resp = g_string_new (NULL); - else - resp = g_string_append_c (resp, ','); - - g_string_append (resp, di->name); - g_string_append_c (resp, ','); - - if (d->attached && di->attached && di->vt > 0) - migratable = TRUE; - else if (gdm_daemon_config_get_value_string (GDM_KEY_XDMCP_PROXY_RECONNECT) != NULL && - d->type == TYPE_XDMCP_PROXY && di->type == TYPE_XDMCP_PROXY) - migratable = TRUE; - - g_string_append_c (resp, migratable ? '1' : '0'); - } - } - - /* send ack */ - if (resp != NULL) { - send_slave_ack (d, resp->str); - g_string_free (resp, TRUE); - } else { - send_slave_ack (d, NULL); - } - } - } else if (strncmp (msg, GDM_SOP_MIGRATE " ", - strlen (GDM_SOP_MIGRATE " ")) == 0) { - GdmDisplay *d; - long slave_pid; - char *p; - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - if (sscanf (msg, GDM_SOP_MIGRATE " %ld", &slave_pid) != 1) - return; - p = strchr (msg, ' '); - if (p != NULL) - p = strchr (p+1, ' '); - if (p == NULL) - return; - - p++; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - if (d == NULL) - return; - - g_debug ("Got MIGRATE %s", p); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *di = li->data; - if (di->logged_in && strcmp (di->name, p) == 0) { - if (d->attached && di->vt > 0) - gdm_change_vt (di->vt); - else if (d->type == TYPE_XDMCP_PROXY && di->type == TYPE_XDMCP_PROXY) - gdm_xdmcp_migrate (d, di); - } - } - send_slave_ack (d, NULL); - } else if (strncmp (msg, GDM_SOP_COOKIE " ", - strlen (GDM_SOP_COOKIE " ")) == 0) { - GdmDisplay *d; - long slave_pid; - char *p; - - if (sscanf (msg, GDM_SOP_COOKIE " %ld", - &slave_pid) != 1) - return; - p = strchr (msg, ' '); - if (p != NULL) - p = strchr (p+1, ' '); - if (p == NULL) - return; - - p++; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - g_free (d->cookie); - d->cookie = g_strdup (p); - g_debug ("Got COOKIE == <secret>"); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_AUTHFILE " ", - strlen (GDM_SOP_AUTHFILE " ")) == 0) { - GdmDisplay *d; - long slave_pid; - char *p; - - if (sscanf (msg, GDM_SOP_AUTHFILE " %ld", - &slave_pid) != 1) - return; - p = strchr (msg, ' '); - if (p != NULL) - p = strchr (p+1, ' '); - if (p == NULL) - return; - - p++; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - g_free (d->authfile); - d->authfile = g_strdup (p); - g_debug ("Got AUTHFILE == %s", d->authfile); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_FLEXI_ERR " ", - strlen (GDM_SOP_FLEXI_ERR " ")) == 0) { - GdmDisplay *d; - long slave_pid; - int err; - - if (sscanf (msg, GDM_SOP_FLEXI_ERR " %ld %d", - &slave_pid, &err) != 2) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - char *error = NULL; - GdmConnection *conn = d->socket_conn; - d->socket_conn = NULL; - - if (conn != NULL) - gdm_connection_set_close_notify (conn, - NULL, NULL); - - if (err == 3) - error = "ERROR 3 X failed\n"; - else if (err == 4) - error = "ERROR 4 X too busy\n"; - else if (err == 5) - error = "ERROR 5 Nested display can't connect\n"; - else - error = "ERROR 999 Unknown error\n"; - if (conn != NULL) - gdm_connection_write (conn, error); - - g_debug ("Got FLEXI_ERR == %d", err); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_FLEXI_OK " ", - strlen (GDM_SOP_FLEXI_OK " ")) == 0) { - GdmDisplay *d; - long slave_pid; - - if (sscanf (msg, GDM_SOP_FLEXI_OK " %ld", - &slave_pid) != 1) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - GdmConnection *conn = d->socket_conn; - d->socket_conn = NULL; - - if (conn != NULL) { - gdm_connection_set_close_notify (conn, - NULL, NULL); - if ( ! gdm_connection_printf (conn, "OK %s\n", d->name)) - gdm_display_unmanage (d); - } - - g_debug ("Got FLEXI_OK"); - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strcmp (msg, GDM_SOP_SOFT_RESTART) == 0) { - gdm_restart_mode = TRUE; - gdm_safe_restart (); - } else if (strcmp (msg, GDM_SOP_DIRTY_SERVERS) == 0) { - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - send_slave_command (d, GDM_NOTIFY_DIRTY_SERVERS); - } - } else if (strcmp (msg, GDM_SOP_SOFT_RESTART_SERVERS) == 0) { - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - send_slave_command (d, GDM_NOTIFY_SOFT_RESTART_SERVERS); - } - } else if (strncmp (msg, GDM_SOP_SYSLOG " ", - strlen (GDM_SOP_SYSLOG " ")) == 0) { - char *p; - long pid; - int type; - p = strchr (msg, ' '); - if (p == NULL) - return; - p++; - if (sscanf (p, "%ld", &pid) != 1) - return; - p = strchr (p, ' '); - if (p == NULL) - return; - p++; - if (sscanf (p, "%d", &type) != 1) - return; - - p = strchr (p, ' '); - if (p == NULL) - return; - p++; - - /* FIXME: use g_critical or g_debug when required */ - g_warning ("(child %ld) %s", pid, p); - } else if (strcmp (msg, GDM_SOP_START_NEXT_LOCAL) == 0) { - gdm_start_first_unborn_local (3 /* delay */); - } else if (strcmp (msg, GDM_SOP_HUP_ALL_GREETERS) == 0) { - /* probably shouldn't be done too often */ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - if (d->greetpid > 1) - kill (d->greetpid, SIGHUP); - else if (d->chooserpid > 1) - kill (d->chooserpid, SIGHUP); - } - } else if (strcmp (msg, GDM_SOP_GO) == 0) { - GSList *li; - gboolean old_wait = gdm_wait_for_go; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - gdm_wait_for_go = FALSE; - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *d = li->data; - send_slave_command (d, GDM_NOTIFY_GO); - } - /* Init XDMCP if applicable */ - if (old_wait && gdm_daemon_config_get_value_bool (GDM_KEY_XDMCP)) { - if (gdm_xdmcp_init ()) { - g_debug ("Accepting XDMCP connections..."); - gdm_xdmcp_run (); - } - } - } else if (strncmp (msg, GDM_SOP_WRITE_X_SERVERS " ", - strlen (GDM_SOP_WRITE_X_SERVERS " ")) == 0) { - GdmDisplay *d; - long slave_pid; - - if (sscanf (msg, GDM_SOP_WRITE_X_SERVERS " %ld", - &slave_pid) != 1) - return; - - /* Find out who this slave belongs to */ - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - write_x_servers (d); - - /* send ack */ - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_SUSPEND_MACHINE " ", - strlen (GDM_SOP_SUSPEND_MACHINE " ")) == 0) { - GdmDisplay *d; - long slave_pid; - gboolean sysmenu; - - if (sscanf (msg, GDM_SOP_SUSPEND_MACHINE " %ld", &slave_pid) != 1) - return; - d = gdm_display_lookup (slave_pid); - - gdm_info (_("Master suspending...")); - - sysmenu = gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, d->name); - if (sysmenu && gdm_daemon_config_get_value_string (GDM_KEY_SUSPEND) != NULL) { - suspend_machine (); - } - } else if (strncmp (msg, GDM_SOP_CHOSEN_THEME " ", - strlen (GDM_SOP_CHOSEN_THEME " ")) == 0) { - GdmDisplay *d; - long slave_pid; - const char *p; - - if (sscanf (msg, GDM_SOP_CHOSEN_THEME " %ld", &slave_pid) != 1) - return; - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - g_free (d->theme_name); - d->theme_name = NULL; - - /* Syntax errors are partially OK here, if there - was no theme argument we just wanted to clear the - theme field */ - p = strchr (msg, ' '); - if (p != NULL) { - p = strchr (p+1, ' '); - if (p != NULL) { - while (*p == ' ') - p++; - if ( ! ve_string_empty (p)) - d->theme_name = g_strdup (p); - } - } - - send_slave_ack (d, NULL); - } - } else if (strncmp (msg, GDM_SOP_CUSTOM_CMD " ", - strlen (GDM_SOP_CUSTOM_CMD " ")) == 0) { - GdmDisplay *d; - long slave_pid; - long cmd_id; - - if (sscanf (msg, GDM_SOP_CUSTOM_CMD " %ld %ld", &slave_pid, &cmd_id) != 2) - return; - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - custom_cmd (cmd_id); - send_slave_ack (d, NULL); - } - } else if (strcmp (msg, GDM_SOP_FLEXI_XSERVER) == 0) { - handle_flexi_server (NULL, TYPE_FLEXI, gdm_daemon_config_get_value_string (GDM_KEY_STANDARD_XSERVER), - TRUE /* handled */, - FALSE /* chooser */, - NULL, 0, NULL, NULL, NULL); - } else if (strncmp (msg, "opcode="GDM_SOP_SHOW_ERROR_DIALOG, - strlen ("opcode="GDM_SOP_SHOW_ERROR_DIALOG)) == 0) { - GdmDisplay *d; - GtkMessageType type; - char **list; - char *ptr; - char *error; - char *details_label; - char *details_file; - long slave_pid; - int uid, gid; - - list = g_strsplit (msg, "$$", -1); - - ptr = strchr (list[1], '='); - slave_pid = atol (ptr + 1); - - ptr = strchr (list[2], '='); - type = atoi (ptr + 1); - - ptr = strchr (list[3], '='); - error = g_malloc0 (strlen (ptr)); - strcpy (error, ptr + 1); - - ptr = strchr (list[4], '='); - details_label = g_malloc0 (strlen (ptr)); - strcpy (details_label, ptr + 1); - - ptr = strchr (list[5], '='); - details_file = g_malloc0 (strlen (ptr)); - strcpy (details_file, ptr + 1); - - ptr = strchr (list[6], '='); - uid = atoi (ptr + 1); - - ptr = strchr (list[7], '='); - gid = atoi (ptr + 1); - - d = gdm_display_lookup (slave_pid); - - if (d != NULL) { - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0644)); - } - - /* FIXME: this is really bad */ - gdm_errorgui_error_box_full (d, type, error, details_label, details_file, 0, 0); - - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0640)); - } - - send_slave_ack_dialog_char (d, GDM_SLAVE_NOTIFY_ERROR_RESPONSE, NULL); - } - - g_free (error); - g_free (details_label); - g_free (details_file); - g_strfreev (list); - } else if (strncmp (msg, "opcode="GDM_SOP_SHOW_YESNO_DIALOG, - strlen ("opcode="GDM_SOP_SHOW_YESNO_DIALOG)) == 0) { - GdmDisplay *d; - char **list; - char *ptr; - char *yesno_msg; - long slave_pid; - gboolean response_yesno; - - list = g_strsplit (msg, "$$", -1); - - ptr = strchr (list [1], '='); - slave_pid = atol (ptr + 1); - - ptr = strchr (list [2], '='); - yesno_msg = g_malloc0 (strlen (ptr)); - strcpy (yesno_msg, ptr + 1); - - d = gdm_display_lookup (slave_pid); - if (d != NULL) { - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0644)); - } - - response_yesno = gdm_errorgui_failsafe_yesno (d, yesno_msg); - - send_slave_ack_dialog_int (d, GDM_SLAVE_NOTIFY_YESNO_RESPONSE, response_yesno); - - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0640)); - } - } - - g_free (yesno_msg); - g_strfreev (list); - } else if (strncmp (msg, "opcode="GDM_SOP_SHOW_QUESTION_DIALOG, - strlen ("opcode="GDM_SOP_SHOW_QUESTION_DIALOG)) == 0) { - GdmDisplay *d; - char **list; - char *ptr; - char *question_msg; - char *response_question; - long slave_pid; - gboolean echo; - - list = g_strsplit (msg, "$$", -1); - - ptr = strchr (list [1], '='); - slave_pid = atol (ptr + 1); - - ptr = strchr (list [2], '='); - question_msg = g_malloc0 (strlen (ptr)); - strcpy (question_msg, ptr + 1); - - ptr = strchr (list [3], '='); - echo = atoi (ptr + 1); - - d = gdm_display_lookup (slave_pid); - if (d != NULL) { - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0644)); - } - - response_question = gdm_errorgui_failsafe_question (d, question_msg, echo); - - send_slave_ack_dialog_char (d, GDM_SLAVE_NOTIFY_QUESTION_RESPONSE, response_question); - - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0640)); - } - } - - g_free (question_msg); - g_strfreev (list); - } else if (strncmp (msg, "opcode="GDM_SOP_SHOW_ASKBUTTONS_DIALOG, - strlen ("opcode="GDM_SOP_SHOW_ASKBUTTONS_DIALOG)) == 0) { - GdmDisplay *d; - char *askbuttons_msg; - char **list; - char *ptr; - char *options[4]; - long slave_pid; - int i; - int response_askbuttons; - - list = g_strsplit (msg, "$$", -1); - - ptr = strchr (list [1], '='); - slave_pid = atol (ptr + 1); - - ptr = strchr (list [2], '='); - askbuttons_msg = g_malloc0 (strlen (ptr)); - strcpy (askbuttons_msg, ptr + 1); - - ptr = strchr (list [3], '='); - options[0] = g_malloc0 (strlen (ptr)); - strcpy (options[0], ptr + 1); - - ptr = strchr (list [4], '='); - options[1] = g_malloc0 (strlen (ptr)); - strcpy (options[1], ptr + 1); - - ptr = strchr (list [5], '='); - options[2] = g_malloc0 (strlen (ptr)); - strcpy (options[2], ptr + 1); - - ptr = strchr (list [6], '='); - options[3] = g_malloc0 (strlen (ptr)); - strcpy (options[3], ptr + 1); - - d = gdm_display_lookup (slave_pid); - if (d != NULL) { - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0644)); - } - - response_askbuttons = gdm_errorgui_failsafe_ask_buttons (d, askbuttons_msg, options); - - send_slave_ack_dialog_int (d, GDM_SLAVE_NOTIFY_ASKBUTTONS_RESPONSE, response_askbuttons); - if (GDM_AUTHFILE (d)) { - VE_IGNORE_EINTR (chmod (GDM_AUTHFILE (d), 0640)); - } - } - - g_free (askbuttons_msg); - - for (i = 0; i < 3; i ++) - g_free (options[i]); - g_strfreev (list); - } -} - -/* extract second word and the rest of the string */ -static void -extract_dispname_uid_xauthfile_cookie (const char *msg, - char **dispname, - uid_t *uid, - char **xauthfile, - char **cookie) -{ - const char *p; - int i; - char *pp; - - *dispname = NULL; - *xauthfile = NULL; - *cookie = NULL; - - /* Get dispname */ - p = strchr (msg, ' '); - if (p == NULL) - return; - - while (*p == ' ') - p++; - - *dispname = g_strdup (p); - pp = strchr (*dispname, ' '); - if (pp != NULL) - *pp = '\0'; - - /* Get uid */ - p = strchr (p, ' '); - if (p == NULL) { - *dispname = NULL; - g_free (*dispname); - return; - } - while (*p == ' ') - p++; - - if (sscanf (p, "%d", &i) != 1) { - *dispname = NULL; - g_free (*dispname); - return; - } - *uid = i; - - /* Get cookie */ - p = strchr (p, ' '); - if (p == NULL) { - *dispname = NULL; - g_free (*dispname); - return; - } - while (*p == ' ') - p++; - - *cookie = g_strdup (p); - pp = strchr (*cookie, ' '); - if (pp != NULL) - *pp = '\0'; - - /* Get xauthfile */ - p = strchr (p, ' '); - if (p == NULL) { - *cookie = NULL; - g_free (*cookie); - *dispname = NULL; - g_free (*dispname); - return; - } - - while (*p == ' ') - p++; - - *xauthfile = g_strstrip (g_strdup (p)); - -} - -static void -close_conn (gpointer data) -{ - GdmDisplay *disp = data; - - /* We still weren't finished, so we want to whack this display */ - if (disp->socket_conn != NULL) { - disp->socket_conn = NULL; - gdm_display_unmanage (disp); - } -} - -static GdmDisplay * -find_display (const char *name) -{ - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (disp->name != NULL && - strcmp (disp->name, name) == 0) - return disp; - } - return NULL; -} - -static char * -extract_dispnum (const char *addy) -{ - int num; - char *p; - - gdm_assert (addy != NULL); - - p = strchr (addy, ':'); - if (p == NULL) - return NULL; - - /* Whee! handles DECnet even if we don't do that */ - while (*p == ':') - p++; - - if (sscanf (p, "%d", &num) != 1) - return NULL; - - return g_strdup_printf ("%d", num); -} - -static char * -dehex_cookie (const char *cookie, int *len) -{ - /* it should be +1 really, but I'm paranoid */ - char *bcookie = g_new0 (char, (strlen (cookie) / 2) + 2); - int i; - const char *p; - - *len = 0; - - for (i = 0, p = cookie; - *p != '\0' && *(p+1) != '\0'; - i++, p += 2) { - unsigned int num; - if (sscanf (p, "%02x", &num) != 1) { - g_free (bcookie); - return NULL; - } - bcookie[i] = num; - } - *len = i; - return bcookie; -} - -/* This runs as the user who owns the file */ -static gboolean -check_cookie (const gchar *file, const gchar *disp, const gchar *cookie) -{ - Xauth *xa; - gchar *number; - gchar *bcookie; - int cookielen; - gboolean ret = FALSE; - int cnt = 0; - - FILE *fp = fopen (file, "r"); - if (fp == NULL) - return FALSE; - - number = extract_dispnum (disp); - if (number == NULL) - return FALSE; - bcookie = dehex_cookie (cookie, &cookielen); - if (bcookie == NULL) { - g_free (number); - return FALSE; - } - - while ((xa = XauReadAuth (fp)) != NULL) { - if (xa->number_length == strlen (number) && - strncmp (xa->number, number, xa->number_length) == 0 && - xa->name_length == strlen ("MIT-MAGIC-COOKIE-1") && - strncmp (xa->name, "MIT-MAGIC-COOKIE-1", - xa->name_length) == 0 && - xa->data_length == cookielen && - memcmp (xa->data, bcookie, cookielen) == 0) { - XauDisposeAuth (xa); - ret = TRUE; - break; - } - XauDisposeAuth (xa); - - /* just being ultra anal */ - cnt++; - if (cnt > 500) - break; - } - - g_free (number); - g_free (bcookie); - - VE_IGNORE_EINTR (fclose (fp)); - - return ret; -} - -static void -handle_flexi_server (GdmConnection *conn, - int type, - const char *server, - gboolean handled, - gboolean chooser, - const char *xnest_disp, - uid_t xnest_uid, - const char *xnest_auth_file, - const char *xnest_cookie, - const char *username) -{ - GdmDisplay *display; - gchar *bin; - uid_t server_uid = 0; - - g_debug ("flexi server: '%s'", server); - - if (gdm_wait_for_go) { - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 1 No more flexi servers\n"); - return; - } - - if (type == TYPE_FLEXI_XNEST) { - gboolean authorized = TRUE; - struct passwd *pw; - gid_t oldgid = getegid (); - - pw = getpwuid (xnest_uid); - if (pw == NULL) { - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - /* paranoia */ - NEVER_FAILS_seteuid (0); - - if (setegid (pw->pw_gid) < 0) - NEVER_FAILS_setegid (gdm_daemon_config_get_gdmgid ()); - - if (seteuid (xnest_uid) < 0) { - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - gdm_assert (xnest_auth_file != NULL); - gdm_assert (xnest_disp != NULL); - gdm_assert (xnest_cookie != NULL); - - if (authorized && - ! gdm_auth_file_check ("handle_flexi_server", xnest_uid, xnest_auth_file, FALSE /* absentok */, NULL)) - authorized = FALSE; - - if (authorized && - ! check_cookie (xnest_auth_file, - xnest_disp, - xnest_cookie)) { - authorized = FALSE; - } - - /* this must always work, thus the asserts */ - NEVER_FAILS_root_set_euid_egid (0, oldgid); - - if (! authorized) { - /* Sorry dude, you're not doing something - * right */ - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - server_uid = xnest_uid; - } - - if (flexi_servers >= gdm_daemon_config_get_value_int (GDM_KEY_FLEXIBLE_XSERVERS)) { - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 1 No more flexi servers\n"); - return; - } - - bin = ve_first_word (server); - if (ve_string_empty (server) || - g_access (bin, X_OK) != 0) { - g_free (bin); - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 6 No server binary\n"); - return; - } - g_free (bin); - - display = gdm_display_alloc (-1, server); - if G_UNLIKELY (display == NULL) { - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 2 Startup errors\n"); - return; - } - - /* It is kind of ugly that we don't use - the standard resolution for this, but - oh well, this makes other things simpler */ - display->handled = handled; - display->use_chooser = chooser; - - if (type == TYPE_FLEXI_XNEST) { - GdmDisplay *parent; - gchar *disp, *p; - gdm_assert (xnest_disp != NULL); - - disp = g_strdup (xnest_disp); - /* whack the screen info */ - p = strchr (disp, ':'); - if (p != NULL) - p = strchr (p+1, '.'); - if (p != NULL) - *p = '\0'; - /* if it's on one of the attached displays we started, - * it's on the console, else it's not (it could be but - * we aren't sure and we don't want to be fooled) */ - parent = find_display (disp); - if (/* paranoia */xnest_disp[0] == ':' && - parent != NULL && - parent->attached) - display->attached = TRUE; - else - display->attached = FALSE; - g_free (disp); - - display->server_uid = server_uid; - } - - flexi_servers++; - - display->preset_user = g_strdup (username); - display->type = type; - display->socket_conn = conn; - display->parent_disp = g_strdup (xnest_disp); - display->parent_auth_file = g_strdup (xnest_auth_file); - if (conn != NULL) - gdm_connection_set_close_notify (conn, display, close_conn); - gdm_daemon_config_display_list_append (display); - - if ( ! gdm_display_manage (display)) { - gdm_display_unmanage (display); - if (conn != NULL) - gdm_connection_write (conn, - "ERROR 2 Startup errors\n"); - return; - } - /* Now we wait for the server to start up (or not) */ -} - -static void -handle_dynamic_server (GdmConnection *conn, int type, gchar *key) -{ - GdmDisplay *disp; - int disp_num; - gchar *msg; - gchar *full; - gchar *val; - - if (!(gdm_daemon_config_get_value_bool (GDM_KEY_DYNAMIC_XSERVERS))) { - gdm_connection_write (conn, "ERROR 200 Dynamic Displays not allowed\n"); - return; - } - - if ( ! GDM_CONN_AUTH_GLOBAL(conn)) { - gdm_info (_("DYNAMIC request denied: " "Not authenticated")); - gdm_connection_write (conn, "ERROR 100 Not authenticated\n"); - return; - } - - if (key == NULL) { - gdm_connection_write (conn, "ERROR 1 Bad display number <NULL>\n"); - return; - } else if ( !(isdigit (*key))) { - msg = g_strdup_printf ("ERROR 1 Bad display number <%s>\n", key); - gdm_connection_write (conn, msg); - g_free (msg); - return; - } - disp_num = atoi (key); - - if (type == DYNAMIC_ADD) { - /* prime an X server for launching */ - - if (mark_display_exists (disp_num)) { - /* need to skip starting this one again */ - gdm_connection_write (conn, "ERROR 2 Existing display\n"); - return; - } - - full = strchr (key, '='); - if (full == NULL || *(full + 1) == 0) { - gdm_connection_write (conn, "ERROR 3 No server string\n"); - return; - } - - val = full + 1; - disp = gdm_display_alloc (disp_num, val); - - if (disp == NULL) { - gdm_connection_write (conn, "ERROR 4 Display startup failure\n"); - return; - } - - gdm_daemon_config_display_list_insert (disp); - - disp->dispstat = DISPLAY_CONFIG; - disp->removeconf = FALSE; - - if (disp_num > gdm_daemon_config_get_high_display_num ()) - gdm_daemon_config_set_high_display_num (disp_num); - - gdm_connection_write (conn, "OK\n"); - return; - } - - if (type == DYNAMIC_REMOVE) { - GSList *li; - GSList *nli; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - /* shutdown a dynamic X server */ - - for (li = displays; li != NULL; li = nli) { - disp = li->data; - nli = li->next; - if (disp->dispnum == disp_num) { - disp->removeconf = TRUE; - gdm_display_unmanage (disp); - gdm_connection_write (conn, "OK\n"); - return; - } - } - - msg = g_strdup_printf ("ERROR 1 Bad display number <%d>\n", disp_num); - gdm_connection_write (conn, msg); - return; - } - - if (type == DYNAMIC_RELEASE) { - /* cause the newly configured X servers to actually run */ - GSList *li; - GSList *nli; - gboolean found = FALSE; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - for (li = displays; li != NULL; li = nli) { - GdmDisplay *disp = li->data; - nli = li->next; - if ((disp->dispnum == disp_num) && - (disp->dispstat == DISPLAY_CONFIG)) { - disp->dispstat = DISPLAY_UNBORN; - - if ( ! gdm_display_manage (disp)) { - gdm_display_unmanage (disp); - } - found = TRUE; - } - } - - if (found) - gdm_connection_write (conn, "OK\n"); - else { - msg = g_strdup_printf ("ERROR 1 Bad display number <%d>\n", disp_num); - gdm_connection_write (conn, msg); - } - - /* Now we wait for the server to start up (or not) */ - return; - } -} - -static void -sup_handle_auth_local (GdmConnection *conn, - const char *msg, - gpointer data) -{ - GSList *li; - char *cookie; - GSList *displays; - - cookie = g_strdup (&msg[strlen (GDM_SUP_AUTH_LOCAL " ")]); - - displays = gdm_daemon_config_get_display_list (); - - g_strstrip (cookie); - if (strlen (cookie) != 16*2) /* 16 bytes in hex form */ { - /* evil, just whack the connection in this case */ - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - gdm_connection_close (conn); - g_free (cookie); - return; - } - /* check if cookie matches one of the attached displays */ - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (disp->attached && - disp->cookie != NULL && - g_ascii_strcasecmp (disp->cookie, cookie) == 0) { - g_free (cookie); - GDM_CONNECTION_SET_USER_FLAG - (conn, GDM_SUP_FLAG_AUTHENTICATED); - gdm_connection_set_display (conn, disp); - gdm_connection_write (conn, "OK\n"); - return; - } - } - - if (gdm_global_cookie != NULL && - g_ascii_strcasecmp ((gchar *) gdm_global_cookie, cookie) == 0) { - g_free (cookie); - GDM_CONNECTION_SET_USER_FLAG - (conn, GDM_SUP_FLAG_AUTH_GLOBAL); - gdm_connection_write (conn, "OK\n"); - return; - } - - /* Hmmm, perhaps this is better defined behaviour */ - GDM_CONNECTION_UNSET_USER_FLAG - (conn, GDM_SUP_FLAG_AUTHENTICATED); - GDM_CONNECTION_UNSET_USER_FLAG - (conn, GDM_SUP_FLAG_AUTH_GLOBAL); - gdm_connection_set_display (conn, NULL); - gdm_connection_write (conn, "ERROR 100 Not authenticated\n"); - g_free (cookie); -} - -static void -sup_handle_attached_servers (GdmConnection *conn, - const char *msg, - gpointer data) -{ - GString *retMsg; - GSList *li; - const gchar *sep = " "; - char *key; - int msgLen=0; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - if (strncmp (msg, GDM_SUP_ATTACHED_SERVERS, - strlen (GDM_SUP_ATTACHED_SERVERS)) == 0) - msgLen = strlen (GDM_SUP_ATTACHED_SERVERS); - else if (strncmp (msg, GDM_SUP_CONSOLE_SERVERS, - strlen (GDM_SUP_CONSOLE_SERVERS)) == 0) - msgLen = strlen (GDM_SUP_CONSOLE_SERVERS); - - key = g_strdup (&msg[msgLen]); - g_strstrip (key); - - retMsg = g_string_new ("OK"); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - - if ( ! disp->attached) - continue; - if (!(strlen (key)) || (g_pattern_match_simple (key, disp->command))) { - g_string_append_printf (retMsg, "%s%s,%s,", sep, - ve_sure_string (disp->name), - ve_sure_string (disp->login)); - sep = ";"; - if (disp->type == TYPE_FLEXI_XNEST) { - g_string_append (retMsg, ve_sure_string (disp->parent_disp)); - } else { - g_string_append_printf (retMsg, "%d", disp->vt); - } - } - } - - g_string_append (retMsg, "\n"); - gdm_connection_write (conn, retMsg->str); - g_free (key); - g_string_free (retMsg, TRUE); -} - -static void -sup_handle_get_server_details (GdmConnection *conn, - const char *msg, - gpointer data) -{ - - const gchar *server = &msg[strlen (GDM_SUP_GET_SERVER_DETAILS " ")]; - gchar **splitstr = g_strsplit (server, " ", 2); - GdmXserver *svr = gdm_daemon_config_find_xserver ((gchar *)splitstr[0]); - - if (svr != NULL) { - if (g_strcasecmp (splitstr[1], "ID") == 0) - gdm_connection_printf (conn, "OK %s\n", svr->id); - else if (g_strcasecmp (splitstr[1], "NAME") == 0) - gdm_connection_printf (conn, "OK %s\n", svr->name); - else if (g_strcasecmp (splitstr[1], "COMMAND") == 0) - gdm_connection_printf (conn, "OK %s\n", svr->command); - else if (g_strcasecmp (splitstr[1], "PRIORITY") == 0) - gdm_connection_printf (conn, "OK %d\n", svr->priority); - else if (g_strcasecmp (splitstr[1], "FLEXIBLE") == 0 && - svr->flexible) - gdm_connection_printf (conn, "OK true\n"); - else if (g_strcasecmp (splitstr[1], "FLEXIBLE") == 0 && - !svr->flexible) - gdm_connection_printf (conn, "OK false\n"); - else if (g_strcasecmp (splitstr[1], "CHOOSABLE") == 0 && - svr->choosable) - gdm_connection_printf (conn, "OK true\n"); - else if (g_strcasecmp (splitstr[1], "CHOOSABLE") == 0 && - !svr->choosable) - gdm_connection_printf (conn, "OK false\n"); - else if (g_strcasecmp (splitstr[1], "HANDLED") == 0 && - svr->handled) - gdm_connection_printf (conn, "OK true\n"); - else if (g_strcasecmp (splitstr[1], "HANDLED") == 0 && - !svr->handled) - gdm_connection_printf (conn, "OK false\n"); - else if (g_strcasecmp (splitstr[1], "CHOOSER") == 0 && - svr->chooser) - gdm_connection_printf (conn, "OK true\n"); - else if (g_strcasecmp (splitstr[1], "CHOOSER") == 0 && - !svr->chooser) - gdm_connection_printf (conn, "OK false\n"); - else - gdm_connection_printf (conn, "ERROR 2 Key not valid\n"); - - g_strfreev (splitstr); - } else { - gdm_connection_printf (conn, "ERROR 1 Server not found\n"); - } -} - -static void -sup_handle_flexi_xserver (GdmConnection *conn, - const char *msg, - gpointer data) -{ - char *name; - const char *command = NULL; - GdmXserver *svr; - const char *rest; - char *username; - char *end; - gboolean has_user; - - has_user = strncmp (msg, GDM_SUP_FLEXI_XSERVER_USER " ", strlen (GDM_SUP_FLEXI_XSERVER_USER " ")) == 0; - - g_debug ("Handling flexi request has-user:%d", has_user); - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn)) { - gdm_info (_("%s request denied: " - "Not authenticated"), "FLEXI_XSERVER"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - if (has_user) { - rest = msg + strlen (GDM_SUP_FLEXI_XSERVER_USER " "); - end = strchr (rest, ' '); - if (end) { - username = g_strndup (rest, end - rest); - rest = end + 1; - } else { - username = g_strdup (rest); - rest = rest + strlen (rest); - } - } else { - rest = msg + strlen (GDM_SUP_FLEXI_XSERVER " "); - username = NULL; - } - - name = g_strdup (rest); - g_strstrip (name); - if (ve_string_empty (name)) { - g_free (name); - name = g_strdup (GDM_STANDARD); - } - - svr = gdm_daemon_config_find_xserver (name); - - if G_UNLIKELY (svr == NULL) { - /* Don't print the name to syslog as it might be - * long and dangerous */ - gdm_error (_("Unknown server type requested; using " - "standard server.")); - command = gdm_daemon_config_get_value_string (GDM_KEY_STANDARD_XSERVER); - } else if G_UNLIKELY ( ! svr->flexible) { - gdm_error (_("Requested server %s not allowed to be " - "used for flexible servers; using " - "standard server."), name); - command = gdm_daemon_config_get_value_string (GDM_KEY_STANDARD_XSERVER); - } else { - command = svr->command; - } - g_free (name); - - handle_flexi_server (conn, - TYPE_FLEXI, - command, - /* It is kind of ugly that we don't use - the standard resolution for this, but - oh well, this makes other things simpler */ - svr->handled, - svr->chooser, - NULL, - 0, - NULL, - NULL, - username); - g_free (username); -} - -static void -sup_handle_flexi_xnest (GdmConnection *conn, - const char *msg, - gpointer data) -{ - char *dispname = NULL; - char *xauthfile = NULL; - char *cookie = NULL; - uid_t uid; - const char *rest; - char *username; - char *end; - gboolean has_user; - - has_user = strncmp (msg, GDM_SUP_FLEXI_XNEST_USER " ", strlen (GDM_SUP_FLEXI_XNEST_USER " ")) == 0; - - g_debug ("Handling flexi xnest request has-user:%d", has_user); - - if (has_user) { - rest = msg + strlen (GDM_SUP_FLEXI_XNEST_USER " "); - end = strchr (rest, ' '); - username = g_strndup (rest, end - rest); - } else { - rest = msg; - username = NULL; - } - - extract_dispname_uid_xauthfile_cookie (rest, - &dispname, - &uid, - &xauthfile, - &cookie); - - if (dispname == NULL) { - /* Something bogus is going on, so just whack the - * connection */ - g_free (xauthfile); - gdm_connection_close (conn); - g_warning ("Unable to get display name from request"); - return; - } - - /* This is probably a pre-2.2.4.2 client */ - if (xauthfile == NULL || cookie == NULL) { - /* evil, just whack the connection in this case */ - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - gdm_connection_close (conn); - g_free (cookie); - return; - } - - handle_flexi_server (conn, - TYPE_FLEXI_XNEST, - gdm_daemon_config_get_value_string (GDM_KEY_XNEST), - TRUE /* handled */, - FALSE /* chooser */, - dispname, - uid, - xauthfile, - cookie, - username); - - g_free (dispname); - g_free (xauthfile); - g_free (username); -} - -static void -sup_handle_get_config (GdmConnection *conn, - const char *msg, - gpointer data) -{ - const char *parms; - char **splitstr; - char *retval; - static gboolean done_prefetch = FALSE; - - retval = NULL; - parms = &msg[strlen (GDM_SUP_GET_CONFIG " ")]; - - splitstr = g_strsplit (parms, " ", 2); - - if (splitstr == NULL || splitstr[0] == NULL) { - goto out; - } - - /* - * It is not meaningful to manage this in a per-display - * fashion since the prefetch program is only run once the - * for the first display that requests the key. So process - * this first and return "Unsupported key" for requests - * after the first request. - */ - if (strcmp (splitstr[0], GDM_KEY_PRE_FETCH_PROGRAM) == 0) { - if (done_prefetch) { - gdm_connection_printf (conn, "OK \n"); - } else { - gdm_connection_printf (conn, "ERROR 50 Unsupported key <%s>\n", splitstr[0]); - done_prefetch = TRUE; - } - goto out; - } - - if (splitstr[0] != NULL) { - gboolean res; - - /* - * Note passing in the display is backwards compatible - * since if it is NULL, it won't try to load the display - * value at all. - */ - - g_debug ("Handling GET_CONFIG: %s for display %s", splitstr[0], splitstr[1]); - - res = gdm_daemon_config_to_string (splitstr[0], splitstr[1], &retval); - - if (res) { - gdm_connection_printf (conn, "OK %s\n", retval); - g_free (retval); - } else { - if (gdm_daemon_config_is_valid_key ((gchar *)splitstr[0])) - gdm_connection_printf (conn, "OK \n"); - else - gdm_connection_printf (conn, - "ERROR 50 Unsupported key <%s>\n", - splitstr[0]); - } - } - out: - g_strfreev (splitstr); -} - -static gboolean -is_action_available (GdmDisplay *disp, gchar *action) -{ - const gchar **allowsyscmd; - const gchar **rbackeys; - gboolean sysmenu; - gboolean ret = FALSE; - int i; - - allowsyscmd = gdm_daemon_config_get_value_string_array (GDM_KEY_ALLOW_LOGOUT_ACTIONS); - rbackeys = gdm_daemon_config_get_value_string_array (GDM_KEY_RBAC_SYSTEM_COMMAND_KEYS); - sysmenu = gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, disp->name); - - if (!disp->attached || !sysmenu) { - return FALSE; - } - - for (i = 0; allowsyscmd[i] != NULL; i++) { - if (strcmp (allowsyscmd[i], action) == 0) { - ret = TRUE; - break; - } - } - -#ifdef HAVE_CHKAUTHATTR - if (ret == TRUE && rbackeys) { - for (i = 0; rbackeys[i] != NULL; i++) { - gchar **rbackey = g_strsplit (rbackeys[i], ":", 2); - - if (! ve_string_empty (rbackey[0]) && - ! ve_string_empty (rbackey[1]) && - strcmp (rbackey[0], action) == 0) { - - if (!chkauthattr (rbackey[1], disp->login)) { - g_strfreev (rbackey); - ret = FALSE; - break; - } - - } - g_strfreev (rbackey); - } - } -#endif - - return ret; -} - -static void -sup_handle_query_logout_action (GdmConnection *conn, - const char *msg, - gpointer data) -{ - GdmLogoutAction logout_action; - GdmDisplay *disp; - GString *reply; - const gchar *sep = " "; - int i; - - disp = gdm_connection_get_display (conn); - - /* Only allow locally authenticated connections */ - if (! GDM_CONN_AUTHENTICATED (conn) || disp == NULL) { - gdm_info (_("%s request denied: " - "Not authenticated"), "QUERY_LOGOUT_ACTION"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - reply = g_string_new ("OK"); - - logout_action = disp->logout_action; - if (logout_action == GDM_LOGOUT_ACTION_NONE) - logout_action = safe_logout_action; - - if (gdm_daemon_config_get_value_string_array (GDM_KEY_HALT) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_HALT)) { - g_string_append_printf (reply, "%s%s", sep, - GDM_SUP_LOGOUT_ACTION_HALT); - if (logout_action == GDM_LOGOUT_ACTION_HALT) - g_string_append (reply, "!"); - sep = ";"; - } - if (gdm_daemon_config_get_value_string_array (GDM_KEY_REBOOT) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_REBOOT)) { - g_string_append_printf (reply, "%s%s", sep, - GDM_SUP_LOGOUT_ACTION_REBOOT); - if (logout_action == GDM_LOGOUT_ACTION_REBOOT) - g_string_append (reply, "!"); - sep = ";"; - } - if (gdm_daemon_config_get_value_string_array (GDM_KEY_SUSPEND) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_SUSPEND)) { - g_string_append_printf (reply, "%s%s", sep, - GDM_SUP_LOGOUT_ACTION_SUSPEND); - if (logout_action == GDM_LOGOUT_ACTION_SUSPEND) - g_string_append (reply, "!"); - sep = ";"; - } - - if (is_action_available (disp, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE)) { - for (i = 0; i < GDM_CUSTOM_COMMAND_MAX; i++) { - gchar *key_string = NULL; - key_string = g_strdup_printf ("%s%d=", GDM_KEY_CUSTOM_CMD_TEMPLATE, i); - - if (! ve_string_empty (gdm_daemon_config_get_value_string (key_string))) { - - g_free (key_string); - key_string = g_strdup_printf("%s%d=", - GDM_KEY_CUSTOM_CMD_IS_PERSISTENT_TEMPLATE, i); - - if (gdm_daemon_config_get_value_bool (key_string)) { - g_string_append_printf (reply, "%s%s%d", sep, - GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE, i); - if (logout_action == (GDM_LOGOUT_ACTION_CUSTOM_CMD_FIRST + i)) - g_string_append (reply, "!"); - sep = ";"; - } - } - g_free(key_string); - } - } - - g_string_append (reply, "\n"); - gdm_connection_write (conn, reply->str); - g_string_free (reply, TRUE); -} - -static void -sup_handle_query_custom_cmd_labels (GdmConnection *conn, - const char *msg, - gpointer data) -{ - - GdmDisplay *disp; - GString *reply; - const gchar *sep = " "; - gboolean sysmenu; - int i; - - disp = gdm_connection_get_display (conn); - sysmenu = gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, disp->name); - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn) || - disp == NULL) { - gdm_info (_("%s request denied: " - "Not authenticated"), "QUERY_CUSTOM_CMD_LABELS"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - reply = g_string_new ("OK"); - - for (i = 0; i < GDM_CUSTOM_COMMAND_MAX; i++) { - gchar *key_string = NULL; - key_string = g_strdup_printf("%s%d=", GDM_KEY_CUSTOM_CMD_TEMPLATE, i); - if (sysmenu && disp->attached && - ! ve_string_empty (gdm_daemon_config_get_value_string (key_string))) { - g_free (key_string); - key_string = g_strdup_printf("%s%d=", GDM_KEY_CUSTOM_CMD_IS_PERSISTENT_TEMPLATE, i); - if (gdm_daemon_config_get_value_bool (key_string)) { - g_free (key_string); - key_string = g_strdup_printf("%s%d=", GDM_KEY_CUSTOM_CMD_LABEL_TEMPLATE, i); - g_string_append_printf (reply, "%s%s", sep, gdm_daemon_config_get_value_string (key_string)); - sep = ";"; - } - } - g_free(key_string); - } - - g_string_append (reply, "\n"); - gdm_connection_write (conn, reply->str); - g_string_free (reply, TRUE); -} - -static void -sup_handle_all_servers (GdmConnection *conn, - const char *msg, - gpointer data) -{ - GString *reply; - GSList *li; - const gchar *sep = " "; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - reply = g_string_new ("OK"); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - g_string_append_printf (reply, "%s%s,%s", sep, - ve_sure_string (disp->name), - ve_sure_string (disp->login)); - sep = ";"; - } - g_string_append (reply, "\n"); - gdm_connection_write (conn, reply->str); - g_string_free (reply, TRUE); -} - -static void -sup_handle_get_server_list (GdmConnection *conn, - const char *msg, - gpointer data) -{ - gchar *retval = gdm_daemon_config_get_xservers (); - - if (retval != NULL) { - gdm_connection_printf (conn, "OK %s\n", retval); - g_free (retval); - } else { - gdm_connection_printf (conn, "ERROR 1 No servers found\n"); - } -} - -static void -sup_handle_get_custom_config_file (GdmConnection *conn, - const char *msg, - gpointer data) -{ - gchar *ret; - - ret = gdm_daemon_config_get_custom_config_file (); - if (ret) - gdm_connection_printf (conn, "OK %s\n", ret); - else - gdm_connection_write (conn, - "ERROR 1 File not found\n"); -} - -static void -sup_handle_greeterpids (GdmConnection *conn, - const char *msg, - gpointer data) -{ - GString *reply; - GSList *li; - const gchar *sep = " "; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - reply = g_string_new ("OK"); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (disp->greetpid > 0) { - g_string_append_printf (reply, "%s%ld", - sep, (long)disp->greetpid); - sep = ";"; - } - } - g_string_append (reply, "\n"); - gdm_connection_write (conn, reply->str); - g_string_free (reply, TRUE); -} - -static void -sup_handle_query_custom_cmd_no_restart_status (GdmConnection *conn, - const char *msg, - gpointer data) -{ - - GdmDisplay *disp; - GString *reply; - gboolean sysmenu; - unsigned long no_restart_status_flag = 0; /* we can store up-to 32 commands this way */ - int i; - - disp = gdm_connection_get_display (conn); - sysmenu = gdm_daemon_config_get_value_bool_per_display (GDM_KEY_SYSTEM_MENU, disp->name); - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn) || - disp == NULL) { - gdm_info (_("%s request denied: " - "Not authenticated"), "QUERY_CUSTOM_CMD_NO_RESTART_STATUS"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - reply = g_string_new ("OK "); - - for (i = 0; i < GDM_CUSTOM_COMMAND_MAX; i++) { - gchar *key_string = NULL; - key_string = g_strdup_printf("%s%d=", GDM_KEY_CUSTOM_CMD_TEMPLATE, i); - if (sysmenu && disp->attached && - ! ve_string_empty (gdm_daemon_config_get_value_string (key_string))) { - g_free (key_string); - key_string = g_strdup_printf("%s%d=", GDM_KEY_CUSTOM_CMD_IS_PERSISTENT_TEMPLATE, i); - if (gdm_daemon_config_get_value_bool (key_string)) { - g_free (key_string); - key_string = g_strdup_printf("%s%d=", GDM_KEY_CUSTOM_CMD_NO_RESTART_TEMPLATE, i); - if(gdm_daemon_config_get_value_bool (key_string)) - no_restart_status_flag |= (1 << i); - } - } - g_free(key_string); - } - - g_string_append_printf (reply, "%ld\n", no_restart_status_flag); - gdm_connection_write (conn, reply->str); - g_string_free (reply, TRUE); -} - -static void -sup_handle_set_logout_action (GdmConnection *conn, - const char *msg, - gpointer data) - -{ - GdmDisplay *disp; - const gchar *action; - gboolean was_ok = FALSE; - - action = &msg[strlen (GDM_SUP_SET_LOGOUT_ACTION " ")]; - disp = gdm_connection_get_display (conn); - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn) || - disp == NULL || - ! disp->logged_in) { - gdm_info (_("%s request denied: " - "Not authenticated"), "SET_LOGOUT_ACTION"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - if (strcmp (action, GDM_SUP_LOGOUT_ACTION_NONE) == 0) { - disp->logout_action = GDM_LOGOUT_ACTION_NONE; - was_ok = TRUE; - } else if (strcmp (action, GDM_SUP_LOGOUT_ACTION_HALT) == 0 && - gdm_daemon_config_get_value_string_array (GDM_KEY_HALT) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_HALT)) { - disp->logout_action = GDM_LOGOUT_ACTION_HALT; - was_ok = TRUE; - } else if (strcmp (action, GDM_SUP_LOGOUT_ACTION_REBOOT) == 0 && - gdm_daemon_config_get_value_string_array (GDM_KEY_REBOOT) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_REBOOT)) { - disp->logout_action = GDM_LOGOUT_ACTION_REBOOT; - was_ok = TRUE; - } else if (strcmp (action, GDM_SUP_LOGOUT_ACTION_SUSPEND) == 0 && - gdm_daemon_config_get_value_string_array (GDM_KEY_SUSPEND) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_SUSPEND)) { - disp->logout_action = GDM_LOGOUT_ACTION_SUSPEND; - was_ok = TRUE; - } - else if (strncmp (action, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE, - strlen (GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE)) == 0 && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE)) { - - int cmd_index; - if (sscanf (action, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE "%d", &cmd_index) == 1) { - gchar *key_string = NULL; - key_string = g_strdup_printf ("%s%d=", GDM_KEY_CUSTOM_CMD_TEMPLATE, cmd_index); - if (! ve_string_empty (gdm_daemon_config_get_value_string (key_string))) { - disp->logout_action = - GDM_LOGOUT_ACTION_CUSTOM_CMD_FIRST + cmd_index; - was_ok = TRUE; - } - g_free(key_string); - } - } - - if (was_ok) { - gdm_connection_write (conn, "OK\n"); - gdm_try_logout_action (disp); - } else { - gdm_connection_write (conn, "ERROR 7 Unknown logout action, or not available\n"); - } -} - -static void -sup_handle_set_safe_logout_action (GdmConnection *conn, - const char *msg, - gpointer data) - -{ - GdmDisplay *disp; - const gchar *action; - gboolean was_ok = FALSE; - - action = &msg[strlen (GDM_SUP_SET_SAFE_LOGOUT_ACTION " ")]; - disp = gdm_connection_get_display (conn); - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn) || - disp == NULL || - ! disp->logged_in) { - gdm_info (_("%s request denied: " - "Not authenticated"), "SET_LOGOUT_ACTION"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - if (strcmp (action, GDM_SUP_LOGOUT_ACTION_NONE) == 0) { - safe_logout_action = GDM_LOGOUT_ACTION_NONE; - was_ok = TRUE; - } else if (strcmp (action, GDM_SUP_LOGOUT_ACTION_HALT) == 0 && - gdm_daemon_config_get_value_string_array (GDM_KEY_HALT) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_HALT)) { - safe_logout_action = GDM_LOGOUT_ACTION_HALT; - was_ok = TRUE; - } else if (strcmp (action, GDM_SUP_LOGOUT_ACTION_REBOOT) == 0 && - gdm_daemon_config_get_value_string_array (GDM_KEY_REBOOT) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_REBOOT)) { - safe_logout_action = GDM_LOGOUT_ACTION_REBOOT; - was_ok = TRUE; - } else if (strcmp (action, GDM_SUP_LOGOUT_ACTION_SUSPEND) == 0 && - gdm_daemon_config_get_value_string_array (GDM_KEY_SUSPEND) && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_SUSPEND)) { - safe_logout_action = GDM_LOGOUT_ACTION_SUSPEND; - was_ok = TRUE; - } - else if (strncmp (action, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE, - strlen (GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE)) == 0 && - is_action_available (disp, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE)) { - - int cmd_index; - if (sscanf (action, GDM_SUP_LOGOUT_ACTION_CUSTOM_CMD_TEMPLATE "%d", &cmd_index) == 1) { - - gchar *key_string = NULL; - key_string = g_strdup_printf ("%s%d=", GDM_KEY_CUSTOM_CMD_TEMPLATE, cmd_index); - - if (! ve_string_empty (gdm_daemon_config_get_value_string (key_string))) { - safe_logout_action = - GDM_LOGOUT_ACTION_CUSTOM_CMD_FIRST + cmd_index; - was_ok = TRUE; - } - g_free(key_string); - } - } - - if (was_ok) { - gdm_connection_write (conn, "OK\n"); - gdm_try_logout_action (disp); - } else { - gdm_connection_write (conn, "ERROR 7 Unknown logout action, or not available\n"); - } -} - -static void -sup_handle_query_vt (GdmConnection *conn, - const char *msg, - gpointer data) - -{ - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn)) { - gdm_info (_("%s request denied: " - "Not authenticated"), "QUERY_VT"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - -#if defined (__linux__) || defined (__FreeBSD__) || defined (__DragonFly__) - gdm_connection_printf (conn, "OK %d\n", gdm_get_cur_vt ()); -#else - gdm_connection_write (conn, "ERROR 8 Virtual terminals not supported\n"); -#endif -} - -static void -sup_handle_set_vt (GdmConnection *conn, - const char *msg, - gpointer data) -{ - int vt; - GSList *li; - GSList *displays; - - displays = gdm_daemon_config_get_display_list (); - - if (sscanf (msg, GDM_SUP_SET_VT " %d", &vt) != 1 || - vt < 0) { - gdm_connection_write (conn, - "ERROR 9 Invalid virtual terminal number\n"); - return; - } - - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn)) { - gdm_info (_("%s request denied: " - "Not authenticated"), "QUERY_VT"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - -#if defined (__linux__) || defined (__FreeBSD__) || defined (__DragonFly__) - gdm_change_vt (vt); - for (li = displays; li != NULL; li = li->next) { - GdmDisplay *disp = li->data; - if (disp->vt == vt) { - send_slave_command (disp, GDM_NOTIFY_TWIDDLE_POINTER); - break; - } - } - gdm_connection_write (conn, "OK\n"); -#else - gdm_connection_write (conn, "ERROR 8 Virtual terminals not supported\n"); -#endif -} - -static void -gdm_handle_user_message (GdmConnection *conn, - const char *msg, - gpointer data) -{ - - g_debug ("Handling user message: '%s'", msg); - - if (gdm_connection_get_message_count (conn) > GDM_SUP_MAX_MESSAGES) { - g_debug ("Closing connection, %d messages reached", GDM_SUP_MAX_MESSAGES); - gdm_connection_write (conn, "ERROR 200 Too many messages\n"); - gdm_connection_close (conn); - return; - } - - if (strncmp (msg, GDM_SUP_AUTH_LOCAL " ", - strlen (GDM_SUP_AUTH_LOCAL " ")) == 0) { - - sup_handle_auth_local (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_FLEXI_XSERVER) == 0) { - /* Only allow locally authenticated connections */ - if ( ! GDM_CONN_AUTHENTICATED (conn)) { - gdm_info (_("%s request denied: " - "Not authenticated"), "FLEXI_XSERVER"); - gdm_connection_write (conn, - "ERROR 100 Not authenticated\n"); - return; - } - - handle_flexi_server (conn, - TYPE_FLEXI, - gdm_daemon_config_get_value_string (GDM_KEY_STANDARD_XSERVER), - TRUE /* handled */, - FALSE /* chooser */, - NULL, - 0, - NULL, - NULL, - NULL); - } else if ((strncmp (msg, GDM_SUP_FLEXI_XSERVER_USER " ", - strlen (GDM_SUP_FLEXI_XSERVER_USER " ")) == 0) || - (strncmp (msg, GDM_SUP_FLEXI_XSERVER " ", - strlen (GDM_SUP_FLEXI_XSERVER " ")) == 0)) { - - sup_handle_flexi_xserver (conn, msg, data); - - } else if ((strncmp (msg, GDM_SUP_FLEXI_XNEST_USER " ", - strlen (GDM_SUP_FLEXI_XNEST_USER " ")) == 0) || - (strncmp (msg, GDM_SUP_FLEXI_XNEST " ", - strlen (GDM_SUP_FLEXI_XNEST " ")) == 0)) { - - sup_handle_flexi_xnest (conn, msg, data); - - } else if ((strncmp (msg, GDM_SUP_ATTACHED_SERVERS, - strlen (GDM_SUP_ATTACHED_SERVERS)) == 0) || - (strncmp (msg, GDM_SUP_CONSOLE_SERVERS, - strlen (GDM_SUP_CONSOLE_SERVERS)) == 0)) { - - sup_handle_attached_servers (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_ALL_SERVERS) == 0) { - - sup_handle_all_servers (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_GET_SERVER_LIST) == 0) { - - sup_handle_get_server_list (conn, msg, data); - - } else if (strncmp (msg, GDM_SUP_GET_SERVER_DETAILS " ", - strlen (GDM_SUP_GET_SERVER_DETAILS " ")) == 0) { - - sup_handle_get_server_details (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_GREETERPIDS) == 0) { - - sup_handle_greeterpids (conn, msg, data); - - } else if (strncmp (msg, GDM_SUP_UPDATE_CONFIG " ", - strlen (GDM_SUP_UPDATE_CONFIG " ")) == 0) { - const char *key; - - key = &msg[strlen (GDM_SUP_UPDATE_CONFIG " ")]; - - if (! gdm_daemon_config_update_key ((gchar *)key)) - gdm_connection_printf (conn, "ERROR 50 Unsupported key <%s>\n", key); - else - gdm_connection_write (conn, "OK\n"); - } else if (strncmp (msg, GDM_SUP_GET_CONFIG " ", - strlen (GDM_SUP_GET_CONFIG " ")) == 0) { - - sup_handle_get_config (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_GET_CONFIG_FILE) == 0) { - /* - * Value is only non-null if passed in on command line. - * Otherwise print compiled-in default file location. - */ - if (config_file == NULL) { - gdm_connection_printf (conn, "OK %s\n", - GDM_DEFAULTS_CONF); - } else { - gdm_connection_printf (conn, "OK %s\n", config_file); - } - } else if (strcmp (msg, GDM_SUP_GET_CUSTOM_CONFIG_FILE) == 0) { - - sup_handle_get_custom_config_file (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_QUERY_LOGOUT_ACTION) == 0) { - - sup_handle_query_logout_action (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_QUERY_CUSTOM_CMD_LABELS) == 0) { - - sup_handle_query_custom_cmd_labels (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_QUERY_CUSTOM_CMD_NO_RESTART_STATUS) == 0) { - - sup_handle_query_custom_cmd_no_restart_status (conn, msg, data); - - } else if (strncmp (msg, GDM_SUP_SET_LOGOUT_ACTION " ", - strlen (GDM_SUP_SET_LOGOUT_ACTION " ")) == 0) { - - sup_handle_set_logout_action (conn, msg, data); - - } else if (strncmp (msg, GDM_SUP_SET_SAFE_LOGOUT_ACTION " ", - strlen (GDM_SUP_SET_SAFE_LOGOUT_ACTION " ")) == 0) { - - sup_handle_set_safe_logout_action (conn, msg, data); - - } else if (strcmp (msg, GDM_SUP_QUERY_VT) == 0) { - - sup_handle_query_vt (conn, msg, data); - - } else if (strncmp (msg, GDM_SUP_SET_VT " ", - strlen (GDM_SUP_SET_VT " ")) == 0) { - - sup_handle_set_vt (conn, msg, data); - - } else if (strncmp (msg, GDM_SUP_ADD_DYNAMIC_DISPLAY " ", - strlen (GDM_SUP_ADD_DYNAMIC_DISPLAY " ")) == 0) { - gchar *key; - - key = g_strdup (&msg[strlen (GDM_SUP_ADD_DYNAMIC_DISPLAY " ")]); - g_strstrip (key); - handle_dynamic_server (conn, DYNAMIC_ADD, key); - g_free (key); - - } else if (strncmp (msg, GDM_SUP_REMOVE_DYNAMIC_DISPLAY " ", - strlen (GDM_SUP_REMOVE_DYNAMIC_DISPLAY " ")) == 0) { - gchar *key; - - key = g_strdup (&msg[strlen (GDM_SUP_REMOVE_DYNAMIC_DISPLAY " ")]); - g_strstrip (key); - handle_dynamic_server (conn, DYNAMIC_REMOVE, key); - g_free (key); - - } else if (strncmp (msg, GDM_SUP_RELEASE_DYNAMIC_DISPLAYS " ", - strlen (GDM_SUP_RELEASE_DYNAMIC_DISPLAYS " ")) == 0) { - - gchar *key; - - key = g_strdup (&msg[strlen (GDM_SUP_RELEASE_DYNAMIC_DISPLAYS " ")]); - g_strstrip (key); - handle_dynamic_server (conn, DYNAMIC_RELEASE, key); - g_free (key); - - } else if (strcmp (msg, GDM_SUP_VERSION) == 0) { - gdm_connection_write (conn, "GDM " VERSION "\n"); - } else if (strcmp (msg, GDM_SUP_SERVER_BUSY) == 0) { - if (gdm_connection_is_server_busy (unixconn)) - gdm_connection_write (conn, "OK true\n"); - else - gdm_connection_write (conn, "OK false\n"); - } else if (strcmp (msg, GDM_SUP_CLOSE) == 0) { - gdm_connection_close (conn); - } else { - gdm_connection_write (conn, "ERROR 0 Not implemented\n"); - gdm_connection_close (conn); - } -} diff --git a/daemon/gdm.h b/daemon/gdm.h index e13e4185..67758bf1 100644 --- a/daemon/gdm.h +++ b/daemon/gdm.h @@ -24,32 +24,6 @@ #define GDM_MAX_PASS 256 /* Define a value for password length. Glibc * leaves MAX_PASS undefined. */ -/* DO NOTE USE 1, that's used as error if x connection fails usually */ -/* Note that there is no reason why these were a power of two, and note - * that they have to fit in 256 */ -/* These are the exit codes */ -#define DISPLAY_REMANAGE 2 /* Restart display */ -#define DISPLAY_ABORT 4 /* Houston, we have a problem */ -#define DISPLAY_REBOOT 8 /* Rebewt */ -#define DISPLAY_HALT 16 /* Halt */ -#define DISPLAY_SUSPEND 17 /* Suspend (don't use, use the interrupt) */ -#define DISPLAY_CHOSEN 20 /* successful chooser session, - restart display */ -#define DISPLAY_RUN_CHOOSER 30 /* Run chooser */ -#define DISPLAY_XFAILED 64 /* X failed */ -#define DISPLAY_GREETERFAILED 65 /* greeter failed (crashed) */ -#define DISPLAY_RESTARTGREETER 127 /* Restart greeter */ -#define DISPLAY_RESTARTGDM 128 /* Restart GDM */ - -enum { - DISPLAY_UNBORN /* Not yet started */, - DISPLAY_ALIVE /* Yay! we're alive (non-XDMCP) */, - XDMCP_PENDING /* Pending XDMCP display */, - XDMCP_MANAGED /* Managed XDMCP display */, - DISPLAY_DEAD /* Left for dead */, - DISPLAY_CONFIG /* in process of being configured */ -}; - /* The dreaded miscellaneous category */ #define PIPE_SIZE 4096 @@ -86,9 +60,4 @@ enum { #define SDTLOGIN_DIR "/var/dt/sdtlogin" #endif -/* If id == NULL, then get the first X server */ -void gdm_final_cleanup (void); - #endif /* GDM_H */ - -/* EOF */ diff --git a/daemon/main.c b/daemon/main.c new file mode 100644 index 00000000..d47a10f5 --- /dev/null +++ b/daemon/main.c @@ -0,0 +1,596 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +/* Needed for signal handling */ +#include "gdm-common.h" + +#include "gdm-manager.h" +#include "gdm-log.h" +#include "gdm-master-config.h" +#include "gdm-daemon-config-entries.h" + +#include "misc.h" + +#define GDM_DBUS_NAME "org.gnome.DisplayManager" + +static void bus_proxy_destroyed_cb (DBusGProxy *bus_proxy, + GdmManager *manager); + +extern char **environ; + +static char **stored_argv = NULL; +static int stored_argc = 0; +static GList *stored_env = NULL; +static GdmManager *manager = NULL; +static GdmDaemonConfig *daemon_config = 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 DBusGProxy * +get_bus_proxy (DBusGConnection *connection) +{ + DBusGProxy *bus_proxy; + + bus_proxy = dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + return bus_proxy; +} + +static gboolean +acquire_name_on_proxy (DBusGProxy *bus_proxy) +{ + GError *error; + guint result; + gboolean res; + gboolean ret; + + ret = FALSE; + + if (bus_proxy == NULL) { + goto out; + } + + error = NULL; + res = dbus_g_proxy_call (bus_proxy, + "RequestName", + &error, + G_TYPE_STRING, GDM_DBUS_NAME, + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &result, + G_TYPE_INVALID); + if (! res) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", GDM_DBUS_NAME, error->message); + g_error_free (error); + } else { + g_warning ("Failed to acquire %s", GDM_DBUS_NAME); + } + goto out; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (error != NULL) { + g_warning ("Failed to acquire %s: %s", GDM_DBUS_NAME, error->message); + g_error_free (error); + } else { + g_warning ("Failed to acquire %s", GDM_DBUS_NAME); + } + goto out; + } + + ret = TRUE; + + out: + return ret; +} + +static DBusGConnection * +get_system_bus (void) +{ + GError *error; + DBusGConnection *bus; + DBusConnection *connection; + + error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to system bus: %s", + error->message); + g_error_free (error); + goto out; + } + + connection = dbus_g_connection_get_connection (bus); + dbus_connection_set_exit_on_disconnect (connection, FALSE); + + out: + return bus; +} + +static gboolean +bus_reconnect (GdmManager *manager) +{ + DBusGConnection *bus; + DBusGProxy *bus_proxy; + gboolean ret; + + ret = TRUE; + + bus = get_system_bus (); + if (bus == NULL) { + goto out; + } + + bus_proxy = get_bus_proxy (bus); + if (bus_proxy == NULL) { + g_warning ("Could not construct bus_proxy object; will retry"); + goto out; + } + + if (! acquire_name_on_proxy (bus_proxy) ) { + g_warning ("Could not acquire name; will retry"); + goto out; + } + + manager = gdm_manager_new (); + if (manager == NULL) { + g_warning ("Could not construct manager object"); + exit (1); + } + + g_signal_connect (bus_proxy, + "destroy", + G_CALLBACK (bus_proxy_destroyed_cb), + manager); + + gdm_debug ("Successfully reconnected to D-Bus"); + + ret = FALSE; + + out: + return ret; +} + +static void +bus_proxy_destroyed_cb (DBusGProxy *bus_proxy, + GdmManager *manager) +{ + gdm_debug ("Disconnected from D-Bus"); + + g_object_unref (manager); + manager = NULL; + + g_timeout_add (3000, (GSourceFunc)bus_reconnect, manager); +} + +static void +delete_pid (void) +{ + 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 diskspace: %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 diskspace: %s"), + GDM_PID_FILE, + g_strerror (errno)); + return; + } + + g_atexit (delete_pid); +} + +static void +gdm_final_cleanup (void) +{ + g_object_unref (manager); +} + +static void +main_saveenv (void) +{ + int i; + + g_list_foreach (stored_env, (GFunc)g_free, NULL); + g_list_free (stored_env); + stored_env = NULL; + + for (i = 0; environ[i] != NULL; i++) { + char *env = environ[i]; + stored_env = g_list_prepend (stored_env, g_strdup (env)); + } +} + +static void +main_restoreenv (void) +{ + GList *li; + + ve_clearenv (); + + /* FIXME: leaks */ + + for (li = stored_env; li != NULL; li = li->next) { + putenv (g_strdup (li->data)); + } +} + +static void +gdm_restart_now (void) +{ + gdm_info (_("GDM restarting ...")); + gdm_final_cleanup (); + main_restoreenv (); + VE_IGNORE_EINTR (execvp (stored_argv[0], stored_argv)); + g_warning (_("Failed to restart self")); + _exit (1); +} + +static void +store_argv (int argc, + char *argv[]) +{ + int i; + + stored_argv = g_new0 (char *, argc + 1); + for (i = 0; i < argc; i++) { + stored_argv[i] = g_strdup (argv[i]); + } + stored_argv[i] = NULL; + stored_argc = argc; +} + +static void +check_logdir (void) +{ + struct stat statbuf; + int r; + char *log_path; + + log_path = NULL; + gdm_daemon_config_get_string_for_id (daemon_config, GDM_ID_LOG_DIR, &log_path); + + VE_IGNORE_EINTR (r = g_stat (log_path, &statbuf)); + if (r < 0 || ! S_ISDIR (statbuf.st_mode)) { + gdm_fail (_("Logdir %s does not exist or isn't a directory."), log_path); + } + + g_free (log_path); +} + +static void +check_servauthdir (const char *auth_path, + struct stat *statbuf) +{ + int r; + + /* Enter paranoia mode */ + VE_IGNORE_EINTR (r = g_stat (auth_path, statbuf)); + if G_UNLIKELY (r < 0) { + gdm_fail (_("Authdir %s does not exist. Aborting."), auth_path); + } + + if G_UNLIKELY (! S_ISDIR (statbuf->st_mode)) { + gdm_fail (_("Authdir %s is not a directory. Aborting."), auth_path); + } +} + +static void +gdm_daemon_check_permissions (uid_t uid, + gid_t gid) +{ + struct stat statbuf; + char *auth_path; + + auth_path = NULL; + gdm_daemon_config_get_string_for_id (daemon_config, GDM_ID_LOG_DIR, &auth_path); + + /* Enter paranoia mode */ + check_servauthdir (auth_path, &statbuf); + + NEVER_FAILS_root_set_euid_egid (0, 0); + + /* Now set things up for us as */ + chown (auth_path, 0, gid); + g_chmod (auth_path, (S_IRWXU|S_IRWXG|S_ISVTX)); + + NEVER_FAILS_root_set_euid_egid (uid, gid); + + /* Again paranoid */ + check_servauthdir (auth_path, &statbuf); + + if G_UNLIKELY (statbuf.st_uid != 0 || statbuf.st_gid != gid) { + gdm_fail (_("Authdir %s is not owned by user %d, group %d. Aborting."), + auth_path, + (int)uid, + (int)gid); + } + + if G_UNLIKELY (statbuf.st_mode != (S_IFDIR|S_IRWXU|S_IRWXG|S_ISVTX)) { + gdm_fail (_("Authdir %s has wrong permissions %o. Should be %o. Aborting."), + auth_path, + statbuf.st_mode, + (S_IRWXU|S_IRWXG|S_ISVTX)); + } + + g_free (auth_path); +} + +static void +gdm_daemon_change_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_daemon_config_get_string_for_id (daemon_config, GDM_ID_USER, &username); + gdm_daemon_config_get_string_for_id (daemon_config, GDM_ID_GROUP, &groupname); + + /* Lookup user and groupid for the GDM user */ + pwent = getpwnam (username); + + /* 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!")); + } + + /* gid remains `gdm' */ + NEVER_FAILS_root_set_euid_egid (uid, gid); + + if (uidp != NULL) { + *uidp = uid; + } + + if (gidp != NULL) { + *gidp = gid; + } + + g_free (username); + g_free (groupname); +} + +static void +setup_signal_handlers (void) +{ + /* FIXME */ +} + +int +main (int argc, + char **argv) +{ + GMainLoop *loop; + GOptionContext *context; + DBusGProxy *bus_proxy; + DBusGConnection *connection; + int ret; + int i; + gboolean debug; + static char *config_file = NULL; + static gboolean no_daemon = FALSE; + static gboolean no_console = FALSE; + static gboolean do_timed_exit = FALSE; + static gboolean print_version = FALSE; + static GOptionEntry entries [] = { + { "config", 0, 0, G_OPTION_ARG_STRING, &config_file, N_("Alternative GDM System Defaults configuration file"), N_("CONFIGFILE") }, + + { "nodaemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_("Don't become a daemon"), NULL }, + { "no-console", 0, 0, G_OPTION_ARG_NONE, &no_console, N_("No console (static) servers to be run"), 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 } + }; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + ret = 1; + + g_type_init (); + + store_argv (argc, argv); + main_saveenv (); + + context = g_option_context_new (_("GNOME Display Manager")); + g_option_context_add_main_entries (context, entries, NULL); + + /* preprocess the arguments to support the xdm style -nodaemon + * option + */ + for (i = 0; i < argc; i++) { + if (strcmp (argv[i], "-nodaemon") == 0) + argv[i] = (char *) "--nodaemon"; + } + + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + if (! no_daemon && daemon (0, 0)) { + g_error ("Could not daemonize: %s", g_strerror (errno)); + } + + connection = get_system_bus (); + if (connection == NULL) { + goto out; + } + + bus_proxy = get_bus_proxy (connection); + if (bus_proxy == NULL) { + g_warning ("Could not construct bus_proxy object; bailing out"); + goto out; + } + + if (! acquire_name_on_proxy (bus_proxy) ) { + g_warning ("Could not acquire name; bailing out"); + goto out; + } + + gdm_log_init (); + + daemon_config = gdm_daemon_config_new (); + gdm_daemon_config_load (daemon_config); + + debug = FALSE; + gdm_daemon_config_get_bool_for_id (daemon_config, GDM_ID_DEBUG, &debug); + gdm_log_set_debug (debug); + + gdm_daemon_change_user (&gdm_uid, &gdm_gid); + gdm_daemon_check_permissions (gdm_uid, gdm_gid); + NEVER_FAILS_root_set_euid_egid (0, 0); + check_logdir (); + + /* XDM compliant error message */ + if (getuid () != 0) { + /* make sure the pid file doesn't get wiped */ + g_warning (_("Only root wants to run GDM")); + exit (-1); + } + + /* pid file */ + delete_pid (); + write_pid (); + + g_chdir (AUTHDIR); + +#ifdef __sun + g_unlink (SDTLOGIN_DIR); + g_mkdir (SDTLOGIN_DIR, 0700); +#endif + + setup_signal_handlers (); + + manager = gdm_manager_new (); + + if (manager == NULL) { + goto out; + } + + g_signal_connect (bus_proxy, + "destroy", + G_CALLBACK (bus_proxy_destroyed_cb), + manager); + + loop = g_main_loop_new (NULL, FALSE); + + if (do_timed_exit) { + g_timeout_add (1000 * 30, (GSourceFunc) timed_exit_cb, loop); + } + + gdm_manager_start (manager); + + g_main_loop_run (loop); + + if (manager != NULL) { + g_object_unref (manager); + } + if (daemon_config != NULL) { + g_object_unref (daemon_config); + } + + g_main_loop_unref (loop); + + ret = 0; + + out: + + return ret; +} diff --git a/daemon/misc.c b/daemon/misc.c index a7ff2337..6d14193d 100644 --- a/daemon/misc.c +++ b/daemon/misc.c @@ -172,37 +172,6 @@ have_ipv6 (void) } #endif -void -gdm_fdprintf (int fd, const gchar *format, ...) -{ - va_list args; - gchar *s; - int written, len; - - va_start (args, format); - s = g_strdup_vprintf (format, args); - va_end (args); - - len = strlen (s); - - if (len == 0) { - g_free (s); - return; - } - - written = 0; - while (written < len) { - int w; - VE_IGNORE_EINTR (w = write (fd, &s[written], len - written)); - if (w < 0) - /* evil! */ - break; - written += w; - } - - g_free (s); -} - /* * Clear environment, but keep the i18n ones, * note that this leaks memory so only use before exec @@ -663,97 +632,6 @@ gdm_exec_wait (char * const *argv, return -1; } -static int sigchld_blocked = 0; -static sigset_t sigchldblock_mask, sigchldblock_oldmask; - -static int sigterm_blocked = 0; -static sigset_t sigtermblock_mask, sigtermblock_oldmask; - -static int sigusr2_blocked = 0; -static sigset_t sigusr2block_mask, sigusr2block_oldmask; - -void -gdm_sigchld_block_push (void) -{ - sigchld_blocked++; - - if (sigchld_blocked == 1) { - /* Set signal mask */ - sigemptyset (&sigchldblock_mask); - sigaddset (&sigchldblock_mask, SIGCHLD); - sigprocmask (SIG_BLOCK, &sigchldblock_mask, &sigchldblock_oldmask); - } -} - -void -gdm_sigchld_block_pop (void) -{ - sigchld_blocked --; - - if (sigchld_blocked == 0) { - /* Reset signal mask back */ - sigprocmask (SIG_SETMASK, &sigchldblock_oldmask, NULL); - } -} - -void -gdm_sigterm_block_push (void) -{ - sigterm_blocked++; - - if (sigterm_blocked == 1) { - /* Set signal mask */ - sigemptyset (&sigtermblock_mask); - sigaddset (&sigtermblock_mask, SIGTERM); - sigaddset (&sigtermblock_mask, SIGINT); - sigaddset (&sigtermblock_mask, SIGHUP); - sigprocmask (SIG_BLOCK, &sigtermblock_mask, &sigtermblock_oldmask); - } -} - -void -gdm_sigterm_block_pop (void) -{ - sigterm_blocked --; - - if (sigterm_blocked == 0) { - /* Reset signal mask back */ - sigprocmask (SIG_SETMASK, &sigtermblock_oldmask, NULL); - } -} - -void -gdm_sigusr2_block_push (void) -{ - sigset_t oldmask; - - if (sigusr2_blocked == 0) { - /* Set signal mask */ - sigemptyset (&sigusr2block_mask); - sigaddset (&sigusr2block_mask, SIGUSR2); - sigprocmask (SIG_BLOCK, &sigusr2block_mask, &oldmask); - } - - sigusr2_blocked++; - - sigusr2block_oldmask = oldmask; -} - -void -gdm_sigusr2_block_pop (void) -{ - sigset_t oldmask; - - oldmask = sigusr2block_oldmask; - - sigusr2_blocked--; - - if (sigusr2_blocked == 0) { - /* Reset signal mask back */ - sigprocmask (SIG_SETMASK, &sigusr2block_oldmask, NULL); - } -} - pid_t gdm_fork_extra (void) { @@ -879,84 +757,6 @@ gdm_ensure_sanity (void) NEVER_FAILS_root_set_euid_egid (old_euid, old_egid); } -const GList * -gdm_address_peek_local_list (void) -{ - static GList *the_list = NULL; - static time_t last_time = 0; - char hostbuf[BUFSIZ]; - struct addrinfo hints; - struct addrinfo *result; - struct addrinfo *res; - - /* Don't check more then every 5 seconds */ - if (last_time + 5 > time (NULL)) { - return the_list; - } - - g_list_foreach (the_list, (GFunc)g_free, NULL); - g_list_free (the_list); - the_list = NULL; - - last_time = time (NULL); - - hostbuf[BUFSIZ-1] = '\0'; - if (gethostname (hostbuf, BUFSIZ-1) != 0) { - gdm_debug ("%s: Could not get server hostname, using localhost", "gdm_peek_local_address_list"); - snprintf (hostbuf, BUFSIZ-1, "localhost"); - } - - memset (&hints, 0, sizeof (hints)); - hints.ai_family = AF_INET; -#ifdef ENABLE_IPV6 - hints.ai_family |= AF_INET6; -#endif - - if (getaddrinfo (hostbuf, NULL, &hints, &result) != 0) { - gdm_debug ("%s: Could not get address from hostname!", "gdm_peek_local_address_list"); - - return NULL; - } - - for (res = result; res != NULL; res = res->ai_next) { - struct sockaddr_storage *sa; - - sa = g_memdup (res->ai_addr, res->ai_addrlen); - the_list = g_list_append (the_list, sa); - } - - if (result) { - freeaddrinfo (result); - result = NULL; - } - - return the_list; -} - - -gboolean -gdm_address_is_local (struct sockaddr_storage *sa) -{ - const GList *list; - - if (gdm_address_is_loopback (sa)) { - return TRUE; - } - - list = gdm_address_peek_local_list (); - - while (list != NULL) { - struct sockaddr_storage *addr = list->data; - - if (gdm_address_equal (sa, addr)) { - return TRUE; - } - - list = list->next; - } - - return FALSE; -} gboolean gdm_setup_gids (const char *login, gid_t gid) @@ -1070,117 +870,6 @@ gdm_test_opt (const char *cmd, const char *help, const char *option) return got_it; } -int -gdm_fdgetc (int fd) -{ - char buf[1]; - int bytes; - - VE_IGNORE_EINTR (bytes = read (fd, buf, 1)); - if (bytes != 1) - return EOF; - else - return (int)buf[0]; -} - -char * -gdm_fdgets (int fd) -{ - int c; - int bytes = 0; - GString *gs = g_string_new (NULL); - for (;;) { - c = gdm_fdgetc (fd); - if (c == '\n') - return g_string_free (gs, FALSE); - /* on EOF */ - if (c < 0) { - if (bytes == 0) { - g_string_free (gs, TRUE); - return NULL; - } else { - return g_string_free (gs, FALSE); - } - } else { - bytes++; - g_string_append_c (gs, c); - } - } -} - -void -gdm_close_all_descriptors (int from, int except, int except2) -{ - DIR *dir; - struct dirent *ent; - GSList *openfds = NULL; - - /* - * Evil, but less evil then going to _SC_OPEN_MAX - * which can be very VERY large - */ - dir = opendir ("/proc/self/fd/"); /* This is the Linux dir */ - if (dir == NULL) - dir = opendir ("/dev/fd/"); /* This is the FreeBSD dir */ - if G_LIKELY (dir != NULL) { - GSList *li; - while ((ent = readdir (dir)) != NULL) { - int fd; - if (ent->d_name[0] == '.') - continue; - fd = atoi (ent->d_name); - if (fd >= from && fd != except && fd != except2) - openfds = g_slist_prepend (openfds, GINT_TO_POINTER (fd)); - } - closedir (dir); - for (li = openfds; li != NULL; li = li->next) { - int fd = GPOINTER_TO_INT (li->data); - VE_IGNORE_EINTR (close (fd)); - } - g_slist_free (openfds); - } else { - int i; - int max = sysconf (_SC_OPEN_MAX); - /* - * Don't go higher then this. This is - * a safety measure to not hang on crazy - * systems - */ - if G_UNLIKELY (max > 4096) { - /* FIXME: warn about this perhaps */ - /* - * Try an open, in case we're really - * leaking fds somewhere badly, this - * should be very high - */ - i = gdm_open_dev_null (O_RDONLY); - max = MAX (i+1, 4096); - } - for (i = from; i < max; i++) { - if G_LIKELY (i != except && i != except2) - VE_IGNORE_EINTR (close (i)); - } - } -} - -int -gdm_open_dev_null (mode_t mode) -{ - int ret; - VE_IGNORE_EINTR (ret = open ("/dev/null", mode)); - if G_UNLIKELY (ret < 0) { - /* - * Never output anything, we're likely in some - * strange state right now - */ - gdm_signal_ignore (SIGPIPE); - VE_IGNORE_EINTR (close (2)); - gdm_fail ("Cannot open /dev/null, system on crack!"); - } - - return ret; -} - void gdm_unset_signals (void) { @@ -1206,439 +895,6 @@ gdm_unset_signals (void) #endif } -void -gdm_signal_ignore (int signal) -{ - struct sigaction ign_signal; - - ign_signal.sa_handler = SIG_IGN; - ign_signal.sa_flags = SA_RESTART; - sigemptyset (&ign_signal.sa_mask); - - if G_UNLIKELY (sigaction (signal, &ign_signal, NULL) < 0) - gdm_error (_("%s: Error setting signal %d to %s"), - "gdm_signal_ignore", signal, "SIG_IGN"); -} - -void -gdm_signal_default (int signal) -{ - struct sigaction def_signal; - - def_signal.sa_handler = SIG_DFL; - def_signal.sa_flags = SA_RESTART; - sigemptyset (&def_signal.sa_mask); - - if G_UNLIKELY (sigaction (signal, &def_signal, NULL) < 0) - gdm_error (_("%s: Error setting signal %d to %s"), - "gdm_signal_ignore", signal, "SIG_DFL"); -} - -static GdmHostent * -fillout_addrinfo (struct addrinfo *res, - struct sockaddr *ia, - const char *name) -{ - GdmHostent *he; - gint i; - gint addr_count = 0; - struct addrinfo *tempaddrinfo; - - he = g_new0 (GdmHostent, 1); - - he->addrs = NULL; - he->addr_count = 0; - - if (res != NULL && res->ai_canonname != NULL) { - he->hostname = g_strdup (res->ai_canonname); - he->not_found = FALSE; - } else { - he->not_found = TRUE; - if (name != NULL) - he->hostname = g_strdup (name); - else { - static char buffer6[INET6_ADDRSTRLEN]; - static char buffer[INET_ADDRSTRLEN]; - const char *new = NULL; - - if (ia->sa_family == AF_INET6) { - if (IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *)ia)->sin6_addr)) { - new = inet_ntop (AF_INET, &(((struct sockaddr_in6 *)ia)->sin6_addr.s6_addr[12]), buffer, sizeof (buffer)); - } else { - new = inet_ntop (AF_INET6, &((struct sockaddr_in6 *)ia)->sin6_addr, buffer6, sizeof (buffer6)); - } - } else if (ia->sa_family == AF_INET) { - new = inet_ntop (AF_INET, &((struct sockaddr_in *)ia)->sin_addr, buffer, sizeof (buffer)); - } - - if (new != NULL) { - he->hostname = g_strdup (new); - } else { - he->hostname = NULL; - } - } - } - - tempaddrinfo = res; - - while (res != NULL) { - addr_count++; - res = res->ai_next; - } - - he->addrs = g_new0 (struct sockaddr_storage, addr_count); - he->addr_count = addr_count; - res = tempaddrinfo; - for (i = 0; ; i++) { - if (res == NULL) - break; - - if ((res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) { - (he->addrs)[i] = *(struct sockaddr_storage *)(res->ai_addr); - } - - res = res->ai_next; - } - - /* We don't want the ::ffff: that could arise here */ - if (he->hostname != NULL && - strncmp (he->hostname, "::ffff:", 7) == 0) { - strcpy (he->hostname, he->hostname + 7); - } - - return he; -} - -static gboolean do_jumpback = FALSE; -static Jmp_buf signal_jumpback; -static struct sigaction oldterm, oldint, oldhup; - -static void -jumpback_sighandler (int signal) -{ - /* - * This avoids a race see Note below. - * We want to jump back only on the first - * signal invocation, even if the signal - * handler didn't return. - */ - gboolean old_do_jumpback = do_jumpback; - do_jumpback = FALSE; - - if (signal == SIGINT) - oldint.sa_handler (signal); - else if (signal == SIGTERM) - oldint.sa_handler (signal); - else if (signal == SIGHUP) - oldint.sa_handler (signal); - /* No others should be set up */ - - /* Note that we may not get here since - the SIGTERM handler in slave.c - might have in fact done the big Longjmp - to the slave's death */ - - if (old_do_jumpback) { - Longjmp (signal_jumpback, 1); - } -} - -/* - * This sets up interruptes to be proxied and the - * gethostbyname/addr to be whacked using longjmp, - * in case INT/TERM/HUP was gotten in which case - * we no longer care for the result of the - * resolution. - */ -#define SETUP_INTERRUPTS_FOR_TERM_DECLS \ - struct sigaction term; - -#define SETUP_INTERRUPTS_FOR_TERM_SETUP \ - do_jumpback = FALSE; \ - \ - term.sa_handler = jumpback_sighandler; \ - term.sa_flags = SA_RESTART; \ - sigemptyset (&term.sa_mask); \ - \ - if G_UNLIKELY (sigaction (SIGTERM, &term, &oldterm) < 0) \ - gdm_fail (_("%s: Error setting up %s signal handler: %s"), \ - "SETUP_INTERRUPTS_FOR_TERM", "TERM", strerror (errno)); \ - \ - if G_UNLIKELY (sigaction (SIGINT, &term, &oldint) < 0) \ - gdm_fail (_("%s: Error setting up %s signal handler: %s"), \ - "SETUP_INTERRUPTS_FOR_TERM", "INT", strerror (errno)); \ - \ - if G_UNLIKELY (sigaction (SIGHUP, &term, &oldhup) < 0) \ - gdm_fail (_("%s: Error setting up %s signal handler: %s"), \ - "SETUP_INTERRUPTS_FOR_TERM", "HUP", strerror (errno)); \ - -#define SETUP_INTERRUPTS_FOR_TERM_TEARDOWN \ - do_jumpback = FALSE; \ - \ - if G_UNLIKELY (sigaction (SIGTERM, &oldterm, NULL) < 0) \ - gdm_fail (_("%s: Error setting up %s signal handler: %s"), \ - "SETUP_INTERRUPTS_FOR_TERM", "TERM", strerror (errno)); \ - \ - if G_UNLIKELY (sigaction (SIGINT, &oldint, NULL) < 0) \ - gdm_fail (_("%s: Error setting up %s signal handler: %s"), \ - "SETUP_INTERRUPTS_FOR_TERM", "INT", strerror (errno)); \ - \ - if G_UNLIKELY (sigaction (SIGHUP, &oldhup, NULL) < 0) \ - gdm_fail (_("%s: Error setting up %s signal handler: %s"), \ - "SETUP_INTERRUPTS_FOR_TERM", "HUP", strerror (errno)); - -GdmHostent * -gdm_gethostbyname (const char *name) -{ - struct addrinfo hints; - /* static because of Setjmp */ - static struct addrinfo *result; - - SETUP_INTERRUPTS_FOR_TERM_DECLS - - /* The cached address */ - static GdmHostent *he = NULL; - static time_t last_time = 0; - static char *cached_hostname = NULL; - - if (cached_hostname != NULL && - strcmp (cached_hostname, name) == 0) { - /* Don't check more then every 60 seconds */ - if (last_time + 60 > time (NULL)) - return gdm_hostent_copy (he); - } - - SETUP_INTERRUPTS_FOR_TERM_SETUP - - if (Setjmp (signal_jumpback) == 0) { - do_jumpback = TRUE; - - /* Find client hostname */ - memset (&hints, 0, sizeof (hints)); - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_CANONNAME; - - if (result) { - freeaddrinfo (result); - result = NULL; - } - - getaddrinfo (name, NULL, &hints, &result); - do_jumpback = FALSE; - } else { - /* Here we got interrupted */ - result = NULL; - } - - SETUP_INTERRUPTS_FOR_TERM_TEARDOWN - - g_free (cached_hostname); - cached_hostname = g_strdup (name); - - gdm_hostent_free (he); - - he = fillout_addrinfo (result, NULL, name); - - last_time = time (NULL); - return gdm_hostent_copy (he); -} - -GdmHostent * -gdm_gethostbyaddr (struct sockaddr_storage *ia) -{ - struct addrinfo hints; - /* static because of Setjmp */ - static struct addrinfo *result = NULL; - struct sockaddr_in6 sin6; - struct sockaddr_in sin; - static struct in6_addr cached_addr6; - - SETUP_INTERRUPTS_FOR_TERM_DECLS - - /* The cached address */ - static GdmHostent *he = NULL; - static time_t last_time = 0; - static struct in_addr cached_addr; - - if (last_time != 0) { - if ((ia->ss_family == AF_INET6) && (memcmp (cached_addr6.s6_addr, ((struct sockaddr_in6 *) ia)->sin6_addr.s6_addr, sizeof (struct in6_addr)) == 0)) { - /* Don't check more then every 60 seconds */ - if (last_time + 60 > time (NULL)) - return gdm_hostent_copy (he); - } else if (ia->ss_family == AF_INET) { - if (memcmp (&cached_addr, &(((struct sockaddr_in *)ia)->sin_addr), sizeof (struct in_addr)) == 0) { - /* Don't check more then every 60 seconds */ - if (last_time + 60 > time (NULL)) - return gdm_hostent_copy (he); - } - } - } - - SETUP_INTERRUPTS_FOR_TERM_SETUP - - if (Setjmp (signal_jumpback) == 0) { - do_jumpback = TRUE; - - /* Find client hostname */ - memset (&hints, 0, sizeof (hints)); - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_CANONNAME; - - if (result) { - freeaddrinfo (result); - result = NULL; - } - - if (ia->ss_family == AF_INET6) { - char buffer6[INET6_ADDRSTRLEN]; - - inet_ntop (AF_INET6, &((struct sockaddr_in6 *)ia)->sin6_addr, buffer6, sizeof (buffer6)); - - /* - * In the case of IPv6 mapped address strip the - * ::ffff: and lookup as an IPv4 address - */ - if (strncmp (buffer6, "::ffff:", 7) == 0) { - char *temp= (buffer6 + 7); - strcpy (buffer6, temp); - } - getaddrinfo (buffer6, NULL, &hints, &result); - - } else if (ia->ss_family == AF_INET) { - char buffer[INET_ADDRSTRLEN]; - - inet_ntop (AF_INET, &((struct sockaddr_in *)ia)->sin_addr, buffer, sizeof (buffer)); - - getaddrinfo (buffer, NULL, &hints, &result); - } - - do_jumpback = FALSE; - } else { - /* Here we got interrupted */ - result = NULL; - } - - SETUP_INTERRUPTS_FOR_TERM_TEARDOWN - - if (ia->ss_family == AF_INET6) { - memcpy (cached_addr6.s6_addr, ((struct sockaddr_in6 *)ia)->sin6_addr.s6_addr, sizeof (struct in6_addr)); - memset (&sin6, 0, sizeof (sin6)); - memcpy (sin6.sin6_addr.s6_addr, cached_addr6.s6_addr, sizeof (struct in6_addr)); - sin6.sin6_family = AF_INET6; - he = fillout_addrinfo (result, (struct sockaddr *)&sin6, NULL); - } - else if (ia->ss_family == AF_INET) { - memcpy (&(cached_addr.s_addr), &(((struct sockaddr_in *)ia)->sin_addr.s_addr), sizeof (struct in_addr)); - memset (&sin, 0, sizeof (sin)); - memcpy (&sin.sin_addr, &cached_addr, sizeof (struct in_addr)); - sin.sin_family = AF_INET; - he = fillout_addrinfo (result, (struct sockaddr *)&sin, NULL); - } - - last_time = time (NULL); - return gdm_hostent_copy (he); -} - -GdmHostent * -gdm_hostent_copy (GdmHostent *he) -{ - GdmHostent *cpy; - - if (he == NULL) - return NULL; - - cpy = g_new0 (GdmHostent, 1); - cpy->not_found = he->not_found; - cpy->hostname = g_strdup (he->hostname); - if (he->addr_count == 0) { - cpy->addr_count = 0; - cpy->addrs = NULL; - } else { - cpy->addr_count = he->addr_count; - cpy->addrs = g_new0 (struct sockaddr_storage, he->addr_count); - memcpy (cpy->addrs, he->addrs, sizeof (struct sockaddr_storage) * he->addr_count); - } - return cpy; -} - -void -gdm_hostent_free (GdmHostent *he) -{ - if (he == NULL) - return; - g_free (he->hostname); - he->hostname = NULL; - - g_free (he->addrs); - he->addrs = NULL; - he->addr_count = 0; - - g_free (he); -} - -/* Like fopen with "w" */ -FILE * -gdm_safe_fopen_w (const char *file, mode_t perm) -{ - int fd; - FILE *ret; - VE_IGNORE_EINTR (g_unlink (file)); - do { - errno = 0; - fd = open (file, O_EXCL|O_CREAT|O_TRUNC|O_WRONLY -#ifdef O_NOCTTY - |O_NOCTTY -#endif -#ifdef O_NOFOLLOW - |O_NOFOLLOW -#endif - , perm); - } while G_UNLIKELY (errno == EINTR); - if (fd < 0) - return NULL; - VE_IGNORE_EINTR (ret = fdopen (fd, "w")); - return ret; -} - -/* Like fopen with "a+" */ -FILE * -gdm_safe_fopen_ap (const char *file, mode_t perm) -{ - int fd; - FILE *ret; - - if (g_access (file, F_OK) == 0) { - do { - errno = 0; - fd = open (file, O_APPEND|O_RDWR -#ifdef O_NOCTTY - |O_NOCTTY -#endif -#ifdef O_NOFOLLOW - |O_NOFOLLOW -#endif - ); - } while G_UNLIKELY (errno == EINTR); - } else { - /* Doesn't exist, open with O_EXCL */ - do { - errno = 0; - fd = open (file, O_EXCL|O_CREAT|O_RDWR -#ifdef O_NOCTTY - |O_NOCTTY -#endif -#ifdef O_NOFOLLOW - |O_NOFOLLOW -#endif - , perm); - } while G_UNLIKELY (errno == EINTR); - } - if (fd < 0) - return NULL; - VE_IGNORE_EINTR (ret = fdopen (fd, "a+")); - return ret; -} - #ifdef RLIM_NLIMITS #define NUM_OF_LIMITS RLIM_NLIMITS #else /* ! RLIM_NLIMITS */ @@ -1954,15 +1210,6 @@ gdm_sleep_no_signal (int secs) } char * -gdm_make_filename (const char *dir, const char *name, const char *extension) -{ - char *base = g_strconcat (name, extension, NULL); - char *full = g_build_filename (dir, base, NULL); - g_free (base); - return full; -} - -char * gdm_ensure_extension (const char *name, const char *extension) { const char *p; @@ -2162,4 +1409,3 @@ gdm_read_default (gchar *key) #endif } -/* EOF */ diff --git a/daemon/misc.h b/daemon/misc.h index 56a83217..2a7500a3 100644 --- a/daemon/misc.h +++ b/daemon/misc.h @@ -25,10 +25,6 @@ #include "gdm.h" #include "display.h" -void gdm_fdprintf (int fd, const gchar *format, ...) G_GNUC_PRINTF (2, 3); -int gdm_fdgetc (int fd); -char *gdm_fdgets (int fd); - /* clear environment, but keep the i18n ones (LANG, LC_ALL, etc...), * note that this leak memory so only use before exec */ void gdm_clearenv_no_lang (void); @@ -45,47 +41,15 @@ int gdm_exec_wait (char * const *argv, gboolean no_display, * exists and has the correct permissions */ void gdm_ensure_sanity (void); -/* This is for race free forks */ -void gdm_sigchld_block_push (void); -void gdm_sigchld_block_pop (void); -void gdm_sigterm_block_push (void); -void gdm_sigterm_block_pop (void); -void gdm_sigusr2_block_push (void); -void gdm_sigusr2_block_pop (void); - pid_t gdm_fork_extra (void); void gdm_wait_for_extra (pid_t pid, int *status); -const GList * gdm_address_peek_local_list (void); -gboolean gdm_address_is_local (struct sockaddr_storage *sa); - -typedef struct { - gboolean not_found; /* hostname below set to fallback, - as gethostbyaddr/name failed */ - char *hostname; /* never a bogus dot, if - invalid/unknown, then set to the - ip address in string form */ - - struct sockaddr_storage *addrs; - int addr_count; -} GdmHostent; - -GdmHostent * gdm_gethostbyname (const char *name); - -GdmHostent *gdm_gethostbyaddr (struct sockaddr_storage *ia); -GdmHostent * gdm_hostent_copy (GdmHostent *he); -void gdm_hostent_free (GdmHostent *he); - gboolean gdm_setup_gids (const char *login, gid_t gid); void gdm_desetuid (void); gboolean gdm_test_opt (const char *cmd, const char *help, const char *option); -void gdm_close_all_descriptors (int from, int except, int except2); - -int gdm_open_dev_null (mode_t mode); - void gdm_unset_signals (void); void gdm_saveenv (void); @@ -93,11 +57,6 @@ const char * gdm_saved_getenv (const char *var); /* leaks */ void gdm_restoreenv (void); -/* like fopen with "w" but unlinks and uses O_EXCL */ -FILE * gdm_safe_fopen_w (const char *file, mode_t perm); -/* like fopen with "a+" and uses O_EXCL and O_NOFOLLOW */ -FILE * gdm_safe_fopen_ap (const char *file, mode_t perm); - /* first must get initial limits before attempting to ever reset those limits */ void gdm_get_initial_limits (void); @@ -106,28 +65,8 @@ void gdm_reset_locale (void); const char *gdm_root_user (void); -#include <setjmp.h> - -/* stolen from xdm sources */ -#if defined(X_NOT_POSIX) || defined(__EMX__) || defined(__NetBSD__) && defined(__sparc__) -#define Setjmp(e) setjmp(e) -#define Longjmp(e,v) longjmp(e,v) -#define Jmp_buf jmp_buf -#else -#define Setjmp(e) sigsetjmp(e,1) -#define Longjmp(e,v) siglongjmp(e,v) -#define Jmp_buf sigjmp_buf -#endif - -void gdm_signal_ignore (int signal); -void gdm_signal_default (int signal); - void gdm_sleep_no_signal (int secs); -/* somewhat like g_build_filename, but does somet hing like - * <dir> "/" <name> <extension> - */ -char * gdm_make_filename (const char *dir, const char *name, const char *extension); char * gdm_ensure_extension (const char *name, const char *extension); char * gdm_strip_extension (const char *name, const char *extension); @@ -142,34 +81,4 @@ const char * gdm_console_translate (const char *str); gchar * gdm_read_default (gchar *key); -#define NEVER_FAILS_seteuid(uid) \ - { int r = 0; \ - if (geteuid () != uid) \ - r = seteuid (uid); \ - if G_UNLIKELY (r != 0) \ - gdm_fail ("GDM file %s: line %d (%s): Cannot run seteuid to %d: %s", \ - __FILE__, \ - __LINE__, \ - G_GNUC_PRETTY_FUNCTION, \ - (int)uid, \ - strerror (errno)); } -#define NEVER_FAILS_setegid(gid) \ - { int r = 0; \ - if (getegid () != gid) \ - r = setegid (gid); \ - if G_UNLIKELY (r != 0) \ - gdm_fail ("GDM file %s: line %d (%s): Cannot run setegid to %d: %s", \ - __FILE__, \ - __LINE__, \ - G_GNUC_PRETTY_FUNCTION, \ - (int)gid, \ - strerror (errno)); } - -/* first goes to euid-root and then sets the egid and euid, to make sure - * this succeeds */ -#define NEVER_FAILS_root_set_euid_egid(uid,gid) \ - { NEVER_FAILS_seteuid (0); \ - NEVER_FAILS_setegid (gid); \ - if (uid != 0) { NEVER_FAILS_seteuid (uid); } } - #endif /* GDM_MISC_H */ diff --git a/daemon/server.h b/daemon/server.h index 2bcc9c15..418018ae 100644 --- a/daemon/server.h +++ b/daemon/server.h @@ -23,21 +23,6 @@ #include "display.h" -typedef struct _GdmXserver GdmXserver; - -struct _GdmXserver -{ - char *id; - char *name; - char *command; - gboolean flexible; - gboolean choosable; /* not implemented yet */ - gboolean chooser; /* instead of greeter, run chooser */ - gboolean handled; - int number; - int priority; -}; - /* These are the servstat values, also used as server * process exit codes */ #define SERVER_TIMEOUT 2 /* Server didn't start */ @@ -72,5 +57,3 @@ GdmXserver * gdm_server_resolve (GdmDisplay *disp); #endif /* GDM_SERVER_H */ - -/* EOF */ diff --git a/daemon/slave-main.c b/daemon/slave-main.c new file mode 100644 index 00000000..b5ae04fc --- /dev/null +++ b/daemon/slave-main.c @@ -0,0 +1,140 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "gdm-log.h" +#include "gdm-slave.h" + +static DBusGConnection * +get_system_bus (void) +{ + GError *error; + DBusGConnection *bus; + DBusConnection *connection; + + error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (bus == NULL) { + g_warning ("Couldn't connect to system bus: %s", + error->message); + g_error_free (error); + goto out; + } + + connection = dbus_g_connection_get_connection (bus); + dbus_connection_set_exit_on_disconnect (connection, FALSE); + + out: + return bus; +} + +static void +setup_signal_handlers (void) +{ + /* FIXME */ +} + +int +main (int argc, + char **argv) +{ + GMainLoop *loop; + GOptionContext *context; + DBusGConnection *connection; + int ret; + GdmSlave *slave; + static char *display_id = NULL; + static GOptionEntry entries [] = { + { "display-id", 0, 0, G_OPTION_ARG_STRING, &display_id, N_("Display ID"), N_("id") }, + { NULL } + }; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + setlocale (LC_ALL, ""); + + ret = 1; + + g_type_init (); + + context = g_option_context_new (_("GNOME Display Manager Slave")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + g_option_context_free (context); + + connection = get_system_bus (); + if (connection == NULL) { + goto out; + } + + gdm_log_init (); + + gdm_log_set_debug (TRUE); + + if (display_id == NULL) { + g_critical ("No display ID set"); + exit (1); + } + + setup_signal_handlers (); + + slave = gdm_slave_new (display_id); + + if (slave == NULL) { + goto out; + } + + loop = g_main_loop_new (NULL, FALSE); + + gdm_slave_start (slave); + + g_main_loop_run (loop); + + if (slave != NULL) { + g_object_unref (slave); + } + + g_main_loop_unref (loop); + + ret = 0; + + out: + + return ret; +} diff --git a/daemon/slave.c b/daemon/slave.c index 268d6af9..6ded39da 100644 --- a/daemon/slave.c +++ b/daemon/slave.c @@ -156,12 +156,6 @@ static GSList *slave_waitpids = NULL; extern gboolean gdm_first_login; -/* The slavepipe (like fifo) connection, this is the write end */ -extern int slave_fifo_pipe_fd; - -/* wait for a GO in the SOP protocol */ -extern gboolean gdm_wait_for_go; - typedef struct { pid_t pid; } GdmWaitPid; @@ -1442,7 +1436,7 @@ gdm_slave_run (GdmDisplay *display) /* Really this will only be useful for the first local server, since that's the only time this can really be on */ - while G_UNLIKELY (gdm_wait_for_go) { + while G_UNLIKELY (d->wait_for_go) { struct timeval tv; /* Wait 1 second. */ tv.tv_sec = 1; @@ -1824,7 +1818,7 @@ run_config (GdmDisplay *display, struct passwd *pwent) gdm_log_shutdown (); - gdm_close_all_descriptors (0 /* from */, slave_fifo_pipe_fd /* except */, d->slave_notify_fd /* except2 */); + gdm_close_all_descriptors (0 /* from */, d->slave_notify_fd /* except */, -1 /* except2 */); /* No error checking here - if it's messed the best response * is to ignore & try to continue */ @@ -2574,7 +2568,7 @@ gdm_slave_greeter (void) gdm_log_shutdown (); - gdm_close_all_descriptors (2 /* from */, slave_fifo_pipe_fd/* except */, d->slave_notify_fd/* except2 */); + gdm_close_all_descriptors (2 /* from */, d->slave_notify_fd /* except */, -1 /* except2 */); gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ @@ -2817,33 +2811,22 @@ gdm_slave_send (const char *str, gboolean wait_for_ack) gdm_ack_response = NULL; } - /* ensure this is sent from the actual slave with the pipe always, this is anal I know */ - if (G_LIKELY (d->slavepid == getppid ()) || G_LIKELY (d->slavepid == getpid ())) { - fd = slave_fifo_pipe_fd; - } else { - fd = -1; - } + fd = -1; - if G_UNLIKELY (fd < 0) { - /* FIXME: This is not likely to ever be used, remove - at some point. Other then slaves shouldn't be using - these functions. And if the pipe creation failed - in main daemon just abort the main daemon. */ - /* Use the fifo as a fallback only now that we have a pipe */ - fifopath = g_build_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), - ".gdmfifo", NULL); - old = geteuid (); - if (old != 0) - seteuid (0); + fifopath = g_build_filename (gdm_daemon_config_get_value_string (GDM_KEY_SERV_AUTHDIR), + ".gdmfifo", + NULL); + old = geteuid (); + if (old != 0) + seteuid (0); #ifdef O_NOFOLLOW - VE_IGNORE_EINTR (fd = open (fifopath, O_WRONLY|O_NOFOLLOW)); + VE_IGNORE_EINTR (fd = open (fifopath, O_WRONLY|O_NOFOLLOW)); #else - VE_IGNORE_EINTR (fd = open (fifopath, O_WRONLY)); + VE_IGNORE_EINTR (fd = open (fifopath, O_WRONLY)); #endif - if (old != 0) - seteuid (old); - g_free (fifopath); - } + if (old != 0) + seteuid (old); + g_free (fifopath); /* eek */ if G_UNLIKELY (fd < 0) { @@ -2854,9 +2837,7 @@ gdm_slave_send (const char *str, gboolean wait_for_ack) gdm_fdprintf (fd, "\n%s\n", str); - if G_UNLIKELY (fd != slave_fifo_pipe_fd) { - VE_IGNORE_EINTR (close (fd)); - } + VE_IGNORE_EINTR (close (fd)); #if defined (_POSIX_PRIORITY_SCHEDULING) && defined (HAVE_SCHED_YIELD) if (wait_for_ack && ! gdm_got_ack) { @@ -3059,7 +3040,7 @@ gdm_slave_chooser (void) gdm_log_shutdown (); VE_IGNORE_EINTR (close (0)); - gdm_close_all_descriptors (2 /* from */, slave_fifo_pipe_fd /* except */, d->slave_notify_fd /* except2 */); + gdm_close_all_descriptors (2 /* from */, d->slave_notify_fd /* except */, -1 /* except2 */); gdm_open_dev_null (O_RDONLY); /* open stdin - fd 0 */ gdm_open_dev_null (O_RDWR); /* open stderr - fd 2 */ @@ -3703,7 +3684,7 @@ session_child_run (struct passwd *pwent, gdm_log_shutdown (); - gdm_close_all_descriptors (3 /* from */, slave_fifo_pipe_fd /* except */, d->slave_notify_fd /* except2 */); + gdm_close_all_descriptors (3 /* from */, d->slave_notify_fd /* except */, -1 /* except2 */); gdm_log_init (); @@ -4973,7 +4954,7 @@ gdm_slave_handle_usr2_message (void) } } } else if (strcmp (&s[1], GDM_NOTIFY_GO) == 0) { - gdm_wait_for_go = FALSE; + d->wait_for_go = FALSE; } else if (strcmp (&s[1], GDM_NOTIFY_TWIDDLE_POINTER) == 0) { gdm_twiddle_pointer (d); } diff --git a/daemon/xdmcp.c b/daemon/xdmcp.c deleted file mode 100644 index 2a07efe7..00000000 --- a/daemon/xdmcp.c +++ /dev/null @@ -1,141 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * GDM - The GNOME Display Manager - * Copyright (C) 1998, 1999, 2000 Martin K. Petersen <mkp@mkp.net> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "config.h" - -#include <glib.h> -#include <glib/gi18n.h> - -#include "display.h" -#include "gdm-daemon-config.h" -#include "gdm-log.h" - -#include "gdm-xdmcp-manager.h" -#include "xdmcp.h" - -#ifdef HAVE_LIBXDMCP - -static GdmXdmcpManager *xdmcp_manager = NULL; - -gboolean -gdm_xdmcp_init (void) -{ - xdmcp_manager = gdm_xdmcp_manager_new (); - return TRUE; -} - -void -gdm_xdmcp_run (void) -{ - if (xdmcp_manager != NULL) { - gdm_xdmcp_manager_start (xdmcp_manager, NULL); - } -} - -void -gdm_xdmcp_close (void) -{ - g_object_unref (xdmcp_manager); -} - -static void -reconnect_to_parent (GdmDisplay *to) -{ - GError *error; - gchar *command_argv[10]; - const gchar *proxyreconnect = gdm_daemon_config_get_value_string (GDM_KEY_XDMCP_PROXY_RECONNECT); - - command_argv[0] = (char *)proxyreconnect; - command_argv[1] = "--display"; - command_argv[2] = to->parent_disp; - command_argv[3] = "--display-authfile"; - command_argv[4] = to->parent_auth_file; - command_argv[5] = "--to"; - command_argv[6] = to->name; - command_argv[7] = "--to-authfile"; - command_argv[8] = to->authfile; - command_argv[9] = NULL; - - gdm_debug ("XDMCP: migrating display by running " - "'%s --display %s --display-authfile %s --to %s --to-authfile %s'", - proxyreconnect, - to->parent_disp, to->parent_auth_file, - to->name, to->authfile); - - error = NULL; - if (!g_spawn_async (NULL, command_argv, NULL, 0, NULL, NULL, NULL, &error)) { - gdm_error (_("%s: Failed to run " - "'%s --display %s --display-authfile %s --to %s --to-authfile %s': %s"), - "gdm_xdmcp_migrate", - proxyreconnect, - to->parent_disp, to->parent_auth_file, - to->name, to->authfile, - error->message); - g_error_free (error); - } -} - -void -gdm_xdmcp_migrate (GdmDisplay *from, GdmDisplay *to) -{ - if (from->type != TYPE_XDMCP_PROXY || - to->type != TYPE_XDMCP_PROXY) - return; - g_free (to->parent_disp); - to->parent_disp = from->parent_disp; - from->parent_disp = NULL; - - g_free (to->parent_auth_file); - to->parent_auth_file = from->parent_auth_file; - from->parent_auth_file = NULL; - - reconnect_to_parent (to); -} - -#else /* HAVE_LIBXDMCP */ - -/* Here come some empty stubs for no XDMCP support */ -int -gdm_xdmcp_init (void) -{ - gdm_error (_("%s: No XDMCP support"), "gdm_xdmcp_init"); - return FALSE; -} - -void -gdm_xdmcp_run (void) -{ - gdm_error (_("%s: No XDMCP support"), "gdm_xdmcp_run"); -} - -void -gdm_xdmcp_close (void) -{ - gdm_error (_("%s: No XDMCP support"), "gdm_xdmcp_close"); -} - -void -gdm_xdmcp_migrate (GdmDisplay *from, GdmDisplay *to) -{ - gdm_error (_("%s: No XDMCP support"), "gdm_xdmcp_migrate"); -} - -#endif /* HAVE_LIBXDMCP */ diff --git a/daemon/xdmcp.h b/daemon/xdmcp.h deleted file mode 100644 index d9870ffd..00000000 --- a/daemon/xdmcp.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * GDM - The GNOME Display Manager - * Copyright (C) 1998, 1999, 2000 Martin K. Petersen <mkp@mkp.net> - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef GDM_XDMCP_H -#define GDM_XDMCP_H - -/* Note that these are defined as empty stubs if there is no XDMCP support */ -gboolean gdm_xdmcp_init (void); -void gdm_xdmcp_run (void); -void gdm_xdmcp_close (void); - -void gdm_xdmcp_migrate (GdmDisplay *from, GdmDisplay *to); - -#endif /* GDM_XDMCP_H */ diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 00000000..2693503c --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,12 @@ +NULL = + +dbusconfdir = $(DBUS_SYS_DIR) +dbusconf_DATA = gdm.conf + +EXTRA_DIST = \ + $(dbusconf_DATA) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in diff --git a/data/gdm.conf b/data/gdm.conf new file mode 100644 index 00000000..a3881dc7 --- /dev/null +++ b/data/gdm.conf @@ -0,0 +1,31 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- Only root can own the service --> + <policy user="root"> + <allow own="org.gnome.DisplayManager"/> + + <allow send_interface="org.gnome.DisplayManager.Manager"/> + <allow send_interface="org.gnome.DisplayManager.Display"/> + <allow send_destination="org.gnome.DisplayManager" + send_interface="org.gnome.DBus.Properties" /> + </policy> + + <!-- Allow anyone to invoke methods on the interfaces --> + <policy context="default"> + <allow send_interface="org.gnome.DisplayManager.Manager"/> + <allow send_interface="org.gnome.DisplayManager.Display"/> + <deny send_destination="org.gnome.DisplayManager" + send_interface="org.gnome.DBus.Properties" /> + </policy> + + <policy user="gdm"> + <allow send_interface="org.gnome.DisplayManager.Manager"/> + <allow send_interface="org.gnome.DisplayManager.Display"/> + <allow send_destination="org.gnome.DisplayManager" + send_interface="org.gnome.DBus.Properties" /> + </policy> + +</busconfig> diff --git a/gui/gdmconfig.c b/gui/gdmconfig.c index 90f43c9d..bfe84d5f 100644 --- a/gui/gdmconfig.c +++ b/gui/gdmconfig.c @@ -34,8 +34,6 @@ #include "gdm-common.h" -#include "server.h" - static GHashTable *int_hash = NULL; static GHashTable *bool_hash = NULL; static GHashTable *string_hash = NULL; diff --git a/gui/gdmconfig.h b/gui/gdmconfig.h index 44044931..652186f4 100644 --- a/gui/gdmconfig.h +++ b/gui/gdmconfig.h @@ -25,6 +25,33 @@ #include "glib.h" +typedef struct _GdmXserver GdmXserver; +struct _GdmXserver +{ + char *id; + char *name; + char *command; + gboolean flexible; + gboolean choosable; /* not implemented yet */ + gboolean chooser; /* instead of greeter, run chooser */ + gboolean handled; + int number; + int priority; +}; + +#define DISPLAY_REMANAGE 2 /* Restart display */ +#define DISPLAY_ABORT 4 /* Houston, we have a problem */ +#define DISPLAY_REBOOT 8 /* Rebewt */ +#define DISPLAY_HALT 16 /* Halt */ +#define DISPLAY_SUSPEND 17 /* Suspend (don't use, use the interrupt) */ +#define DISPLAY_CHOSEN 20 /* successful chooser session, + restart display */ +#define DISPLAY_RUN_CHOOSER 30 /* Run chooser */ +#define DISPLAY_XFAILED 64 /* X failed */ +#define DISPLAY_GREETERFAILED 65 /* greeter failed (crashed) */ +#define DISPLAY_RESTARTGREETER 127 /* Restart greeter */ +#define DISPLAY_RESTARTGDM 128 /* Restart GDM */ + void gdm_config_never_cache (gboolean never_cache); void gdm_config_set_comm_retries (int tries); gchar * gdm_config_get_string (const gchar *key); |