2 * Copyright © 2010 Codethink 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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
22 #define _XOPEN_SOURCE 600
23 #include "dconf-shmdir.h"
24 #include "dconf-engine.h"
25 #include <gvdb-reader.h>
35 dconf_engine_message_destroy (DConfEngineMessage
*dcem
)
39 for (i
= 0; dcem
->parameters
[i
]; i
++)
40 g_variant_unref (dcem
->parameters
[i
]);
41 g_free (dcem
->parameters
);
45 dconf_engine_message_copy (DConfEngineMessage
*orig
,
46 DConfEngineMessage
*copy
)
52 for (n
= 0; orig
->parameters
[n
]; n
++);
53 copy
->parameters
= g_new (GVariant
*, n
+ 1);
54 for (i
= 0; i
< n
; i
++)
55 copy
->parameters
[i
] = g_variant_ref (orig
->parameters
[i
]);
56 copy
->parameters
[i
] = NULL
;
60 dconf_engine_get_session_dir (void)
62 static const gchar
*session_dir
;
63 static gsize initialised
;
65 if (g_once_init_enter (&initialised
))
67 session_dir
= dconf_shmdir_from_environment ();
68 g_once_init_leave (&initialised
, 1);
81 GvdbTable
**lock_tables
;
90 dconf_engine_setup_user (DConfEngine
*engine
,
93 /* invariant: we never have user gvdb without shm */
94 g_assert ((engine
->gvdbs
[i
] == NULL
) >= (engine
->shm
[i
] == NULL
));
98 const gchar
*session_dir
= dconf_engine_get_session_dir ();
105 filename
= g_build_filename (session_dir
,
108 fd
= open (filename
, O_RDWR
| O_CREAT
, 0600);
113 if (ftruncate (fd
, 1) == 0)
115 engine
->shm
[i
] = mmap (NULL
, 1, PROT_READ
, MAP_SHARED
, fd
, 0);
117 if (engine
->shm
[i
] == MAP_FAILED
)
118 engine
->shm
[i
] = NULL
;
129 filename
= g_build_filename (g_get_user_config_dir (),
133 engine
->gvdbs
[i
] = gvdb_table_new (filename
, FALSE
, NULL
);
138 g_assert ((engine
->gvdbs
[i
] == NULL
) >= (engine
->shm
[i
] == NULL
));
142 dconf_engine_refresh_user (DConfEngine
*engine
,
145 g_assert ((engine
->gvdbs
[i
] == NULL
) >= (engine
->shm
[i
] == NULL
));
147 /* if we failed the first time, fail forever */
148 if (engine
->shm
[i
] && *engine
->shm
[i
] == 1)
150 if (engine
->gvdbs
[i
])
152 gvdb_table_unref (engine
->gvdbs
[i
]);
153 engine
->gvdbs
[i
] = NULL
;
156 munmap (engine
->shm
[i
], 1);
157 engine
->shm
[i
] = NULL
;
159 dconf_engine_setup_user (engine
, i
);
163 g_assert ((engine
->gvdbs
[i
] == NULL
) >= (engine
->shm
[i
] == NULL
));
167 dconf_engine_refresh_system (DConfEngine
*engine
,
170 if (engine
->gvdbs
[i
] && !gvdb_table_is_valid (engine
->gvdbs
[i
]))
172 gvdb_table_unref (engine
->gvdbs
[i
]);
173 engine
->gvdbs
[i
] = NULL
;
176 if (engine
->gvdbs
[i
] == NULL
)
178 gchar
*filename
= g_build_filename ("/etc/dconf/db",
179 engine
->names
[i
], NULL
);
180 engine
->gvdbs
[i
] = gvdb_table_new (filename
, TRUE
, NULL
);
181 if (engine
->gvdbs
[i
] == NULL
)
182 g_error ("Unable to open '%s', specified in dconf profile\n",
184 engine
->lock_tables
[i
] = gvdb_table_get_table (engine
->gvdbs
[i
],
192 dconf_engine_refresh (DConfEngine
*engine
)
196 for (i
= 0; i
< engine
->n_dbs
; i
++)
197 if (engine
->bus_types
[i
] == 'e')
198 dconf_engine_refresh_user (engine
, i
);
200 dconf_engine_refresh_system (engine
, i
);
204 dconf_engine_setup (DConfEngine
*engine
)
208 for (i
= 0; i
< engine
->n_dbs
; i
++)
209 if (engine
->bus_types
[i
] == 'e')
210 dconf_engine_setup_user (engine
, i
);
212 dconf_engine_refresh_system (engine
, i
);
216 dconf_engine_get_state (DConfEngine
*engine
)
220 g_mutex_lock (&engine
->lock
);
222 dconf_engine_refresh (engine
);
223 state
= engine
->state
;
225 g_mutex_unlock (&engine
->lock
);
231 dconf_engine_load_profile (const gchar
*profile
,
242 /* DCONF_PROFILE starting with '/' gives an absolute path to a profile */
243 if (profile
[0] != '/')
244 filename
= g_build_filename ("/etc/dconf/profile", profile
, NULL
);
246 filename
= g_strdup (profile
);
248 f
= fopen (filename
, "r");
252 gint saved_errno
= errno
;
254 g_set_error (error
, G_FILE_ERROR
,
255 g_file_error_from_errno (saved_errno
),
256 "open '%s': %s", filename
, g_strerror (saved_errno
));
262 *bus_types
= g_new (gchar
, allocated
);
263 *names
= g_new (gchar
*, allocated
);
266 /* quick and dirty is good enough for now */
267 while (fgets (line
, sizeof line
, f
))
272 end
= strchr (line
, '\n');
275 g_error ("long line in %s", filename
);
283 if (*n_dbs
== allocated
)
286 *names
= g_renew (gchar
*, *names
, allocated
);
287 *bus_types
= g_renew (gchar
, *bus_types
, allocated
);
290 sep
= strchr (line
, ':');
294 /* strings MUST be 'user-db' or 'system-db'. we do the check
295 * this way here merely because it is the fastest.
297 (*bus_types
)[*n_dbs
] = (line
[0] == 'u') ? 'e' : 'y';
298 (*names
)[*n_dbs
] = g_strndup (sep
+ 1, end
- (sep
+ 1));
302 /* default is for first DB to be user and rest to be system */
303 (*bus_types
)[*n_dbs
] = (*n_dbs
== 0) ? 'e' : 'y';
304 (*names
)[*n_dbs
] = g_strndup (line
, end
- line
);
310 *bus_types
= g_renew (gchar
, *bus_types
, *n_dbs
);
311 *names
= g_renew (gchar
*, *names
, *n_dbs
);
319 dconf_engine_new (const gchar
*profile
)
324 engine
= g_slice_new (DConfEngine
);
325 g_mutex_init (&engine
->lock
);
328 profile
= getenv ("DCONF_PROFILE");
332 GError
*error
= NULL
;
334 if (!dconf_engine_load_profile (profile
, &engine
->bus_types
, &engine
->names
, &engine
->n_dbs
, &error
))
335 g_error ("Error loading dconf profile '%s': %s\n",
336 profile
, error
->message
);
340 if (!dconf_engine_load_profile ("user", &engine
->bus_types
, &engine
->names
, &engine
->n_dbs
, NULL
))
342 engine
->names
= g_new (gchar
*, 1);
343 engine
->names
[0] = g_strdup ("user");
344 engine
->bus_types
= g_strdup ("e");
349 if (strcmp (engine
->names
[0], "-") == 0)
351 g_free (engine
->names
[0]);
352 engine
->names
[0] = NULL
;
355 engine
->object_paths
= g_new (gchar
*, engine
->n_dbs
);
356 engine
->gvdbs
= g_new0 (GvdbTable
*, engine
->n_dbs
);
357 engine
->lock_tables
= g_new0 (GvdbTable
*, engine
->n_dbs
);
358 engine
->shm
= g_new0 (guint8
*, engine
->n_dbs
);
361 for (i
= 0; i
< engine
->n_dbs
; i
++)
362 if (engine
->names
[i
])
363 engine
->object_paths
[i
] = g_strjoin (NULL
,
364 "/ca/desrt/dconf/Writer/",
368 engine
->object_paths
[i
] = NULL
;
370 dconf_engine_setup (engine
);
376 dconf_engine_free (DConfEngine
*engine
)
380 for (i
= 0; i
< engine
->n_dbs
; i
++)
382 g_free (engine
->object_paths
[i
]);
383 g_free (engine
->names
[i
]);
385 if (engine
->gvdbs
[i
])
386 gvdb_table_unref (engine
->gvdbs
[i
]);
388 if (engine
->lock_tables
[i
])
389 gvdb_table_unref (engine
->lock_tables
[i
]);
392 munmap (engine
->shm
[i
], 1);
396 g_mutex_clear (&engine
->lock
);
398 g_free (engine
->object_paths
);
399 g_free (engine
->bus_types
);
400 g_free (engine
->names
);
401 g_free (engine
->gvdbs
);
402 g_free (engine
->lock_tables
);
403 g_free (engine
->shm
);
405 g_slice_free (DConfEngine
, engine
);
409 dconf_engine_read_internal (DConfEngine
*engine
,
414 GVariant
*value
= NULL
;
419 g_mutex_lock (&engine
->lock
);
421 dconf_engine_refresh (engine
);
423 /* Bound the search space depending on the databases that we are
426 limit
= system
? engine
->n_dbs
: 1;
427 lowest
= user
? 0 : 1;
429 /* We want i equal to the index of the highest database containing a
430 * lock, or i == lowest if there is no lock. For that reason, we
431 * don't actually check the lowest database for a lock. That makes
432 * sense, because even if it had a lock, it would not change our
433 * search policy (which would be to check the lowest one first).
435 * Note that we intentionally dishonour 'limit' here -- we want to
436 * ensure that values in the user database are always ignored when
439 for (i
= MAX (engine
->n_dbs
- 1, lowest
); lowest
< i
; i
--)
440 if (engine
->lock_tables
[i
] != NULL
&&
441 gvdb_table_has_value (engine
->lock_tables
[i
], key
))
444 while (i
< limit
&& value
== NULL
)
446 if (engine
->gvdbs
[i
] != NULL
)
447 value
= gvdb_table_get_value (engine
->gvdbs
[i
], key
);
451 g_mutex_unlock (&engine
->lock
);
457 dconf_engine_read (DConfEngine
*engine
,
460 return dconf_engine_read_internal (engine
, key
, TRUE
, TRUE
);
464 dconf_engine_read_default (DConfEngine
*engine
,
467 return dconf_engine_read_internal (engine
, key
, FALSE
, TRUE
);
471 dconf_engine_read_no_default (DConfEngine
*engine
,
474 return dconf_engine_read_internal (engine
, key
, TRUE
, FALSE
);
478 dconf_engine_make_match_rule (DConfEngine
*engine
,
479 DConfEngineMessage
*dcem
,
481 const gchar
*method_name
)
485 dcem
->bus_name
= "org.freedesktop.DBus";
486 dcem
->object_path
= "/org/freedesktop/DBus";
487 dcem
->interface_name
= "org.freedesktop.DBus";
488 dcem
->method_name
= method_name
;
490 dcem
->parameters
= g_new (GVariant
*, engine
->n_dbs
+ 1);
491 for (i
= 0; i
< engine
->n_dbs
; i
++)
495 rule
= g_strdup_printf ("type='signal',"
496 "interface='ca.desrt.dconf.Writer',"
499 engine
->object_paths
[i
],
501 dcem
->parameters
[i
] = g_variant_new ("(s)", rule
);
502 g_variant_ref_sink (dcem
->parameters
[i
]);
505 dcem
->parameters
[i
] = NULL
;
507 dcem
->bus_types
= engine
->bus_types
;
508 dcem
->n_messages
= engine
->n_dbs
;
509 dcem
->reply_type
= G_VARIANT_TYPE_UNIT
;
513 dconf_engine_watch (DConfEngine
*engine
,
515 DConfEngineMessage
*dcem
)
517 dconf_engine_make_match_rule (engine
, dcem
, name
, "AddMatch");
521 dconf_engine_unwatch (DConfEngine
*engine
,
523 DConfEngineMessage
*dcem
)
525 dconf_engine_make_match_rule (engine
, dcem
, name
, "RemoveMatch");
529 dconf_engine_is_writable (DConfEngine
*engine
,
532 gboolean writable
= TRUE
;
534 /* Only check if we have more than one database */
535 if (engine
->n_dbs
> 1)
539 g_mutex_lock (&engine
->lock
);
541 dconf_engine_refresh (engine
);
543 /* Don't check for locks in the top database (i == 0). */
544 for (i
= engine
->n_dbs
- 1; 0 < i
; i
--)
545 if (engine
->lock_tables
[i
] != NULL
&&
546 gvdb_table_has_value (engine
->lock_tables
[i
], name
))
552 g_mutex_unlock (&engine
->lock
);
558 /* be conservative and fast: false negatives are OK */
560 is_dbusable (GVariant
*value
)
564 type
= g_variant_get_type_string (value
);
566 /* maybe definitely won't work.
567 * variant? too lazy to check inside...
569 if (strchr (type
, 'v') || strchr (type
, 'm'))
572 /* XXX: we could also check for '{}' not inside an array...
573 * but i'm not sure we want to support that anyway.
576 /* this will avoid any too-deeply-nested limits */
577 return strlen (type
) < 32;
581 fake_maybe (GVariant
*value
)
583 GVariantBuilder builder
;
585 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("av"));
589 if (is_dbusable (value
))
590 g_variant_builder_add (&builder
, "v", value
);
597 variant
= g_variant_new_variant (value
);
598 ay
= g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING
,
599 g_variant_get_data (variant
),
600 g_variant_get_size (variant
),
602 (GDestroyNotify
) g_variant_unref
,
604 g_variant_builder_add (&builder
, "v", ay
);
606 g_variant_builder_add (&builder
, "v",
607 g_variant_new_string ("serialised GVariant"));
611 return g_variant_builder_end (&builder
);
615 dconf_engine_dcem (DConfEngine
*engine
,
616 DConfEngineMessage
*dcem
,
617 const gchar
*method_name
,
618 const gchar
*format_string
,
623 dcem
->bus_name
= "ca.desrt.dconf";
624 dcem
->object_path
= engine
->object_paths
[0];
625 dcem
->interface_name
= "ca.desrt.dconf.Writer";
626 dcem
->method_name
= method_name
;
627 dcem
->parameters
= g_new (GVariant
*, 2);
628 dcem
->n_messages
= 1;
630 va_start (ap
, format_string
);
631 dcem
->parameters
[0] = g_variant_new_va (format_string
, NULL
, &ap
);
632 g_variant_ref_sink (dcem
->parameters
[0]);
633 dcem
->parameters
[1] = NULL
;
636 dcem
->bus_types
= engine
->bus_types
;
637 dcem
->reply_type
= G_VARIANT_TYPE ("(s)");
641 dconf_engine_write (DConfEngine
*engine
,
644 DConfEngineMessage
*dcem
,
647 dconf_engine_dcem (engine
, dcem
,
649 name
, fake_maybe (value
));
655 dconf_engine_write_many (DConfEngine
*engine
,
657 const gchar
* const *keys
,
659 DConfEngineMessage
*dcem
,
662 GVariantBuilder builder
;
665 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("a(sav)"));
667 for (i
= 0; keys
[i
]; i
++)
668 g_variant_builder_add (&builder
, "(s@av)",
669 keys
[i
], fake_maybe (values
[i
]));
671 dconf_engine_dcem (engine
, dcem
, "WriteMany", "(sa(sav))", prefix
, &builder
);
677 dconf_engine_list (DConfEngine
*engine
,
683 g_mutex_lock (&engine
->lock
);
685 dconf_engine_refresh (engine
);
687 if (engine
->gvdbs
[0])
688 list
= gvdb_table_list (engine
->gvdbs
[0], dir
);
693 list
= g_new0 (char *, 1);
696 *length
= g_strv_length (list
);
698 g_mutex_unlock (&engine
->lock
);
704 dconf_engine_decode_notify (DConfEngine
*engine
,
705 const gchar
*anti_expose
,
714 if (strcmp (iface
, "ca.desrt.dconf.Writer") || strcmp (method
, "Notify"))
717 if (!g_variant_is_of_type (body
, G_VARIANT_TYPE ("(sass)")))
724 g_variant_get_child (body
, 2, "&s", &ae
);
726 if (strcmp (ae
, anti_expose
) == 0)
730 g_variant_get (body
, "(&s^a&ss)", path
, rels
, NULL
);
736 dconf_engine_decode_writability_notify (const gchar
**path
,
741 if (strcmp (iface
, "ca.desrt.dconf.Writer") ||
742 strcmp (method
, "WritabilityNotify"))
745 if (!g_variant_is_of_type (body
, G_VARIANT_TYPE ("(s)")))
748 g_variant_get_child (body
, 0, "&s", path
);