1
2 /* GConf
3 * Copyright (C) 1999, 2000 Red Hat Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
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 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <config.h>
22 #include "gconf/gconf-backend.h"
23 #include "gconf/gconf-internals.h"
24 #include "gconf/gconf.h"
25
26 #include "xml-cache.h"
27
28
29 #include <libxml/tree.h>
30 #include <libxml/parser.h>
31
32 #include <stdio.h>
33 #include <time.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <limits.h>
42
43 /*
44 * Overview
45 *
46 * Basically we have a directory tree underneath an arbitrary root
47 * directory. The directory tree reflects the configuration
48 * namespace. Each directory contains an XML file which contains
49 * metadata for the directory and the key-value pairs in that
50 * directory. The magic file in each directory is called %gconf.xml,
51 * and can't clash with the database namespace because names containing
52 * % aren't allowed. So:
53 *
54 * /
55 * %gconf.xml
56 * guppi/
57 * %gconf.xml
58 * gnumeric/
59 * %gconf.xml
60 *
61 *
62 * Locking
63 *
64 * Locking doesn't _really_ matter because there's only one instance
65 * of the daemon at a time. However, eventually we want a non-daemon
66 * command line tool and library, e.g. for the debconf stuff,
67 * so we will eventually have locking. I'll figure out then how
68 * it will work.
69 *
70 * Caching
71 *
72 * I haven't decided the best way to do caching yet. As a first cut;
73 * we'll cache the parse tree for any files we've looked at. The cache
74 * will contain time stamps; we'll nuke cache entries that haven't been
75 * used in a while, either in a main loop timeout or by checking whenever
76 * we add a new cache entry. Parse trees correspond to "directories" in the
77 * configuration namespace.
78 *
79 * A more precise cache will store specific key-value pairs; this cache
80 * will probably contain a pointer to the parse tree node the key-value
81 * pair is inside.
82 *
83 * We'll of course need a "dirty" list of stuff not yet written to disk.
84 *
85 * We'll save the mod time of parse trees when we load them, so we can
86 * paranoia check that no one has change the file before we save.
87 *
88 * Ideally we could monitor our own process size and also free up
89 * cache whenever we started to use massive RAM. However, not sure
90 * this can be done at all portably. Could possibly have some measure
91 * of parse tree size.
92 *
93 * The libxml parse trees are pretty huge, so in theory we could
94 * "compress" them by extracting all the information we want into a
95 * specialized data structure, then nuking the parse tree. However,
96 * that would add more CPU overhead at load and save time. Anyway, as
97 * a first cut I'm not going to do this, we might do it later.
98 *
99 * Atomic Saving
100 *
101 * We'll want to save atomically by creating a temporary file for the
102 * new file version, renaming the original file, moving the temporary
103 * file into place, then deleting the original file, checking for
104 * errors and mod times along the way.
105 *
106 * Failed lookup caching
107 *
108 * If a key/directory doesn't exist, we create a cache entry anyway
109 * so we can rapidly re-determine that it doesn't exist.
110 * We also need to save "dirty" nonexistent entries, so we can delete
111 * the stuff off disk.
112 *
113 */
114
115 typedef struct _XMLSource XMLSource;
116
117 /* XMLSource **/
118
119 struct _XMLSource {
120 GConfSource source; /* inherit from GConfSource */
121 Cache* cache;
122 gchar* root_dir;
123 guint timeout_id;
124 GConfLock* lock;
125 guint dir_mode;
126 guint file_mode;
127 };
128
129 static XMLSource* xs_new (const gchar* root_dir,
130 guint dir_mode,
131 guint file_mode,
132 GConfLock* lock);
133 static void xs_destroy (XMLSource* source);
134
135 /*
136 * VTable functions
137 */
138
139 /* shutdown() is a BSD libc function */
140 static void x_shutdown (GError** err);
141
142 static GConfSource* resolve_address (const gchar* address,
143 GError** err);
144
145 static void lock (GConfSource* source,
146 GError** err);
147
148 static void unlock (GConfSource* source,
149 GError** err);
150
151 static gboolean readable (GConfSource* source,
152 const gchar* key,
153 GError** err);
154
155 static gboolean writable (GConfSource* source,
156 const gchar* key,
157 GError** err);
158
159 static GConfValue* query_value (GConfSource* source,
160 const gchar* key,
161 const gchar** locales,
162 gchar** schema_name,
163 GError** err);
164
165 static GConfMetaInfo*query_metainfo (GConfSource* source,
166 const gchar* key,
167 GError** err);
168
169 static void set_value (GConfSource* source,
170 const gchar* key,
171 const GConfValue* value,
172 GError** err);
173
174 static GSList* all_entries (GConfSource* source,
175 const gchar* dir,
176 const gchar** locales,
177 GError** err);
178
179 static GSList* all_subdirs (GConfSource* source,
180 const gchar* dir,
181 GError** err);
182
183 static void unset_value (GConfSource* source,
184 const gchar* key,
185 const gchar* locale,
186 GError** err);
187
188 static gboolean dir_exists (GConfSource *source,
189 const gchar *dir,
190 GError** err);
191
192 static void remove_dir (GConfSource* source,
193 const gchar* dir,
194 GError** err);
195
196 static void set_schema (GConfSource* source,
197 const gchar* key,
198 const gchar* schema_key,
199 GError** err);
200
201 static gboolean sync_all (GConfSource* source,
202 GError** err);
203
204 static void destroy_source (GConfSource* source);
205
206 static void clear_cache (GConfSource* source);
207
208 static void blow_away_locks (const char *address);
209
210 static GConfBackendVTable xml_vtable = {
211 sizeof (GConfBackendVTable),
212 x_shutdown,
213 resolve_address,
214 lock,
215 unlock,
216 readable,
217 writable,
218 query_value,
219 query_metainfo,
220 set_value,
221 all_entries,
222 all_subdirs,
223 unset_value,
224 dir_exists,
225 remove_dir,
226 set_schema,
227 sync_all,
228 destroy_source,
229 clear_cache,
230 blow_away_locks,
231 NULL, /* set_notify_func */
232 NULL, /* add_listener */
233 NULL /* remove_listener */
234 };
235
236 static void
x_shutdown(GError ** err)237 x_shutdown (GError** err)
238 {
239 gconf_log(GCL_DEBUG, _("Unloading XML backend module."));
240 }
241
242 static void
lock(GConfSource * source,GError ** err)243 lock (GConfSource* source,
244 GError** err)
245 {
246
247
248 }
249
250 static void
unlock(GConfSource * source,GError ** err)251 unlock (GConfSource* source,
252 GError** err)
253 {
254
255
256 }
257
258 static gboolean
readable(GConfSource * source,const gchar * key,GError ** err)259 readable (GConfSource* source,
260 const gchar* key,
261 GError** err)
262 {
263
264 return TRUE;
265 }
266
267 static gboolean
writable(GConfSource * source,const gchar * key,GError ** err)268 writable (GConfSource* source,
269 const gchar* key,
270 GError** err)
271 {
272
273 return TRUE;
274 }
275
276 static char*
get_dir_from_address(const char * address,GError ** err)277 get_dir_from_address (const char *address,
278 GError **err)
279 {
280 char *root_dir;
281 int len;
282
283 root_dir = gconf_address_resource (address);
284
285 if (root_dir == NULL)
286 {
287 gconf_set_error (err, GCONF_ERROR_BAD_ADDRESS,
288 _("Couldn't find the XML root directory in the address `%s'"),
289 address);
290 return NULL;
291 }
292
293 /* Chop trailing '/' to canonicalize */
294 len = strlen (root_dir);
295
296 if (root_dir[len-1] == '/')
297 root_dir[len-1] = '\0';
298
299 return root_dir;
300 }
301
302 static char*
get_lock_dir_from_root_dir(const char * root_dir)303 get_lock_dir_from_root_dir (const char *root_dir)
304 {
305 gchar* lockdir;
306
307 lockdir = gconf_concat_dir_and_key (root_dir, "%gconf-xml-backend.lock");
308
309 return lockdir;
310 }
311
312 static GConfSource*
resolve_address(const gchar * address,GError ** err)313 resolve_address (const gchar* address, GError** err)
314 {
315 struct stat statbuf;
316 gchar* root_dir;
317 XMLSource* xsource;
318 GConfSource* source;
319 gint flags = 0;
320 GConfLock* lock = NULL;
321 guint dir_mode = 0700;
322 guint file_mode = 0600;
323 gchar** address_flags;
324 gchar** iter;
325 gboolean force_readonly;
326
327 root_dir = get_dir_from_address (address, err);
328 if (root_dir == NULL)
329 return NULL;
330
331 if (g_stat (root_dir, &statbuf) == 0)
332 {
333 /* Already exists, base our dir_mode on it */
334 dir_mode = _gconf_mode_t_to_mode (statbuf.st_mode);
335
336 /* dir_mode without search bits */
337 file_mode = dir_mode & (~0111);
338 }
339 else if (g_mkdir (root_dir, dir_mode) < 0)
340 {
341 /* Error out even on EEXIST - shouldn't happen anyway */
342 gconf_set_error (err, GCONF_ERROR_FAILED,
343 _("Could not make directory `%s': %s"),
344 (gchar *)root_dir, g_strerror (errno));
345 g_free (root_dir);
346 return NULL;
347 }
348
349 force_readonly = FALSE;
350
351 address_flags = gconf_address_flags (address);
352 if (address_flags)
353 {
354 iter = address_flags;
355 while (*iter)
356 {
357 if (strcmp (*iter, "readonly") == 0)
358 {
359 force_readonly = TRUE;
360 break;
361 }
362
363 ++iter;
364 }
365 }
366
367 g_strfreev (address_flags);
368
369 {
370 /* See if we're writable */
371 gboolean writable;
372 int fd;
373 gchar* testfile;
374
375 writable = FALSE;
376
377 if (!force_readonly)
378 {
379 testfile = g_strconcat(root_dir, "/.testing.writeability", NULL);
380
381 fd = g_open(testfile, O_CREAT|O_WRONLY, S_IRWXU);
382
383 if (fd >= 0)
384 {
385 writable = TRUE;
386 close(fd);
387 }
388
389 g_unlink(testfile);
390
391 g_free(testfile);
392 }
393
394 if (writable)
395 flags |= GCONF_SOURCE_ALL_WRITEABLE;
396
397 #ifdef HAVE_CORBA
398 /* We only do locking if it's writable,
399 * and if not using local locks,
400 * which is sort of broken but close enough
401 */
402 if (writable && !gconf_use_local_locks ())
403 {
404 gchar* lockdir;
405
406 lockdir = get_lock_dir_from_root_dir (root_dir);
407
408 lock = gconf_get_lock(lockdir, err);
409
410 if (lock != NULL)
411 gconf_log(GCL_DEBUG, "Acquired lock directory `%s'", lockdir);
412
413 g_free(lockdir);
414
415 if (lock == NULL)
416 {
417 g_free(root_dir);
418 return NULL;
419 }
420 }
421 #endif
422 }
423
424 {
425 /* see if we're readable */
426 gboolean readable = FALSE;
427 GDir* d;
428
429 d = g_dir_open(root_dir, 0, NULL);
430
431 if (d != NULL)
432 {
433 readable = TRUE;
434 g_dir_close(d);
435 }
436
437 if (readable)
438 flags |= GCONF_SOURCE_ALL_READABLE;
439 }
440
441 if (!(flags & GCONF_SOURCE_ALL_READABLE) &&
442 !(flags & GCONF_SOURCE_ALL_WRITEABLE))
443 {
444 gconf_set_error(err, GCONF_ERROR_BAD_ADDRESS, _("Can't read from or write to the XML root directory in the address \"%s\""), address);
445 g_free(root_dir);
446 return NULL;
447 }
448
449 /* Create the new source */
450
451 xsource = xs_new(root_dir, dir_mode, file_mode, lock);
452
453 gconf_log(GCL_DEBUG,
454 _("Directory/file permissions for XML source at root %s are: %o/%o"),
455 root_dir, dir_mode, file_mode);
456
457 source = (GConfSource*)xsource;
458
459 source->flags = flags;
460
461 g_free(root_dir);
462
463 return source;
464 }
465
466 static GConfValue*
query_value(GConfSource * source,const gchar * key,const gchar ** locales,gchar ** schema_name,GError ** err)467 query_value (GConfSource* source,
468 const gchar* key,
469 const gchar** locales,
470 gchar** schema_name,
471 GError** err)
472 {
473 XMLSource* xs = (XMLSource*)source;
474 gchar* parent;
475 Dir* dir;
476 GError* error = NULL;
477
478 parent = gconf_key_directory(key);
479
480 g_assert(parent != NULL);
481
482 dir = cache_lookup(xs->cache, parent, FALSE, &error);
483
484 /* We DO NOT want to return an error unless it represents a general
485 problem with the backend; since we don't want to report stuff
486 like "this key doesn't exist yet" - however this is a maintenance
487 problem, since some errors may be added that need reporting. */
488 if (error != NULL)
489 {
490 gconf_log(GCL_WARNING, "%s", error->message);
491 g_error_free(error);
492 error = NULL;
493 }
494
495 g_free(parent);
496 parent = NULL;
497
498 if (dir != NULL)
499 {
500 const gchar* relative_key;
501 GConfValue* retval;
502
503 relative_key = gconf_key_key(key);
504
505 retval = dir_get_value(dir, relative_key, locales, schema_name, &error);
506
507 /* perhaps we should be reporting this error... */
508 if (error != NULL)
509 {
510 gconf_log(GCL_WARNING, "%s", error->message);
511 g_error_free(error);
512 error = NULL;
513 }
514
515 return retval;
516 }
517 else
518 return NULL;
519 }
520
521 static GConfMetaInfo*
query_metainfo(GConfSource * source,const gchar * key,GError ** err)522 query_metainfo (GConfSource* source, const gchar* key,
523 GError** err)
524 {
525 XMLSource* xs = (XMLSource*)source;
526 gchar* parent;
527 Dir* dir;
528
529 parent = gconf_key_directory(key);
530
531 if (parent != NULL)
532 {
533 dir = cache_lookup(xs->cache, parent, FALSE, err);
534 g_free(parent);
535 parent = NULL;
536
537 if (dir != NULL)
538 {
539 const gchar* relative_key;
540
541 relative_key = gconf_key_key (key);
542
543 return dir_get_metainfo (dir, relative_key, err);
544 }
545 }
546
547 /* No metainfo found */
548 return NULL;
549 }
550
551 static void
set_value(GConfSource * source,const gchar * key,const GConfValue * value,GError ** err)552 set_value (GConfSource* source, const gchar* key, const GConfValue* value,
553 GError** err)
554 {
555 XMLSource* xs = (XMLSource*)source;
556 Dir* dir;
557 gchar* parent;
558
559 g_return_if_fail(value != NULL);
560 g_return_if_fail(source != NULL);
561
562 parent = gconf_key_directory(key);
563
564 g_assert(parent != NULL);
565
566 dir = cache_lookup(xs->cache, parent, TRUE, err);
567
568 g_free(parent);
569 parent = NULL;
570
571 if (dir == NULL)
572 {
573 g_return_if_fail((err == NULL || *err != NULL));
574 return;
575 }
576 else
577 {
578 const gchar* relative_key;
579
580 relative_key = gconf_key_key(key);
581
582 dir_set_value(dir, relative_key, value, err);
583 }
584 }
585
586
587 static GSList*
all_entries(GConfSource * source,const gchar * key,const gchar ** locales,GError ** err)588 all_entries (GConfSource* source,
589 const gchar* key,
590 const gchar** locales,
591 GError** err)
592 {
593 XMLSource* xs = (XMLSource*)source;
594 Dir* dir;
595
596 dir = cache_lookup(xs->cache, key, FALSE, err);
597
598 if (dir == NULL)
599 return NULL;
600 else
601 return dir_all_entries(dir, locales, err);
602 }
603
604 static GSList*
all_subdirs(GConfSource * source,const gchar * key,GError ** err)605 all_subdirs (GConfSource* source,
606 const gchar* key,
607 GError** err)
608 {
609 Dir* dir;
610 XMLSource* xs = (XMLSource*)source;
611 GError *sync_err;
612
613 /* We have to sync before we can do this, to see which
614 * subdirs have gone away.
615 */
616 sync_err = NULL;
617 cache_sync (xs->cache, &sync_err);
618 if (sync_err)
619 {
620 gconf_log (GCL_WARNING, _("Error syncing the XML backend directory cache: %s"),
621 sync_err->message);
622 g_error_free (sync_err);
623 sync_err = NULL;
624 /* continue, may as well try our best. */
625 }
626
627 dir = cache_lookup (xs->cache, key, FALSE, err);
628
629 if (dir == NULL)
630 return NULL;
631 else
632 return dir_all_subdirs (dir, err);
633 }
634
635 static void
unset_value(GConfSource * source,const gchar * key,const gchar * locale,GError ** err)636 unset_value (GConfSource* source,
637 const gchar* key,
638 const gchar* locale,
639 GError** err)
640 {
641 XMLSource* xs = (XMLSource*)source;
642 Dir* dir;
643 gchar* parent;
644
645 gconf_log(GCL_DEBUG, "XML backend: unset value `%s'", key);
646
647 parent = gconf_key_directory(key);
648
649 dir = cache_lookup(xs->cache, parent, FALSE, err);
650
651 g_free(parent);
652
653 if (dir == NULL)
654 return;
655 else
656 {
657 const gchar* relative_key;
658
659 relative_key = gconf_key_key(key);
660
661 dir_unset_value(dir, relative_key, locale, err);
662 }
663 }
664
665 static gboolean
dir_exists(GConfSource * source,const gchar * key,GError ** err)666 dir_exists (GConfSource*source,
667 const gchar* key,
668 GError** err)
669 {
670 XMLSource *xs = (XMLSource*)source;
671 Dir* dir;
672
673 dir = cache_lookup(xs->cache, key, FALSE, err);
674
675 return (dir != NULL);
676 }
677
678 static void
remove_dir(GConfSource * source,const gchar * key,GError ** err)679 remove_dir (GConfSource* source,
680 const gchar* key,
681 GError** err)
682 {
683 g_set_error (err, GCONF_ERROR,
684 GCONF_ERROR_FAILED,
685 _("Remove directory operation is no longer supported, just remove all the values in the directory"));
686 }
687
688 static void
set_schema(GConfSource * source,const gchar * key,const gchar * schema_key,GError ** err)689 set_schema (GConfSource *source,
690 const gchar *key,
691 const gchar *schema_key,
692 GError **err)
693 {
694 XMLSource* xs = (XMLSource*)source;
695
696 Dir* dir;
697 gchar* parent;
698
699 g_return_if_fail (source != NULL);
700 g_return_if_fail (key != NULL);
701
702 parent = gconf_key_directory (key);
703
704 g_assert (parent != NULL);
705
706 dir = cache_lookup (xs->cache, parent, TRUE, err);
707
708 g_free (parent);
709 parent = NULL;
710
711 if (dir == NULL)
712 return; /* error should be set */
713 else
714 {
715 const gchar* relative_key;
716
717 relative_key = gconf_key_key (key);
718
719 dir_set_schema (dir, relative_key, schema_key, err);
720 }
721 }
722
723 static gboolean
sync_all(GConfSource * source,GError ** err)724 sync_all (GConfSource* source,
725 GError** err)
726 {
727 XMLSource* xs = (XMLSource*)source;
728
729 return cache_sync (xs->cache, err);
730 }
731
732 static void
destroy_source(GConfSource * source)733 destroy_source (GConfSource* source)
734 {
735 xs_destroy((XMLSource*)source);
736 }
737
738 static void
clear_cache(GConfSource * source)739 clear_cache (GConfSource* source)
740 {
741 XMLSource* xs = (XMLSource*)source;
742
743 /* clean all entries older than 0 seconds */
744 cache_clean(xs->cache, 0);
745 }
746
747 static void
blow_away_locks(const char * address)748 blow_away_locks (const char *address)
749 {
750 char *root_dir;
751 char *lock_dir;
752 GDir *dp;
753 const char *dent;
754
755 /* /tmp locks should never be stuck, and possible security issue to
756 * blow them away
757 */
758 if (gconf_use_local_locks ())
759 return;
760
761 root_dir = get_dir_from_address (address, NULL);
762 if (root_dir == NULL)
763 return;
764
765 lock_dir = get_lock_dir_from_root_dir (root_dir);
766
767 dp = g_dir_open (lock_dir, 0, NULL);
768
769 if (dp == NULL)
770 {
771 g_printerr (_("Could not open lock directory for %s to remove locks: %s\n"),
772 address, g_strerror (errno));
773 goto out;
774 }
775
776 while ((dent = g_dir_read_name (dp)) != NULL)
777 {
778 char *path;
779
780 path = g_build_filename (lock_dir, dent, NULL);
781
782 if (g_unlink (path) < 0)
783 {
784 g_printerr (_("Could not remove file %s: %s\n"),
785 path, g_strerror (errno));
786 }
787
788 g_free (path);
789 }
790
791 out:
792
793 if (dp)
794 g_dir_close (dp);
795
796 g_free (root_dir);
797 g_free (lock_dir);
798 }
799
800 /* Initializer */
801
802 #ifndef G_PLATFORM_WIN32
803 /* If we use G_MODULE_EXPORT, *only* thusly marked functions will be
804 * exported, and xml-test uses other ones, too.
805 */
806 G_MODULE_EXPORT
807 #endif
808 const gchar*
g_module_check_init(GModule * module)809 g_module_check_init (GModule *module)
810 {
811 gconf_log(GCL_DEBUG, _("Initializing XML backend module"));
812
813 LIBXML_TEST_VERSION;
814 xmlKeepBlanksDefault(1);
815
816 return NULL;
817 }
818
819 #ifndef G_PLATFORM_WIN32
820 G_MODULE_EXPORT
821 #endif
822 GConfBackendVTable*
gconf_backend_get_vtable(void)823 gconf_backend_get_vtable(void)
824 {
825 return &xml_vtable;
826 }
827
828 /* ****************************************************/
829
830 /*
831 * XMLSource
832 */
833
834 /* This timeout periodically cleans up
835 the old cruft in the cache */
836 static gboolean
cleanup_timeout(gpointer data)837 cleanup_timeout(gpointer data)
838 {
839 XMLSource* xs = (XMLSource*)data;
840
841 cache_clean(xs->cache, 60*5 /* 5 minutes */);
842
843 return TRUE;
844 }
845
846 static XMLSource*
xs_new(const gchar * root_dir,guint dir_mode,guint file_mode,GConfLock * lock)847 xs_new (const gchar* root_dir, guint dir_mode, guint file_mode, GConfLock* lock)
848 {
849 XMLSource* xs;
850
851 g_return_val_if_fail(root_dir != NULL, NULL);
852
853 xs = g_new0(XMLSource, 1);
854
855 xs->root_dir = g_strdup(root_dir);
856
857 xs->cache = cache_get(xs->root_dir, dir_mode, file_mode);
858
859 xs->timeout_id = g_timeout_add_seconds(60*5, /* 1 sec * 60 s/min * 5 min */
860 cleanup_timeout,
861 xs);
862
863 xs->lock = lock;
864
865 xs->dir_mode = dir_mode;
866 xs->file_mode = file_mode;
867
868 return xs;
869 }
870
871 static void
xs_destroy(XMLSource * xs)872 xs_destroy (XMLSource* xs)
873 {
874 #ifdef HAVE_CORBA
875 GError* error = NULL;
876 #endif
877
878 g_return_if_fail(xs != NULL);
879
880 #ifdef HAVE_CORBA
881 /* do this first in case we're in a "fast cleanup just before exit"
882 situation */
883 if (xs->lock != NULL && !gconf_release_lock(xs->lock, &error))
884 {
885 gconf_log (GCL_ERR, _("Failed to give up lock on XML directory \"%s\": %s"),
886 xs->root_dir, error->message);
887 g_error_free(error);
888 error = NULL;
889 }
890 #endif
891
892 if (!g_source_remove(xs->timeout_id))
893 {
894 /* should not happen, don't translate */
895 gconf_log(GCL_ERR, "timeout not found to remove?");
896 }
897
898 cache_unref(xs->cache);
899 g_free(xs->root_dir);
900 g_free(xs);
901 }
902
903