Declare libdconf_service as a dependency
[dconf.git] / service / dconf-writer.c
blob5fb346701a1cc00146969b272bc74e590d31ded6
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, see <http://www.gnu.org/licenses/>.
18 * Author: Ryan Lortie <desrt@desrt.ca>
21 #include "config.h"
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;
44 gboolean need_write;
46 DConfChangeset *uncommited_values;
47 DConfChangeset *commited_values;
49 GQueue uncommited_changes;
50 GQueue commited_changes;
53 typedef struct
55 DConfChangeset *changeset;
56 gchar *tag;
57 } TaggedChange;
59 static void dconf_writer_iface_init (DConfDBusWriterIface *iface);
61 G_DEFINE_TYPE_WITH_CODE (DConfWriter, dconf_writer, DCONF_DBUS_TYPE_WRITER_SKELETON,
62 G_ADD_PRIVATE (DConfWriter)
63 G_IMPLEMENT_INTERFACE (DCONF_DBUS_TYPE_WRITER, dconf_writer_iface_init))
65 static void
66 dconf_writer_real_list (GHashTable *set)
68 const gchar *name;
69 gchar *dirname;
70 GDir *dir;
72 dirname = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
73 dir = g_dir_open (dirname, 0, NULL);
75 if (!dir)
76 return;
78 while ((name = g_dir_read_name (dir)))
80 if (!strchr (name, '.'))
81 g_hash_table_add (set, g_strdup (name));
84 g_dir_close (dir);
87 static gchar *
88 dconf_writer_get_tag (DConfWriter *writer)
90 GDBusConnection *connection;
92 connection = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (writer));
94 return g_strdup_printf ("%s:%s:%" G_GUINT64_FORMAT,
95 g_dbus_connection_get_unique_name (connection),
96 writer->priv->name, writer->priv->tag++);
99 static gboolean
100 dconf_writer_real_begin (DConfWriter *writer,
101 GError **error)
103 /* If this is the first time, populate the value table with the
104 * existing values.
106 if (writer->priv->commited_values == NULL)
108 gboolean missing;
110 writer->priv->commited_values = dconf_gvdb_utils_read_and_back_up_file (writer->priv->filename, &missing, error);
112 if (!writer->priv->commited_values)
113 return FALSE;
115 /* If this is a non-native writer and the file doesn't exist, we
116 * will need to write it on commit so that the client can open it.
118 if (missing && !writer->priv->native)
119 writer->priv->need_write = TRUE;
122 writer->priv->uncommited_values = dconf_changeset_new_database (writer->priv->commited_values);
124 return TRUE;
127 static void
128 dconf_writer_real_change (DConfWriter *writer,
129 DConfChangeset *changeset,
130 const gchar *tag)
132 g_return_if_fail (writer->priv->uncommited_values != NULL);
134 dconf_changeset_change (writer->priv->uncommited_values, changeset);
136 if (tag)
138 TaggedChange *change;
140 change = g_slice_new (TaggedChange);
141 change->changeset = dconf_changeset_ref (changeset);
142 change->tag = g_strdup (tag);
144 g_queue_push_tail (&writer->priv->uncommited_changes, change);
147 writer->priv->need_write = TRUE;
150 static gboolean
151 dconf_writer_real_commit (DConfWriter *writer,
152 GError **error)
154 gint invalidate_fd = -1;
156 if (!writer->priv->need_write)
158 g_assert (g_queue_is_empty (&writer->priv->uncommited_changes));
159 g_assert (g_queue_is_empty (&writer->priv->commited_changes));
160 dconf_changeset_unref (writer->priv->uncommited_values);
161 writer->priv->uncommited_values = NULL;
163 return TRUE;
166 if (!writer->priv->native)
167 /* If it fails, it doesn't matter... */
168 invalidate_fd = open (writer->priv->filename, O_WRONLY);
170 if (!dconf_gvdb_utils_write_file (writer->priv->filename, writer->priv->uncommited_values, error))
171 return FALSE;
173 if (writer->priv->native)
174 dconf_shm_flag (writer->priv->name);
176 if (invalidate_fd != -1)
178 write (invalidate_fd, "\0\0\0\0\0\0\0\0", 8);
179 close (invalidate_fd);
182 if (writer->priv->commited_values)
183 dconf_changeset_unref (writer->priv->commited_values);
184 writer->priv->commited_values = writer->priv->uncommited_values;
185 writer->priv->uncommited_values = NULL;
188 GQueue empty_queue = G_QUEUE_INIT;
190 g_assert (g_queue_is_empty (&writer->priv->commited_changes));
191 writer->priv->commited_changes = writer->priv->uncommited_changes;
192 writer->priv->uncommited_changes = empty_queue;
195 return TRUE;
198 static void
199 dconf_writer_real_end (DConfWriter *writer)
201 while (!g_queue_is_empty (&writer->priv->uncommited_changes))
203 TaggedChange *change = g_queue_pop_head (&writer->priv->uncommited_changes);
204 dconf_changeset_unref (change->changeset);
205 g_free (change->tag);
206 g_slice_free (TaggedChange, change);
209 while (!g_queue_is_empty (&writer->priv->commited_changes))
211 TaggedChange *change = g_queue_pop_head (&writer->priv->commited_changes);
212 const gchar *prefix;
213 const gchar * const *paths;
214 guint n;
216 n = dconf_changeset_describe (change->changeset, &prefix, &paths, NULL);
217 g_assert (n != 0);
218 dconf_dbus_writer_emit_notify_signal (DCONF_DBUS_WRITER (writer), prefix, paths, change->tag);
219 dconf_changeset_unref (change->changeset);
220 g_free (change->tag);
221 g_slice_free (TaggedChange, change);
224 g_clear_pointer (&writer->priv->uncommited_values, dconf_changeset_unref);
227 static gboolean
228 dconf_writer_begin (DConfWriter *writer,
229 GError **error)
231 return DCONF_WRITER_GET_CLASS (writer)->begin (writer, error);
234 static void
235 dconf_writer_change (DConfWriter *writer,
236 DConfChangeset *changeset,
237 const gchar *tag)
239 DCONF_WRITER_GET_CLASS (writer)->change (writer, changeset, tag);
242 static gboolean
243 dconf_writer_commit (DConfWriter *writer,
244 GError **error)
246 return DCONF_WRITER_GET_CLASS (writer)->commit (writer, error);
249 static void
250 dconf_writer_end (DConfWriter *writer)
252 return DCONF_WRITER_GET_CLASS (writer)->end (writer);
255 static gboolean
256 dconf_writer_handle_init (DConfDBusWriter *dbus_writer,
257 GDBusMethodInvocation *invocation)
259 DConfWriter *writer = DCONF_WRITER (dbus_writer);
260 GError *error = NULL;
262 dconf_blame_record (invocation);
264 if (dconf_writer_begin (writer, &error))
265 dconf_writer_commit (writer, &error);
267 if (error)
269 g_dbus_method_invocation_return_gerror (invocation, error);
270 g_error_free (error);
273 else
274 g_dbus_method_invocation_return_value (invocation, NULL);
276 dconf_writer_end (writer);
278 return TRUE;
281 static gboolean
282 dconf_writer_handle_change (DConfDBusWriter *dbus_writer,
283 GDBusMethodInvocation *invocation,
284 GVariant *blob)
286 DConfWriter *writer = DCONF_WRITER (dbus_writer);
287 DConfChangeset *changeset;
288 GError *error = NULL;
289 GVariant *tmp, *args;
290 gchar *tag;
292 dconf_blame_record (invocation);
294 tmp = g_variant_new_from_data (G_VARIANT_TYPE ("a{smv}"),
295 g_variant_get_data (blob), g_variant_get_size (blob), FALSE,
296 (GDestroyNotify) g_variant_unref, g_variant_ref (blob));
297 g_variant_ref_sink (tmp);
298 args = g_variant_get_normal_form (tmp);
299 g_variant_unref (tmp);
301 changeset = dconf_changeset_deserialise (args);
302 g_variant_unref (args);
304 tag = dconf_writer_get_tag (writer);
306 /* Don't bother with empty changesets... */
307 if (dconf_changeset_describe (changeset, NULL, NULL, NULL))
309 if (!dconf_writer_begin (writer, &error))
310 goto out;
312 dconf_writer_change (writer, changeset, tag);
314 if (!dconf_writer_commit (writer, &error))
315 goto out;
318 out:
319 dconf_changeset_unref (changeset);
321 if (error)
323 g_dbus_method_invocation_return_gerror (invocation, error);
324 g_error_free (error);
327 else
328 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", tag));
330 g_free (tag);
332 dconf_writer_end (writer);
334 return TRUE;
337 static void
338 dconf_writer_iface_init (DConfDBusWriterIface *iface)
340 iface->handle_init = dconf_writer_handle_init;
341 iface->handle_change = dconf_writer_handle_change;
344 static void
345 dconf_writer_init (DConfWriter *writer)
347 writer->priv = dconf_writer_get_instance_private (writer);
348 writer->priv->basepath = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
349 writer->priv->native = TRUE;
352 static void
353 dconf_writer_set_property (GObject *object, guint prop_id,
354 const GValue *value, GParamSpec *pspec)
356 DConfWriter *writer = DCONF_WRITER (object);
358 g_assert_cmpint (prop_id, ==, 1);
360 g_assert (!writer->priv->name);
361 writer->priv->name = g_value_dup_string (value);
363 writer->priv->filename = g_build_filename (writer->priv->basepath, writer->priv->name, NULL);
366 static void
367 dconf_writer_class_init (DConfWriterClass *class)
369 GObjectClass *object_class = G_OBJECT_CLASS (class);
371 object_class->set_property = dconf_writer_set_property;
373 class->begin = dconf_writer_real_begin;
374 class->change = dconf_writer_real_change;
375 class->commit = dconf_writer_real_commit;
376 class->end = dconf_writer_real_end;
377 class->list = dconf_writer_real_list;
379 g_object_class_install_property (object_class, 1,
380 g_param_spec_string ("name", "name", "name", NULL,
381 G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY |
382 G_PARAM_WRITABLE));
385 void
386 dconf_writer_set_basepath (DConfWriter *writer,
387 const gchar *name)
389 g_free (writer->priv->basepath);
390 writer->priv->basepath = g_build_filename (g_get_user_runtime_dir (), "dconf-service", name, NULL);
391 writer->priv->native = FALSE;
394 DConfChangeset *
395 dconf_writer_diff (DConfWriter *writer,
396 DConfChangeset *changeset)
398 return dconf_changeset_diff (writer->priv->uncommited_values, changeset);
401 const gchar *
402 dconf_writer_get_name (DConfWriter *writer)
404 return writer->priv->name;
407 void
408 dconf_writer_list (GType type,
409 GHashTable *set)
411 DConfWriterClass *class;
413 g_return_if_fail (g_type_is_a (type, DCONF_TYPE_WRITER));
415 class = g_type_class_ref (type);
416 class->list (set);
417 g_type_class_unref (class);
420 GDBusInterfaceSkeleton *
421 dconf_writer_new (GType type,
422 const gchar *name)
424 g_return_val_if_fail (g_type_is_a (type, DCONF_TYPE_WRITER), NULL);
426 return g_object_new (type, "name", name, NULL);