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>
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"
37 struct _DConfWriterPrivate
46 DConfChangeset
*uncommited_values
;
47 DConfChangeset
*commited_values
;
49 GQueue uncommited_changes
;
50 GQueue commited_changes
;
55 DConfChangeset
*changeset
;
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
))
66 dconf_writer_real_list (GHashTable
*set
)
72 dirname
= g_build_filename (g_get_user_config_dir (), "dconf", NULL
);
73 dir
= g_dir_open (dirname
, 0, NULL
);
78 while ((name
= g_dir_read_name (dir
)))
80 if (!strchr (name
, '.'))
81 g_hash_table_add (set
, g_strdup (name
));
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
++);
100 dconf_writer_real_begin (DConfWriter
*writer
,
103 /* If this is the first time, populate the value table with the
106 if (writer
->priv
->commited_values
== NULL
)
110 writer
->priv
->commited_values
= dconf_gvdb_utils_read_and_back_up_file (writer
->priv
->filename
, &missing
, error
);
112 if (!writer
->priv
->commited_values
)
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
);
128 dconf_writer_real_change (DConfWriter
*writer
,
129 DConfChangeset
*changeset
,
132 g_return_if_fail (writer
->priv
->uncommited_values
!= NULL
);
134 dconf_changeset_change (writer
->priv
->uncommited_values
, changeset
);
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
;
151 dconf_writer_real_commit (DConfWriter
*writer
,
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
;
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
))
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
;
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
);
213 const gchar
* const *paths
;
216 n
= dconf_changeset_describe (change
->changeset
, &prefix
, &paths
, NULL
);
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
);
228 dconf_writer_begin (DConfWriter
*writer
,
231 return DCONF_WRITER_GET_CLASS (writer
)->begin (writer
, error
);
235 dconf_writer_change (DConfWriter
*writer
,
236 DConfChangeset
*changeset
,
239 DCONF_WRITER_GET_CLASS (writer
)->change (writer
, changeset
, tag
);
243 dconf_writer_commit (DConfWriter
*writer
,
246 return DCONF_WRITER_GET_CLASS (writer
)->commit (writer
, error
);
250 dconf_writer_end (DConfWriter
*writer
)
252 return DCONF_WRITER_GET_CLASS (writer
)->end (writer
);
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
);
269 g_dbus_method_invocation_return_gerror (invocation
, error
);
270 g_error_free (error
);
274 g_dbus_method_invocation_return_value (invocation
, NULL
);
276 dconf_writer_end (writer
);
282 dconf_writer_handle_change (DConfDBusWriter
*dbus_writer
,
283 GDBusMethodInvocation
*invocation
,
286 DConfWriter
*writer
= DCONF_WRITER (dbus_writer
);
287 DConfChangeset
*changeset
;
288 GError
*error
= NULL
;
289 GVariant
*tmp
, *args
;
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
))
312 dconf_writer_change (writer
, changeset
, tag
);
314 if (!dconf_writer_commit (writer
, &error
))
319 dconf_changeset_unref (changeset
);
323 g_dbus_method_invocation_return_gerror (invocation
, error
);
324 g_error_free (error
);
328 g_dbus_method_invocation_return_value (invocation
, g_variant_new ("(s)", tag
));
332 dconf_writer_end (writer
);
338 dconf_writer_iface_init (DConfDBusWriterIface
*iface
)
340 iface
->handle_init
= dconf_writer_handle_init
;
341 iface
->handle_change
= dconf_writer_handle_change
;
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
;
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
);
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
|
386 dconf_writer_set_basepath (DConfWriter
*writer
,
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
;
395 dconf_writer_diff (DConfWriter
*writer
,
396 DConfChangeset
*changeset
)
398 return dconf_changeset_diff (writer
->priv
->uncommited_values
, changeset
);
402 dconf_writer_get_name (DConfWriter
*writer
)
404 return writer
->priv
->name
;
408 dconf_writer_list (GType type
,
411 DConfWriterClass
*class;
413 g_return_if_fail (g_type_is_a (type
, DCONF_TYPE_WRITER
));
415 class = g_type_class_ref (type
);
417 g_type_class_unref (class);
420 GDBusInterfaceSkeleton
*
421 dconf_writer_new (GType type
,
424 g_return_val_if_fail (g_type_is_a (type
, DCONF_TYPE_WRITER
), NULL
);
426 return g_object_new (type
, "name", name
, NULL
);