Merge branch 'private-data' into 'master'
[dconf.git] / service / dconf-service.c
blob9127472121a499232b1ac8d44fd0cb6b3d827f32
1 /*
2 * Copyright © 2012 Canonical Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 * Author: Ryan Lortie <desrt@desrt.ca>
20 #include "config.h"
22 #include "dconf-service.h"
24 #include "dconf-generated.h"
25 #include "dconf-writer.h"
26 #include "dconf-blame.h"
28 #include <glib-unix.h>
29 #include <string.h>
30 #include <fcntl.h>
32 typedef GApplicationClass DConfServiceClass;
33 typedef struct
35 GApplication parent_instance;
37 GIOExtensionPoint *extension_point;
39 DConfBlame *blame;
40 GHashTable *writers;
41 GArray *subtree_ids;
43 gboolean released;
44 } DConfService;
46 G_DEFINE_TYPE (DConfService, dconf_service, G_TYPE_APPLICATION)
48 static gboolean
49 dconf_service_signalled (gpointer user_data)
51 DConfService *service = user_data;
53 if (!service->released)
54 g_application_release (G_APPLICATION (service));
56 service->released = TRUE;
58 return G_SOURCE_REMOVE;
61 static gchar **
62 string_set_free (GHashTable *set)
64 GHashTableIter iter;
65 gchar **result;
66 gint n_items;
67 gpointer key;
68 gint i = 0;
70 n_items = g_hash_table_size (set);
71 result = g_new (gchar *, n_items + 1);
73 g_hash_table_iter_init (&iter, set);
74 while (g_hash_table_iter_next (&iter, &key, NULL))
76 result[i++] = key;
77 g_hash_table_iter_steal (&iter);
79 result[i] = NULL;
81 g_assert_cmpint (n_items, ==, i);
82 g_hash_table_unref (set);
84 return result;
87 static GHashTable *
88 string_set_new (void)
90 return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
93 static void
94 string_set_add (GHashTable *set,
95 const gchar *string)
97 g_hash_table_add (set, g_strdup (string));
100 static GType
101 dconf_service_find_writer_type (DConfService *service,
102 const gchar *object_path,
103 GHashTable **writers)
105 GIOExtension *extension;
106 const gchar *path;
107 GHashTable *table;
109 path = object_path + strlen ("/ca/desrt/dconf");
110 g_assert (*path == '/');
111 path++;
113 extension = g_io_extension_point_get_extension_by_name (service->extension_point, path);
114 g_assert (extension != NULL);
116 table = g_hash_table_lookup (service->writers, path);
117 if (table == NULL)
119 table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
120 g_hash_table_insert (service->writers, g_strdup (path), table);
123 *writers = table;
125 return g_io_extension_get_type (extension);
128 static gchar **
129 dconf_service_subtree_enumerate (GDBusConnection *connection,
130 const gchar *sender,
131 const gchar *object_path,
132 gpointer user_data)
134 DConfService *service = user_data;
135 GHashTableIter iter;
136 GHashTable *writers;
137 GType writer_type;
138 GHashTable *set;
139 gpointer key;
141 set = string_set_new ();
142 writer_type = dconf_service_find_writer_type (service, object_path, &writers);
143 g_hash_table_iter_init (&iter, writers);
144 while (g_hash_table_iter_next (&iter, &key, NULL))
145 string_set_add (set, key);
147 dconf_writer_list (writer_type, set);
149 return string_set_free (set);
152 static GDBusInterfaceInfo **
153 dconf_service_subtree_introspect (GDBusConnection *connection,
154 const gchar *sender,
155 const gchar *object_path,
156 const gchar *node,
157 gpointer user_data)
159 GDBusInterfaceInfo **result;
161 if (node == NULL)
162 return NULL;
164 result = g_new (GDBusInterfaceInfo *, 2);
165 result[0] = dconf_dbus_writer_interface_info ();
166 result[1] = NULL;
168 return result;
171 static gpointer
172 dconf_service_get_writer (DConfService *service,
173 GDBusConnection *connection,
174 const gchar *base_path,
175 const gchar *name)
177 GDBusInterfaceSkeleton *writer;
178 GHashTable *writers;
179 GType writer_type;
181 writer_type = dconf_service_find_writer_type (service, base_path, &writers);
183 writer = g_hash_table_lookup (writers, name);
185 if (writer == NULL)
187 GError *error = NULL;
188 gchar *object_path;
190 writer = dconf_writer_new (writer_type, name);
191 g_hash_table_insert (writers, g_strdup (name), writer);
192 object_path = g_strjoin ("/", base_path, name, NULL);
193 g_dbus_interface_skeleton_export (writer, connection, object_path, &error);
194 g_assert_no_error (error);
195 g_free (object_path);
198 return writer;
201 static const GDBusInterfaceVTable *
202 dconf_service_subtree_dispatch (GDBusConnection *connection,
203 const gchar *sender,
204 const gchar *object_path,
205 const gchar *interface_name,
206 const gchar *node,
207 gpointer *out_user_data,
208 gpointer user_data)
210 DConfService *service = user_data;
212 g_assert_cmpstr (interface_name, ==, "ca.desrt.dconf.Writer");
213 g_assert (node != NULL);
215 *out_user_data = dconf_service_get_writer (service, connection, object_path, node);
217 return g_dbus_interface_skeleton_get_vtable (*out_user_data);
220 static gboolean
221 dconf_service_dbus_register (GApplication *application,
222 GDBusConnection *connection,
223 const gchar *object_path,
224 GError **error)
226 const GDBusSubtreeVTable subtree_vtable = {
227 dconf_service_subtree_enumerate,
228 dconf_service_subtree_introspect,
229 dconf_service_subtree_dispatch
231 DConfService *service = DCONF_SERVICE (application);
232 GError *local_error = NULL;
233 GList *node;
234 guint id;
236 service->extension_point = g_io_extension_point_register ("dconf-backend");
237 g_io_extension_point_set_required_type (service->extension_point, DCONF_TYPE_WRITER);
238 g_io_extension_point_implement ("dconf-backend", DCONF_TYPE_WRITER, "Writer", 0);
239 g_io_extension_point_implement ("dconf-backend", DCONF_TYPE_KEYFILE_WRITER, "keyfile", 0);
240 g_io_extension_point_implement ("dconf-backend", DCONF_TYPE_SHM_WRITER, "shm", 0);
242 service->blame = dconf_blame_get ();
243 if (service->blame)
245 g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->blame),
246 connection, object_path, &local_error);
247 g_assert_no_error (local_error);
250 for (node = g_io_extension_point_get_extensions (service->extension_point); node; node = node->next)
252 gchar *path;
254 path = g_strconcat ("/ca/desrt/dconf/", g_io_extension_get_name (node->data), NULL);
255 id = g_dbus_connection_register_subtree (connection, path, &subtree_vtable,
256 G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
257 g_object_ref (service), g_object_unref, &local_error);
258 g_assert_no_error (local_error);
259 g_array_append_vals (service->subtree_ids, &id, 1);
260 g_free (path);
263 return TRUE;
266 static void
267 dconf_service_dbus_unregister (GApplication *application,
268 GDBusConnection *connection,
269 const gchar *object_path)
271 DConfService *service = DCONF_SERVICE (application);
272 gint i;
274 if (service->blame)
276 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (service->blame));
277 g_object_unref (service->blame);
278 service->blame = NULL;
281 for (i = 0; i < service->subtree_ids->len; i++)
282 g_dbus_connection_unregister_subtree (connection, g_array_index (service->subtree_ids, guint, i));
283 g_array_set_size (service->subtree_ids, 0);
286 static void
287 dconf_service_startup (GApplication *application)
289 DConfService *service = DCONF_SERVICE (application);
291 G_APPLICATION_CLASS (dconf_service_parent_class)
292 ->startup (application);
294 g_unix_signal_add (SIGTERM, dconf_service_signalled, service);
295 g_unix_signal_add (SIGINT, dconf_service_signalled, service);
296 g_unix_signal_add (SIGHUP, dconf_service_signalled, service);
298 g_application_hold (application);
301 static void
302 dconf_service_shutdown (GApplication *application)
304 G_APPLICATION_CLASS (dconf_service_parent_class)
305 ->shutdown (application);
308 static void
309 dconf_service_init (DConfService *service)
311 service->writers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
312 service->subtree_ids = g_array_new (FALSE, TRUE, sizeof (guint));
315 static void
316 dconf_service_finalize (GObject *object)
318 DConfService *service = (DConfService *) object;
320 g_assert_cmpint (service->subtree_ids->len, ==, 0);
321 g_array_free (service->subtree_ids, TRUE);
323 G_OBJECT_CLASS (dconf_service_parent_class)->finalize (object);
326 static void
327 dconf_service_class_init (GApplicationClass *class)
329 GObjectClass *object_class = G_OBJECT_CLASS (class);
331 object_class->finalize = dconf_service_finalize;
333 class->dbus_register = dconf_service_dbus_register;
334 class->dbus_unregister = dconf_service_dbus_unregister;
335 class->startup = dconf_service_startup;
336 class->shutdown = dconf_service_shutdown;
339 GApplication *
340 dconf_service_new (void)
342 return g_object_new (DCONF_TYPE_SERVICE,
343 "application-id", "ca.desrt.dconf",
344 "flags", G_APPLICATION_IS_SERVICE,
345 NULL);