summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 14:30:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 14:30:14 +0000
commitf38bddc70a6ec7c1ab26d9a8b06eed25d618ae22 (patch)
tree6437d0de9ba7fb9b0933a7c0eaa610a404d79e4e /common
parentInitial commit. (diff)
downloadgdm3-f38bddc70a6ec7c1ab26d9a8b06eed25d618ae22.tar.xz
gdm3-f38bddc70a6ec7c1ab26d9a8b06eed25d618ae22.zip
Adding upstream version 3.38.2.1.upstream/3.38.2.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'common')
-rw-r--r--common/gdb-cmd3
-rw-r--r--common/gdm-address.c551
-rw-r--r--common/gdm-address.h73
-rw-r--r--common/gdm-common-unknown-origin.h35
-rw-r--r--common/gdm-common.c976
-rw-r--r--common/gdm-common.h92
-rw-r--r--common/gdm-log.c145
-rw-r--r--common/gdm-log.h47
-rw-r--r--common/gdm-profile.c75
-rw-r--r--common/gdm-profile.h53
-rw-r--r--common/gdm-settings-backend.c165
-rw-r--r--common/gdm-settings-backend.h79
-rw-r--r--common/gdm-settings-desktop-backend.c437
-rw-r--r--common/gdm-settings-desktop-backend.h37
-rw-r--r--common/gdm-settings-direct.c258
-rw-r--r--common/gdm-settings-direct.h50
-rw-r--r--common/gdm-settings-keys.h64
-rw-r--r--common/gdm-settings-utils.c332
-rw-r--r--common/gdm-settings-utils.h61
-rw-r--r--common/gdm-settings.c242
-rw-r--r--common/gdm-settings.h57
-rw-r--r--common/meson.build43
-rw-r--r--common/test-log.c60
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;
+}