2
* Copyright (C) 1999, 2000 Red Hat Inc.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, 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
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library 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.
21
#include "xml-entry.h"
23
#include <libxml/parser.h>
30
#include <sys/types.h>
37
#include <gconf/gconf-internals.h>
38
#include "xml-entry.h"
40
/* This makes hash table safer when debugging */
41
#ifndef GCONF_ENABLE_DEBUG
42
#define safe_g_hash_table_insert g_hash_table_insert
45
safe_g_hash_table_insert(GHashTable* ht, gpointer key, gpointer value)
47
gpointer oldkey = NULL, oldval = NULL;
49
if (g_hash_table_lookup_extended(ht, key, &oldkey, &oldval))
51
gconf_log(GCL_WARNING, "Hash key `%s' is already in the table!",
57
g_hash_table_insert(ht, key, value);
62
static gchar* parent_dir(const gchar* dir);
69
GTime last_access; /* so we know when to un-cache */
71
GHashTable* entry_cache; /* store key-value entries */
72
GHashTable* subdir_cache; /* store subdirectories */
80
dir_load_doc(Dir* d, GError** err);
82
static Entry* dir_make_new_entry(Dir* d, const gchar* relative_key);
84
static gboolean dir_forget_entry_if_useless(Dir* d, Entry* e);
87
dir_blank(const gchar* key)
93
#ifdef GCONF_ENABLE_DEBUG
96
if (!gconf_valid_key(key, &why)) {
97
gconf_log(GCL_DEBUG, "key `%s' invalid: %s",
100
g_assert(gconf_valid_key(key, NULL));
104
d->key = g_strdup(key);
106
d->last_access = time(NULL);
109
d->entry_cache = g_hash_table_new(g_str_hash, g_str_equal);
121
dir_new(const gchar *keyname,
122
const gchar *xml_root_dir,
128
d = dir_blank(keyname);
130
/* sync with dir_load() */
131
d->fs_dirname = gconf_concat_dir_and_key(xml_root_dir, keyname);
132
d->xml_filename = g_strconcat(d->fs_dirname, "/%gconf.xml", NULL);
133
d->root_dir_len = strlen(xml_root_dir);
135
d->dir_mode = dir_mode;
136
d->file_mode = file_mode;
142
dir_load (const gchar* key, const gchar* xml_root_dir, GError** err)
147
guint dir_mode = 0700;
148
guint file_mode = 0600;
150
g_return_val_if_fail(gconf_valid_key(key, NULL), NULL);
152
fs_dirname = gconf_concat_dir_and_key(xml_root_dir, key);
153
xml_filename = g_strconcat(fs_dirname, "/%gconf.xml", NULL);
157
gboolean notfound = FALSE;
159
if (stat(xml_filename, &s) != 0)
163
gconf_set_error(err, GCONF_ERROR_FAILED,
164
_("Could not stat `%s': %s"),
165
xml_filename, strerror(errno));
171
else if (S_ISDIR(s.st_mode))
173
gconf_set_error(err, GCONF_ERROR_FAILED,
174
_("XML filename `%s' is a directory"),
181
gconf_log(GCL_DEBUG, "dir file %s not found", xml_filename);
183
g_free(xml_filename);
188
/* Take directory mode from the xml_root_dir, if possible */
189
if (stat (xml_root_dir, &s) == 0)
191
dir_mode = mode_t_to_mode(s.st_mode);
194
file_mode = dir_mode & ~0111; /* turn off search bits */
200
/* sync with dir_new() */
201
d->fs_dirname = fs_dirname;
202
d->xml_filename = xml_filename;
203
d->root_dir_len = strlen(xml_root_dir);
205
d->dir_mode = dir_mode;
206
d->file_mode = file_mode;
208
gconf_log(GCL_DEBUG, "loaded dir %s", fs_dirname);
215
entry_destroy_foreach(const gchar* name, Entry* e, gpointer data)
224
g_free(d->fs_dirname);
225
g_free(d->xml_filename);
227
g_hash_table_foreach(d->entry_cache, (GHFunc)entry_destroy_foreach,
230
g_hash_table_destroy(d->entry_cache);
239
create_fs_dir(const gchar* dir, const gchar* xml_filename,
241
guint dir_mode, guint file_mode,
245
dir_ensure_exists (Dir* d,
248
if (!create_fs_dir(d->fs_dirname, d->xml_filename, d->root_dir_len,
249
d->dir_mode, d->file_mode,
253
/* check that error is set */
254
g_return_val_if_fail( (err == NULL) || (*err != NULL), FALSE );
265
entry_sync_foreach(const gchar* name, Entry* e, gpointer data)
267
entry_sync_to_node(e);
271
dir_sync_pending (Dir *d)
277
dir_sync (Dir* d, GError** err)
279
gboolean retval = TRUE;
281
/* note that if we are deleted but already
282
synced, this returns now, making the
283
dircache's recursive delete tactic reasonably
289
/* We should have a doc if dirty is TRUE */
290
g_assert(d->doc != NULL);
292
d->last_access = time(NULL);
296
if (unlink(d->xml_filename) != 0)
298
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to delete `%s': %s"),
299
d->xml_filename, strerror(errno));
303
if (rmdir(d->fs_dirname) != 0)
305
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to delete `%s': %s"),
306
d->fs_dirname, strerror(errno));
312
gboolean old_existed = FALSE;
316
/* First make sure entry values are synced to their
318
g_hash_table_foreach(d->entry_cache, (GHFunc)entry_sync_foreach, NULL);
320
tmp_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.tmp", NULL);
321
old_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.old", NULL);
323
if (xmlSaveFile(tmp_filename, d->doc) < 0)
325
gboolean recovered = FALSE;
327
/* Try to solve the problem by creating the FS dir */
328
if (!gconf_file_exists(d->fs_dirname))
330
if (create_fs_dir(d->fs_dirname, d->xml_filename,
332
d->dir_mode, d->file_mode,
335
if (xmlSaveFile(tmp_filename, d->doc) >= 0)
342
/* I think libxml may mangle errno, but we might as well
343
try. Don't set error if it's already set by some
345
if (err && *err == NULL)
346
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to write file `%s': %s"),
347
tmp_filename, strerror(errno));
351
goto failed_end_of_sync;
355
/* Set permissions on the new file */
356
if (chmod (tmp_filename, d->file_mode) != 0)
358
gconf_set_error(err, GCONF_ERROR_FAILED,
359
_("Failed to set mode on `%s': %s"),
360
tmp_filename, strerror(errno));
363
goto failed_end_of_sync;
366
old_existed = gconf_file_exists(d->xml_filename);
370
if (rename(d->xml_filename, old_filename) < 0)
372
gconf_set_error(err, GCONF_ERROR_FAILED,
373
_("Failed to rename `%s' to `%s': %s"),
374
d->xml_filename, old_filename, strerror(errno));
377
goto failed_end_of_sync;
381
if (rename(tmp_filename, d->xml_filename) < 0)
383
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to rename `%s' to `%s': %s"),
384
tmp_filename, d->xml_filename, strerror(errno));
386
/* Put the original file back, so this isn't a total disaster. */
387
if (rename(old_filename, d->xml_filename) < 0)
389
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to restore `%s' from `%s': %s"),
390
d->xml_filename, old_filename, strerror(errno));
394
goto failed_end_of_sync;
399
if (unlink(old_filename) < 0)
401
gconf_log(GCL_WARNING, _("Failed to delete old file `%s': %s"),
402
old_filename, strerror(errno));
403
/* Not a failure, just leaves cruft around. */
409
g_free(old_filename);
410
g_free(tmp_filename);
420
dir_set_value (Dir* d, const gchar* relative_key,
421
GConfValue* value, GError** err)
426
dir_load_doc(d, err);
430
g_return_if_fail( (err == NULL) || (*err != NULL) );
434
e = g_hash_table_lookup(d->entry_cache, relative_key);
437
e = dir_make_new_entry(d, relative_key);
439
entry_set_value(e, value);
441
d->last_access = time(NULL);
442
entry_set_mod_time(e, d->last_access);
444
entry_set_mod_user(e, g_get_user_name());
450
dir_get_last_access (Dir *d)
452
return d->last_access;
456
dir_get_value (Dir* d,
457
const gchar* relative_key,
458
const gchar** locales,
465
dir_load_doc(d, err);
469
g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL );
473
e = g_hash_table_lookup(d->entry_cache, relative_key);
475
d->last_access = time(NULL);
479
/* No entry; return */
488
val = entry_get_value (e, locales, err);
490
/* Get schema name if requested */
491
if (schema_name && entry_get_schema_name (e))
492
*schema_name = g_strdup (entry_get_schema_name (e));
494
/* return copy of the value */
496
return gconf_value_copy(val);
503
dir_get_name (Dir *d)
505
g_return_val_if_fail(d != NULL, NULL);
510
dir_get_metainfo(Dir* d, const gchar* relative_key, GError** err)
514
d->last_access = time(NULL);
517
dir_load_doc(d, err);
521
g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL );
525
e = g_hash_table_lookup(d->entry_cache, relative_key);
530
return entry_get_metainfo(e);
534
dir_unset_value (Dir* d, const gchar* relative_key,
535
const gchar* locale, GError** err)
539
d->last_access = time(NULL);
542
dir_load_doc(d, err);
546
g_return_if_fail( (err == NULL) || (*err != NULL) );
550
e = g_hash_table_lookup(d->entry_cache, relative_key);
552
if (e == NULL) /* nothing to change */
555
if (entry_unset_value(e, locale))
557
/* If entry_unset() returns TRUE then
558
the entry was changed (not already unset) */
562
if (dir_forget_entry_if_useless(d, e))
564
/* entry is destroyed */
569
entry_set_mod_time(e, d->last_access);
570
entry_set_mod_user(e, g_get_user_name());
575
/* Check uselessness anyway; this ensures that if it was useless
576
when the daemon started or we otherwise missed its lack of
577
utility, we clean it up if the user does an explicit unset */
578
dir_forget_entry_if_useless(d, e);
582
typedef struct _ListifyData ListifyData;
584
struct _ListifyData {
587
const gchar** locales;
591
listify_foreach(const gchar* key, Entry* e, ListifyData* ld)
595
GError* error = NULL;
597
val = entry_get_value (e, ld->locales, &error);
601
g_assert (val == NULL);
602
g_error_free (error);
606
entry = gconf_entry_new_nocopy (g_strdup(key),
607
val ? gconf_value_copy(val) : NULL);
610
entry_get_schema_name (e))
612
gconf_entry_set_schema_name (entry, entry_get_schema_name (e));
615
ld->list = g_slist_prepend(ld->list, entry);
619
dir_all_entries (Dir* d, const gchar** locales, GError** err)
624
dir_load_doc(d, err);
628
g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL );
634
ld.locales = locales;
636
g_hash_table_foreach(d->entry_cache, (GHFunc)listify_foreach,
643
dir_all_subdirs (Dir* d, GError** err)
648
GSList* retval = NULL;
655
dir_load_doc(d, err);
659
g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL );
663
dp = opendir(d->fs_dirname);
668
len = strlen(d->fs_dirname);
669
subdir_len = PATH_MAX - len;
671
fullpath = g_malloc0(subdir_len + len + 20); /* ensure null termination */
672
strcpy(fullpath, d->fs_dirname);
674
fullpath_end = fullpath + len;
677
*fullpath_end = '\0';
679
while ((dent = readdir(dp)) != NULL)
681
/* ignore ., .., and all dot-files */
682
if (dent->d_name[0] == '.')
685
len = strlen(dent->d_name);
687
if (len < subdir_len)
689
strcpy(fullpath_end, dent->d_name);
690
strncpy(fullpath_end+len, "/%gconf.xml", subdir_len - len);
693
continue; /* Shouldn't ever happen since PATH_MAX is available */
695
if (stat(fullpath, &statbuf) < 0)
697
/* This is some kind of cruft, not an XML directory */
701
retval = g_slist_prepend(retval, g_strdup(dent->d_name));
704
/* if this fails, we really can't do a thing about it
705
and it's not a meaningful error */
714
dir_set_schema (Dir* d,
715
const gchar* relative_key,
716
const gchar* schema_key,
722
dir_load_doc(d, err);
726
g_return_if_fail( (err == NULL) || (*err != NULL) );
731
d->last_access = time(NULL);
733
e = g_hash_table_lookup(d->entry_cache, relative_key);
736
e = dir_make_new_entry(d, relative_key);
738
entry_set_mod_time(e, d->last_access);
740
entry_set_schema_name(e, schema_key);
742
if (schema_key == NULL)
743
dir_forget_entry_if_useless(d, e);
747
dir_mark_deleted(Dir* d)
755
/* go ahead and free the XML document */
763
dir_is_deleted (Dir* d)
769
dir_last_access (Dir* d)
771
return d->last_access;
774
/* private Dir functions */
777
dir_fill_cache_from_doc(Dir* d);
780
dir_load_doc(Dir* d, GError** err)
782
gboolean xml_already_exists = TRUE;
783
gboolean need_backup = FALSE;
786
g_return_if_fail(d->doc == NULL);
788
if (stat(d->xml_filename, &statbuf) < 0)
793
xml_already_exists = FALSE;
802
/* These are all fatal errors */
803
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to stat `%s': %s"),
804
d->xml_filename, strerror(errno));
810
if (statbuf.st_size == 0)
812
xml_already_exists = FALSE;
815
if (xml_already_exists)
816
d->doc = xmlParseFile(d->xml_filename);
818
/* We recover from these errors instead of passing them up */
820
/* This has the potential to just blow away an entire corrupted
821
config file; but I think that is better than the alternatives
822
(disabling config for a directory because the document is mangled)
825
/* Also we create empty %gconf.xml files when we create a new dir,
826
and those return a parse error */
830
if (xml_already_exists)
831
need_backup = TRUE; /* we want to save whatever broken stuff was in the file */
833
/* Create a new doc */
835
d->doc = xmlNewDoc("1.0");
838
if (d->doc->root == NULL)
841
d->doc->root = xmlNewDocNode(d->doc, NULL, "gconf", NULL);
843
else if (strcmp(d->doc->root->name, "gconf") != 0)
846
d->doc = xmlNewDoc("1.0");
847
d->doc->root = xmlNewDocNode(d->doc, NULL, "gconf", NULL);
848
need_backup = TRUE; /* save broken stuff */
852
/* We had an initial doc with a valid root */
853
/* Fill child_cache from entries */
854
dir_fill_cache_from_doc(d);
859
/* Back up the file we failed to parse, if it exists,
860
we aren't going to be able to do anything if this call
864
gchar* backup = g_strconcat(d->xml_filename, ".bak", NULL);
867
rename(d->xml_filename, backup);
869
/* Recreate %gconf.xml to maintain our integrity and be sure
871
/* If we failed to rename, we just give up and truncate the file */
872
fd = open(d->xml_filename, O_CREAT | O_WRONLY | O_TRUNC, d->file_mode);
879
g_assert(d->doc != NULL);
880
g_assert(d->doc->root != NULL);
884
dir_make_new_entry(Dir* d, const gchar* relative_key)
888
g_return_val_if_fail(d->doc != NULL, NULL);
889
g_return_val_if_fail(d->doc->root != NULL, NULL);
891
e = entry_new(relative_key);
893
entry_set_node(e, xmlNewChild(d->doc->root, NULL, "entry", NULL));
895
safe_g_hash_table_insert(d->entry_cache, (gchar*)entry_get_name(e), e);
901
dir_forget_entry_if_useless(Dir* d, Entry* e)
905
if (entry_get_schema_name(e) != NULL)
908
val = entry_get_value(e, NULL, NULL);
911
return FALSE; /* not useless */
913
g_hash_table_remove(d->entry_cache, entry_get_name(e));
921
dir_fill_cache_from_doc(Dir* d)
925
if (d->doc == NULL ||
926
d->doc->root == NULL ||
927
d->doc->root->childs == NULL)
929
/* Empty document - just return. */
933
node = d->doc->root->childs;
937
if (node->type == XML_ELEMENT_NODE &&
938
(strcmp(node->name, "entry") == 0))
940
gchar* attr = my_xmlGetProp(node, "name");
944
if (g_hash_table_lookup(d->entry_cache, attr) != NULL)
946
gconf_log(GCL_WARNING,
947
_("Duplicate entry `%s' in `%s', ignoring"),
948
attr, d->xml_filename);
956
entry_set_node(e, node);
958
entry_fill_from_node(e);
960
safe_g_hash_table_insert(d->entry_cache,
961
(gchar*)entry_get_name(e), e);
968
gconf_log(GCL_WARNING,
969
_("Entry with no name in XML file `%s', ignoring"),
975
if (node->type == XML_ELEMENT_NODE)
976
gconf_log(GCL_WARNING,
977
_("A toplevel node in XML file `%s' is <%s> rather than <entry>, ignoring"),
979
node->name ? (char*) node->name : "unknown");
991
create_fs_dir(const gchar* dir, const gchar* xml_filename,
992
guint root_dir_len, guint dir_mode, guint file_mode,
995
g_return_val_if_fail(xml_filename != NULL, FALSE);
997
gconf_log(GCL_DEBUG, "Enter create_fs_dir: %s", dir);
999
if (gconf_file_test(xml_filename, GCONF_FILE_ISFILE))
1001
gconf_log(GCL_DEBUG, "XML backend file %s already exists", xml_filename);
1005
/* Don't create anything above the root directory */
1006
if (strlen(dir) > root_dir_len)
1010
parent = parent_dir(dir);
1012
gconf_log(GCL_DEBUG, "Parent dir is %s", parent);
1016
gchar* parent_xml = NULL;
1017
gboolean success = FALSE;
1020
parent_xml = g_strconcat(parent, "/%gconf.xml", NULL);
1022
success = create_fs_dir(parent, parent_xml, root_dir_len,
1023
dir_mode, file_mode, err);
1026
gconf_log(GCL_DEBUG, "created parent: %s", parent);
1028
gconf_log(GCL_DEBUG, "failed parent: %s", parent);
1039
gconf_log(GCL_DEBUG, "%s has no parent", dir);
1043
gconf_log(GCL_DEBUG, "Making directory %s", dir);
1045
if (mkdir(dir, dir_mode) < 0)
1047
if (errno != EEXIST)
1049
gconf_set_error(err, GCONF_ERROR_FAILED,
1050
_("Could not make directory `%s': %s"),
1051
(gchar*)dir, strerror(errno));
1056
if (xml_filename != NULL)
1059
/* don't truncate the file, it may well already exist */
1060
fd = open(xml_filename, O_CREAT | O_WRONLY, file_mode);
1062
gconf_log(GCL_DEBUG, "Creating XML file %s", xml_filename);
1066
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to create file `%s': %s"),
1067
xml_filename, strerror(errno));
1074
gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to close file `%s': %s"),
1075
xml_filename, strerror(errno));
1082
gconf_log(GCL_DEBUG, "No XML filename passed to create_fs_dir() for %s", dir);
1089
parent_dir(const gchar* dir)
1091
/* We assume the dir doesn't have a trailing slash, since that's our
1092
standard canonicalization in GConf */
1096
g_return_val_if_fail(*dir != '\0', NULL);
1100
g_assert(dir[0] == '/');
1104
parent = g_strdup(dir);
1106
last_slash = strrchr(parent, '/');
1108
/* dir must have had at least the root slash in it */
1109
g_assert(last_slash != NULL);
1111
if (last_slash != parent)
1125
mode_t_to_mode(mode_t orig)
1127
/* I don't think this is portable. */
1129
guint fullmask = S_IRWXG | S_IRWXU | S_IRWXO;
1132
mode = orig & fullmask;
1134
g_return_val_if_fail(mode <= 0777, 0700);