service: add support for service-db
[dconf.git] / service / dconf-writer.c
blob6df600ade97a99487cdf4b3f72a678ac3ee53eb3
1 /*
2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2012 Canonical Limited
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the licence, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Ryan Lortie <desrt@desrt.ca>
23 #include "dconf-writer.h"
25 #include "../shm/dconf-shm.h"
26 #include "dconf-gvdb-utils.h"
27 #include "dconf-generated.h"
28 #include "dconf-blame.h"
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <stdio.h>
37 struct _DConfWriterPrivate
39 gchar *filename;
40 gboolean native;
41 gchar *basepath;
42 gchar *name;
43 guint64 tag;
45 DConfChangeset *uncommited_values;
46 DConfChangeset *commited_values;
48 GQueue uncommited_changes;
49 GQueue commited_changes;
52 typedef struct
54 DConfChangeset *changeset;
55 gchar *tag;
56 } TaggedChange;
58 static void dconf_writer_iface_init (DConfDBusWriterIface *iface);
60 G_DEFINE_TYPE_WITH_CODE (DConfWriter, dconf_writer, DCONF_DBUS_TYPE_WRITER_SKELETON,
61 G_IMPLEMENT_INTERFACE (DCONF_DBUS_TYPE_WRITER, dconf_writer_iface_init))
63 static void
64 dconf_writer_real_list (GHashTable *set)
66 const gchar *name;
67 gchar *dirname;
68 GDir *dir;
70 dirname = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
71 dir = g_dir_open (dirname, 0, NULL);
73 if (!dir)
74 return;
76 while ((name = g_dir_read_name (dir)))
77 g_hash_table_add (set, g_strdup (name));
79 g_dir_close (dir);
82 static gchar *
83 dconf_writer_get_tag (DConfWriter *writer)
85 GDBusConnection *connection;
87 connection = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (writer));
89 return g_strdup_printf ("%s:%s:%" G_GUINT64_FORMAT,
90 g_dbus_connection_get_unique_name (connection),
91 writer->priv->name, writer->priv->tag++);
94 static gboolean
95 dconf_writer_real_begin (DConfWriter *writer,
96 GError **error)
98 /* If this is the first time, populate the value table with the
99 * existing values.
101 if (writer->priv->commited_values == NULL)
103 writer->priv->commited_values = dconf_gvdb_utils_read_file (writer->priv->filename, error);
105 if (!writer->priv->commited_values)
106 return FALSE;
109 writer->priv->uncommited_values = dconf_changeset_new_database (writer->priv->commited_values);
111 return TRUE;
114 static void
115 dconf_writer_real_change (DConfWriter *writer,
116 DConfChangeset *changeset,
117 const gchar *tag)
119 g_return_if_fail (writer->priv->uncommited_values != NULL);
121 dconf_changeset_change (writer->priv->uncommited_values, changeset);
123 if (tag)
125 TaggedChange *change;
127 change = g_slice_new (TaggedChange);
128 change->changeset = dconf_changeset_ref (changeset);
129 change->tag = g_strdup (tag);
131 g_queue_push_tail (&writer->priv->uncommited_changes, change);
135 static gboolean
136 dconf_writer_real_commit (DConfWriter *writer,
137 GError **error)
139 gint invalidate_fd = -1;
141 if (!writer->priv->native)
142 /* If it fails, it doesn't matter... */
143 invalidate_fd = open (writer->priv->filename, O_WRONLY);
145 if (!dconf_gvdb_utils_write_file (writer->priv->filename, writer->priv->uncommited_values, error))
146 return FALSE;
148 if (writer->priv->native)
149 dconf_shm_flag (writer->priv->name);
151 if (invalidate_fd != -1)
153 write (invalidate_fd, "\0\0\0\0\0\0\0\0", 8);
154 close (invalidate_fd);
157 if (writer->priv->commited_values)
158 dconf_changeset_unref (writer->priv->commited_values);
159 writer->priv->commited_values = writer->priv->uncommited_values;
160 writer->priv->uncommited_values = NULL;
163 GQueue empty_queue = G_QUEUE_INIT;
165 g_assert (g_queue_is_empty (&writer->priv->commited_changes));
166 writer->priv->commited_changes = writer->priv->uncommited_changes;
167 writer->priv->uncommited_changes = empty_queue;
170 return TRUE;
173 static void
174 dconf_writer_real_end (DConfWriter *writer)
176 while (!g_queue_is_empty (&writer->priv->uncommited_changes))
178 TaggedChange *change = g_queue_pop_head (&writer->priv->uncommited_changes);
179 g_free (change->tag);
180 g_slice_free (TaggedChange, change);
183 while (!g_queue_is_empty (&writer->priv->commited_changes))
185 TaggedChange *change = g_queue_pop_head (&writer->priv->commited_changes);
186 const gchar *prefix;
187 const gchar * const *paths;
189 dconf_changeset_describe (change->changeset, &prefix, &paths, NULL);
190 dconf_dbus_writer_emit_notify_signal (DCONF_DBUS_WRITER (writer), prefix, paths, change->tag);
191 dconf_changeset_unref (change->changeset);
192 g_free (change->tag);
193 g_slice_free (TaggedChange, change);
196 g_clear_pointer (&writer->priv->uncommited_values, dconf_changeset_unref);
199 gboolean
200 dconf_writer_begin (DConfWriter *writer,
201 GError **error)
203 return DCONF_WRITER_GET_CLASS (writer)->begin (writer, error);
206 void
207 dconf_writer_change (DConfWriter *writer,
208 DConfChangeset *changeset,
209 const gchar *tag)
211 DCONF_WRITER_GET_CLASS (writer)->change (writer, changeset, tag);
214 gboolean
215 dconf_writer_commit (DConfWriter *writer,
216 GError **error)
218 return DCONF_WRITER_GET_CLASS (writer)->commit (writer, error);
221 void
222 dconf_writer_end (DConfWriter *writer)
224 return DCONF_WRITER_GET_CLASS (writer)->end (writer);
227 static gboolean
228 dconf_writer_handle_init (DConfDBusWriter *dbus_writer,
229 GDBusMethodInvocation *invocation)
231 DConfWriter *writer = DCONF_WRITER (dbus_writer);
232 GError *error = NULL;
234 dconf_blame_record (invocation);
236 dconf_writer_begin (writer, &error) && dconf_writer_commit (writer, &error);
238 if (error)
240 g_dbus_method_invocation_return_gerror (invocation, error);
241 g_error_free (error);
244 else
245 g_dbus_method_invocation_return_value (invocation, NULL);
247 dconf_writer_end (writer);
249 return TRUE;
252 static gboolean
253 dconf_writer_handle_change (DConfDBusWriter *dbus_writer,
254 GDBusMethodInvocation *invocation,
255 GVariant *blob)
257 DConfWriter *writer = DCONF_WRITER (dbus_writer);
258 DConfChangeset *changeset;
259 GError *error = NULL;
260 GVariant *tmp, *args;
261 gchar *tag;
263 dconf_blame_record (invocation);
265 tmp = g_variant_new_from_data (G_VARIANT_TYPE ("a{smv}"),
266 g_variant_get_data (blob), g_variant_get_size (blob), FALSE,
267 (GDestroyNotify) g_variant_unref, g_variant_ref (blob));
268 g_variant_ref_sink (tmp);
269 args = g_variant_get_normal_form (tmp);
270 g_variant_unref (tmp);
272 changeset = dconf_changeset_deserialise (args);
273 g_variant_unref (args);
275 tag = dconf_writer_get_tag (writer);
277 if (!dconf_writer_begin (writer, &error))
278 goto out;
280 dconf_writer_change (writer, changeset, tag);
281 dconf_changeset_unref (changeset);
283 if (!dconf_writer_commit (writer, &error))
284 goto out;
286 out:
287 if (error)
289 g_dbus_method_invocation_return_gerror (invocation, error);
290 g_error_free (error);
293 else
294 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", tag));
296 g_free (tag);
298 dconf_writer_end (writer);
300 return TRUE;
303 static void
304 dconf_writer_iface_init (DConfDBusWriterIface *iface)
306 iface->handle_init = dconf_writer_handle_init;
307 iface->handle_change = dconf_writer_handle_change;
310 static void
311 dconf_writer_init (DConfWriter *writer)
313 writer->priv = G_TYPE_INSTANCE_GET_PRIVATE (writer, DCONF_TYPE_WRITER, DConfWriterPrivate);
314 writer->priv->basepath = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
315 writer->priv->native = TRUE;
318 static void
319 dconf_writer_set_property (GObject *object, guint prop_id,
320 const GValue *value, GParamSpec *pspec)
322 DConfWriter *writer = DCONF_WRITER (object);
324 g_assert_cmpint (prop_id, ==, 1);
326 g_assert (!writer->priv->name);
327 writer->priv->name = g_value_dup_string (value);
329 writer->priv->filename = g_build_filename (writer->priv->basepath, writer->priv->name, NULL);
332 static void
333 dconf_writer_class_init (DConfWriterClass *class)
335 GObjectClass *object_class = G_OBJECT_CLASS (class);
337 object_class->set_property = dconf_writer_set_property;
339 class->begin = dconf_writer_real_begin;
340 class->change = dconf_writer_real_change;
341 class->commit = dconf_writer_real_commit;
342 class->end = dconf_writer_real_end;
343 class->list = dconf_writer_real_list;
345 g_object_class_install_property (object_class, 1,
346 g_param_spec_string ("name", "name", "name", NULL,
347 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY |
348 G_PARAM_WRITABLE));
350 g_type_class_add_private (class, sizeof (DConfWriterPrivate));
353 void
354 dconf_writer_set_basepath (DConfWriter *writer,
355 const gchar *name)
357 g_free (writer->priv->basepath);
358 writer->priv->basepath = g_build_filename (g_get_user_runtime_dir (), "dconf-service", name, NULL);
359 writer->priv->native = FALSE;
362 const gchar *
363 dconf_writer_get_name (DConfWriter *writer)
365 return writer->priv->name;
368 void
369 dconf_writer_list (GType type,
370 GHashTable *set)
372 DConfWriterClass *class;
374 g_return_if_fail (g_type_is_a (type, DCONF_TYPE_WRITER));
376 class = g_type_class_ref (type);
377 class->list (set);
378 g_type_class_unref (class);
381 GDBusInterfaceSkeleton *
382 dconf_writer_new (GType type,
383 const gchar *name)
385 g_return_val_if_fail (g_type_is_a (type, DCONF_TYPE_WRITER), NULL);
387 return g_object_new (type, "name", name, NULL);