source: trunk/third/GConf/backends/xml-backend.c @ 18003

Revision 18003, 20.1 KB checked in by rbasch, 22 years ago (diff)
Hacks to support logins to multiple machines by the same user, plus other integration with our environment, and bug fixes: * Move the daemon lock directory to local disk, in /tmp/gconfd-$USER, so a per-machine instance of gconfd can run. * Don't hold a lock on the XML backend; instead, lock the backend only around a sync. * Add gconf_mkdir_private(), a wrapper for creating a private directory (mode 0700), which also sets the ACL in case the directory is in AFS. * Run gconfd under dustbuster; call setsid() in the daemon process, to disassociate it from the calling client. * Try to ping the server after we start it, in case it exited due to a failure to acquire the lock, to avoid greater lossage later. * Add an fsync() after the daemon writes its IOR to the lock file (otherwise, the file appears empty from other machines when it lives in AFS). * Handle a failure to read the IOR from the lock file. * Plug a descriptor leak in fork_exec_with_pipes(). * Fix a buffer overrun in subst_variables() (from the GConf mainline).
Line 
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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21
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 <dirent.h>
42#include <limits.h>
43
44/*
45 * Overview
46 *
47 * Basically we have a directory tree underneath an arbitrary root
48 * directory.  The directory tree reflects the configuration
49 * namespace. Each directory contains an XML file which contains
50 * metadata for the directory and the key-value pairs in that
51 * directory.  The magic file in each directory is called %gconf.xml,
52 * and can't clash with the database namespace because names containing
53 * % aren't allowed.  So:
54 *
55 * /
56 *  %gconf.xml
57 *   guppi/
58 *     %gconf.xml
59 *   gnumeric/
60 *     %gconf.xml
61 *
62 *
63 * Locking
64 *
65 * Locking doesn't _really_ matter because there's only one instance
66 * of the daemon at a time. However, eventually we want a non-daemon
67 * command line tool and library, e.g. for the debconf stuff,
68 * so we will eventually have locking. I'll figure out then how
69 * it will work.
70 *
71 * Caching
72 *
73 * I haven't decided the best way to do caching yet. As a first cut;
74 * we'll cache the parse tree for any files we've looked at. The cache
75 * will contain time stamps; we'll nuke cache entries that haven't been
76 * used in a while, either in a main loop timeout or by checking whenever
77 * we add a new cache entry. Parse trees correspond to "directories" in the
78 * configuration namespace.
79 *
80 * A more precise cache will store specific key-value pairs; this cache
81 * will probably contain a pointer to the parse tree node the key-value
82 * pair is inside.
83 *
84 * We'll of course need a "dirty" list of stuff not yet written to disk.
85 *
86 * We'll save the mod time of parse trees when we load them, so we can
87 * paranoia check that no one has change the file before we save.
88 *
89 * Ideally we could monitor our own process size and also free up
90 * cache whenever we started to use massive RAM. However, not sure
91 * this can be done at all portably. Could possibly have some measure
92 * of parse tree size.
93 *
94 * The libxml parse trees are pretty huge, so in theory we could
95 * "compress" them by extracting all the information we want into a
96 * specialized data structure, then nuking the parse tree. However,
97 * that would add more CPU overhead at load and save time. Anyway, as
98 * a first cut I'm not going to do this, we might do it later.
99 *
100 * Atomic Saving
101 *
102 * We'll want to save atomically by creating a temporary file for the
103 * new file version, renaming the original file, moving the temporary
104 * file into place, then deleting the original file, checking for
105 * errors and mod times along the way.
106 *     
107 * Failed lookup caching
108 *
109 * If a key/directory doesn't exist, we create a cache entry anyway
110 * so we can rapidly re-determine that it doesn't exist.
111 * We also need to save "dirty" nonexistent entries, so we can delete
112 * the stuff off disk. 
113 *
114 */
115
116typedef struct _XMLSource XMLSource;
117
118/* XMLSource **/
119
120struct _XMLSource {
121  GConfSource source; /* inherit from GConfSource */
122  Cache* cache;
123  gchar* root_dir;
124  guint timeout_id;
125  int lock_fd;
126  guint dir_mode;
127  guint file_mode;
128};
129
130static XMLSource* xs_new       (const gchar* root_dir,
131                                guint dir_mode,
132                                guint file_mode,
133                                int lock_fd);
134static void       xs_destroy   (XMLSource* source);
135
136/*
137 * VTable functions
138 */
139
140 /* shutdown() is a BSD libc function */
141static void          x_shutdown        (GError** err);
142
143static GConfSource*  resolve_address (const gchar* address,
144                                      GError** err);
145
146static void          lock            (GConfSource* source,
147                                      GError** err);
148
149static void          unlock          (GConfSource* source,
150                                      GError** err);
151
152static gboolean     readable         (GConfSource* source,
153                                      const gchar* key,
154                                      GError** err);
155
156static gboolean     writable        (GConfSource* source,
157                                      const gchar* key,
158                                      GError** err);
159
160static GConfValue*   query_value     (GConfSource* source,
161                                      const gchar* key,
162                                      const gchar** locales,
163                                      gchar** schema_name,
164                                      GError** err);
165
166static GConfMetaInfo*query_metainfo  (GConfSource* source,
167                                      const gchar* key,
168                                      GError** err);
169
170static void          set_value       (GConfSource* source,
171                                      const gchar* key,
172                                      GConfValue* value,
173                                      GError** err);
174
175static GSList*       all_entries    (GConfSource* source,
176                                     const gchar* dir,
177                                     const gchar** locales,
178                                     GError** err);
179
180static GSList*       all_subdirs     (GConfSource* source,
181                                      const gchar* dir,
182                                      GError** err);
183
184static void          unset_value     (GConfSource* source,
185                                      const gchar* key,
186                                      const gchar* locale,
187                                      GError** err);
188
189static gboolean      dir_exists      (GConfSource *source,
190                                      const gchar *dir,
191                                      GError** err);
192
193static void          remove_dir      (GConfSource* source,
194                                      const gchar* dir,
195                                      GError** err);
196
197static void          set_schema      (GConfSource* source,
198                                      const gchar* key,
199                                      const gchar* schema_key,
200                                      GError** err);
201
202static gboolean      sync_all        (GConfSource* source,
203                                      GError** err);
204
205static void          destroy_source  (GConfSource* source);
206
207static void          clear_cache     (GConfSource* source);
208
209static GConfBackendVTable xml_vtable = {
210  x_shutdown,
211  resolve_address,
212  lock,
213  unlock,
214  readable,
215  writable,
216  query_value,
217  query_metainfo,
218  set_value,
219  all_entries,
220  all_subdirs,
221  unset_value,
222  dir_exists,
223  remove_dir,
224  set_schema,
225  sync_all,
226  destroy_source,
227  clear_cache
228};
229
230static void         
231x_shutdown (GError** err)
232{
233  gconf_log(GCL_DEBUG, _("Unloading XML backend module."));
234}
235
236static void
237lock (GConfSource* source,
238      GError** err)
239{
240  XMLSource *xs;
241  int timeout = 5, status, i;
242  struct flock fl;
243
244  xs = (XMLSource *) source;
245
246  if (xs->lock_fd == -1)
247    return;
248
249  gconf_log(GCL_DEBUG, _("Locking XML backend"));
250
251  /* Athena local mod: acquire a write lock.  Note that we use
252   * a retry/sleep loop, rather than waiting for the lock with
253   * a timeout, due to problems using F_SETLKW with alarm() on
254   * a file in AFS.
255   */
256  for (i = 0; ; i++)
257    {
258      fl.l_type = F_WRLCK;
259      fl.l_whence = SEEK_SET;
260      fl.l_start = 0;
261      fl.l_len = 0;
262      status = fcntl(xs->lock_fd, F_SETLK, &fl);
263      if (status != -1 || i >= timeout)
264        break;
265      sleep(1);
266    }
267  if (status == -1)
268    gconf_set_error(err, GCONF_ERROR_FAILED,
269                    _("Cannot lock the XML backend: %s"),
270                    strerror(errno));
271  return;
272}
273
274static void
275unlock (GConfSource* source,
276        GError** err)
277{
278  XMLSource *xs;
279  struct flock fl;
280
281  xs = (XMLSource *) source;
282
283  if (xs->lock_fd == -1)
284    return;
285 
286  gconf_log(GCL_DEBUG, _("Unlocking XML backend"));
287
288  /* Athena local mod: release our acquired lock. */
289  fl.l_type = F_UNLCK;
290  fl.l_whence = SEEK_SET;
291  fl.l_start = 0;
292  fl.l_len = 0;
293  if (fcntl(xs->lock_fd, F_SETLK, &fl) == -1)
294    gconf_log(GCL_WARNING, _("Cannot unlock XML backend: %s"),
295              strerror(errno));
296
297  return;
298}
299
300static gboolean
301readable (GConfSource* source,
302          const gchar* key,
303          GError** err)
304{
305
306  return TRUE;
307}
308
309static gboolean
310writable (GConfSource* source,
311           const gchar* key,
312           GError** err)
313{
314
315  return TRUE;
316}
317
318static GConfSource* 
319resolve_address (const gchar* address, GError** err)
320{
321  gchar* root_dir;
322  XMLSource* xsource;
323  GConfSource* source;
324  guint len;
325  gint flags = 0;
326  int lock_fd = -1;
327  guint dir_mode = 0700;
328  guint file_mode = 0600;
329  gchar** address_flags;
330  gchar** iter;
331  gboolean force_readonly;
332 
333  root_dir = gconf_address_resource(address);
334
335  if (root_dir == NULL)
336    {
337      gconf_set_error(err, GCONF_ERROR_BAD_ADDRESS, _("Couldn't find the XML root directory in the address `%s'"), address);
338      return NULL;
339    }
340
341  /* Chop trailing '/' to canonicalize */
342  len = strlen(root_dir);
343
344  if (root_dir[len-1] == '/')
345    root_dir[len-1] = '\0';
346
347  if (gconf_mkdir_private(root_dir) < 0)
348    {
349      if (errno != EEXIST)
350        {
351          gconf_set_error(err, GCONF_ERROR_FAILED,
352                          _("Could not make directory `%s': %s"),
353                          (gchar*)root_dir, strerror(errno));
354          g_free(root_dir);
355          return NULL;
356        }
357      else
358        {
359          /* Already exists, base our dir_mode on it */
360          struct stat statbuf;
361          if (stat(root_dir, &statbuf) == 0)
362            {
363              dir_mode = mode_t_to_mode(statbuf.st_mode);
364              /* dir_mode without search bits */
365              file_mode = dir_mode & (~0111);
366            }
367        }
368    }
369
370  force_readonly = FALSE;
371 
372  address_flags = gconf_address_flags (address); 
373  if (address_flags)
374    {
375      iter = address_flags;
376      while (*iter)
377        {
378          if (strcmp (*iter, "readonly") == 0)
379            {
380              force_readonly = TRUE;
381              break;
382            }
383
384          ++iter;
385        }
386    }
387
388  g_strfreev (address_flags);
389
390  {
391    /* See if we're writable */
392    gboolean writable;
393    int fd;
394    gchar* testfile;
395
396    writable = FALSE;
397   
398    if (!force_readonly)
399      {
400        testfile = g_strconcat(root_dir, "/.testing.writeability", NULL);   
401       
402        fd = open(testfile, O_CREAT|O_WRONLY, S_IRWXU);
403       
404        if (fd >= 0)
405          {
406            writable = TRUE;
407            close(fd);
408          }
409       
410        unlink(testfile);
411       
412        g_free(testfile);
413      }
414   
415    if (writable)
416      flags |= GCONF_SOURCE_ALL_WRITEABLE;
417
418    /* We only do locking if it's writable,
419       which is sort of broken but close enough
420    */
421    if (writable)
422      {
423        gchar* lockdir;
424        gchar* lockfile;
425
426        lockdir = gconf_concat_dir_and_key(root_dir, "%gconf-xml-backend.lock");
427        /* Athena local mod: open a file we'll lock when syncing the
428         * database, but leave the backend unlocked so that multiple
429         * gconfd instances can access it.
430         */
431        if (gconf_mkdir_private(lockdir) < 0)
432          {
433            if (errno != EEXIST)
434              {
435                gconf_set_error(err, GCONF_ERROR_FAILED,
436                                _("Could not make directory `%s': %s"),
437                                lockdir, strerror(errno));
438                g_free(root_dir);
439                return NULL;
440              }
441          }
442        lockfile = g_strconcat(lockdir, "/lockfile", NULL);
443        g_free(lockdir);
444        lock_fd = open(lockfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
445        if (lock_fd == -1)
446          {
447            gconf_set_error(err, GCONF_ERROR_FAILED,
448                            _("Could not open lock file `%s': %s"),
449                            lockfile, strerror(errno));
450            g_free(root_dir);
451            g_free(lockfile);
452            return NULL;
453          }
454        g_free(lockfile);
455      }
456  }
457
458  {
459    /* see if we're readable */
460    gboolean readable = FALSE;
461    DIR* d;
462
463    d = opendir(root_dir);
464
465    if (d != NULL)
466      {
467        readable = TRUE;
468        closedir(d);
469      }
470   
471    if (readable)
472      flags |= GCONF_SOURCE_ALL_READABLE;
473  }
474
475  if (!(flags & GCONF_SOURCE_ALL_READABLE) &&
476      !(flags & GCONF_SOURCE_ALL_WRITEABLE))
477    {
478      gconf_set_error(err, GCONF_ERROR_BAD_ADDRESS, _("Can't read from or write to the XML root directory in the address `%s'"), address);
479      g_free(root_dir);
480      return NULL;
481    } 
482 
483  /* Create the new source */
484
485  xsource = xs_new(root_dir, dir_mode, file_mode, lock_fd);
486
487  gconf_log(GCL_DEBUG,
488            _("Directory/file permissions for XML source at root %s are: %o/%o"),
489            root_dir, dir_mode, file_mode);
490 
491  source = (GConfSource*)xsource;
492
493  source->flags = flags;
494 
495  g_free(root_dir);
496 
497  return source;
498}
499
500static GConfValue*
501query_value (GConfSource* source,
502             const gchar* key,
503             const gchar** locales,
504             gchar** schema_name,
505             GError** err)
506{
507  XMLSource* xs = (XMLSource*)source;
508  gchar* parent;
509  Dir* dir;
510  GError* error = NULL;
511
512  parent = gconf_key_directory(key);
513 
514  g_assert(parent != NULL);
515 
516  dir = cache_lookup(xs->cache, parent, FALSE, &error);
517
518  /* We DO NOT want to return an error unless it represents a general
519     problem with the backend; since we don't want to report stuff
520     like "this key doesn't exist yet" - however this is a maintenance
521     problem, since some errors may be added that need reporting. */
522  if (error != NULL)
523    {
524      gconf_log(GCL_WARNING, "%s", error->message);
525      g_error_free(error);
526      error = NULL;
527    }
528 
529  g_free(parent);
530  parent = NULL;
531 
532  if (dir != NULL)
533    {
534      const gchar* relative_key;
535      GConfValue* retval;
536     
537      relative_key = gconf_key_key(key);
538
539      retval = dir_get_value(dir, relative_key, locales, schema_name, &error);
540
541      /* perhaps we should be reporting this error... */
542      if (error != NULL)
543        {
544          gconf_log(GCL_WARNING, "%s", error->message);
545          g_error_free(error);
546          error = NULL;
547        }
548     
549      return retval;
550    }
551  else
552    return NULL;
553}
554
555static GConfMetaInfo*
556query_metainfo  (GConfSource* source, const gchar* key,
557                 GError** err)
558{
559  XMLSource* xs = (XMLSource*)source;
560  gchar* parent;
561  Dir* dir;
562
563  parent = gconf_key_directory(key);
564
565  if (parent != NULL)
566    {
567      dir = cache_lookup(xs->cache, parent, FALSE, err);
568      g_free(parent);
569      parent = NULL;
570     
571      if (dir != NULL)
572        return dir_get_metainfo(dir, key, err);
573    }
574
575  /* No metainfo found */
576  return NULL;
577}
578
579static void         
580set_value (GConfSource* source, const gchar* key, GConfValue* value,
581           GError** err)
582{
583  XMLSource* xs = (XMLSource*)source;
584  Dir* dir;
585  gchar* parent;
586 
587  g_return_if_fail(value != NULL);
588  g_return_if_fail(source != NULL);
589 
590  parent = gconf_key_directory(key);
591 
592  g_assert(parent != NULL);
593 
594  dir = cache_lookup(xs->cache, parent, TRUE, err);
595 
596  g_free(parent);
597  parent = NULL;
598
599  if (dir == NULL)
600    {
601      g_return_if_fail((err == NULL || *err != NULL));
602      return;
603    }
604  else
605    {
606      const gchar* relative_key;
607     
608      relative_key = gconf_key_key(key);
609     
610      dir_set_value(dir, relative_key, value, err);
611    }
612}
613
614
615static GSList*             
616all_entries    (GConfSource* source,
617                const gchar* key,
618                const gchar** locales,
619                GError** err)
620{
621  XMLSource* xs = (XMLSource*)source;
622  Dir* dir;
623
624  dir = cache_lookup(xs->cache, key, FALSE, err);
625 
626  if (dir == NULL)
627    return NULL;
628  else
629    return dir_all_entries(dir, locales, err);
630}
631
632static GSList*
633all_subdirs     (GConfSource* source,
634                 const gchar* key,
635                 GError** err)
636{
637  Dir* dir;
638  XMLSource* xs = (XMLSource*)source;
639
640  dir = cache_lookup (xs->cache, key, FALSE, err);
641 
642  if (dir == NULL)
643    return NULL;
644  else
645    return dir_all_subdirs (dir, err);
646}
647
648static void         
649unset_value     (GConfSource* source,
650                 const gchar* key,
651                 const gchar* locale,
652                 GError** err)
653{
654  XMLSource* xs = (XMLSource*)source;
655  Dir* dir;
656  gchar* parent;
657
658  gconf_log(GCL_DEBUG, "XML backend: unset value `%s'", key);
659 
660  parent = gconf_key_directory(key);
661 
662  dir = cache_lookup(xs->cache, parent, FALSE, err);
663
664  g_free(parent);
665 
666  if (dir == NULL)
667    return;
668  else
669    {
670      const gchar* relative_key;
671 
672      relative_key = gconf_key_key(key);
673
674      dir_unset_value(dir, relative_key, locale, err);
675    }
676}
677
678static gboolean
679dir_exists      (GConfSource*source,
680                 const gchar* key,
681                 GError** err)
682{
683  XMLSource *xs = (XMLSource*)source;
684  Dir* dir;
685 
686  dir = cache_lookup(xs->cache, key, FALSE, err);
687 
688  return (dir != NULL);
689
690
691static void         
692remove_dir      (GConfSource* source,
693                 const gchar* key,
694                 GError** err)
695{
696  XMLSource* xs = (XMLSource*)source;
697  Dir* dir;
698 
699  dir = cache_lookup(xs->cache, key, FALSE, err);
700 
701  if (dir == NULL)
702    return;
703  else
704    {
705      dir_mark_deleted(dir);
706    }
707}
708
709static void         
710set_schema      (GConfSource* source,
711                 const gchar* key,
712                 const gchar* schema_key,
713                 GError** err)
714{
715  XMLSource* xs = (XMLSource*)source;
716
717  Dir* dir;
718  gchar* parent;
719 
720  g_return_if_fail(schema_key != NULL);
721
722  parent = gconf_key_directory(key);
723 
724  g_assert(parent != NULL);
725 
726  dir = cache_lookup(xs->cache, parent, TRUE, err);
727 
728  g_free(parent);
729  parent = NULL;
730
731  if (dir == NULL)
732    return; /* error should be set */
733  else
734    {
735      const gchar* relative_key;
736     
737      relative_key = gconf_key_key(key);
738     
739      dir_set_schema(dir, relative_key, schema_key, err);
740    }
741}
742
743static gboolean     
744sync_all        (GConfSource* source,
745                 GError** err)
746{
747  XMLSource* xs = (XMLSource*)source;
748
749  return cache_sync(xs->cache, err);
750}
751
752static void         
753destroy_source  (GConfSource* source)
754{
755  xs_destroy((XMLSource*)source);
756}
757
758static void
759clear_cache     (GConfSource* source)
760{
761  XMLSource* xs = (XMLSource*)source;
762
763  /* clean all entries older than 0 seconds */
764  cache_clean(xs->cache, 0);
765}
766
767/* Initializer */
768
769G_MODULE_EXPORT const gchar*
770g_module_check_init (GModule *module)
771{
772  gconf_log(GCL_DEBUG, _("Initializing XML backend module"));
773
774  return NULL;
775}
776
777G_MODULE_EXPORT GConfBackendVTable*
778gconf_backend_get_vtable(void)
779{
780  return &xml_vtable;
781}
782
783/* ****************************************************/
784
785/*
786 *  XMLSource
787 */
788
789/* This timeout periodically cleans up
790   the old cruft in the cache */
791static gboolean
792cleanup_timeout(gpointer data)
793{
794  XMLSource* xs = (XMLSource*)data;
795
796  cache_clean(xs->cache, 60*5 /* 5 minutes */);
797
798  return TRUE;
799}
800
801static XMLSource*
802xs_new       (const gchar* root_dir, guint dir_mode, guint file_mode, int lock_fd)
803{
804  XMLSource* xs;
805
806  g_return_val_if_fail(root_dir != NULL, NULL);
807
808  xs = g_new0(XMLSource, 1);
809
810  xs->root_dir = g_strdup(root_dir);
811
812  xs->cache = cache_new(xs->root_dir, dir_mode, file_mode);
813
814  xs->timeout_id = g_timeout_add(1000*60*5, /* 1 sec * 60 s/min * 5 min */
815                                 cleanup_timeout,
816                                 xs);
817
818  xs->lock_fd = lock_fd;
819
820  xs->dir_mode = dir_mode;
821  xs->file_mode = file_mode;
822 
823  return xs;
824}
825
826static void
827xs_destroy   (XMLSource* xs)
828{
829  g_return_if_fail(xs != NULL);
830
831  /* do this first in case we're in a "fast cleanup just before exit"
832     situation */
833  if (xs->lock_fd != -1)
834    close(xs->lock_fd);
835
836  if (!g_source_remove(xs->timeout_id))
837    {
838      /* should not happen, don't translate */
839      gconf_log(GCL_ERR, "timeout not found to remove?");
840    }
841 
842  cache_destroy(xs->cache);
843  g_free(xs->root_dir);
844  g_free(xs);
845}
Note: See TracBrowser for help on using the repository browser.