diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/gdb-cmd | 3 | ||||
-rw-r--r-- | common/gdm-address.c | 551 | ||||
-rw-r--r-- | common/gdm-address.h | 73 | ||||
-rw-r--r-- | common/gdm-common-unknown-origin.h | 35 | ||||
-rw-r--r-- | common/gdm-common.c | 976 | ||||
-rw-r--r-- | common/gdm-common.h | 92 | ||||
-rw-r--r-- | common/gdm-log.c | 145 | ||||
-rw-r--r-- | common/gdm-log.h | 47 | ||||
-rw-r--r-- | common/gdm-profile.c | 75 | ||||
-rw-r--r-- | common/gdm-profile.h | 53 | ||||
-rw-r--r-- | common/gdm-settings-backend.c | 165 | ||||
-rw-r--r-- | common/gdm-settings-backend.h | 79 | ||||
-rw-r--r-- | common/gdm-settings-desktop-backend.c | 437 | ||||
-rw-r--r-- | common/gdm-settings-desktop-backend.h | 37 | ||||
-rw-r--r-- | common/gdm-settings-direct.c | 258 | ||||
-rw-r--r-- | common/gdm-settings-direct.h | 50 | ||||
-rw-r--r-- | common/gdm-settings-keys.h | 64 | ||||
-rw-r--r-- | common/gdm-settings-utils.c | 332 | ||||
-rw-r--r-- | common/gdm-settings-utils.h | 61 | ||||
-rw-r--r-- | common/gdm-settings.c | 242 | ||||
-rw-r--r-- | common/gdm-settings.h | 57 | ||||
-rw-r--r-- | common/meson.build | 43 | ||||
-rw-r--r-- | common/test-log.c | 60 |
23 files changed, 3935 insertions, 0 deletions
diff --git a/common/gdb-cmd b/common/gdb-cmd new file mode 100644 index 0000000..aea32eb --- /dev/null +++ b/common/gdb-cmd @@ -0,0 +1,3 @@ +bt +thread apply all bt full +q diff --git a/common/gdm-address.c b/common/gdm-address.c new file mode 100644 index 0000000..a8b73e2 --- /dev/null +++ b/common/gdm-address.c @@ -0,0 +1,551 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_STROPTS_H +#include <stropts.h> +#endif +#include <string.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#include <netdb.h> +#include <sys/ioctl.h> +#include <net/if.h> + +#ifndef G_OS_WIN32 +#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. + * @size: size of sockaddr in bytes. + * + * Creates a new #GdmAddress from @sa. + * + * Return value: The new #GdmAddress + * or %NULL if @sa was invalid or the address family isn't supported. + **/ +GdmAddress * +gdm_address_new_from_sockaddr (struct sockaddr *sa, + size_t size) +{ + GdmAddress *addr; + + g_return_val_if_fail (sa != NULL, NULL); + g_return_val_if_fail (size >= sizeof (struct sockaddr), NULL); + g_return_val_if_fail (size <= sizeof (struct sockaddr_storage), NULL); + + addr = g_new0 (GdmAddress, 1); + addr->ss = g_new0 (struct sockaddr_storage, 1); + memcpy (addr->ss, sa, size); + + 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); + g_return_val_if_fail (address->ss != 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, FALSE); + g_return_val_if_fail (a->ss != NULL, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + g_return_val_if_fail (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; +} + +/* for debugging */ +static const char * +address_family_str (GdmAddress *address) +{ + const char *str; + switch (address->ss->ss_family) { + case AF_INET: + str = "inet"; + break; + case AF_INET6: + str = "inet6"; + break; + case AF_UNIX: + str = "unix"; + break; + case AF_UNSPEC: + str = "unspecified"; + break; + default: + str = "unknown"; + break; + } + return str; +} + +static void +_gdm_address_debug (GdmAddress *address, + const char *host, + const char *port) +{ + g_debug ("Address family:%d (%s) host:%s port:%s local:%d loopback:%d", + + address->ss->ss_family, + address_family_str (address) ? address_family_str (address) : "(null)", + host ? host : "(null)", + port ? port : "(null)", + gdm_address_is_local (address), + gdm_address_is_loopback (address)); +} + +void +gdm_address_debug (GdmAddress *address) +{ + char *hostname = NULL; + char *host = NULL; + char *port = NULL; + + gdm_address_get_numeric_info (address, &host, &port); + + _gdm_address_debug (address, host, port); + + g_free (hostname); + g_free (host); + g_free (port); +} + +gboolean +gdm_address_get_hostname (GdmAddress *address, + char **hostnamep) +{ + char host [NI_MAXHOST]; + int res; + gboolean ret; + + g_return_val_if_fail (address != NULL, FALSE); + g_return_val_if_fail (address->ss != NULL, FALSE); + + ret = FALSE; + + host [0] = '\0'; + res = getnameinfo ((const struct sockaddr *)address->ss, + (int) gdm_sockaddr_len (address->ss), + host, sizeof (host), + NULL, 0, + 0); + if (res == 0) { + ret = TRUE; + goto done; + } else { + const char *err_msg; + + err_msg = gai_strerror (res); + g_warning ("Unable to lookup hostname: %s", + err_msg ? err_msg : "(null)"); + _gdm_address_debug (address, NULL, NULL); + + } + + /* try numeric? */ + + done: + if (hostnamep != NULL) { + *hostnamep = g_strdup (host); + } + + return ret; +} + +gboolean +gdm_address_get_numeric_info (GdmAddress *address, + char **hostp, + char **servp) +{ + char host [NI_MAXHOST]; + char serv [NI_MAXSERV]; + int res; + gboolean ret; + + g_return_val_if_fail (address != NULL, FALSE); + g_return_val_if_fail (address->ss != NULL, FALSE); + + ret = FALSE; + + host [0] = '\0'; + serv [0] = '\0'; + res = getnameinfo ((const struct sockaddr *)address->ss, + (int) gdm_sockaddr_len (address->ss), + host, sizeof (host), + serv, sizeof (serv), + NI_NUMERICHOST | NI_NUMERICSERV); + if (res != 0) { + const char *err_msg; + + err_msg = gai_strerror (res); + g_warning ("Unable to lookup numeric info: %s", + err_msg ? err_msg : "(null)"); + _gdm_address_debug (address, NULL, NULL); + } else { + ret = TRUE; + } + + if (servp != NULL) { + if (g_str_has_prefix (serv, "::ffff:")) { + *servp = g_strdup (serv + 7); + } else { + *servp = g_strdup (serv); + } + } + if (hostp != NULL) { + if (g_str_has_prefix (host, "::ffff:")) { + *hostp = g_strdup (host + 7); + } else { + *hostp = g_strdup (host); + } + } + + return ret; +} + +gboolean +gdm_address_is_loopback (GdmAddress *address) +{ + g_return_val_if_fail (address != NULL, FALSE); + g_return_val_if_fail (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 == htonl (((struct sockaddr_in *)address->ss)->sin_addr.s_addr)); + break; + default: + break; + } + + return FALSE; +} + +static void +add_local_siocgifconf (GList **list) +{ + struct ifconf ifc; + struct ifreq ifreq; + struct ifreq *ifr; + struct ifreq *the_end; + int sock; + char buf[BUFSIZ]; + + if ((sock = socket (PF_INET, SOCK_DGRAM, 0)) < 0) { + perror ("socket"); + return; + } + + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + if (ioctl (sock, SIOCGIFCONF, (char *) &ifc) < 0) { + perror ("SIOCGIFCONF"); + close (sock); + return; + } + + /* Get IP address of each active IP network interface. */ + the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); + + for (ifr = ifc.ifc_req; ifr < the_end; ifr++) { + if (ifr->ifr_addr.sa_family == AF_INET) { + /* IP net interface */ + ifreq = *ifr; + + if (ioctl (sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) { + perror("SIOCGIFFLAGS"); + } else if (ifreq.ifr_flags & IFF_UP) { /* active interface */ + if (ioctl (sock, SIOCGIFADDR, (char *) &ifreq) < 0) { + perror("SIOCGIFADDR"); + } else { + GdmAddress *address; + address = gdm_address_new_from_sockaddr ((struct sockaddr *)&ifreq.ifr_addr, + sizeof (struct sockaddr)); + + *list = g_list_append (*list, address); + } + } + } + + /* Support for variable-length addresses. */ +#ifdef HAS_SA_LEN + ifr = (struct ifreq *) ((caddr_t) ifr + + ifr->ifr_addr.sa_len - sizeof(struct sockaddr)); +#endif + } + + close (sock); +} + +static void +add_local_addrinfo (GList **list) +{ + char hostbuf[BUFSIZ]; + struct addrinfo *result; + struct addrinfo *res; + struct addrinfo hints; + + 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_UNSPEC; + hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST; + + + g_debug ("GdmAddress: looking up hostname: %s", hostbuf); + result = NULL; + if (getaddrinfo (hostbuf, NULL, &hints, &result) != 0) { + g_debug ("%s: Could not get address from hostname!", "gdm_peek_local_address_list"); + + return; + } + + for (res = result; res != NULL; res = res->ai_next) { + GdmAddress *address; + + g_debug ("family=%d sock_type=%d protocol=%d flags=0x%x canonname=%s\n", + res->ai_family, + res->ai_socktype, + res->ai_protocol, + res->ai_flags, + res->ai_canonname ? res->ai_canonname : "(null)"); + address = gdm_address_new_from_sockaddr (res->ai_addr, res->ai_addrlen); + *list = g_list_append (*list, address); + } + + if (result != NULL) { + freeaddrinfo (result); + result = NULL; + } +} + +const GList * +gdm_address_peek_local_list (void) +{ + static GList *list = NULL; + static time_t last_time = 0; + + /* Don't check more then every 5 seconds */ + if (last_time + 5 > time (NULL)) { + return list; + } + + g_list_foreach (list, (GFunc)gdm_address_free, NULL); + g_list_free (list); + list = NULL; + + last_time = time (NULL); + + add_local_siocgifconf (&list); + add_local_addrinfo (&list); + + return 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 0000000..b1ff34a --- /dev/null +++ b/common/gdm-address.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#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 ()) +#define gdm_sockaddr_len(sa) ((sa)->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) + +typedef struct _GdmAddress GdmAddress; + +GType gdm_address_get_type (void); + +GdmAddress * gdm_address_new_from_sockaddr (struct sockaddr *sa, + size_t size); + +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); + +gboolean gdm_address_get_hostname (GdmAddress *address, + char **hostname); +gboolean 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); + + +void gdm_address_debug (GdmAddress *address); + +const GList * gdm_address_peek_local_list (void); + + +G_END_DECLS + +#endif /* __GDM_ADDRESS_H */ diff --git a/common/gdm-common-unknown-origin.h b/common/gdm-common-unknown-origin.h new file mode 100644 index 0000000..30dbcca --- /dev/null +++ b/common/gdm-common-unknown-origin.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * (c) 2000 Eazel, Inc. + * (c) 2001,2002 George Lebl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GDM_COMMON_UNKNOWN_H +#define _GDM_COMMON_UNKNOWN_H + +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <glib.h> + +G_BEGIN_DECLS + + +G_END_DECLS + +#endif /* _GDM_COMMON_UNKNOWN_H */ diff --git a/common/gdm-common.c b/common/gdm-common.c new file mode 100644 index 0000000..b8de755 --- /dev/null +++ b/common/gdm-common.c @@ -0,0 +1,976 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <locale.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <grp.h> +#include <pwd.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include "gdm-common.h" + +#include <systemd/sd-login.h> + +#define GDM_DBUS_NAME "org.gnome.DisplayManager" +#define GDM_DBUS_LOCAL_DISPLAY_FACTORY_PATH "/org/gnome/DisplayManager/LocalDisplayFactory" +#define GDM_DBUS_LOCAL_DISPLAY_FACTORY_INTERFACE "org.gnome.DisplayManager.LocalDisplayFactory" + +G_DEFINE_QUARK (gdm-common-error, gdm_common_error); + +gboolean +gdm_clear_close_on_exec_flag (int fd) +{ + int flags; + + if (fd < 0) { + return FALSE; + } + + flags = fcntl (fd, F_GETFD, 0); + + if (flags < 0) { + return FALSE; + } + + if ((flags & FD_CLOEXEC) != 0) { + int status; + + status = fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC); + + return status != -1; + } + + return TRUE; +} + +gboolean +gdm_get_pwent_for_name (const char *name, + struct passwd **pwentp) +{ + struct passwd *pwent; + + do { + errno = 0; + pwent = getpwnam (name); + } while (pwent == NULL && errno == EINTR); + + if (pwentp != NULL) { + *pwentp = pwent; + } + + return (pwent != NULL); +} + +static gboolean +gdm_get_grent_for_gid (gint gid, + struct group **grentp) +{ + struct group *grent; + + do { + errno = 0; + grent = getgrgid (gid); + } while (grent == NULL && errno == EINTR); + + if (grentp != NULL) { + *grentp = grent; + } + + return (grent != NULL); +} + +int +gdm_wait_on_and_disown_pid (int pid, + int timeout) +{ + int status; + int ret; + int num_tries; + int flags; + gboolean already_reaped; + + if (timeout > 0) { + flags = WNOHANG; + num_tries = 10 * timeout; + } else { + flags = 0; + num_tries = 0; + } + wait_again: + errno = 0; + already_reaped = FALSE; + ret = waitpid (pid, &status, flags); + if (ret < 0) { + if (errno == EINTR) { + goto wait_again; + } else if (errno == ECHILD) { + already_reaped = TRUE; + } else { + g_debug ("GdmCommon: waitpid () should not fail"); + } + } else if (ret == 0) { + num_tries--; + + if (num_tries > 0) { + g_usleep (G_USEC_PER_SEC / 10); + } else { + char *path; + char *command; + + path = g_strdup_printf ("/proc/%ld/cmdline", (long) pid); + if (g_file_get_contents (path, &command, NULL, NULL)) {; + g_warning ("GdmCommon: process (pid:%d, command '%s') isn't dying after %d seconds, now ignoring it.", + (int) pid, command, timeout); + g_free (command); + } else { + g_warning ("GdmCommon: process (pid:%d) isn't dying after %d seconds, now ignoring it.", + (int) pid, timeout); + } + g_free (path); + + return 0; + } + goto wait_again; + } + + g_debug ("GdmCommon: process (pid:%d) done (%s:%d)", + (int) pid, + already_reaped? "reaped earlier" : + WIFEXITED (status) ? "status" + : WIFSIGNALED (status) ? "signal" + : "unknown", + already_reaped? 1 : + WIFEXITED (status) ? WEXITSTATUS (status) + : WIFSIGNALED (status) ? WTERMSIG (status) + : -1); + + return status; +} + +int +gdm_wait_on_pid (int pid) +{ + return gdm_wait_on_and_disown_pid (pid, 0); +} + +int +gdm_signal_pid (int pid, + int signal) +{ + int status = -1; + + /* perhaps block sigchld */ + g_debug ("GdmCommon: sending signal %d to process %d", signal, pid); + errno = 0; + status = kill (pid, signal); + + if (status < 0) { + if (errno == ESRCH) { + g_warning ("Child process %d was already dead.", + (int)pid); + } else { + g_warning ("Couldn't kill child process %d: %s", + pid, + g_strerror (errno)); + } + } + + /* perhaps unblock sigchld */ + + return status; +} + +static gboolean +_fd_is_character_device (int fd) +{ + struct stat file_info; + + if (fstat (fd, &file_info) < 0) { + return FALSE; + } + + return S_ISCHR (file_info.st_mode); +} + +static gboolean +_read_bytes (int fd, + char *bytes, + gsize number_of_bytes, + GError **error) +{ + size_t bytes_left_to_read; + size_t total_bytes_read = 0; + gboolean premature_eof; + + bytes_left_to_read = number_of_bytes; + premature_eof = FALSE; + do { + size_t bytes_read = 0; + + errno = 0; + bytes_read = read (fd, ((guchar *) bytes) + total_bytes_read, + bytes_left_to_read); + + if (bytes_read > 0) { + total_bytes_read += bytes_read; + bytes_left_to_read -= bytes_read; + } else if (bytes_read == 0) { + premature_eof = TRUE; + break; + } else if ((errno != EINTR)) { + break; + } + } while (bytes_left_to_read > 0); + + if (premature_eof) { + g_set_error (error, + G_FILE_ERROR, + G_FILE_ERROR_FAILED, + "No data available"); + + return FALSE; + } else if (bytes_left_to_read > 0) { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** + * Pulls a requested number of bytes from /dev/urandom + * + * @param size number of bytes to pull + * @param error error if read fails + * @returns The requested number of random bytes or #NULL if fail + */ + +char * +gdm_generate_random_bytes (gsize size, + GError **error) +{ + int fd; + char *bytes; + GError *read_error; + + /* We don't use the g_rand_* glib apis because they don't document + * how much entropy they are seeded with, and it might be less + * than the passed in size. + */ + + errno = 0; + fd = open ("/dev/urandom", O_RDONLY); + + if (fd < 0) { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "%s", g_strerror (errno)); + close (fd); + return NULL; + } + + if (!_fd_is_character_device (fd)) { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (ENODEV), + _("/dev/urandom is not a character device")); + close (fd); + return NULL; + } + + bytes = g_malloc (size); + read_error = NULL; + if (!_read_bytes (fd, bytes, size, &read_error)) { + g_propagate_error (error, read_error); + g_free (bytes); + close (fd); + return NULL; + } + + close (fd); + return bytes; +} +static gboolean +create_transient_display (GDBusConnection *connection, + GError **error) +{ + GError *local_error = NULL; + GVariant *reply; + const char *value; + + reply = g_dbus_connection_call_sync (connection, + GDM_DBUS_NAME, + GDM_DBUS_LOCAL_DISPLAY_FACTORY_PATH, + GDM_DBUS_LOCAL_DISPLAY_FACTORY_INTERFACE, + "CreateTransientDisplay", + NULL, /* parameters */ + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &local_error); + if (reply == NULL) { + g_warning ("Unable to create transient display: %s", local_error->message); + g_propagate_error (error, local_error); + return FALSE; + } + + g_variant_get (reply, "(&o)", &value); + g_debug ("Started %s", value); + + g_variant_unref (reply); + return TRUE; +} + +gboolean +gdm_activate_session_by_id (GDBusConnection *connection, + const char *seat_id, + const char *session_id) +{ + GError *local_error = NULL; + GVariant *reply; + + reply = g_dbus_connection_call_sync (connection, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ActivateSessionOnSeat", + g_variant_new ("(ss)", session_id, seat_id), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &local_error); + if (reply == NULL) { + g_warning ("Unable to activate session: %s", local_error->message); + g_error_free (local_error); + return FALSE; + } + + g_variant_unref (reply); + + return TRUE; +} + +gboolean +gdm_get_login_window_session_id (const char *seat_id, + char **session_id) +{ + gboolean ret; + int res, i; + char **sessions; + char *service_id; + char *service_class; + char *state; + + res = sd_seat_get_sessions (seat_id, &sessions, NULL, NULL); + if (res < 0) { + g_debug ("Failed to determine sessions: %s", strerror (-res)); + return FALSE; + } + + if (sessions == NULL || sessions[0] == NULL) { + *session_id = NULL; + ret = FALSE; + goto out; + } + + for (i = 0; sessions[i]; i ++) { + + res = sd_session_get_class (sessions[i], &service_class); + if (res < 0) { + if (res == -ENXIO) + continue; + + g_debug ("failed to determine class of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_class, "greeter") != 0) { + free (service_class); + continue; + } + + free (service_class); + + ret = sd_session_get_state (sessions[i], &state); + if (ret < 0) { + if (res == -ENXIO) + continue; + + g_debug ("failed to determine state of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (g_strcmp0 (state, "closing") == 0) { + free (state); + continue; + } + free (state); + + res = sd_session_get_service (sessions[i], &service_id); + if (res < 0) { + if (res == -ENXIO) + continue; + + g_debug ("failed to determine service of session %s: %s", sessions[i], strerror (-res)); + ret = FALSE; + goto out; + } + + if (strcmp (service_id, "gdm-launch-environment") == 0) { + *session_id = g_strdup (sessions[i]); + ret = TRUE; + + free (service_id); + goto out; + } + + free (service_id); + } + + *session_id = NULL; + ret = FALSE; + +out: + if (sessions) { + for (i = 0; sessions[i]; i ++) { + free (sessions[i]); + } + + free (sessions); + } + + return ret; +} + +static gboolean +goto_login_session (GDBusConnection *connection, + GError **error) +{ + gboolean ret; + int res; + char *our_session; + char *session_id; + char *seat_id; + GError *local_error = NULL; + + ret = FALSE; + session_id = NULL; + seat_id = NULL; + + /* First look for any existing LoginWindow sessions on the seat. + If none are found, create a new one. */ + + /* Note that we mostly use free () here, instead of g_free () + * since the data allocated is from libsystemd-logind, which + * does not use GLib's g_malloc (). */ + + if (!gdm_find_display_session (0, getuid (), &our_session, &local_error)) { + g_propagate_prefixed_error (error, local_error, _("Could not identify the current session: ")); + + return FALSE; + } + + res = sd_session_get_seat (our_session, &seat_id); + free (our_session); + if (res < 0) { + g_debug ("failed to determine own seat: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current seat.")); + + return FALSE; + } + + res = sd_seat_can_multi_session (seat_id); + if (res < 0) { + free (seat_id); + + g_debug ("failed to determine whether seat can do multi session: %s", strerror (-res)); + g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to determine whether to switch to an existing login screen or start up a new login screen.")); + + return FALSE; + } + + if (res == 0) { + free (seat_id); + + g_set_error (error, GDM_COMMON_ERROR, 0, _("The system is unable to start up a new login screen.")); + + return FALSE; + } + + res = gdm_get_login_window_session_id (seat_id, &session_id); + if (res && session_id != NULL) { + res = gdm_activate_session_by_id (connection, seat_id, session_id); + + if (res) { + ret = TRUE; + } + } + + if (! ret && g_strcmp0 (seat_id, "seat0") == 0) { + res = create_transient_display (connection, error); + if (res) { + ret = TRUE; + } + } + + free (seat_id); + g_free (session_id); + + return ret; +} + +gboolean +gdm_goto_login_session (GError **error) +{ + GError *local_error; + GDBusConnection *connection; + + local_error = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error); + if (connection == NULL) { + g_debug ("Failed to connect to the D-Bus daemon: %s", local_error->message); + g_propagate_error (error, local_error); + return FALSE; + } + + return goto_login_session (connection, error); +} + +static void +listify_hash (const char *key, + const char *value, + GPtrArray *env) +{ + char *str; + str = g_strdup_printf ("%s=%s", key, value); + g_debug ("Gdm: script environment: %s", str); + g_ptr_array_add (env, str); +} + +GPtrArray * +gdm_get_script_environment (const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file) +{ + GPtrArray *env; + 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); + + /* 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")); + + if (username != NULL) { + 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)); + + gdm_get_pwent_for_name (username, &pwent); + 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)); + + /* Also get group name and propagate down */ + struct group *grent; + + if (gdm_get_grent_for_gid (pwent->pw_gid, &grent)) { + g_hash_table_insert (hash, g_strdup ("GROUP"), g_strdup (grent->gr_name)); + } + } + } + + if (display_hostname) { + g_hash_table_insert (hash, g_strdup ("REMOTE_HOST"), g_strdup (display_hostname)); + } + + /* Runs as root */ + if (display_x11_authority_file) { + g_hash_table_insert (hash, g_strdup ("XAUTHORITY"), g_strdup (display_x11_authority_file)); + } + + if (display_name) { + g_hash_table_insert (hash, g_strdup ("DISPLAY"), g_strdup (display_name)); + } + g_hash_table_insert (hash, g_strdup ("PATH"), g_strdup (GDM_SESSION_DEFAULT_PATH)); + g_hash_table_insert (hash, g_strdup ("RUNNING_UNDER_GDM"), g_strdup ("true")); + + 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; +} + +gboolean +gdm_run_script (const char *dir, + const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file) +{ + char *script; + char **argv; + gint status; + GError *error; + GPtrArray *env; + gboolean res; + gboolean ret; + + ret = FALSE; + + g_assert (dir != NULL); + g_assert (username != NULL); + + script = g_build_filename (dir, display_name, NULL); + g_debug ("Trying script %s", script); + if (! (g_file_test (script, G_FILE_TEST_IS_REGULAR) + && g_file_test (script, G_FILE_TEST_IS_EXECUTABLE))) { + g_debug ("script %s not found; skipping", script); + g_free (script); + script = NULL; + } + + if (script == NULL + && display_hostname != NULL + && display_hostname[0] != '\0') { + script = g_build_filename (dir, display_hostname, NULL); + g_debug ("Trying script %s", script); + if (! (g_file_test (script, G_FILE_TEST_IS_REGULAR) + && g_file_test (script, G_FILE_TEST_IS_EXECUTABLE))) { + g_debug ("script %s not found; skipping", script); + g_free (script); + script = NULL; + } + } + + if (script == NULL) { + script = g_build_filename (dir, "Default", NULL); + g_debug ("Trying script %s", script); + if (! (g_file_test (script, G_FILE_TEST_IS_REGULAR) + && g_file_test (script, G_FILE_TEST_IS_EXECUTABLE))) { + g_debug ("script %s not found; skipping", script); + g_free (script); + script = NULL; + } + } + + if (script == NULL) { + g_debug ("no script found"); + return TRUE; + } + + 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 = gdm_get_script_environment (username, + display_name, + display_hostname, + display_x11_authority_file); + + 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); + g_strfreev (argv); + + if (! res) { + g_warning ("Unable to run script: %s", error->message); + g_error_free (error); + } + + if (WIFEXITED (status)) { + g_debug ("Process exit status: %d", WEXITSTATUS (status)); + ret = WEXITSTATUS (status) == 0; + } + + out: + g_free (script); + + return ret; +} + +gboolean +gdm_shell_var_is_valid_char (gchar c, gboolean first) +{ + return (!first && g_ascii_isdigit (c)) || + c == '_' || + g_ascii_isalpha (c); +} + +/* This expands a string somewhat similar to how a shell would do it + if it was enclosed inside double quotes. It handles variable + expansion like $FOO and ${FOO}, single-char escapes using \, and + non-escaped # at the begining of a word is taken as a comment and ignored */ +char * +gdm_shell_expand (const char *str, + GdmExpandVarFunc expand_var_func, + gpointer user_data) +{ + GString *s = g_string_new(""); + const gchar *p, *start; + gchar c; + gboolean at_new_word; + + p = str; + at_new_word = TRUE; + while (*p) { + c = *p; + if (c == '\\') { + p++; + c = *p; + if (c != '\0') { + p++; + switch (c) { + case '\\': + g_string_append_c (s, '\\'); + break; + case '$': + g_string_append_c (s, '$'); + break; + case '#': + g_string_append_c (s, '#'); + break; + default: + g_string_append_c (s, '\\'); + g_string_append_c (s, c); + break; + } + } + } else if (c == '#' && at_new_word) { + break; + } else if (c == '$') { + gboolean brackets = FALSE; + p++; + if (*p == '{') { + brackets = TRUE; + p++; + } + start = p; + while (*p != '\0' && + gdm_shell_var_is_valid_char (*p, p == start)) + p++; + if (p == start || (brackets && *p != '}')) { + /* Invalid variable, use as-is */ + g_string_append_c (s, '$'); + if (brackets) + g_string_append_c (s, '{'); + g_string_append_len (s, start, p - start); + } else { + gchar *expanded; + gchar *var = g_strndup (start, p - start); + if (brackets && *p == '}') + p++; + + expanded = expand_var_func (var, user_data); + if (expanded) + g_string_append (s, expanded); + g_free (var); + g_free (expanded); + } + } else { + p++; + g_string_append_c (s, c); + at_new_word = g_ascii_isspace (c); + } + } + return g_string_free (s, FALSE); +} + +static gboolean +_systemd_session_is_graphical (const char *session_id) +{ + const gchar * const graphical_session_types[] = { "wayland", "x11", "mir", NULL }; + int saved_errno; + g_autofree gchar *type = NULL; + + saved_errno = sd_session_get_type (session_id, &type); + if (saved_errno < 0) { + g_warning ("Couldn't get type for session '%s': %s", + session_id, + g_strerror (-saved_errno)); + return FALSE; + } + + if (!g_strv_contains (graphical_session_types, type)) { + g_debug ("Session '%s' is not a graphical session (type: '%s')", + session_id, + type); + return FALSE; + } + + return TRUE; +} + +static gboolean +_systemd_session_is_active (const char *session_id) +{ + const gchar * const active_states[] = { "active", "online", NULL }; + int saved_errno; + g_autofree gchar *state = NULL; + + /* + * display sessions can be 'closing' if they are logged out but some + * processes are lingering; we shouldn't consider these (this is + * checking for a race condition since we specified + * GDM_SYSTEMD_SESSION_REQUIRE_ONLINE) + */ + saved_errno = sd_session_get_state (session_id, &state); + if (saved_errno < 0) { + g_warning ("Couldn't get state for session '%s': %s", + session_id, + g_strerror (-saved_errno)); + return FALSE; + } + + if (!g_strv_contains (active_states, state)) { + g_debug ("Session '%s' is not active or online", session_id); + return FALSE; + } + + return TRUE; +} + +gboolean +gdm_find_display_session (GPid pid, + const uid_t uid, + char **out_session_id, + GError **error) +{ + char *local_session_id = NULL; + g_auto(GStrv) sessions = NULL; + int n_sessions; + int res; + + g_return_val_if_fail (out_session_id != NULL, FALSE); + + /* First try to look up the session using the pid. We need this + * at least for the greeter, because it currently runs multiple + * sessions under the same user. + * See also commit 2b52d8933c8ab38e7ee83318da2363d00d8c5581 + * which added an explicit dbus-run-session for all but seat0. + */ + res = sd_pid_get_session (pid, &local_session_id); + if (res >= 0) { + g_debug ("GdmCommon: Found session %s for PID %d, using", local_session_id, pid); + + *out_session_id = g_strdup (local_session_id); + free (local_session_id); + + return TRUE; + } else { + if (res != -ENODATA) + g_warning ("GdmCommon: Failed to retrieve session information for pid %d: %s", + pid, strerror (-res)); + } + + g_debug ("Finding a graphical session for user %d", uid); + + n_sessions = sd_uid_get_sessions (uid, + GDM_SYSTEMD_SESSION_REQUIRE_ONLINE, + &sessions); + + if (n_sessions < 0) { + g_set_error (error, + GDM_COMMON_ERROR, + 0, + "Failed to get sessions for user %d", + uid); + return FALSE; + } + + for (int i = 0; i < n_sessions; ++i) { + g_debug ("Considering session '%s'", sessions[i]); + + if (!_systemd_session_is_graphical (sessions[i])) + continue; + + if (!_systemd_session_is_active (sessions[i])) + continue; + + /* + * We get the sessions from newest to oldest, so take the last + * one we find that's good + */ + local_session_id = sessions[i]; + } + + if (local_session_id == NULL) { + g_set_error (error, + GDM_COMMON_ERROR, + 0, + "Could not find a graphical session for user %d", + uid); + return FALSE; + } + + *out_session_id = g_strdup (local_session_id); + + return TRUE; +} diff --git a/common/gdm-common.h b/common/gdm-common.h new file mode 100644 index 0000000..001b70c --- /dev/null +++ b/common/gdm-common.h @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GDM_COMMON_H +#define _GDM_COMMON_H + +#include <glib-unix.h> +#include <gio/gio.h> + +#include <pwd.h> +#include <errno.h> + +#define REGISTER_SESSION_TIMEOUT 10 + +#define VE_IGNORE_EINTR(expr) \ + do { \ + errno = 0; \ + expr; \ + } while G_UNLIKELY (errno == EINTR); + +#define GDM_SYSTEMD_SESSION_REQUIRE_ONLINE 0 + +GQuark gdm_common_error_quark (void); +#define GDM_COMMON_ERROR gdm_common_error_quark() + +typedef char * (*GdmExpandVarFunc) (const char *var, + gpointer user_data); + +G_BEGIN_DECLS + +int gdm_wait_on_pid (int pid); +int gdm_wait_on_and_disown_pid (int pid, + int timeout); +int gdm_signal_pid (int pid, + int signal); + +gboolean gdm_find_display_session (GPid pid, + const uid_t uid, + char **out_session_id, + GError **error); + +gboolean gdm_get_pwent_for_name (const char *name, + struct passwd **pwentp); + +gboolean gdm_clear_close_on_exec_flag (int fd); + +char *gdm_generate_random_bytes (gsize size, + GError **error); +gboolean gdm_get_login_window_session_id (const char *seat_id, + char **session_id); +gboolean gdm_goto_login_session (GError **error); + +GPtrArray *gdm_get_script_environment (const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file); +gboolean gdm_run_script (const char *dir, + const char *username, + const char *display_name, + const char *display_hostname, + const char *display_x11_authority_file); + +gboolean gdm_shell_var_is_valid_char (char c, + gboolean first); +char * gdm_shell_expand (const char *str, + GdmExpandVarFunc expand_func, + gpointer user_data); + +gboolean gdm_activate_session_by_id (GDBusConnection *connection, + const char *seat_id, + const char *session_id); + +G_END_DECLS + +#endif /* _GDM_COMMON_H */ diff --git a/common/gdm-log.c b/common/gdm-log.c new file mode 100644 index 0000000..ddbf853 --- /dev/null +++ b/common/gdm-log.c @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Authors: William Jon McCann <mccann@jhu.edu> + * + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> +#include <time.h> +#include <unistd.h> + +#include <syslog.h> +#include <systemd/sd-daemon.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "gdm-log.h" + +static gboolean initialized = FALSE; +static gboolean is_sd_booted = FALSE; +static gboolean debug_enabled = FALSE; + +static gint +get_syslog_priority_from_log_level (GLogLevelFlags log_level) +{ + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_FLAG_FATAL: + return LOG_EMERG; + case G_LOG_LEVEL_ERROR: + /* fatal error - a critical error, in the syslog world */ + return LOG_CRIT; + case G_LOG_LEVEL_CRITICAL: + /* critical warning - an error, in the syslog world */ + return LOG_ERR; + case G_LOG_LEVEL_WARNING: + case G_LOG_LEVEL_MESSAGE: + return LOG_NOTICE; + case G_LOG_LEVEL_INFO: + return LOG_INFO; + case G_LOG_LEVEL_DEBUG: + default: + return LOG_DEBUG; + } +} + +static void +gdm_log_default_handler (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer unused_data) +{ + int priority; + + gdm_log_init (); + + if ((log_level & G_LOG_LEVEL_MASK) == G_LOG_LEVEL_DEBUG && + !debug_enabled) { + return; + } + + /* Process the message prefix and priority */ + priority = get_syslog_priority_from_log_level (log_level); + + if (is_sd_booted) { + fprintf (stderr, + "<%d>%s%s%s\n", + priority, + log_domain != NULL? log_domain : "", + log_domain != NULL? ": " : "", + message); + fflush (stderr); + } else { + syslog (priority, + "%s%s%s\n", + log_domain != NULL? log_domain : "", + log_domain != NULL? ": " : "", + message); + } +} + +void +gdm_log_toggle_debug (void) +{ + gdm_log_set_debug (!debug_enabled); +} + +void +gdm_log_set_debug (gboolean debug) +{ + g_assert (initialized); + if (debug_enabled == debug) { + return; + } + + if (debug) { + debug_enabled = debug; + g_debug ("Enabling debugging"); + } else { + g_debug ("Disabling debugging"); + debug_enabled = debug; + } +} + +void +gdm_log_init (void) +{ + if (initialized) + return; + + initialized = TRUE; + + g_log_set_default_handler (gdm_log_default_handler, NULL); +} + +void +gdm_log_shutdown (void) +{ + if (!initialized) + return; + if (!is_sd_booted) + closelog (); + initialized = FALSE; +} + diff --git a/common/gdm-log.h b/common/gdm-log.h new file mode 100644 index 0000000..db6ed87 --- /dev/null +++ b/common/gdm-log.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Authors: William Jon McCann <mccann@jhu.edu> + * + */ + +#ifndef __GDM_LOG_H +#define __GDM_LOG_H + +#include <stdarg.h> +#include <glib.h> + +G_BEGIN_DECLS + +void gdm_log_set_debug (gboolean debug); +void gdm_log_toggle_debug (void); +void gdm_log_init (void); +void gdm_log_shutdown (void); + +/* compatibility */ +#define gdm_fail g_critical +#define gdm_error g_warning +#define gdm_info g_message +#define gdm_debug g_debug + +#define gdm_assert g_assert +#define gdm_assert_not_reached g_assert_not_reached + +G_END_DECLS + +#endif /* __GDM_LOG_H */ diff --git a/common/gdm-profile.c b/common/gdm-profile.c new file mode 100644 index 0000000..1a37d32 --- /dev/null +++ b/common/gdm-profile.c @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Authors: William Jon McCann <mccann@jhu.edu> + * + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> +#include <time.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "gdm-profile.h" + +void +_gdm_profile_log (const char *func, + const char *note, + const char *format, + ...) +{ + va_list args; + char *str; + char *formatted; + const char *prgname; + + if (format == NULL) { + formatted = g_strdup (""); + } else { + va_start (args, format); + formatted = g_strdup_vprintf (format, args); + va_end (args); + } + + prgname = g_get_prgname(); + + if (func != NULL) { + str = g_strdup_printf ("MARK: %s %s: %s %s", + prgname ? prgname : "(null)", + func, + note ? note : "", + formatted); + } else { + str = g_strdup_printf ("MARK: %s: %s %s", + prgname ? prgname : "(null)", + note ? note : "", + formatted); + } + + g_free (formatted); + + g_access (str, F_OK); + g_free (str); +} diff --git a/common/gdm-profile.h b/common/gdm-profile.h new file mode 100644 index 0000000..1f2781b --- /dev/null +++ b/common/gdm-profile.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Authors: William Jon McCann <mccann@jhu.edu> + * + */ + +#ifndef __GDM_PROFILE_H +#define __GDM_PROFILE_H + +#include <glib.h> + +G_BEGIN_DECLS + +#ifdef ENABLE_PROFILING +#ifdef G_HAVE_ISO_VARARGS +#define gdm_profile_start(...) _gdm_profile_log (G_STRFUNC, "start", __VA_ARGS__) +#define gdm_profile_end(...) _gdm_profile_log (G_STRFUNC, "end", __VA_ARGS__) +#define gdm_profile_msg(...) _gdm_profile_log (NULL, NULL, __VA_ARGS__) +#elif defined(G_HAVE_GNUC_VARARGS) +#define gdm_profile_start(format...) _gdm_profile_log (G_STRFUNC, "start", format) +#define gdm_profile_end(format...) _gdm_profile_log (G_STRFUNC, "end", format) +#define gdm_profile_msg(format...) _gdm_profile_log (NULL, NULL, format) +#endif +#else +#define gdm_profile_start(...) +#define gdm_profile_end(...) +#define gdm_profile_msg(...) +#endif + +void _gdm_profile_log (const char *func, + const char *note, + const char *format, + ...) G_GNUC_PRINTF (3, 4); + +G_END_DECLS + +#endif /* __GDM_PROFILE_H */ diff --git a/common/gdm-settings-backend.c b/common/gdm-settings-backend.c new file mode 100644 index 0000000..5ad3022 --- /dev/null +++ b/common/gdm-settings-backend.c @@ -0,0 +1,165 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib-object.h> + +#include "gdm-settings-backend.h" + +enum { + VALUE_CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void gdm_settings_backend_class_init (GdmSettingsBackendClass *klass); +static void gdm_settings_backend_init (GdmSettingsBackend *settings_backend); +static void gdm_settings_backend_finalize (GObject *object); + +G_DEFINE_ABSTRACT_TYPE (GdmSettingsBackend, gdm_settings_backend, G_TYPE_OBJECT) + +GQuark +gdm_settings_backend_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_settings_backend_error"); + } + + return ret; +} + +static gboolean +gdm_settings_backend_real_get_value (GdmSettingsBackend *settings_backend, + const char *key, + char **value, + GError **error) +{ + g_return_val_if_fail (GDM_IS_SETTINGS_BACKEND (settings_backend), FALSE); + + return FALSE; +} + +static gboolean +gdm_settings_backend_real_set_value (GdmSettingsBackend *settings_backend, + const char *key, + const char *value, + GError **error) +{ + g_return_val_if_fail (GDM_IS_SETTINGS_BACKEND (settings_backend), FALSE); + + return FALSE; +} + +gboolean +gdm_settings_backend_get_value (GdmSettingsBackend *settings_backend, + const char *key, + char **value, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (GDM_IS_SETTINGS_BACKEND (settings_backend), FALSE); + + g_object_ref (settings_backend); + ret = GDM_SETTINGS_BACKEND_GET_CLASS (settings_backend)->get_value (settings_backend, key, value, error); + g_object_unref (settings_backend); + + return ret; +} + +gboolean +gdm_settings_backend_set_value (GdmSettingsBackend *settings_backend, + const char *key, + const char *value, + GError **error) +{ + gboolean ret; + + g_return_val_if_fail (GDM_IS_SETTINGS_BACKEND (settings_backend), FALSE); + + g_object_ref (settings_backend); + ret = GDM_SETTINGS_BACKEND_GET_CLASS (settings_backend)->set_value (settings_backend, key, value, error); + g_object_unref (settings_backend); + + return ret; +} + +void +gdm_settings_backend_value_changed (GdmSettingsBackend *settings_backend, + const char *key, + const char *old_value, + const char *new_value) +{ + g_return_if_fail (GDM_IS_SETTINGS_BACKEND (settings_backend)); + + g_signal_emit (settings_backend, signals[VALUE_CHANGED], 0, key, old_value, new_value); +} + +static void +gdm_settings_backend_class_init (GdmSettingsBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdm_settings_backend_finalize; + + klass->get_value = gdm_settings_backend_real_get_value; + klass->set_value = gdm_settings_backend_real_set_value; + + signals [VALUE_CHANGED] = + g_signal_new ("value-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmSettingsBackendClass, value_changed), + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); +} + +static void +gdm_settings_backend_init (GdmSettingsBackend *settings_backend) +{ +} + +static void +gdm_settings_backend_finalize (GObject *object) +{ + g_return_if_fail (GDM_IS_SETTINGS_BACKEND (object)); + + G_OBJECT_CLASS (gdm_settings_backend_parent_class)->finalize (object); +} diff --git a/common/gdm-settings-backend.h b/common/gdm-settings-backend.h new file mode 100644 index 0000000..3499690 --- /dev/null +++ b/common/gdm-settings-backend.h @@ -0,0 +1,79 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef __GDM_SETTINGS_BACKEND_H +#define __GDM_SETTINGS_BACKEND_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_SETTINGS_BACKEND (gdm_settings_backend_get_type ()) +G_DECLARE_DERIVABLE_TYPE (GdmSettingsBackend, gdm_settings_backend, GDM, SETTINGS_BACKEND, GObject) + +struct _GdmSettingsBackendClass +{ + GObjectClass parent_class; + + /* methods */ + gboolean (*get_value) (GdmSettingsBackend *settings_backend, + const char *key, + char **value, + GError **error); + gboolean (*set_value) (GdmSettingsBackend *settings_backend, + const char *key, + const char *value, + GError **error); + + /* signals */ + void (* value_changed) (GdmSettingsBackend *settings_backend, + const char *key, + const char *old_value, + const char **new_value); +}; + +typedef enum +{ + GDM_SETTINGS_BACKEND_ERROR_GENERAL, + GDM_SETTINGS_BACKEND_ERROR_KEY_NOT_FOUND +} GdmSettingsBackendError; + +#define GDM_SETTINGS_BACKEND_ERROR gdm_settings_backend_error_quark () + +GQuark gdm_settings_backend_error_quark (void); + +gboolean gdm_settings_backend_get_value (GdmSettingsBackend *settings_backend, + const char *key, + char **value, + GError **error); +gboolean gdm_settings_backend_set_value (GdmSettingsBackend *settings_backend, + const char *key, + const char *value, + GError **error); + +void gdm_settings_backend_value_changed (GdmSettingsBackend *settings_backend, + const char *key, + const char *old_value, + const char *new_value); + +G_END_DECLS + +#endif /* __GDM_SETTINGS_BACKEND_H */ diff --git a/common/gdm-settings-desktop-backend.c b/common/gdm-settings-desktop-backend.c new file mode 100644 index 0000000..e899fe5 --- /dev/null +++ b/common/gdm-settings-desktop-backend.c @@ -0,0 +1,437 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "gdm-settings-desktop-backend.h" + +struct _GdmSettingsDesktopBackend +{ + GdmSettingsBackend parent; + + char *filename; + GKeyFile *key_file; + gboolean dirty; + guint save_id; +}; + +enum { + PROP_0, + PROP_FILENAME, +}; + +static void gdm_settings_desktop_backend_class_init (GdmSettingsDesktopBackendClass *klass); +static void gdm_settings_desktop_backend_init (GdmSettingsDesktopBackend *settings_desktop_backend); +static void gdm_settings_desktop_backend_finalize (GObject *object); + +G_DEFINE_TYPE (GdmSettingsDesktopBackend, gdm_settings_desktop_backend, GDM_TYPE_SETTINGS_BACKEND) + +static void +_gdm_settings_desktop_backend_set_file_name (GdmSettingsDesktopBackend *backend, + const char *filename) +{ + gboolean res; + GError *error; + char *contents; + + g_free (backend->filename); + backend->filename = g_strdup (filename); + + backend->key_file = g_key_file_new (); + + error = NULL; + res = g_key_file_load_from_file (backend->key_file, + backend->filename, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, + &error); + if (! res) { + g_warning ("Unable to load file '%s': %s", backend->filename, error->message); + } + + contents = g_key_file_to_data (backend->key_file, NULL, NULL); + + if (contents != NULL) { + g_debug ("GdmSettings: %s is:\n%s\n", backend->filename, contents); + g_free (contents); + } + +} + +static void +gdm_settings_desktop_backend_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmSettingsDesktopBackend *self; + + self = GDM_SETTINGS_DESKTOP_BACKEND (object); + + switch (prop_id) { + case PROP_FILENAME: + _gdm_settings_desktop_backend_set_file_name (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_settings_desktop_backend_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmSettingsDesktopBackend *self; + + self = GDM_SETTINGS_DESKTOP_BACKEND (object); + + switch (prop_id) { + case PROP_FILENAME: + g_value_set_string (value, self->filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +parse_key_string (const char *keystring, + char **group, + char **key, + char **locale, + char **value) +{ + char **split1; + char **split2; + char *g; + char *k; + char *l; + char *v; + char *tmp1; + char *tmp2; + gboolean ret; + + g_return_val_if_fail (keystring != NULL, FALSE); + + ret = FALSE; + g = k = v = l = NULL; + split1 = split2 = NULL; + + if (group != NULL) { + *group = g; + } + if (key != NULL) { + *key = k; + } + if (locale != NULL) { + *locale = l; + } + if (value != NULL) { + *value = v; + } + + /*g_debug ("Attempting to parse key string: %s", keystring);*/ + + split1 = g_strsplit (keystring, "/", 2); + if (split1 == NULL + || split1 [0] == NULL + || split1 [1] == NULL + || split1 [0][0] == '\0' + || split1 [1][0] == '\0') { + g_warning ("GdmSettingsDesktopBackend: invalid key: %s", keystring); + goto out; + } + + g = split1 [0]; + + split2 = g_strsplit (split1 [1], "=", 2); + if (split2 == NULL) { + k = split1 [1]; + } else { + k = split2 [0]; + v = split2 [1]; + } + + /* trim off the locale */ + tmp1 = strchr (k, '['); + tmp2 = strchr (k, ']'); + if (tmp1 != NULL && tmp2 != NULL && tmp2 > tmp1) { + l = g_strndup (tmp1 + 1, tmp2 - tmp1 - 1); + *tmp1 = '\0'; + } + + ret = TRUE; + + if (group != NULL) { + *group = g_strdup (g); + } + if (key != NULL) { + *key = g_strdup (k); + } + if (locale != NULL) { + *locale = g_strdup (l); + } + if (value != NULL) { + *value = g_strdup (v); + } + out: + + g_strfreev (split1); + g_strfreev (split2); + + return ret; +} + +static gboolean +gdm_settings_desktop_backend_get_value (GdmSettingsBackend *backend, + const char *key, + char **value, + GError **error) +{ + GError *local_error; + char *val; + char *g; + char *k; + char *l; + gboolean ret; + + g_return_val_if_fail (GDM_IS_SETTINGS_BACKEND (backend), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + ret = FALSE; + + if (value != NULL) { + *value = NULL; + } + + val = g = k = l = NULL; + /*GDM_SETTINGS_BACKEND_CLASS (gdm_settings_desktop_backend_parent_class)->get_value (display);*/ + if (! parse_key_string (key, &g, &k, &l, NULL)) { + g_set_error (error, GDM_SETTINGS_BACKEND_ERROR, GDM_SETTINGS_BACKEND_ERROR_KEY_NOT_FOUND, "Key not found"); + goto out; + } + + /*g_debug ("Getting key: %s %s %s", g, k, l);*/ + local_error = NULL; + val = g_key_file_get_value (GDM_SETTINGS_DESKTOP_BACKEND (backend)->key_file, + g, + k, + &local_error); + if (local_error != NULL) { + g_error_free (local_error); + g_set_error (error, GDM_SETTINGS_BACKEND_ERROR, GDM_SETTINGS_BACKEND_ERROR_KEY_NOT_FOUND, "Key not found"); + goto out; + } + + if (value != NULL) { + *value = g_strdup (val); + } + ret = TRUE; + out: + g_free (val); + g_free (g); + g_free (k); + g_free (l); + + return ret; +} + +static void +save_settings (GdmSettingsDesktopBackend *backend) +{ + GError *local_error; + char *contents; + gsize length; + + if (! backend->dirty) { + return; + } + + g_debug ("Saving settings to %s", backend->filename); + + local_error = NULL; + contents = g_key_file_to_data (backend->key_file, &length, &local_error); + if (local_error != NULL) { + g_warning ("Unable to save settings: %s", local_error->message); + g_error_free (local_error); + return; + } + + local_error = NULL; + g_file_set_contents (backend->filename, + contents, + length, + &local_error); + if (local_error != NULL) { + g_warning ("Unable to save settings: %s", local_error->message); + g_error_free (local_error); + g_free (contents); + return; + } + + g_free (contents); + + backend->dirty = FALSE; +} + +static gboolean +save_settings_timer (GdmSettingsDesktopBackend *backend) +{ + save_settings (backend); + backend->save_id = 0; + return FALSE; +} + +static void +queue_save (GdmSettingsDesktopBackend *backend) +{ + if (! backend->dirty) { + return; + } + + if (backend->save_id != 0) { + /* already pending */ + return; + } + + backend->save_id = g_timeout_add_seconds (5, (GSourceFunc)save_settings_timer, backend); +} + +static gboolean +gdm_settings_desktop_backend_set_value (GdmSettingsBackend *backend, + const char *key, + const char *value, + GError **error) +{ + GError *local_error; + char *old_val; + char *g; + char *k; + char *l; + + g_return_val_if_fail (GDM_IS_SETTINGS_BACKEND (backend), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + /*GDM_SETTINGS_BACKEND_CLASS (gdm_settings_desktop_backend_parent_class)->get_value (display);*/ + if (! parse_key_string (key, &g, &k, &l, NULL)) { + g_set_error (error, GDM_SETTINGS_BACKEND_ERROR, GDM_SETTINGS_BACKEND_ERROR_KEY_NOT_FOUND, "Key not found"); + return FALSE; + } + + local_error = NULL; + old_val = g_key_file_get_value (GDM_SETTINGS_DESKTOP_BACKEND (backend)->key_file, + g, + k, + &local_error); + if (local_error != NULL) { + g_error_free (local_error); + } + + /*g_debug ("Setting key: %s %s %s", g, k, l);*/ + local_error = NULL; + g_key_file_set_value (GDM_SETTINGS_DESKTOP_BACKEND (backend)->key_file, + g, + k, + value); + + GDM_SETTINGS_DESKTOP_BACKEND (backend)->dirty = TRUE; + queue_save (GDM_SETTINGS_DESKTOP_BACKEND (backend)); + + gdm_settings_backend_value_changed (backend, key, old_val, value); + + g_free (old_val); + + return TRUE; +} + +static void +gdm_settings_desktop_backend_class_init (GdmSettingsDesktopBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdmSettingsBackendClass *backend_class = GDM_SETTINGS_BACKEND_CLASS (klass); + + object_class->get_property = gdm_settings_desktop_backend_get_property; + object_class->set_property = gdm_settings_desktop_backend_set_property; + object_class->finalize = gdm_settings_desktop_backend_finalize; + + backend_class->get_value = gdm_settings_desktop_backend_get_value; + backend_class->set_value = gdm_settings_desktop_backend_set_value; + + g_object_class_install_property (object_class, + PROP_FILENAME, + g_param_spec_string ("filename", + "File Name", + "The name of the configuration file", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +static void +gdm_settings_desktop_backend_init (GdmSettingsDesktopBackend *backend) +{ +} + +static void +gdm_settings_desktop_backend_finalize (GObject *object) +{ + GdmSettingsDesktopBackend *backend; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_SETTINGS_DESKTOP_BACKEND (object)); + + backend = GDM_SETTINGS_DESKTOP_BACKEND (object); + + save_settings (backend); + g_key_file_free (backend->key_file); + g_free (backend->filename); + + G_OBJECT_CLASS (gdm_settings_desktop_backend_parent_class)->finalize (object); +} + +GdmSettingsBackend * +gdm_settings_desktop_backend_new (const char* filename) +{ + GObject *object; + + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + return NULL; + + object = g_object_new (GDM_TYPE_SETTINGS_DESKTOP_BACKEND, + "filename", filename, + NULL); + return GDM_SETTINGS_BACKEND (object); +} diff --git a/common/gdm-settings-desktop-backend.h b/common/gdm-settings-desktop-backend.h new file mode 100644 index 0000000..419fe22 --- /dev/null +++ b/common/gdm-settings-desktop-backend.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef __GDM_SETTINGS_DESKTOP_BACKEND_H +#define __GDM_SETTINGS_DESKTOP_BACKEND_H + +#include <glib-object.h> +#include "gdm-settings-backend.h" + +G_BEGIN_DECLS + +#define GDM_TYPE_SETTINGS_DESKTOP_BACKEND (gdm_settings_desktop_backend_get_type ()) +G_DECLARE_FINAL_TYPE (GdmSettingsDesktopBackend, gdm_settings_desktop_backend, GDM, SETTINGS_DESKTOP_BACKEND, GdmSettingsBackend) + +GdmSettingsBackend *gdm_settings_desktop_backend_new (const char* filename); + +G_END_DECLS + +#endif /* __GDM_SETTINGS_DESKTOP_BACKEND_H */ diff --git a/common/gdm-settings-direct.c b/common/gdm-settings-direct.c new file mode 100644 index 0000000..ddb3190 --- /dev/null +++ b/common/gdm-settings-direct.c @@ -0,0 +1,258 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "gdm-settings.h" +#include "gdm-settings-utils.h" +#include "gdm-settings-direct.h" + +static GHashTable *schemas; +static GdmSettings *settings_object; + +static GdmSettingsEntry * +get_entry_for_key (const char *key) +{ + GdmSettingsEntry *entry; + + entry = g_hash_table_lookup (schemas, key); + + return entry; +} + +static void +assert_signature (GdmSettingsEntry *entry, + const char *signature) +{ + const char *sig; + + sig = gdm_settings_entry_get_signature (entry); + + g_assert (sig != NULL); + g_assert (signature != NULL); + g_assert (strcmp (signature, sig) == 0); +} + +static gboolean +get_value (const char *key, + char **value) +{ + GError *error; + char *str; + gboolean res; + + error = NULL; + res = gdm_settings_get_value (settings_object, key, &str, &error); + if (! res) { + if (error != NULL) { + g_error_free (error); + } else { + } + + return FALSE; + } + + if (value != NULL) { + *value = g_strdup (str); + } + + g_free (str); + + return TRUE; +} + +gboolean +gdm_settings_direct_get_int (const char *key, + int *value) +{ + GdmSettingsEntry *entry; + gboolean ret; + gboolean res; + char *str; + + g_return_val_if_fail (key != NULL, FALSE); + + entry = get_entry_for_key (key); + g_assert (entry != NULL); + + assert_signature (entry, "i"); + + ret = FALSE; + + res = get_value (key, &str); + + if (! res) { + /* use the default */ + str = g_strdup (gdm_settings_entry_get_default_value (entry)); + } + + ret = gdm_settings_parse_value_as_integer (str, value); + + g_free (str); + + return ret; +} + +gboolean +gdm_settings_direct_get_uint (const char *key, + uint *value) +{ + gboolean ret; + int intvalue; + + ret = FALSE; + ret = gdm_settings_direct_get_int (key, &intvalue); + + if (intvalue >= 0) + *value = intvalue; + else + ret = FALSE; + + return ret; +} + +gboolean +gdm_settings_direct_get_boolean (const char *key, + gboolean *value) +{ + GdmSettingsEntry *entry; + gboolean ret; + gboolean res; + char *str; + + g_return_val_if_fail (key != NULL, FALSE); + + entry = get_entry_for_key (key); + g_assert (entry != NULL); + + assert_signature (entry, "b"); + + ret = FALSE; + + res = get_value (key, &str); + + if (! res) { + /* use the default */ + str = g_strdup (gdm_settings_entry_get_default_value (entry)); + } + + ret = gdm_settings_parse_value_as_boolean (str, value); + + g_free (str); + + return ret; +} + +gboolean +gdm_settings_direct_get_string (const char *key, + char **value) +{ + GdmSettingsEntry *entry; + gboolean ret; + gboolean res; + char *str; + + g_return_val_if_fail (key != NULL, FALSE); + + entry = get_entry_for_key (key); + g_assert (entry != NULL); + + assert_signature (entry, "s"); + + ret = TRUE; + + res = get_value (key, &str); + + if (! res) { + /* use the default */ + str = g_strdup (gdm_settings_entry_get_default_value (entry)); + } + + if (value != NULL) { + *value = g_strdup (str); + } + + g_free (str); + + return ret; +} + +gboolean +gdm_settings_direct_set (const char *key, + GValue *value) +{ + return TRUE; +} + +static void +hashify_list (GdmSettingsEntry *entry, + gpointer data) +{ + g_hash_table_insert (schemas, g_strdup (gdm_settings_entry_get_key (entry)), entry); +} + +gboolean +gdm_settings_direct_init (GdmSettings *settings, + const char *file, + const char *root) +{ + GSList *list; + + g_return_val_if_fail (file != NULL, FALSE); + g_return_val_if_fail (root != NULL, FALSE); + + g_debug ("Settings Direct Init"); + if (schemas != NULL) { + g_hash_table_unref (schemas); + schemas = NULL; + } + + if (! gdm_settings_parse_schemas (file, root, &list)) { + g_warning ("Unable to parse schemas"); + return FALSE; + } + + schemas = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)gdm_settings_entry_free); + g_slist_foreach (list, (GFunc)hashify_list, NULL); + + settings_object = settings; + + return TRUE; +} + +void +gdm_settings_direct_shutdown (void) +{ + +} diff --git a/common/gdm-settings-direct.h b/common/gdm-settings-direct.h new file mode 100644 index 0000000..156489c --- /dev/null +++ b/common/gdm-settings-direct.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef __GDM_SETTINGS_DIRECT_H +#define __GDM_SETTINGS_DIRECT_H + +#include <glib-object.h> +#include "gdm-settings.h" + +G_BEGIN_DECLS + +gboolean gdm_settings_direct_init (GdmSettings *settings, + const char *schemas_file, + const char *root); +void gdm_settings_direct_shutdown (void); + +gboolean gdm_settings_direct_get (const char *key, + GValue *value); +gboolean gdm_settings_direct_set (const char *key, + GValue *value); +gboolean gdm_settings_direct_get_int (const char *key, + int *value); +gboolean gdm_settings_direct_get_uint (const char *key, + uint *value); +gboolean gdm_settings_direct_get_boolean (const char *key, + gboolean *value); +gboolean gdm_settings_direct_get_string (const char *key, + char **value); + +G_END_DECLS + +#endif /* __GDM_SETTINGS_DIRECT_H */ diff --git a/common/gdm-settings-keys.h b/common/gdm-settings-keys.h new file mode 100644 index 0000000..f0059b5 --- /dev/null +++ b/common/gdm-settings-keys.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GDM_SETTINGS_KEYS_H +#define _GDM_SETTINGS_KEYS_H + +#include <glib.h> + +G_BEGIN_DECLS + +#define GDM_KEY_USER "daemon/User" +#define GDM_KEY_GROUP "daemon/Group" +#define GDM_KEY_AUTO_LOGIN_ENABLE "daemon/AutomaticLoginEnable" +#define GDM_KEY_AUTO_LOGIN_USER "daemon/AutomaticLogin" +#define GDM_KEY_TIMED_LOGIN_ENABLE "daemon/TimedLoginEnable" +#define GDM_KEY_TIMED_LOGIN_USER "daemon/TimedLogin" +#define GDM_KEY_TIMED_LOGIN_DELAY "daemon/TimedLoginDelay" +#define GDM_KEY_INITIAL_SETUP_ENABLE "daemon/InitialSetupEnable" +#define GDM_KEY_WAYLAND_ENABLE "daemon/WaylandEnable" + +#define GDM_KEY_DEBUG "debug/Enable" + +#define GDM_KEY_INCLUDE "greeter/Include" +#define GDM_KEY_EXCLUDE "greeter/Exclude" +#define GDM_KEY_INCLUDE_ALL "greeter/IncludeAll" + +#define GDM_KEY_DISALLOW_TCP "security/DisallowTCP" +#define GDM_KEY_ALLOW_REMOTE_AUTOLOGIN "security/AllowRemoteAutoLogin" + +#define GDM_KEY_XDMCP_ENABLE "xdmcp/Enable" +#define GDM_KEY_SHOW_LOCAL_GREETER "xdmcp/ShowLocalGreeter" +#define GDM_KEY_MAX_PENDING "xdmcp/MaxPending" +#define GDM_KEY_MAX_SESSIONS "xdmcp/MaxSessions" +#define GDM_KEY_MAX_WAIT "xdmcp/MaxWait" +#define GDM_KEY_DISPLAYS_PER_HOST "xdmcp/DisplaysPerHost" +#define GDM_KEY_UDP_PORT "xdmcp/Port" +#define GDM_KEY_INDIRECT "xdmcp/HonorIndirect" +#define GDM_KEY_MAX_WAIT_INDIRECT "xdmcp/MaxWaitIndirect" +#define GDM_KEY_PING_INTERVAL "xdmcp/PingIntervalSeconds" +#define GDM_KEY_WILLING "xdmcp/Willing" + +#define GDM_KEY_MULTICAST "chooser/Multicast" +#define GDM_KEY_MULTICAST_ADDR "chooser/MulticastAddr" + +G_END_DECLS + +#endif /* _GDM_SETTINGS_KEYS_H */ diff --git a/common/gdm-settings-utils.c b/common/gdm-settings-utils.c new file mode 100644 index 0000000..4e63a56 --- /dev/null +++ b/common/gdm-settings-utils.c @@ -0,0 +1,332 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#include "gdm-settings-utils.h" + +struct _GdmSettingsEntry +{ + char *key; + char *signature; + char *default_value; + char *value; +}; + +GdmSettingsEntry * +gdm_settings_entry_new (void) +{ + GdmSettingsEntry *entry = NULL; + + entry = g_new0 (GdmSettingsEntry, 1); + entry->key = NULL; + entry->signature = NULL; + entry->value = NULL; + entry->default_value = NULL; + + return entry; +} + +const char * +gdm_settings_entry_get_key (GdmSettingsEntry *entry) +{ + return entry->key; +} + +const char * +gdm_settings_entry_get_signature (GdmSettingsEntry *entry) +{ + return entry->signature; +} + +const char * +gdm_settings_entry_get_default_value (GdmSettingsEntry *entry) +{ + return entry->default_value; +} + +const char * +gdm_settings_entry_get_value (GdmSettingsEntry *entry) +{ + return entry->value; +} + +void +gdm_settings_entry_set_value (GdmSettingsEntry *entry, + const char *value) +{ + g_free (entry->value); + entry->value = g_strdup (value); +} + +void +gdm_settings_entry_free (GdmSettingsEntry *entry) +{ + g_free (entry->key); + g_free (entry->signature); + g_free (entry->default_value); + g_free (entry->value); + g_free (entry); +} + +typedef struct { + GSList *list; + GdmSettingsEntry *entry; + gboolean in_key; + gboolean in_signature; + gboolean in_default; +} ParserInfo; + +static void +start_element_cb (GMarkupParseContext *ctx, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + gpointer user_data, + GError **error) +{ + ParserInfo *info; + + info = (ParserInfo *) user_data; + + /*g_debug ("parsing start: '%s'", element_name);*/ + + if (strcmp (element_name, "schema") == 0) { + info->entry = gdm_settings_entry_new (); + } else if (strcmp (element_name, "key") == 0) { + info->in_key = TRUE; + } else if (strcmp (element_name, "signature") == 0) { + info->in_signature = TRUE; + } else if (strcmp (element_name, "default") == 0) { + info->in_default = TRUE; + } +} + +static void +add_schema_entry (ParserInfo *info) +{ + /*g_debug ("Inserting entry %s", info->entry->key);*/ + + info->list = g_slist_prepend (info->list, info->entry); +} + +static void +end_element_cb (GMarkupParseContext *ctx, + const char *element_name, + gpointer user_data, + GError **error) +{ + ParserInfo *info; + + info = (ParserInfo *) user_data; + + /*g_debug ("parsing end: '%s'", element_name);*/ + + if (strcmp (element_name, "schema") == 0) { + add_schema_entry (info); + } else if (strcmp (element_name, "key") == 0) { + info->in_key = FALSE; + } else if (strcmp (element_name, "signature") == 0) { + info->in_signature = FALSE; + } else if (strcmp (element_name, "default") == 0) { + info->in_default = FALSE; + } +} + +static void +text_cb (GMarkupParseContext *ctx, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ParserInfo *info; + char *t; + + info = (ParserInfo *) user_data; + + t = g_strndup (text, text_len); + + if (info->in_key) { + info->entry->key = g_strdup (t); + } else if (info->in_signature) { + info->entry->signature = g_strdup (t); + } else if (info->in_default) { + info->entry->default_value = g_strdup (t); + } + + g_free (t); + +} + +static void +error_cb (GMarkupParseContext *ctx, + GError *error, + gpointer user_data) +{ +} + +static GMarkupParser parser = { + start_element_cb, + end_element_cb, + text_cb, + NULL, + error_cb +}; + +gboolean +gdm_settings_parse_schemas (const char *file, + const char *root, + GSList **schemas) +{ + GMarkupParseContext *ctx; + ParserInfo *info; + char *contents; + gsize len; + GError *error; + gboolean res; + + g_return_val_if_fail (file != NULL, FALSE); + g_return_val_if_fail (root != NULL, FALSE); + + g_assert (schemas != NULL); + + contents = NULL; + error = NULL; + res = g_file_get_contents (file, &contents, &len, &error); + if (! res) { + g_warning ("Unable to read schemas file: %s", error->message); + g_error_free (error); + return FALSE; + } + + info = g_new0 (ParserInfo, 1); + ctx = g_markup_parse_context_new (&parser, 0, info, NULL); + g_markup_parse_context_parse (ctx, contents, len, NULL); + + *schemas = info->list; + + g_markup_parse_context_free (ctx); + g_free (info); + g_free (contents); + + return TRUE; +} + +char * +gdm_settings_parse_double_as_value (gdouble doubleval) +{ + char result[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_dtostr (result, sizeof (result), doubleval); + + return g_strdup (result); +} + +char * +gdm_settings_parse_integer_as_value (int intval) + +{ + return g_strdup_printf ("%d", intval); +} + +char * +gdm_settings_parse_boolean_as_value (gboolean boolval) +{ + if (boolval) { + return g_strdup ("true"); + } else { + return g_strdup ("false"); + } +} + + +/* adapted from GKeyFile */ +gboolean +gdm_settings_parse_value_as_boolean (const char *value, + gboolean *bool) +{ + if (g_ascii_strcasecmp (value, "true") == 0 || strcmp (value, "1") == 0) { + *bool = TRUE; + return TRUE; + } else if (g_ascii_strcasecmp (value, "false") == 0 || strcmp (value, "0") == 0) { + *bool = FALSE; + return TRUE; + } else { + return FALSE; + } +} + +gboolean +gdm_settings_parse_value_as_integer (const char *value, + int *intval) +{ + char *end_of_valid_int; + glong long_value; + gint int_value; + + errno = 0; + long_value = strtol (value, &end_of_valid_int, 10); + + if (*value == '\0' || *end_of_valid_int != '\0') { + return FALSE; + } + + int_value = long_value; + if (int_value != long_value || errno == ERANGE) { + return FALSE; + } + + *intval = int_value; + + return TRUE; +} + +gboolean +gdm_settings_parse_value_as_double (const char *value, + gdouble *doubleval) +{ + char *end_of_valid_d; + gdouble double_value = 0; + + double_value = g_ascii_strtod (value, &end_of_valid_d); + + if (*end_of_valid_d != '\0' || end_of_valid_d == value) { + return FALSE; + } + + *doubleval = double_value; + return TRUE; +} diff --git a/common/gdm-settings-utils.h b/common/gdm-settings-utils.h new file mode 100644 index 0000000..7a05446 --- /dev/null +++ b/common/gdm-settings-utils.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef __GDM_SETTINGS_UTILS_H +#define __GDM_SETTINGS_UTILS_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct _GdmSettingsEntry GdmSettingsEntry; + +GdmSettingsEntry * gdm_settings_entry_new (void); +GdmSettingsEntry * gdm_settings_entry_copy (GdmSettingsEntry *entry); +void gdm_settings_entry_free (GdmSettingsEntry *entry); + +const char * gdm_settings_entry_get_key (GdmSettingsEntry *entry); +const char * gdm_settings_entry_get_signature (GdmSettingsEntry *entry); +const char * gdm_settings_entry_get_default_value (GdmSettingsEntry *entry); +const char * gdm_settings_entry_get_value (GdmSettingsEntry *entry); + +void gdm_settings_entry_set_value (GdmSettingsEntry *entry, + const char *value); + +gboolean gdm_settings_parse_schemas (const char *file, + const char *root, + GSList **list); + +gboolean gdm_settings_parse_value_as_boolean (const char *value, + gboolean *bool); +gboolean gdm_settings_parse_value_as_integer (const char *value, + int *intval); +gboolean gdm_settings_parse_value_as_double (const char *value, + gdouble *doubleval); + +char * gdm_settings_parse_boolean_as_value (gboolean boolval); +char * gdm_settings_parse_integer_as_value (int intval); +char * gdm_settings_parse_double_as_value (gdouble doubleval); + + +G_END_DECLS + +#endif /* __GDM_SETTINGS_UTILS_H */ diff --git a/common/gdm-settings.c b/common/gdm-settings.c new file mode 100644 index 0000000..e6f46ec --- /dev/null +++ b/common/gdm-settings.c @@ -0,0 +1,242 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> +#include <gio/gio.h> + +#include "gdm-settings.h" + +#include "gdm-settings-desktop-backend.h" + +struct _GdmSettings +{ + GObject parent; + + GList *backends; +}; + +enum { + VALUE_CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void gdm_settings_class_init (GdmSettingsClass *klass); +static void gdm_settings_init (GdmSettings *settings); +static void gdm_settings_finalize (GObject *object); + +static gpointer settings_object = NULL; + +G_DEFINE_TYPE (GdmSettings, gdm_settings, G_TYPE_OBJECT) + +GQuark +gdm_settings_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("gdm_settings_error"); + } + + return ret; +} + +gboolean +gdm_settings_get_value (GdmSettings *settings, + const char *key, + char **value, + GError **error) +{ + GError *local_error; + gboolean res; + GList *l; + + g_return_val_if_fail (GDM_IS_SETTINGS (settings), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + local_error = NULL; + + for (l = settings->backends; l; l = g_list_next (l)) { + GdmSettingsBackend *backend = l->data; + + if (local_error) { + g_error_free (local_error); + local_error = NULL; + } + + res = gdm_settings_backend_get_value (backend, + key, + value, + &local_error); + if (res) + break; + } + if (! res) { + g_propagate_error (error, local_error); + } + + return res; +} + +gboolean +gdm_settings_set_value (GdmSettings *settings, + const char *key, + const char *value, + GError **error) +{ + GError *local_error; + gboolean res; + GList *l; + + g_return_val_if_fail (GDM_IS_SETTINGS (settings), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + g_debug ("Setting value %s", key); + + local_error = NULL; + + for (l = settings->backends; l; l = g_list_next (l)) { + GdmSettingsBackend *backend = l->data; + + if (local_error) { + g_error_free (local_error); + local_error = NULL; + } + + res = gdm_settings_backend_set_value (backend, + key, + value, + &local_error); + if (res) + break; + } + + if (! res) { + g_propagate_error (error, local_error); + } + + return res; +} + +static void +gdm_settings_class_init (GdmSettingsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdm_settings_finalize; + + signals [VALUE_CHANGED] = + g_signal_new ("value-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); +} + +static void +backend_value_changed (GdmSettingsBackend *backend, + const char *key, + const char *old_value, + const char *new_value, + GdmSettings *settings) +{ + g_debug ("Emitting value-changed %s %s %s", key, old_value, new_value); + + /* proxy it to internal listeners */ + g_signal_emit (settings, signals [VALUE_CHANGED], 0, key, old_value, new_value); +} + +static void +gdm_settings_init (GdmSettings *settings) +{ + GList *l; + GdmSettingsBackend *backend; + + backend = gdm_settings_desktop_backend_new (GDM_CUSTOM_CONF); + if (backend) + settings->backends = g_list_prepend (NULL, backend); + + backend = gdm_settings_desktop_backend_new (GDM_RUNTIME_CONF); + if (backend) + settings->backends = g_list_prepend (settings->backends, backend); + + for (l = settings->backends; l; l = g_list_next (l)) { + backend = l->data; + + g_signal_connect (backend, + "value-changed", + G_CALLBACK (backend_value_changed), + settings); + } +} + +static void +gdm_settings_finalize (GObject *object) +{ + GdmSettings *settings; + + g_return_if_fail (object != NULL); + g_return_if_fail (GDM_IS_SETTINGS (object)); + + settings = GDM_SETTINGS (object); + + g_return_if_fail (settings != NULL); + + g_list_foreach (settings->backends, (GFunc) g_object_unref, NULL); + g_list_free (settings->backends); + settings->backends = NULL; + + settings_object = NULL; + + G_OBJECT_CLASS (gdm_settings_parent_class)->finalize (object); +} + +GdmSettings * +gdm_settings_new (void) +{ + if (settings_object != NULL) { + g_object_ref (settings_object); + } else { + settings_object = g_object_new (GDM_TYPE_SETTINGS, NULL); + } + + return GDM_SETTINGS (settings_object); +} diff --git a/common/gdm-settings.h b/common/gdm-settings.h new file mode 100644 index 0000000..786868a --- /dev/null +++ b/common/gdm-settings.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef __GDM_SETTINGS_H +#define __GDM_SETTINGS_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define GDM_TYPE_SETTINGS (gdm_settings_get_type ()) +G_DECLARE_FINAL_TYPE (GdmSettings, gdm_settings, GDM, SETTINGS, GObject) + +typedef enum +{ + GDM_SETTINGS_ERROR_GENERAL, + GDM_SETTINGS_ERROR_KEY_NOT_FOUND +} GdmSettingsError; + +#define GDM_SETTINGS_ERROR gdm_settings_error_quark () + +GQuark gdm_settings_error_quark (void); + +GdmSettings * gdm_settings_new (void); + +/* exported */ + +gboolean gdm_settings_get_value (GdmSettings *settings, + const char *key, + char **value, + GError **error); +gboolean gdm_settings_set_value (GdmSettings *settings, + const char *key, + const char *value, + GError **error); + +G_END_DECLS + +#endif /* __GDM_SETTINGS_H */ diff --git a/common/meson.build b/common/meson.build new file mode 100644 index 0000000..074dd92 --- /dev/null +++ b/common/meson.build @@ -0,0 +1,43 @@ +libgdmcommon_src = files( + 'gdm-address.c', + 'gdm-common.c', + 'gdm-log.c', + 'gdm-profile.c', + 'gdm-settings-backend.c', + 'gdm-settings-desktop-backend.c', + 'gdm-settings-direct.c', + 'gdm-settings-utils.c', + 'gdm-settings.c', +) + +libgdmcommon_deps = [ + libsystemd_dep, + gobject_dep, + gio_dep, + gio_unix_dep, +] + +if libselinux_dep.found() + libgdmcommon_deps += libselinux_dep +endif + +libgdmcommon_lib = static_library('gdmcommon', + libgdmcommon_src, + dependencies: libgdmcommon_deps, + include_directories: config_h_dir, +) + +libgdmcommon_dep = declare_dependency( + link_with: libgdmcommon_lib, + dependencies: libgdmcommon_deps, + include_directories: include_directories('.'), +) + +install_data('gdb-cmd') + +# test-log exectuable +test_log = executable('test-log', + 'test-log.c', + dependencies: libgdmcommon_dep, + include_directories: config_h_dir, +) diff --git a/common/test-log.c b/common/test-log.c new file mode 100644 index 0000000..e653c24 --- /dev/null +++ b/common/test-log.c @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <pwd.h> +#include <string.h> +#include <errno.h> + +#include <locale.h> + +#include <glib.h> + +#include "gdm-common.h" +#include "gdm-log.h" + +static void +test_log (void) +{ + gdm_log_init (); + + g_debug ("Test debug 1"); + gdm_log_set_debug (TRUE); + g_debug ("Test debug 2"); + + g_message ("Test message"); + g_warning ("Test warning"); + g_critical ("Test critical"); + g_error ("Test error"); +} + +int +main (int argc, char **argv) +{ + + test_log (); + + return 0; +} |