source: trunk/third/GConf/gconf/gconfd.c @ 17087

Revision 17087, 51.7 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17086, which included commits to RCS files with non-trunk default branches.
Line 
1/* GConf
2 * Copyright (C) 1999, 2000 Red Hat Inc.
3 * Developed by Havoc Pennington, some code in here borrowed from
4 * gnome-name-server and libgnorba (Elliot Lee)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22
23/*
24 * This is the per-user configuration daemon.
25 * (has debug crap in it now)
26 */
27
28#include <config.h>
29
30#include "gconf-internals.h"
31#include "gconf-sources.h"
32#include "gconf-listeners.h"
33#include "gconf-locale.h"
34#include "gconf-schema.h"
35#include "gconf-glib-private.h"
36#include "gconf.h"
37#include "gconfd.h"
38#include "gconf-database.h"
39#include <orb/orbit.h>
40
41#include "GConf.h"
42
43#include <sys/types.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <signal.h>
48#include <unistd.h>
49#include <sys/stat.h>
50#include <fcntl.h>
51#include <errno.h>
52#include <ctype.h>
53#include <syslog.h>
54#include <time.h>
55
56/* This makes hash table safer when debugging */
57#ifndef GCONF_ENABLE_DEBUG
58#define safe_g_hash_table_insert g_hash_table_insert
59#else
60static void
61safe_g_hash_table_insert(GHashTable* ht, gpointer key, gpointer value)
62{
63  gpointer oldkey = NULL, oldval = NULL;
64
65  if (g_hash_table_lookup_extended(ht, key, &oldkey, &oldval))
66    {
67      gconf_log(GCL_WARNING, "Hash key `%s' is already in the table!",
68                (gchar*) key);
69      return;
70    }
71  else
72    {
73      g_hash_table_insert(ht, key, value);
74    }
75}
76#endif
77
78/*
79 * Declarations
80 */
81
82static void     gconf_main            (void);
83static void     gconf_main_quit       (void);
84static gboolean gconf_main_is_running (void);
85
86static void logfile_save (void);
87static void logfile_read (void);
88static void log_client_add (const ConfigListener client);
89static void log_client_remove (const ConfigListener client);
90
91static void    add_client            (const ConfigListener  client);
92static void    remove_client         (const ConfigListener  client);
93static GSList *list_clients          (void);
94static void    log_clients_to_string (GString              *str);
95static void    drop_old_clients      (void);
96static guint   client_count          (void);
97
98static void    enter_shutdown          (void);
99
100static void                 init_databases (void);
101static void                 shutdown_databases (void);
102static void                 set_default_database (GConfDatabase* db);
103static void                 register_database (GConfDatabase* db);
104static void                 unregister_database (GConfDatabase* db);
105static GConfDatabase*       lookup_database (const gchar *address);
106static GConfDatabase*       obtain_database (const gchar *address,
107                                             GError **err);
108static void                 drop_old_databases (void);
109static gboolean             no_databases_in_use (void);
110
111/*
112 * Flag indicating that we are shutting down, so return errors
113 * on any attempted operation. We do this instead of unregistering with
114 * OAF or deactivating the server object, because we want to avoid
115 * another gconfd starting up before we finish shutting down.
116 */
117
118static gboolean in_shutdown = FALSE;
119
120/*
121 * CORBA goo
122 */
123
124static ConfigServer server = CORBA_OBJECT_NIL;
125static PortableServer_POA the_poa;
126static GConfLock *daemon_lock = NULL;
127
128static ConfigDatabase
129gconfd_get_default_database(PortableServer_Servant servant,
130                            CORBA_Environment* ev);
131
132static ConfigDatabase
133gconfd_get_database(PortableServer_Servant servant,
134                    const CORBA_char* address,
135                    CORBA_Environment* ev);
136
137static void
138gconfd_add_client (PortableServer_Servant servant,
139                   const ConfigListener client,
140                   CORBA_Environment *ev);
141
142static void
143gconfd_remove_client (PortableServer_Servant servant,
144                      const ConfigListener client,
145                      CORBA_Environment *ev);
146
147static CORBA_long
148gconfd_ping(PortableServer_Servant servant, CORBA_Environment *ev);
149
150static void
151gconfd_shutdown(PortableServer_Servant servant, CORBA_Environment *ev);
152
153static PortableServer_ServantBase__epv base_epv = {
154  NULL,
155  NULL,
156  NULL
157};
158
159static POA_ConfigServer__epv server_epv = {
160  NULL,
161  gconfd_get_default_database,
162  gconfd_get_database,
163  gconfd_add_client,
164  gconfd_remove_client,
165  gconfd_ping,
166  gconfd_shutdown
167};
168
169static POA_ConfigServer__vepv poa_server_vepv = { &base_epv, &server_epv };
170static POA_ConfigServer poa_server_servant = { NULL, &poa_server_vepv };
171
172static ConfigDatabase
173gconfd_get_default_database(PortableServer_Servant servant,
174                            CORBA_Environment* ev)
175{
176  GConfDatabase *db;
177
178  if (gconfd_check_in_shutdown (ev))
179    return CORBA_OBJECT_NIL;
180 
181  db = lookup_database (NULL);
182
183  if (db)
184    return CORBA_Object_duplicate (db->objref, ev);
185  else
186    return CORBA_OBJECT_NIL;
187}
188
189static ConfigDatabase
190gconfd_get_database(PortableServer_Servant servant,
191                    const CORBA_char* address,
192                    CORBA_Environment* ev)
193{
194  GConfDatabase *db;
195  GError* error = NULL; 
196
197  if (gconfd_check_in_shutdown (ev))
198    return CORBA_OBJECT_NIL;
199 
200  db = obtain_database (address, &error);
201
202  if (db != NULL)
203    return CORBA_Object_duplicate (db->objref, ev);
204  else if (gconf_set_exception(&error, ev))
205    return CORBA_OBJECT_NIL;
206  else
207    return CORBA_OBJECT_NIL;
208}
209
210static void
211gconfd_add_client (PortableServer_Servant servant,
212                   const ConfigListener client,
213                   CORBA_Environment *ev)
214{
215  if (gconfd_check_in_shutdown (ev))
216    return;
217 
218  add_client (client);
219}
220
221static void
222gconfd_remove_client (PortableServer_Servant servant,
223                      const ConfigListener client,
224                      CORBA_Environment *ev)
225{
226  if (gconfd_check_in_shutdown (ev))
227    return;
228 
229  remove_client (client);
230}
231
232static CORBA_long
233gconfd_ping(PortableServer_Servant servant, CORBA_Environment *ev)
234{
235  if (gconfd_check_in_shutdown (ev))
236    return 0;
237 
238  return getpid();
239}
240
241static void
242gconfd_shutdown(PortableServer_Servant servant, CORBA_Environment *ev)
243{
244  if (gconfd_check_in_shutdown (ev))
245    return;
246 
247  gconf_log(GCL_DEBUG, _("Shutdown request received"));
248
249  gconf_main_quit();
250}
251
252/*
253 * Main code
254 */
255
256/* This needs to be called before we register with OAF
257 */
258static void
259gconf_server_load_sources(void)
260{
261  gchar** addresses;
262  GList* tmp;
263  gboolean have_writable = FALSE;
264  gchar* conffile;
265  GConfSources* sources = NULL;
266  GError* error = NULL;
267 
268  conffile = g_strconcat(GCONF_CONFDIR, "/path", NULL);
269
270  addresses = gconf_load_source_path(conffile, NULL);
271
272  g_free(conffile);
273
274#ifdef GCONF_ENABLE_DEBUG
275  /* -- Debug only */
276 
277  if (addresses == NULL)
278    {
279      gconf_log(GCL_DEBUG, _("gconfd compiled with debugging; trying to load gconf.path from the source directory"));
280      conffile = g_strconcat(GCONF_SRCDIR, "/gconf/gconf.path", NULL);
281      addresses = gconf_load_source_path(conffile, NULL);
282      g_free(conffile);
283    }
284
285  /* -- End of Debug Only */
286#endif
287
288  if (addresses == NULL)
289    {     
290      /* Try using the default address xml:readwrite:$(HOME)/.gconf */
291      addresses = g_new0(gchar*, 2);
292
293      addresses[0] = g_strconcat("xml:readwrite:", g_get_home_dir(), "/.gconf", NULL);
294
295      addresses[1] = NULL;
296     
297      gconf_log(GCL_DEBUG, _("No configuration files found, trying to use the default config source `%s'"), addresses[0]);
298    }
299 
300  if (addresses == NULL)
301    {
302      /* We want to stay alive but do nothing, because otherwise every
303         request would result in another failed gconfd being spawned. 
304      */
305      const gchar* empty_addr[] = { NULL };
306      gconf_log(GCL_ERR, _("No configuration sources in the source path, configuration won't be saved; edit "GCONF_CONFDIR"/path"));
307      /* don't request error since there aren't any addresses */
308      sources = gconf_sources_new_from_addresses(empty_addr, NULL);
309
310      /* Install the sources as the default database */
311      set_default_database (gconf_database_new(sources));
312    }
313  else
314    {
315      sources = gconf_sources_new_from_addresses((const gchar**)addresses,
316                                                 &error);
317
318      if (error != NULL)
319        {
320          gconf_log(GCL_ERR, _("Error loading some config sources: %s"),
321                    error->message);
322
323          g_error_free(error);
324          error = NULL;
325        }
326     
327      g_free(addresses);
328
329      g_assert(sources != NULL);
330
331      if (sources->sources == NULL)
332        gconf_log(GCL_ERR, _("No config source addresses successfully resolved, can't load or store config data"));
333   
334      tmp = sources->sources;
335
336      while (tmp != NULL)
337        {
338          if (((GConfSource*)tmp->data)->flags & GCONF_SOURCE_ALL_WRITEABLE)
339            {
340              have_writable = TRUE;
341              break;
342            }
343
344          tmp = g_list_next(tmp);
345        }
346
347      /* In this case, some sources may still return TRUE from their writable() function */
348      if (!have_writable)
349        gconf_log(GCL_WARNING, _("No writable config sources successfully resolved, may not be able to save some configuration changes"));
350
351       
352      /* Install the sources as the default database */
353      set_default_database (gconf_database_new(sources));
354    }
355}
356
357static void
358signal_handler (int signo)
359{
360  static gint in_fatal = 0;
361
362  /* avoid loops */
363  if (in_fatal > 0)
364    return;
365 
366  ++in_fatal;
367 
368  switch(signo) {
369    /* Fast cleanup only */
370  case SIGSEGV:
371  case SIGBUS:
372  case SIGILL:
373    enter_shutdown ();
374    gconf_log(GCL_ERR,
375              _("Received signal %d, dumping core. Please report a GConf bug."),
376              signo);
377    abort ();
378    break;
379
380  case SIGFPE:
381  case SIGPIPE:
382    /* Go ahead and try the full cleanup on these,
383     * though it could well not work out very well.
384     */
385    enter_shutdown ();
386
387    /* let the fatal signals interrupt us */
388    --in_fatal;
389   
390    gconf_log (GCL_ERR,
391               _("Received signal %d, shutting down abnormally. Please file a GConf bug report."),
392               signo);
393
394
395    if (gconf_main_is_running ())
396      gconf_main_quit ();
397   
398    break;
399
400  case SIGTERM:
401  case SIGHUP:
402    enter_shutdown ();
403
404    /* let the fatal signals interrupt us */
405    --in_fatal;
406   
407    gconf_log (GCL_INFO,
408               _("Received signal %d, shutting down cleanly"), signo);
409
410    if (gconf_main_is_running ())
411      gconf_main_quit ();
412    break;
413
414  case SIGUSR1:
415    /* it'd be nice to log a message here but it's not very safe, so */
416    gconf_log_debug_messages = !gconf_log_debug_messages;
417    break;
418   
419  default:
420    break;
421  }
422}
423
424PortableServer_POA
425gconf_get_poa ()
426{
427  return the_poa;
428}
429
430static void
431log_handler (const gchar   *log_domain,
432             GLogLevelFlags log_level,
433             const gchar   *message,
434             gpointer       user_data)
435{
436  GConfLogPriority pri = GCL_WARNING;
437 
438  switch (log_level)
439    {
440    case G_LOG_LEVEL_ERROR:
441    case G_LOG_LEVEL_CRITICAL:
442      pri = GCL_ERR;
443      break;
444
445    case G_LOG_LEVEL_WARNING:
446      pri = GCL_WARNING;
447      break;
448
449    case G_LOG_LEVEL_MESSAGE:
450    case G_LOG_LEVEL_INFO:
451      pri = GCL_INFO;
452      break;
453
454    case G_LOG_LEVEL_DEBUG:
455      pri = GCL_DEBUG;
456      break;
457
458    default:
459      break;
460    }
461
462  gconf_log (pri, "%s", message);
463}
464
465int
466main(int argc, char** argv)
467{
468  struct sigaction act;
469  sigset_t empty_mask;
470  /*   PortableServer_ObjectId objid = {0, sizeof("ConfigServer"), "ConfigServer"}; */
471  PortableServer_ObjectId* objid;
472  CORBA_Environment ev;
473  CORBA_ORB orb;
474  gchar* logname;
475  const gchar* username;
476  guint len;
477  gchar* ior;
478  int exit_code = 0;
479  GError *err;
480  char *lock_dir;
481  char *gconfd_dir;
482  int dev_null_fd;
483  int write_byte_fd;
484 
485  /* Now this is an argument parser */
486  if (argc > 1)
487    write_byte_fd = atoi (argv[1]);
488  else
489    write_byte_fd = -1;
490 
491  chdir ("/");
492
493  /* This is so we don't prevent unmounting of devices. We divert
494   * all messages to syslog
495   */
496
497  if (!g_getenv ("GCONF_DEBUG_OUTPUT"))
498    {
499      dev_null_fd = open ("/dev/null", O_RDWR);
500      if (dev_null_fd >= 0)
501        {
502          dup2 (dev_null_fd, 0);
503          dup2 (dev_null_fd, 1);
504          dup2 (dev_null_fd, 2);
505        }
506    }
507 
508  umask (022);
509 
510  gconf_set_daemon_mode(TRUE);
511 
512  /* Logs */
513  username = g_get_user_name();
514  len = strlen(username) + strlen("gconfd") + 15;
515  logname = g_malloc(len);
516  g_snprintf(logname, len, "gconfd (%s-%u)", username, (guint)getpid());
517
518  openlog (logname, LOG_NDELAY, LOG_USER);
519
520  g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
521                     log_handler, NULL);
522 
523  /* openlog() does not copy logname - what total brokenness.
524     So we free it at the end of main() */
525 
526  gconf_log (GCL_INFO, _("starting (version %s), pid %u user '%s'"),
527             VERSION, (guint)getpid(), g_get_user_name());
528
529#ifdef GCONF_ENABLE_DEBUG
530  gconf_log (GCL_DEBUG, "GConf was built with debugging features enabled");
531#endif
532 
533  /* Session setup */
534  sigemptyset (&empty_mask);
535  act.sa_handler = signal_handler;
536  act.sa_mask    = empty_mask;
537  act.sa_flags   = 0;
538  sigaction (SIGTERM,  &act, 0);
539  sigaction (SIGILL,  &act, 0);
540  sigaction (SIGBUS,  &act, 0);
541  sigaction (SIGFPE,  &act, 0);
542  sigaction (SIGHUP,  &act, 0);
543  sigaction (SIGSEGV, &act, 0);
544  sigaction (SIGABRT, &act, 0);
545
546  act.sa_handler = SIG_IGN;
547  sigaction (SIGINT, &act, 0);
548
549  CORBA_exception_init(&ev);
550
551  init_databases ();
552
553  orb = gconf_orb_get();
554 
555  POA_ConfigServer__init(&poa_server_servant, &ev);
556 
557  the_poa = (PortableServer_POA)CORBA_ORB_resolve_initial_references(orb, "RootPOA", &ev);
558  PortableServer_POAManager_activate(PortableServer_POA__get_the_POAManager(the_poa, &ev), &ev);
559
560  objid = PortableServer_POA_activate_object(the_poa, &poa_server_servant, &ev);
561 
562  server = PortableServer_POA_servant_to_reference(the_poa,
563                                                   &poa_server_servant,
564                                                   &ev);
565  if (CORBA_Object_is_nil(server, &ev))
566    {
567      gconf_log(GCL_ERR, _("Failed to get object reference for ConfigServer"));
568      return 1;
569    }
570
571  /* Needs to be done before loading sources */
572  ior = CORBA_ORB_object_to_string (orb, server, &ev);
573  gconf_set_daemon_ior (ior);
574  CORBA_free (ior);
575
576  gconfd_dir = gconf_get_daemon_dir ();
577  lock_dir = gconf_get_lock_dir ();
578
579  if (mkdir (gconfd_dir, 0700) < 0 && errno != EEXIST)
580    gconf_log (GCL_WARNING, _("Failed to create %s: %s"),
581               gconfd_dir, g_strerror (errno));
582 
583 
584  err = NULL;
585  daemon_lock = gconf_get_lock (lock_dir, &err);
586
587  if (daemon_lock != NULL)
588    {
589      /* This loads backends and so on. It needs to be done before
590       * we can handle any requests, so before we hit the
591       * main loop. if daemon_lock == NULL we won't hit the
592       * main loop.
593       */
594      gconf_server_load_sources ();
595    }
596 
597  /* notify caller that we're done either getting the lock
598   * or not getting it
599   */
600  if (write_byte_fd >= 0)
601    {
602      char buf[1] = { 'g' };
603      if (write (write_byte_fd, buf, 1) != 1)
604        {
605          gconf_log (GCL_ERR, _("Failed to write byte to pipe fd %d so client program may hang: %s"), write_byte_fd, g_strerror (errno));
606        }
607
608      close (write_byte_fd);
609    }
610 
611  if (daemon_lock == NULL)
612    {
613      g_assert (err);
614
615      gconf_log (GCL_WARNING, _("Failed to get lock for daemon, exiting: %s"),
616                 err->message);
617      g_error_free (err);
618
619      enter_shutdown ();
620      shutdown_databases ();
621     
622      return 1;
623    }
624
625  /* Read saved log file, if any */
626  logfile_read ();
627 
628  gconf_main ();
629
630  if (in_shutdown)
631    exit_code = 1; /* means someone already called enter_shutdown() */
632 
633  /* This starts bouncing all incoming requests (and we aren't running
634   * the main loop anyway, so they won't get processed)
635   */
636  enter_shutdown ();
637
638  /* Save current state in logfile (may compress the logfile a good
639   * bit)
640   */
641  logfile_save ();
642 
643  shutdown_databases ();
644
645  gconfd_locale_cache_drop ();
646
647  /* Now we can release the lock */
648
649  server = CORBA_OBJECT_NIL;
650
651  if (daemon_lock)
652    {
653      err = NULL;
654      gconf_release_lock (daemon_lock, &err);
655      if (err != NULL)
656        {
657          gconf_log (GCL_WARNING, _("Error releasing lockfile: %s"),
658                     err->message);
659          g_error_free (err);
660        }
661    }
662
663  daemon_lock = NULL;
664 
665  gconf_log (GCL_INFO, _("Exiting"));
666 
667  g_free (logname);
668 
669  return exit_code;
670}
671
672/*
673 * Main loop
674 */
675
676static GSList* main_loops = NULL;
677static guint timeout_id = 0;
678static gboolean need_log_cleanup = FALSE;
679
680static gboolean
681periodic_cleanup_timeout(gpointer data)
682
683  gconf_log (GCL_DEBUG, "Performing periodic cleanup, expiring cache cruft");
684 
685  drop_old_clients ();
686  drop_old_databases ();
687
688  if (no_databases_in_use () && client_count () == 0)
689    {
690      gconf_log (GCL_INFO, _("GConf server is not in use, shutting down."));
691      gconf_main_quit ();
692      return FALSE;
693    }
694 
695  /* expire old locale cache entries */
696  gconfd_locale_cache_expire ();
697
698  if (!need_log_cleanup)
699    {
700      gconf_log (GCL_DEBUG, "No log file saving needed in periodic cleanup handler");
701      return TRUE;
702    }
703 
704  /* Compress the running state file */
705  logfile_save ();
706
707  need_log_cleanup = FALSE;
708 
709  return TRUE;
710}
711
712void
713gconfd_need_log_cleanup (void)
714{
715  need_log_cleanup = TRUE;
716}
717
718static void
719gconf_main(void)
720{
721  GMainLoop* loop;
722
723  loop = g_main_new(TRUE);
724
725  if (main_loops == NULL)
726    {
727#ifdef GCONF_ENABLE_DEBUG
728      gulong timeout_len = 1000*60*1; /* 1 sec * 60 s/min * 1 min */
729#else
730      gulong timeout_len = 1000*60*15; /* 1 sec * 60 s/min * 2 min */
731#endif
732     
733      g_assert(timeout_id == 0);
734      timeout_id = g_timeout_add (timeout_len,
735                                  periodic_cleanup_timeout,
736                                  NULL);
737
738    }
739 
740  main_loops = g_slist_prepend(main_loops, loop);
741
742  g_main_run(loop);
743
744  main_loops = g_slist_remove(main_loops, loop);
745
746  if (main_loops == NULL)
747    {
748      g_assert(timeout_id != 0);
749      g_source_remove(timeout_id);
750      timeout_id = 0;
751    }
752 
753  g_main_destroy(loop);
754}
755
756static void
757gconf_main_quit(void)
758{
759  g_return_if_fail(main_loops != NULL);
760
761  g_main_quit(main_loops->data);
762}
763
764static gboolean
765gconf_main_is_running (void)
766{
767  return main_loops != NULL;
768}
769
770/*
771 * Database storage
772 */
773
774static GList* db_list = NULL;
775static GHashTable* dbs_by_address = NULL;
776static GConfDatabase *default_db = NULL;
777
778static void
779init_databases (void)
780{
781  gconfd_need_log_cleanup ();
782 
783  g_assert(db_list == NULL);
784  g_assert(dbs_by_address == NULL);
785 
786  dbs_by_address = g_hash_table_new (g_str_hash, g_str_equal);
787
788  /* Default database isn't in the address hash since it has
789     multiple addresses in a stack
790  */
791}
792
793static void
794set_default_database (GConfDatabase* db)
795{
796  gconfd_need_log_cleanup ();
797 
798  default_db = db;
799 
800  /* Default database isn't in the address hash since it has
801     multiple addresses in a stack
802  */
803}
804
805static void
806register_database (GConfDatabase *db)
807{
808  gconfd_need_log_cleanup ();
809 
810  if (db->sources->sources)
811    safe_g_hash_table_insert(dbs_by_address,
812                             ((GConfSource*)db->sources->sources->data)->address,
813                             db);
814 
815  db_list = g_list_prepend (db_list, db);
816}
817
818static void
819unregister_database (GConfDatabase *db)
820{
821  gconfd_need_log_cleanup ();
822 
823  if (db->sources->sources)
824    g_hash_table_remove(dbs_by_address,
825                        ((GConfSource*)(db->sources->sources->data))->address);
826
827  db_list = g_list_remove (db_list, db);
828
829  gconf_database_free (db);
830}
831
832static GConfDatabase*
833lookup_database (const gchar *address)
834{
835  if (address == NULL)
836    return default_db;
837  else
838    return g_hash_table_lookup (dbs_by_address, address);
839}
840
841static GConfDatabase*
842obtain_database (const gchar *address,
843                 GError **err)
844{
845 
846  GConfSources* sources;
847  const gchar* addresses[] = { NULL, NULL };
848  GError* error = NULL;
849  GConfDatabase *db;
850
851  addresses[0] = address;
852  db = lookup_database (address);
853
854  if (db)
855    return db;
856 
857  sources = gconf_sources_new_from_addresses(addresses, &error);
858
859  if (error != NULL)
860    {
861      if (err)
862        *err = error;
863      else
864        g_error_free (error);
865
866      return NULL;
867    }
868 
869  if (sources == NULL)
870    return NULL;
871
872  db = gconf_database_new (sources);
873
874  register_database (db);
875
876  return db;
877}
878
879static void
880drop_old_databases(void)
881{
882  GList *tmp_list;
883  GList *dead = NULL;
884  GTime now;
885 
886  now = time(NULL);
887
888  gconf_database_drop_dead_listeners (default_db);
889 
890  tmp_list = db_list;
891  while (tmp_list)
892    {
893      GConfDatabase* db = tmp_list->data;
894
895      /* Drop any listeners whose clients are gone. */
896      gconf_database_drop_dead_listeners (db);
897     
898      if (db->listeners &&                             /* not already hibernating */
899          gconf_listeners_count(db->listeners) == 0 && /* Can hibernate */
900          (now - db->last_access) > (60*20))           /* 20 minutes without access */
901        {
902          dead = g_list_prepend (dead, db);
903        }
904     
905      tmp_list = g_list_next (tmp_list);
906    }
907
908  tmp_list = dead;
909  while (tmp_list)
910    {
911      GConfDatabase* db = tmp_list->data;
912
913      unregister_database (db);
914           
915      tmp_list = g_list_next (tmp_list);
916    }
917
918  g_list_free (dead);
919}
920
921static void
922shutdown_databases (void)
923{
924  GList *tmp_list; 
925
926  /* This may be called before we init fully,
927   * so check that everything != NULL
928   */
929 
930  tmp_list = db_list;
931
932  while (tmp_list)
933    {
934      GConfDatabase *db = tmp_list->data;
935
936      gconf_database_free (db);
937     
938      tmp_list = g_list_next (tmp_list);
939    }
940
941  g_list_free (db_list);
942  db_list = NULL;
943
944  if (dbs_by_address)
945    g_hash_table_destroy(dbs_by_address);
946
947  dbs_by_address = NULL;
948
949  if (default_db)
950    gconf_database_free (default_db);
951
952  default_db = NULL;
953}
954
955static gboolean
956no_databases_in_use (void)
957{
958  /* Only the default database still open, and
959   * it has no listeners
960   */
961  return db_list == NULL &&
962    gconf_listeners_count (default_db->listeners) == 0;
963}
964
965/*
966 * Cleanup
967 */
968
969static void
970enter_shutdown(void)
971{
972  in_shutdown = TRUE;
973}
974
975
976/* Exceptions */
977
978gboolean
979gconf_set_exception(GError** error,
980                    CORBA_Environment* ev)
981{
982  GConfError en;
983
984  if (error == NULL)
985    return FALSE;
986
987  if (*error == NULL)
988    return FALSE;
989 
990  en = (*error)->code;
991
992  /* success is not supposed to get set */
993  g_return_val_if_fail(en != GCONF_ERROR_SUCCESS, FALSE);
994 
995  {
996    ConfigException* ce;
997
998    ce = ConfigException__alloc();
999    g_assert(error != NULL);
1000    g_assert(*error != NULL);
1001    g_assert((*error)->message != NULL);
1002    ce->message = CORBA_string_dup((gchar*)(*error)->message); /* cast const */
1003     
1004    switch (en)
1005      {
1006      case GCONF_ERROR_FAILED:
1007        ce->err_no = ConfigFailed;
1008        break;
1009      case GCONF_ERROR_NO_PERMISSION:
1010        ce->err_no = ConfigNoPermission;
1011        break;
1012      case GCONF_ERROR_BAD_ADDRESS:
1013        ce->err_no = ConfigBadAddress;
1014        break;
1015      case GCONF_ERROR_BAD_KEY:
1016        ce->err_no = ConfigBadKey;
1017        break;
1018      case GCONF_ERROR_PARSE_ERROR:
1019        ce->err_no = ConfigParseError;
1020        break;
1021      case GCONF_ERROR_CORRUPT:
1022        ce->err_no = ConfigCorrupt;
1023        break;
1024      case GCONF_ERROR_TYPE_MISMATCH:
1025        ce->err_no = ConfigTypeMismatch;
1026        break;
1027      case GCONF_ERROR_IS_DIR:
1028        ce->err_no = ConfigIsDir;
1029        break;
1030      case GCONF_ERROR_IS_KEY:
1031        ce->err_no = ConfigIsKey;
1032        break;
1033      case GCONF_ERROR_NO_WRITABLE_DATABASE:
1034        ce->err_no = ConfigNoWritableDatabase;       
1035        break;
1036      case GCONF_ERROR_IN_SHUTDOWN:
1037        ce->err_no = ConfigInShutdown;
1038        break;
1039      case GCONF_ERROR_OVERRIDDEN:
1040        ce->err_no = ConfigOverridden;
1041        break;
1042      case GCONF_ERROR_LOCK_FAILED:
1043        ce->err_no = ConfigLockFailed;
1044        break;
1045
1046      case GCONF_ERROR_OAF_ERROR:
1047      case GCONF_ERROR_LOCAL_ENGINE:
1048      case GCONF_ERROR_NO_SERVER:
1049      case GCONF_ERROR_SUCCESS:
1050      default:
1051        gconf_log (GCL_ERR, "Unhandled error code %d", en);
1052        g_assert_not_reached();
1053        break;
1054      }
1055
1056    CORBA_exception_set(ev, CORBA_USER_EXCEPTION,
1057                        ex_ConfigException, ce);
1058
1059    gconf_log(GCL_ERR, _("Returning exception: %s"), (*error)->message);
1060     
1061    g_error_free(*error);
1062    *error = NULL;
1063     
1064    return TRUE;
1065  }
1066}
1067
1068gboolean
1069gconfd_check_in_shutdown (CORBA_Environment *ev)
1070{
1071  if (in_shutdown)
1072    {
1073      ConfigException* ce;
1074     
1075      ce = ConfigException__alloc();
1076      ce->message = CORBA_string_dup("config server is currently shutting down");
1077      ce->err_no = ConfigInShutdown;
1078
1079      CORBA_exception_set(ev, CORBA_USER_EXCEPTION,
1080                          ex_ConfigException, ce);
1081
1082      return TRUE;
1083    }
1084  else
1085    return FALSE;
1086}
1087
1088/*
1089 * Logging
1090 */
1091
1092/*
1093   The log file records the current listeners we have registered,
1094   so we can restore them if we exit and restart.
1095
1096   Basically:
1097
1098   1) On startup, we parse any logfile and try to restore the
1099      listeners contained therein. As we restore each listener (give
1100      clients a new listener ID) we append a removal of the previous
1101      daemon's listener and the addition of our own listener to the
1102      logfile; this means that if we crash and have to restore a
1103      client's listener a second time, we'll have the client's current
1104      listener ID. If all goes well we then atomically rewrite the
1105      parsed logfile with the resulting current state, to keep the logfile
1106      compact.
1107
1108   2) While running, we keep a FILE* open and whenever we add/remove
1109      a listener we write a line to the logfile recording it,
1110      to keep the logfile always up-to-date.
1111
1112   3) On normal exit, and also periodically (every hour or so, say) we
1113      atomically write over the running log with our complete current
1114      state, to keep the running log from growing without bound.
1115*/
1116
1117static void
1118get_log_names (gchar **logdir, gchar **logfile)
1119{
1120  *logdir = gconf_concat_dir_and_key (g_get_home_dir (), ".gconfd");
1121  *logfile = gconf_concat_dir_and_key (*logdir, "saved_state");
1122}
1123
1124static void close_append_handle (void);
1125
1126static FILE* append_handle = NULL;
1127static guint append_handle_timeout = 0;
1128
1129static gboolean
1130close_append_handle_timeout(gpointer data)
1131{
1132  close_append_handle ();
1133
1134  /* uninstall the timeout */
1135  append_handle_timeout = 0;
1136  return FALSE;
1137}
1138
1139static gboolean
1140open_append_handle (GError **err)
1141{
1142  if (append_handle == NULL)
1143    {
1144      gchar *logdir;
1145      gchar *logfile;
1146
1147      get_log_names (&logdir, &logfile);
1148     
1149      mkdir (logdir, 0700); /* ignore failure, we'll catch failures
1150                             * that matter on open()
1151                             */
1152     
1153      append_handle = fopen (logfile, "a");
1154
1155      if (append_handle == NULL)
1156        {
1157          gconf_set_error (err,
1158                           GCONF_ERROR_FAILED,
1159                           _("Failed to open gconfd logfile; won't be able to restore listeners after gconfd shutdown (%s)"),
1160                           strerror (errno));
1161         
1162          g_free (logdir);
1163          g_free (logfile);
1164
1165          return FALSE;
1166        }
1167     
1168      g_free (logdir);
1169      g_free (logfile);
1170
1171
1172      {
1173        const gulong timeout_len = 1000*60*0.5; /* 1 sec * 60 s/min * 0.5 min */
1174
1175        if (append_handle_timeout != 0)
1176          g_source_remove (append_handle_timeout);
1177       
1178        append_handle_timeout = g_timeout_add (timeout_len,
1179                                               close_append_handle_timeout,
1180                                               NULL);
1181      }
1182    }
1183
1184  return TRUE;
1185}
1186
1187static void
1188close_append_handle (void)
1189{
1190  if (append_handle)
1191    {
1192      if (fclose (append_handle) < 0)
1193        gconf_log (GCL_WARNING,
1194                   _("Failed to close gconfd logfile; data may not have been properly saved (%s)"),
1195                   strerror (errno));
1196
1197      append_handle = NULL;
1198
1199      if (append_handle_timeout != 0)
1200        {
1201          g_source_remove (append_handle_timeout);
1202          append_handle_timeout = 0;
1203        }
1204    }
1205}
1206
1207/* Atomically save our current state, if possible; otherwise
1208 * leave the running log in place.
1209 */
1210static void
1211logfile_save (void)
1212{
1213  GList *tmp_list;
1214  gchar *logdir = NULL;
1215  gchar *logfile = NULL;
1216  gchar *tmpfile = NULL;
1217  gchar *tmpfile2 = NULL;
1218  GString *saveme = NULL;
1219  gint fd = -1;
1220 
1221  /* Close the running log */
1222  close_append_handle ();
1223 
1224  get_log_names (&logdir, &logfile);
1225
1226  mkdir (logdir, 0700); /* ignore failure, we'll catch failures
1227                         * that matter on open()
1228                         */
1229
1230  saveme = g_string_new ("");
1231
1232  /* Clients */
1233  log_clients_to_string (saveme);
1234 
1235  /* Default database */
1236  gconf_database_log_listeners_to_string (default_db,
1237                                          TRUE,
1238                                          saveme);
1239
1240  /* Other databases */
1241 
1242  tmp_list = db_list;
1243
1244  while (tmp_list)
1245    {
1246      GConfDatabase *db = tmp_list->data;
1247
1248      gconf_database_log_listeners_to_string (db,
1249                                              FALSE,
1250                                              saveme);
1251     
1252      tmp_list = g_list_next (tmp_list);
1253    }
1254 
1255  /* Now try saving the string to a temporary file */
1256  tmpfile = g_strconcat (logfile, ".tmp", NULL);
1257 
1258  fd = open (tmpfile, O_WRONLY | O_CREAT | O_TRUNC, 0700);
1259
1260  if (fd < 0)
1261    {
1262      gconf_log (GCL_WARNING,
1263                 _("Could not open saved state file '%s' for writing: %s"),
1264                 tmpfile, strerror (errno));
1265     
1266      goto out;
1267    }
1268
1269 again:
1270 
1271  if (write (fd, saveme->str, saveme->len) < 0)
1272    {
1273      if (errno == EINTR)
1274        goto again;
1275     
1276      gconf_log (GCL_WARNING,
1277                 _("Could not write saved state file '%s' fd: %d: %s"),
1278                 tmpfile, fd, strerror (errno));
1279
1280      goto out;
1281    }
1282
1283  if (close (fd) < 0)
1284    {
1285      gconf_log (GCL_WARNING,
1286                 _("Failed to close new saved state file '%s': %s"),
1287                 tmpfile, strerror (errno));
1288      goto out;
1289    }
1290
1291  fd = -1;
1292 
1293  /* Move the main saved state file aside, if it exists */
1294  if (gconf_file_exists (logfile))
1295    {
1296      tmpfile2 = g_strconcat (logfile, ".orig", NULL);
1297      if (rename (logfile, tmpfile2) < 0)
1298        {
1299          gconf_log (GCL_WARNING,
1300                     _("Could not move aside old saved state file '%s': %s"),
1301                     logfile, strerror (errno));
1302          goto out;
1303        }
1304    }
1305
1306  /* Move the new saved state file into place */
1307  if (rename (tmpfile, logfile) < 0)
1308    {
1309      gconf_log (GCL_WARNING,
1310                 _("Failed to move new save state file into place: %s"),
1311                 strerror (errno));
1312
1313      /* Try to restore old file */
1314      if (tmpfile2)
1315        {
1316          if (rename (tmpfile2, logfile) < 0)
1317            {
1318              gconf_log (GCL_WARNING,
1319                         _("Failed to restore original saved state file that had been moved to '%s': %s"),
1320                         tmpfile2, strerror (errno));
1321
1322            }
1323        }
1324     
1325      goto out;
1326    }
1327
1328  /* Get rid of original saved state file if everything succeeded */
1329  if (tmpfile2)
1330    unlink (tmpfile2);
1331 
1332 out:
1333  if (saveme)
1334    g_string_free (saveme, TRUE);
1335  g_free (logdir);
1336  g_free (logfile);
1337  g_free (tmpfile);
1338  g_free (tmpfile2);
1339
1340  if (fd >= 0)
1341    close (fd);
1342}
1343
1344typedef struct _ListenerLogEntry ListenerLogEntry;
1345
1346struct _ListenerLogEntry
1347{
1348  guint connection_id;
1349  gchar *ior;
1350  gchar *address;
1351  gchar *location;
1352};
1353
1354static guint
1355listener_logentry_hash (gconstpointer v)
1356{
1357  const ListenerLogEntry *lle = v;
1358
1359  return
1360    (lle->connection_id         & 0xff000000) |
1361    (g_str_hash (lle->ior)      & 0x00ff0000) |
1362    (g_str_hash (lle->address)  & 0x0000ff00) |
1363    (g_str_hash (lle->location) & 0x000000ff);
1364}
1365
1366static gboolean
1367listener_logentry_equal (gconstpointer ap, gconstpointer bp)
1368{
1369  const ListenerLogEntry *a = ap;
1370  const ListenerLogEntry *b = bp;
1371
1372  return
1373    a->connection_id == b->connection_id &&
1374    strcmp (a->location, b->location) == 0 &&
1375    strcmp (a->ior, b->ior) == 0 &&
1376    strcmp (a->address, b->address) == 0;
1377}
1378
1379/* Return value indicates whether we "handled" this line of text */
1380static gboolean
1381parse_listener_entry (GHashTable *entries,
1382                      gchar      *text)
1383{
1384  gboolean add;
1385  gchar *p;
1386  gchar *ior;
1387  gchar *address;
1388  gchar *location;
1389  gchar *end;
1390  guint connection_id;
1391  GError *err;
1392  ListenerLogEntry *lle;
1393  ListenerLogEntry *old;
1394 
1395  if (strncmp (text, "ADD", 3) == 0)
1396    {
1397      add = TRUE;
1398      p = text + 3;
1399    }
1400  else if (strncmp (text, "REMOVE", 6) == 0)
1401    {
1402      add = FALSE;
1403      p = text + 6;
1404    }
1405  else
1406    {
1407      return FALSE;
1408    }
1409 
1410  while (*p && isspace (*p))
1411    ++p;
1412
1413  errno = 0;
1414  end = NULL;
1415  connection_id = strtoul (p, &end, 10);
1416  if (end == p || errno != 0)
1417    {
1418      gconf_log (GCL_DEBUG,
1419                 "Failed to parse connection ID in saved state file");
1420     
1421      return TRUE;
1422    }
1423
1424  if (connection_id == 0)
1425    {
1426      gconf_log (GCL_DEBUG,
1427                 "Connection ID 0 in saved state file is not valid");
1428      return TRUE;
1429    }
1430 
1431  p = end;
1432
1433  while (*p && isspace (*p))
1434    ++p;
1435
1436  err = NULL;
1437  end = NULL;
1438  gconf_unquote_string_inplace (p, &end, &err);
1439  if (err != NULL)
1440    {
1441      gconf_log (GCL_DEBUG,
1442                 "Failed to unquote config source address from saved state file: %s",
1443                 err->message);
1444
1445      g_error_free (err);
1446     
1447      return TRUE;
1448    }
1449
1450  address = p;
1451  p = end;
1452
1453  while (*p && isspace (*p))
1454    ++p;
1455 
1456  err = NULL;
1457  end = NULL;
1458  gconf_unquote_string_inplace (p, &end, &err);
1459  if (err != NULL)
1460    {
1461      gconf_log (GCL_DEBUG,
1462                 "Failed to unquote listener location from saved state file: %s",
1463                 err->message);
1464
1465      g_error_free (err);
1466     
1467      return TRUE;
1468    }
1469
1470  location = p;
1471  p = end;
1472
1473  while (*p && isspace (*p))
1474    ++p;
1475 
1476  err = NULL;
1477  end = NULL;
1478  gconf_unquote_string_inplace (p, &end, &err);
1479  if (err != NULL)
1480    {
1481      gconf_log (GCL_DEBUG,
1482                 "Failed to unquote IOR from saved state file: %s",
1483                 err->message);
1484     
1485      g_error_free (err);
1486     
1487      return TRUE;
1488    }
1489 
1490  ior = p;
1491  p = end;   
1492
1493  lle = g_new (ListenerLogEntry, 1);
1494  lle->connection_id = connection_id;
1495  lle->address = address;
1496  lle->ior = ior;
1497  lle->location = location;
1498
1499  if (*(lle->address) == '\0' ||
1500      *(lle->ior) == '\0' ||
1501      *(lle->location) == '\0')
1502    {
1503      gconf_log (GCL_DEBUG,
1504                 "Saved state file listener entry didn't contain all the fields; ignoring.");
1505
1506      g_free (lle);
1507
1508      return TRUE;
1509    }
1510 
1511  old = g_hash_table_lookup (entries, lle);
1512
1513  if (old)
1514    {
1515      if (add)
1516        {
1517          gconf_log (GCL_DEBUG,
1518                     "Saved state file records the same listener added twice; ignoring the second instance");
1519          goto quit;
1520        }
1521      else
1522        {
1523          /* This entry was added, then removed. */
1524          g_hash_table_remove (entries, lle);
1525          goto quit;
1526        }
1527    }
1528  else
1529    {
1530      if (add)
1531        {
1532          g_hash_table_insert (entries, lle, lle);
1533         
1534          return TRUE;
1535        }
1536      else
1537        {
1538          gconf_log (GCL_DEBUG,
1539                     "Saved state file had a removal of a listener that wasn't added; ignoring the removal.");
1540          goto quit;
1541        }
1542    }
1543 
1544 quit:
1545  g_free (lle);
1546
1547  return TRUE;
1548}               
1549
1550/* Return value indicates whether we "handled" this line of text */
1551static gboolean
1552parse_client_entry (GHashTable *clients,
1553                    gchar      *text)
1554{
1555  gboolean add;
1556  gchar *ior;
1557  GError *err;
1558  gchar *old;
1559  gchar *p;
1560  gchar *end;
1561 
1562  if (strncmp (text, "CLIENTADD", 9) == 0)
1563    {
1564      add = TRUE;
1565      p = text + 9;
1566    }
1567  else if (strncmp (text, "CLIENTREMOVE", 12) == 0)
1568    {
1569      add = FALSE;
1570      p = text + 12;
1571    }
1572  else
1573    {
1574      return FALSE;
1575    }
1576 
1577  while (*p && isspace (*p))
1578    ++p;
1579 
1580  err = NULL;
1581  end = NULL;
1582  gconf_unquote_string_inplace (p, &end, &err);
1583  if (err != NULL)
1584    {
1585      gconf_log (GCL_DEBUG,
1586                 "Failed to unquote IOR from saved state file: %s",
1587                 err->message);
1588     
1589      g_error_free (err);
1590     
1591      return TRUE;
1592    }
1593 
1594  ior = p;
1595  p = end;   
1596 
1597  old = g_hash_table_lookup (clients, ior);
1598
1599  if (old)
1600    {
1601      if (add)
1602        {
1603          gconf_log (GCL_DEBUG,
1604                     "Saved state file records the same client added twice; ignoring the second instance");
1605          goto quit;
1606        }
1607      else
1608        {
1609          /* This entry was added, then removed. */
1610          g_hash_table_remove (clients, ior);
1611          goto quit;
1612        }
1613    }
1614  else
1615    {
1616      if (add)
1617        {
1618          g_hash_table_insert (clients, ior, ior);
1619         
1620          return TRUE;
1621        }
1622      else
1623        {
1624          gconf_log (GCL_DEBUG,
1625                     "Saved state file had a removal of a client that wasn't added; ignoring the removal.");
1626          goto quit;
1627        }
1628    }
1629 
1630 quit:
1631
1632  return TRUE;
1633}
1634
1635static void
1636restore_client (const gchar *ior)
1637{
1638  ConfigListener cl;
1639  CORBA_Environment ev;
1640 
1641  CORBA_exception_init (&ev);
1642 
1643  cl = CORBA_ORB_string_to_object (gconf_orb_get (),
1644                                   (gchar*)ior,
1645                                   &ev);
1646
1647  CORBA_exception_free (&ev);
1648 
1649  if (CORBA_Object_is_nil (cl, &ev))
1650    {
1651      CORBA_exception_free (&ev);
1652
1653      gconf_log (GCL_DEBUG,
1654                 "Client in saved state file no longer exists, not restoring it as a client");
1655     
1656      return;
1657    }
1658
1659  ConfigListener_drop_all_caches (cl, &ev);
1660 
1661  if (ev._major != CORBA_NO_EXCEPTION)
1662    {
1663      gconf_log (GCL_DEBUG, "Failed to update client in saved state file, probably the client no longer exists");
1664
1665      goto finished;
1666    }
1667
1668  /* Add the client, since it still exists. Note that the client still
1669   * has the wrong server object reference, so next time it tries to
1670   * contact the server it will re-add itself; we just live with that,
1671   * it's not a problem.
1672   */
1673  add_client (cl);
1674 
1675 finished:
1676  CORBA_Object_release (cl, &ev);
1677
1678  CORBA_exception_free (&ev);
1679}
1680
1681static void
1682restore_listener (GConfDatabase* db,
1683                  ListenerLogEntry *lle)
1684{
1685  ConfigListener cl;
1686  CORBA_Environment ev;
1687  guint new_cnxn;
1688  GError *err;
1689 
1690  CORBA_exception_init (&ev);
1691 
1692  cl = CORBA_ORB_string_to_object (gconf_orb_get (),
1693                                   lle->ior,
1694                                   &ev);
1695
1696  CORBA_exception_free (&ev);
1697 
1698  if (CORBA_Object_is_nil (cl, &ev))
1699    {
1700      CORBA_exception_free (&ev);
1701
1702      gconf_log (GCL_DEBUG,
1703                 "Client in saved state file no longer exists, not updating its listener connections");
1704     
1705      return;
1706    }
1707
1708  /* "Cancel" the addition of the listener in the saved state file,
1709   * so that if we reload the saved state file a second time
1710   * for some reason, we don't try to add this listener that time.
1711   */
1712
1713  err = NULL; 
1714  if (!gconfd_logfile_change_listener (db,
1715                                       FALSE, /* remove */
1716                                       lle->connection_id,
1717                                       cl,
1718                                       lle->location,
1719                                       &err))
1720    {
1721      gconf_log (GCL_DEBUG,
1722                 "Failed to cancel previous daemon's listener in saved state file: %s",
1723                 err->message);
1724      g_error_free (err);
1725    } 
1726
1727  new_cnxn = gconf_database_readd_listener (db, cl, lle->location);
1728
1729  gconf_log (GCL_DEBUG, "Attempting to update listener from saved state file, old connection %u, new connectin %u", (guint) lle->connection_id, (guint) new_cnxn);
1730 
1731  ConfigListener_update_listener (cl,
1732                                  db->objref,
1733                                  lle->address,
1734                                  lle->connection_id,
1735                                  lle->location,
1736                                  new_cnxn,
1737                                  &ev);
1738 
1739  if (ev._major != CORBA_NO_EXCEPTION)
1740    {
1741      gconf_log (GCL_DEBUG, "Failed to update listener in saved state file, probably the client no longer exists");
1742
1743      /* listener will get removed next time we try to notify -
1744       * we already appended a cancel of the listener to the
1745       * saved state file.
1746       */
1747      goto finished;
1748    }
1749
1750  /* Successfully notified client of new connection ID, so put that
1751   * connection ID in the saved state file.
1752   */
1753  err = NULL; 
1754  if (!gconfd_logfile_change_listener (db,
1755                                       TRUE, /* add */
1756                                       new_cnxn,
1757                                       cl,
1758                                       lle->location,
1759                                       &err))
1760    {
1761      gconf_log (GCL_DEBUG,
1762                 "Failed to re-add this daemon's listener ID in saved state file: %s",
1763                 err->message);
1764      g_error_free (err);
1765    }
1766
1767  /* We updated the listener, and logged that to the saved state
1768   * file. Yay!
1769   */
1770 
1771 finished:
1772 
1773  CORBA_Object_release (cl, &ev);
1774
1775  CORBA_exception_free (&ev);
1776}
1777
1778static void
1779listener_logentry_restore_and_destroy_foreach (gpointer key,
1780                                               gpointer value,
1781                                               gpointer data)
1782{
1783  ListenerLogEntry *lle = key;
1784  GConfDatabase *db;
1785 
1786  if (strcmp (lle->address, "def") == 0)
1787    db = default_db;
1788  else
1789    db = obtain_database (lle->address, NULL);
1790 
1791  if (db == NULL)
1792    {
1793      gconf_log (GCL_WARNING,
1794                 _("Unable to restore a listener on address '%s', couldn't resolve the database"),
1795                 lle->address);
1796      return;
1797    }
1798
1799  restore_listener (db, lle);
1800
1801  /* We don't need it anymore */
1802  g_free (lle);
1803}
1804
1805static void
1806restore_client_foreach (gpointer key,
1807                        gpointer value,
1808                        gpointer data)
1809{
1810  restore_client (key);
1811}
1812
1813
1814#ifndef HAVE_FLOCKFILE
1815#  define flockfile(f) (void)1
1816#  define funlockfile(f) (void)1
1817#  define getc_unlocked(f) getc(f)
1818#endif /* !HAVE_FLOCKFILE */
1819
1820static gchar*
1821read_line (FILE *f)
1822{
1823  int c;
1824  GString *str;
1825 
1826  str = g_string_new ("");
1827 
1828  flockfile (f);
1829
1830  while (TRUE)
1831    {
1832      c = getc_unlocked (f);
1833
1834      switch (c)
1835        {
1836        case EOF:
1837          if (ferror (f))
1838            {
1839              gconf_log (GCL_ERR,
1840                         _("Error reading saved state file: %s"),
1841                         g_strerror (errno));
1842            }
1843
1844          /* FALL THRU */
1845        case '\n':
1846          goto done;
1847          break;
1848
1849        default:
1850          g_string_append_c (str, c);
1851          break;
1852        }
1853    }
1854
1855 done:
1856  funlockfile (f);
1857
1858  if (str->len == 0)
1859    {
1860      g_string_free (str, TRUE);
1861      return NULL;
1862    }
1863  else
1864    {
1865      gchar *ret;
1866     
1867      ret = str->str;
1868      g_string_free (str, FALSE);
1869      return ret;
1870    }
1871}
1872
1873static void
1874logfile_read (void)
1875{
1876  gchar *logfile;
1877  gchar *logdir;
1878  GHashTable *entries;
1879  GHashTable *clients;
1880  FILE *f;
1881  gchar *line;
1882  GSList *lines = NULL;
1883 
1884  /* Just for good form */
1885  close_append_handle ();
1886 
1887  get_log_names (&logdir, &logfile);
1888
1889  f = fopen (logfile, "r");
1890 
1891  if (f == NULL)
1892    {
1893      gconf_log (GCL_ERR, _("Unable to open saved state file '%s': %s"),
1894                 logfile, g_strerror (errno));
1895
1896      goto finished;
1897    }
1898
1899  entries = g_hash_table_new (listener_logentry_hash, listener_logentry_equal);
1900  clients = g_hash_table_new (g_str_hash, g_str_equal);
1901
1902  line = read_line (f);
1903  while (line)
1904    {
1905      if (!parse_listener_entry (entries, line))
1906        {
1907          if (!parse_client_entry (clients, line))
1908            {
1909              gconf_log (GCL_DEBUG,
1910                         "Didn't understand line in saved state file: '%s'",
1911                         line);
1912              g_free (line);
1913              line = NULL;
1914            }
1915        }
1916
1917      if (line)
1918        lines = g_slist_prepend (lines, line);
1919     
1920      line = read_line (f);
1921    }
1922 
1923  /* Restore clients first */
1924  g_hash_table_foreach (clients,
1925                        restore_client_foreach,
1926                        NULL);
1927 
1928  /* Entries that still remain in the listener hash table were added
1929   * but not removed, so add them in this daemon instantiation and
1930   * update their listeners with the new connection ID etc.
1931   */
1932  g_hash_table_foreach (entries,
1933                        listener_logentry_restore_and_destroy_foreach,
1934                        NULL);
1935
1936  g_hash_table_destroy (entries);
1937  g_hash_table_destroy (clients);
1938
1939  /* Note that we need the strings to remain valid until we are totally
1940   * finished, because we store pointers to them in the log entry
1941   * hash.
1942   */
1943  g_slist_foreach (lines, (GFunc)g_free, NULL);
1944  g_slist_free (lines);
1945 
1946 finished:
1947 
1948  g_free (logfile);
1949  g_free (logdir);
1950}
1951
1952gboolean
1953gconfd_logfile_change_listener (GConfDatabase *db,
1954                                gboolean add,
1955                                guint connection_id,
1956                                ConfigListener listener,
1957                                const gchar *where,
1958                                GError **err)
1959{
1960  gchar *ior = NULL;
1961  gchar *quoted_db_name;
1962  gchar *quoted_where;
1963  gchar *quoted_ior;
1964 
1965  if (!open_append_handle (err))
1966    return FALSE;
1967 
1968  ior = gconf_object_to_string (listener, err);
1969 
1970  if (ior == NULL)
1971    return FALSE;
1972
1973  quoted_ior = gconf_quote_string (ior);
1974  g_free (ior);
1975  ior = NULL;
1976 
1977  if (db == default_db)
1978    quoted_db_name = gconf_quote_string ("def");
1979  else
1980    {
1981      const gchar *db_name;
1982     
1983      db_name = gconf_database_get_persistent_name (db);
1984     
1985      quoted_db_name = gconf_quote_string (db_name);
1986    }
1987
1988  quoted_where = gconf_quote_string (where);
1989
1990  /* KEEP IN SYNC with gconf-database.c log to string function */
1991  if (fprintf (append_handle, "%s %u %s %s %s\n",
1992               add ? "ADD" : "REMOVE", connection_id,
1993               quoted_db_name, quoted_where, quoted_ior) < 0)
1994    goto error;
1995
1996  if (fflush (append_handle) < 0)
1997    goto error;
1998
1999  g_free (quoted_db_name);
2000  g_free (quoted_ior);
2001  g_free (quoted_where);
2002 
2003  return TRUE;
2004
2005 error:
2006
2007  if (add)
2008    gconf_set_error (err,
2009                     GCONF_ERROR_FAILED,
2010                     _("Failed to log addition of listener to gconfd logfile; won't be able to re-add the listener if gconfd exits or shuts down (%s)"),
2011                     g_strerror (errno));
2012  else
2013    gconf_set_error (err,
2014                     GCONF_ERROR_FAILED,
2015                     _("Failed to log removal of listener to gconfd logfile; might erroneously re-add the listener if gconfd exits or shuts down (%s)"),
2016                     g_strerror (errno));
2017
2018  g_free (quoted_db_name);
2019  g_free (quoted_ior);
2020  g_free (quoted_where);
2021
2022  return FALSE;
2023}
2024
2025static void
2026log_client_change (const ConfigListener client,
2027                   gboolean add)
2028{
2029  gchar *ior = NULL;
2030  gchar *quoted_ior = NULL;
2031  GError *err;
2032 
2033  err = NULL;
2034  ior = gconf_object_to_string (client, &err);
2035
2036  if (err != NULL)
2037    {
2038      gconf_log (GCL_WARNING, _("Failed to get IOR for client: %s"),
2039                 err->message);
2040      g_error_free (err);
2041      return;
2042    }
2043     
2044  if (ior == NULL)
2045    return;
2046
2047  quoted_ior = gconf_quote_string (ior);
2048  g_free (ior);
2049  ior = NULL;
2050 
2051  if (!open_append_handle (&err))
2052    {
2053      gconf_log (GCL_WARNING, _("Failed to open saved state file: %s"),
2054                 err->message);
2055
2056      g_error_free (err);
2057     
2058      goto error;
2059    }
2060
2061  /* KEEP IN SYNC with log to string function */
2062  if (fprintf (append_handle, "%s %s\n",
2063               add ? "CLIENTADD" : "CLIENTREMOVE", quoted_ior) < 0)
2064    {
2065      gconf_log (GCL_WARNING,
2066                 _("Failed to write client add to saved state file: %s"),
2067                 strerror (errno));
2068      goto error;
2069    }
2070
2071  if (fflush (append_handle) < 0)
2072    {
2073      gconf_log (GCL_WARNING,
2074                 _("Failed to flush client add to saved state file: %s"),
2075                 strerror (errno));
2076      goto error;
2077    }
2078
2079 error:
2080  g_free (ior);
2081  g_free (quoted_ior);
2082}
2083
2084static void
2085log_client_add (const ConfigListener client)
2086{
2087  log_client_change (client, TRUE);
2088}
2089
2090static void
2091log_client_remove (const ConfigListener client)
2092{
2093  log_client_change (client, FALSE);
2094}
2095
2096/*
2097 * Client handling
2098 */
2099
2100static GHashTable *client_table = NULL;
2101
2102static void
2103add_client (const ConfigListener client)
2104{
2105  gconfd_need_log_cleanup ();
2106 
2107  if (client_table == NULL)
2108    client_table = g_hash_table_new ((GHashFunc) g_CORBA_Object_hash,
2109                                     (GCompareFunc) g_CORBA_Object_equal);
2110
2111  if (g_hash_table_lookup (client_table, client))
2112    {
2113      /* Ignore this case; it happens normally when we added a client
2114       * from the logfile, and the client also adds itself
2115       * when it gets a new server objref.
2116       */
2117      return;
2118    }
2119  else
2120    {
2121      CORBA_Environment ev;
2122      ConfigListener copy;
2123     
2124      CORBA_exception_init (&ev);
2125      copy = CORBA_Object_duplicate (client, &ev);
2126      g_hash_table_insert (client_table, copy, copy);
2127      CORBA_exception_free (&ev);
2128
2129      log_client_add (client);
2130
2131      gconf_log (GCL_DEBUG, "Added a new client");
2132    }
2133}
2134
2135static void
2136remove_client (const ConfigListener client)
2137{
2138  ConfigListener old_client;
2139  CORBA_Environment ev;
2140
2141  gconfd_need_log_cleanup ();
2142 
2143  if (client_table == NULL)
2144    goto notfound;
2145 
2146  old_client = g_hash_table_lookup (client_table,
2147                                    client);
2148
2149  if (old_client == NULL)
2150    goto notfound;
2151
2152  g_hash_table_remove (client_table,
2153                       old_client);
2154
2155  log_client_remove (old_client);
2156 
2157  CORBA_exception_init (&ev);
2158  CORBA_Object_release (old_client, &ev);
2159  CORBA_exception_free (&ev);
2160
2161  return;
2162 
2163 notfound:
2164  gconf_log (GCL_WARNING, _("Some client removed itself from the GConf server when it hadn't been added.")); 
2165}
2166
2167static void
2168hash_listify_func(gpointer key, gpointer value, gpointer user_data)
2169{
2170  GSList** list_p = user_data;
2171
2172  *list_p = g_slist_prepend(*list_p, value);
2173}
2174
2175static GSList*
2176list_clients (void)
2177{
2178  GSList *clients = NULL;
2179
2180  if (client_table == NULL)
2181    return NULL;
2182
2183  g_hash_table_foreach (client_table, hash_listify_func, &clients);
2184
2185  return clients;
2186}
2187
2188static void
2189log_clients_foreach (gpointer key, gpointer value, gpointer data)
2190{
2191  ConfigListener client;
2192  gchar *ior = NULL;
2193  gchar *quoted_ior = NULL;
2194  GError *err;
2195
2196  client = value;
2197 
2198  err = NULL;
2199  ior = gconf_object_to_string (client, &err);
2200
2201  if (err != NULL)
2202    {
2203      gconf_log (GCL_WARNING, _("Failed to get IOR for client: %s"),
2204                 err->message);
2205      g_error_free (err);
2206      return;
2207    }
2208     
2209  if (ior == NULL)
2210    return;
2211
2212  quoted_ior = gconf_quote_string (ior);
2213  g_free (ior);
2214  ior = NULL;
2215
2216  g_string_append (data, "CLIENTADD ");
2217  g_string_append (data, quoted_ior);
2218  g_string_append_c (data, '\n');
2219  g_free (quoted_ior);
2220}
2221
2222static void
2223log_clients_to_string (GString *str)
2224{
2225  if (client_table == NULL)
2226    return;
2227
2228  g_hash_table_foreach (client_table, log_clients_foreach, str);
2229}
2230
2231static void
2232drop_old_clients (void)
2233{
2234  GSList *clients;
2235  GSList *tmp;
2236 
2237  clients = list_clients ();
2238
2239  if (clients)
2240    {
2241      CORBA_Environment ev;
2242
2243      CORBA_exception_init (&ev);
2244     
2245      tmp = clients;
2246      while (tmp != NULL)
2247        {
2248          ConfigListener cl = tmp->data;
2249         
2250          ConfigListener_ping (cl, &ev);
2251
2252          if (ev._major != CORBA_NO_EXCEPTION)
2253            {
2254              gconf_log (GCL_DEBUG, "Removing stale client");
2255             
2256              remove_client (cl);
2257             
2258              CORBA_exception_free (&ev);
2259            }
2260         
2261          tmp = g_slist_next (tmp);
2262        }
2263
2264      g_slist_free (clients);
2265    }
2266}
2267
2268static guint
2269client_count (void)
2270{
2271  if (client_table == NULL)
2272    return 0;
2273  else
2274    return g_hash_table_size (client_table);
2275}
Note: See TracBrowser for help on using the repository browser.