static void xs_destroy (XMLSource* xs) { GError* error = NULL; g_return_if_fail(xs != NULL); /* do this first in case we're in a "fast cleanup just before exit" situation */ if (xs->lock != NULL && !gconf_release_lock(xs->lock, &error)) { gconf_log (GCL_ERR, _("Failed to give up lock on XML directory \"%s\": %s"), xs->root_dir, error->message); g_error_free(error); error = NULL; } if (!g_source_remove(xs->timeout_id)) { /* should not happen, don't translate */ gconf_log(GCL_ERR, "timeout not found to remove?"); } cache_unref(xs->cache); g_free(xs->root_dir); g_free(xs); }
gboolean gconfd_dbus_init (void) { DBusError error; gint ret; dbus_error_init (&error); bus_conn = dbus_bus_get (DBUS_BUS_SESSION, &error); if (!bus_conn) { gconf_log (GCL_ERR, _("Daemon failed to connect to the D-BUS daemon:\n%s"), error.message); dbus_error_free (&error); return FALSE; } /* We handle exiting ourselves on disconnect. */ dbus_connection_set_exit_on_disconnect (bus_conn, FALSE); /* Add message filter to handle Disconnected. */ dbus_connection_add_filter (bus_conn, (DBusHandleMessageFunction) server_filter_func, NULL, NULL); ret = dbus_bus_request_name (bus_conn, GCONF_DBUS_SERVICE, 0, &error); if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { gconf_log (GCL_ERR, "Daemon could not become primary owner"); return FALSE; } if (dbus_error_is_set (&error)) { gconf_log (GCL_ERR, _("Daemon failed to acquire gconf service:\n%s"), error.message); dbus_error_free (&error); return FALSE; } if (!dbus_connection_register_object_path (bus_conn, server_path, &server_vtable, NULL)) { gconf_log (GCL_ERR, _("Failed to register server object with the D-BUS bus daemon")); return FALSE; } nr_of_connections = 1; dbus_connection_setup_with_g_main (bus_conn, NULL); return TRUE; }
GConfValue* entry_get_value(Entry* e, const gchar** locales, GError** err) { const gchar* sl; g_return_val_if_fail(e != NULL, NULL); if (e->cached_value == NULL) return NULL; /* only schemas have locales for now anyway */ if (e->cached_value->type != GCONF_VALUE_SCHEMA) return e->cached_value; g_assert(e->cached_value->type == GCONF_VALUE_SCHEMA); sl = gconf_schema_get_locale(gconf_value_get_schema(e->cached_value)); gconf_log (GCL_DEBUG, "Cached schema value has locale \"%s\", looking for %s", sl ? sl : "null", locales && locales [0] ? locales[0] : "null"); /* optimize most common cases first */ if (sl == NULL && (locales == NULL || *locales == NULL)) return e->cached_value; else if (sl && locales && *locales && strcmp(sl, *locales) == 0) return e->cached_value; else { /* We want a locale other than the currently-loaded one */ GConfValue* newval; GError* error = NULL; entry_sync_if_needed(e); newval = node_extract_value(e->node, locales, &error); if (newval != NULL) { /* We found a schema with an acceptable locale */ gconf_value_free(e->cached_value); e->cached_value = newval; g_return_val_if_fail(error == NULL, e->cached_value); } else if (error != NULL) { /* There was an error */ gconf_log(GCL_WARNING, _("Ignoring XML node with name `%s': %s"), e->name, error->message); g_error_free(error); /* Fall back to currently-loaded thing if any */ } /* else fall back to the currently-loaded schema */ } return e->cached_value; }
static GConfValue* query_value (GConfSource* source, const gchar* key, const gchar** locales, gchar** schema_name, GError** err) { XMLSource* xs = (XMLSource*)source; gchar* parent; Dir* dir; GError* error = NULL; parent = gconf_key_directory(key); g_assert(parent != NULL); dir = cache_lookup(xs->cache, parent, FALSE, &error); /* We DO NOT want to return an error unless it represents a general problem with the backend; since we don't want to report stuff like "this key doesn't exist yet" - however this is a maintenance problem, since some errors may be added that need reporting. */ if (error != NULL) { gconf_log(GCL_WARNING, "%s", error->message); g_error_free(error); error = NULL; } g_free(parent); parent = NULL; if (dir != NULL) { const gchar* relative_key; GConfValue* retval; relative_key = gconf_key_key(key); retval = dir_get_value(dir, relative_key, locales, schema_name, &error); /* perhaps we should be reporting this error... */ if (error != NULL) { gconf_log(GCL_WARNING, "%s", error->message); g_error_free(error); error = NULL; } return retval; } else return NULL; }
gchar* gconf_backend_file(const gchar* address) { gchar* back; gchar* file; gchar* retval; const gchar* backenddir; g_return_val_if_fail(address != NULL, NULL); back = gconf_address_backend(address); if (back == NULL) return NULL; backenddir = g_getenv("GCONF_BACKEND_DIR"); if (backenddir == NULL) backenddir = GCONF_BACKEND_DIR; file = g_strconcat("gconfbackend-", back, NULL); retval = g_module_build_path(backenddir, file); g_free(back); if (g_file_test(retval, G_FILE_TEST_EXISTS)) { g_free(file); return retval; } else { /* -- debug only */ #ifdef GCONF_ENABLE_DEBUG gchar* dir; g_free(retval); dir = g_strconcat(GCONF_SRCDIR, "/gconf/", GCONF_BUILDDIR, "/backends/.libs", NULL); retval = g_module_build_path(dir, file); g_free(dir); if (g_file_test(retval, G_FILE_TEST_EXISTS)) { g_free(file); return retval; } #endif /* -- end debug only */ gconf_log(GCL_ERR, _("No such file `%s'\n"), retval); g_free(file); g_free(retval); return NULL; } }
static GConfSource * resolve_address (const char *address, GError **err) { EvoSource *esource; char *conf_file; if ((conf_file = gconf_address_resource (address)) == NULL) { g_set_error (err, GCONF_ERROR, GCONF_ERROR_BAD_ADDRESS, _("Failed to get configuration file path from '%s'"), address); return NULL; } esource = g_new0 (EvoSource, 1); esource->conf_file = conf_file; esource->source.flags = GCONF_SOURCE_ALL_READABLE | GCONF_SOURCE_NEVER_WRITEABLE; gconf_log (GCL_DEBUG, _("Created Evolution/LDAP source using configuration file '%s'"), esource->conf_file); return (GConfSource *) esource; }
static void unset_value (GConfSource* source, const gchar* key, const gchar* locale, GError** err) { XMLSource* xs = (XMLSource*)source; Dir* dir; gchar* parent; gconf_log(GCL_DEBUG, "XML backend: unset value `%s'", key); parent = gconf_key_directory(key); dir = cache_lookup(xs->cache, parent, FALSE, err); g_free(parent); if (dir == NULL) return; else { const gchar* relative_key; relative_key = gconf_key_key(key); dir_unset_value(dir, relative_key, locale, err); } }
static GSList* all_subdirs (GConfSource* source, const gchar* key, GError** err) { Dir* dir; XMLSource* xs = (XMLSource*)source; GError *sync_err; /* We have to sync before we can do this, to see which * subdirs have gone away. */ sync_err = NULL; cache_sync (xs->cache, &sync_err); if (sync_err) { gconf_log (GCL_WARNING, _("Error syncing the XML backend directory cache: %s"), sync_err->message); g_error_free (sync_err); sync_err = NULL; /* continue, may as well try our best. */ } dir = cache_lookup (xs->cache, key, FALSE, err); if (dir == NULL) return NULL; else return dir_all_subdirs (dir, err); }
static gboolean cache_clean_foreach(const gchar* key, Dir* dir, CleanData* cd) { GTime last_access; last_access = dir_get_last_access(dir); if ((cd->now - last_access) >= cd->length) { if (!dir_sync_pending(dir)) { dir_destroy(dir); return TRUE; } else { gconf_log(GCL_WARNING, _("Unable to remove directory `%s' from the XML backend cache, because it has not been successfully synced to disk"), dir_get_name(dir)); return FALSE; } } else return FALSE; }
static void cache_insert (Cache* cache, Dir* d) { g_return_if_fail(d != NULL); gconf_log(GCL_DEBUG, "Caching dir %s", dir_get_name(d)); safe_g_hash_table_insert(cache->cache, (gchar*)dir_get_name(d), d); }
static void cache_destroy_foreach(const gchar* key, Dir* dir, gpointer data) { #ifdef GCONF_ENABLE_DEBUG if (dir_sync_pending (dir)) gconf_log(GCL_DEBUG, "Destroying a directory (%s) with sync still pending", dir_get_name (dir)); #endif dir_destroy (dir); }
/* If we use G_MODULE_EXPORT, *only* thusly marked functions will be * exported, and xml-test uses other ones, too. */ G_MODULE_EXPORT #endif const gchar* g_module_check_init (GModule *module) { gconf_log(GCL_DEBUG, _("Initializing XML backend module")); LIBXML_TEST_VERSION; xmlKeepBlanksDefault(1); return NULL; }
gboolean entry_unset_value (Entry *e, const gchar *locale) { g_return_val_if_fail(e != NULL, FALSE); if (e->cached_value != NULL) { if (locale && e->cached_value->type == GCONF_VALUE_SCHEMA) { GError* error = NULL; /* Remove the localized node from the XML tree */ g_assert(e->node != NULL); node_unset_by_locale(e->node, locale); /* e->cached_value is always non-NULL if some value is available; in the schema case there may be leftover values */ gconf_value_free(e->cached_value); e->cached_value = node_extract_value(e->node, NULL, &error); if (error != NULL) { gconf_log(GCL_WARNING, "%s", error->message); g_error_free(error); error = NULL; } } else if (e->cached_value->type == GCONF_VALUE_SCHEMA) { /* if locale == NULL nuke all the locales */ if (e->cached_value) gconf_value_free(e->cached_value); e->cached_value = NULL; } else { gconf_value_free(e->cached_value); e->cached_value = NULL; } e->dirty = TRUE; return TRUE; } else return FALSE; }
static void safe_g_hash_table_insert(GHashTable* ht, gpointer key, gpointer value) { gpointer oldkey = NULL, oldval = NULL; if (g_hash_table_lookup_extended(ht, key, &oldkey, &oldval)) { gconf_log(GCL_WARNING, "Hash key `%s' is already in the table!", (gchar*)key); return; } else { g_hash_table_insert(ht, key, value); } }
gboolean cache_sync (Cache *cache, GError **err) { SyncData sd = { FALSE, NULL, FALSE }; GSList *list; sd.dc = cache; gconf_log (GCL_DEBUG, "Syncing the dir cache"); redo: sd.failed = FALSE; sd.deleted_some = FALSE; /* get a list of everything; we can't filter by * whether a sync is pending since we may make parents * of removed directories dirty when we sync their child * dir. */ list = NULL; g_hash_table_foreach (cache->cache, (GHFunc)listify_foreach, &list); /* sort subdirs before parents */ list = g_slist_sort (list, dircmp); /* sync it all */ g_slist_foreach (list, (GFunc) cache_sync_foreach, &sd); g_slist_free (list); /* If we deleted some subdirs, we may now be able to delete * more parent dirs. So go ahead and do the sync again. * Yeah this could be more efficient. */ if (!sd.failed && sd.deleted_some) goto redo; if (sd.failed && err && *err == NULL) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to sync XML cache contents to disk")); } return !sd.failed; }
static void server_handle_shutdown (DBusConnection *connection, DBusMessage *message) { DBusMessage *reply; if (gconfd_dbus_check_in_shutdown (connection, message)) return; gconf_log(GCL_DEBUG, _("Shutdown request received")); reply = dbus_message_new_method_return (message); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); dbus_connection_unregister_object_path (connection, server_path); gconfd_main_quit(); }
static void database_handle_remove_notify (DBusConnection *conn, DBusMessage *message, GConfDatabase *db) { gchar *namespace_section; DBusMessage *reply; const char *sender; NotificationData *notification; ListeningClientData *client; if (!gconfd_dbus_get_message_args (conn, message, DBUS_TYPE_STRING, &namespace_section, DBUS_TYPE_INVALID)) return; sender = dbus_message_get_sender (message); notification = g_hash_table_lookup (db->notifications, namespace_section); client = g_hash_table_lookup (db->listening_clients, sender); if (client) { client->nr_of_notifications--; if (client->nr_of_notifications == 0) { database_remove_listening_client (db, client); } } /* Notification can be NULL if the client and server get out of sync. */ if (notification == NULL || !database_remove_notification_data (db, notification, sender)) { gconf_log (GCL_DEBUG, _("Notification on %s doesn't exist"), namespace_section); } reply = dbus_message_new_method_return (message); dbus_connection_send (conn, reply, NULL); dbus_message_unref (reply); }
void cache_clean (Cache *cache, GTime older_than) { CleanData cd = { 0, NULL, 0 }; cd.cache = cache; cd.length = older_than; cd.now = time(NULL); /* ha ha, it's an online store! */ g_hash_table_foreach_remove(cache->cache, (GHRFunc)cache_clean_foreach, &cd); #if 0 size = g_hash_table_size(cache->cache); if (size != 0) gconf_log (GCL_DEBUG, "%u items remain in the cache after cleaning already-synced items older than %u seconds", size, older_than); #endif }
static void cache_sync_foreach (Dir *dir, SyncData *sd) { GError* error = NULL; gboolean deleted; deleted = FALSE; /* log errors but don't report the specific ones */ if (!dir_sync (dir, &deleted, &error)) { sd->failed = TRUE; g_return_if_fail (error != NULL); gconf_log (GCL_ERR, "%s", error->message); g_error_free (error); g_return_if_fail (dir_sync_pending (dir)); } else { g_return_if_fail (error == NULL); g_return_if_fail (!dir_sync_pending (dir)); if (deleted) { /* Get rid of this directory */ cache_remove_from_parent (sd->dc, dir); g_hash_table_remove (sd->dc->cache, dir_get_name (dir)); cache_set_nonexistent (sd->dc, dir_get_name (dir), TRUE); dir_destroy (dir); sd->deleted_some = TRUE; } } }
static Dir* dir_blank(const gchar* key) { Dir* d; d = g_new0(Dir, 1); #ifdef GCONF_ENABLE_DEBUG { gchar* why; if (!gconf_valid_key(key, &why)) { gconf_log(GCL_DEBUG, "key `%s' invalid: %s", key, why); } g_assert(gconf_valid_key(key, NULL)); } #endif d->key = g_strdup (key); d->parent_key = gconf_key_directory (key); d->last_access = time(NULL); d->doc = NULL; d->entry_cache = g_hash_table_new (g_str_hash, g_str_equal); d->dirty = FALSE; d->need_rescan_subdirs = TRUE; d->subdir_names = NULL; d->dir_mode = 0700; d->file_mode = 0600; return d; }
Dir* cache_lookup (Cache *cache, const gchar *key, gboolean create_if_missing, GError **err) { Dir* dir; g_assert(key != NULL); g_return_val_if_fail(cache != NULL, NULL); /* Check cache */ dir = g_hash_table_lookup(cache->cache, key); if (dir != NULL) { gconf_log(GCL_DEBUG, "Using dir %s from cache", key); return dir; } else { /* Not in cache, check whether we already failed to load it */ if (cache_is_nonexistent(cache, key)) { if (!create_if_missing) return NULL; } else { /* Didn't already fail to load, try to load */ dir = dir_load (key, cache->root_dir, err); if (dir != NULL) { g_assert(err == NULL || *err == NULL); /* Cache it and add to parent */ cache_insert (cache, dir); cache_add_to_parent (cache, dir); return dir; } else { /* Remember that we failed to load it */ if (!create_if_missing) { cache_set_nonexistent(cache, key, TRUE); return NULL; } else { if (err && *err) { g_error_free(*err); *err = NULL; } } } } } g_assert(dir == NULL); g_assert(create_if_missing); g_assert(err == NULL || *err == NULL); if (dir == NULL) { gconf_log(GCL_DEBUG, "Creating new dir %s", key); dir = dir_new(key, cache->root_dir, cache->dir_mode, cache->file_mode); if (!dir_ensure_exists(dir, err)) { dir_destroy(dir); g_return_val_if_fail((err == NULL) || (*err != NULL) , NULL); return NULL; } else { cache_insert (cache, dir); cache_add_to_parent (cache, dir); cache_unset_nonexistent (cache, dir_get_name (dir)); } } return dir; }
gboolean gconfd_dbus_set_exception (DBusConnection *connection, DBusMessage *message, GError **error) { GConfError en; const char *name = NULL; DBusMessage *reply; if (error == NULL || *error == NULL) return FALSE; en = (*error)->code; /* success is not supposed to get set */ g_return_val_if_fail(en != GCONF_ERROR_SUCCESS, FALSE); switch (en) { case GCONF_ERROR_FAILED: name = GCONF_DBUS_ERROR_FAILED; break; case GCONF_ERROR_NO_PERMISSION: name = GCONF_DBUS_ERROR_NO_PERMISSION; break; case GCONF_ERROR_BAD_ADDRESS: name = GCONF_DBUS_ERROR_BAD_ADDRESS; break; case GCONF_ERROR_BAD_KEY: name = GCONF_DBUS_ERROR_BAD_KEY; break; case GCONF_ERROR_PARSE_ERROR: name = GCONF_DBUS_ERROR_PARSE_ERROR; break; case GCONF_ERROR_CORRUPT: name = GCONF_DBUS_ERROR_CORRUPT; break; case GCONF_ERROR_TYPE_MISMATCH: name = GCONF_DBUS_ERROR_TYPE_MISMATCH; break; case GCONF_ERROR_IS_DIR: name = GCONF_DBUS_ERROR_IS_DIR; break; case GCONF_ERROR_IS_KEY: name = GCONF_DBUS_ERROR_IS_KEY; break; case GCONF_ERROR_NO_WRITABLE_DATABASE: name = GCONF_DBUS_ERROR_NO_WRITABLE_DATABASE; break; case GCONF_ERROR_IN_SHUTDOWN: name = GCONF_DBUS_ERROR_IN_SHUTDOWN; break; case GCONF_ERROR_OVERRIDDEN: name = GCONF_DBUS_ERROR_OVERRIDDEN; break; case GCONF_ERROR_LOCK_FAILED: name = GCONF_DBUS_ERROR_LOCK_FAILED; break; case GCONF_ERROR_OAF_ERROR: case GCONF_ERROR_LOCAL_ENGINE: case GCONF_ERROR_NO_SERVER: case GCONF_ERROR_SUCCESS: default: gconf_log (GCL_ERR, "Unhandled error code %d", en); g_assert_not_reached(); break; } reply = dbus_message_new_error (message, name, (*error)->message); dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); return TRUE; }
gboolean dir_sync (Dir *d, gboolean *deleted, GError **err) { gboolean retval = TRUE; if (deleted) *deleted = FALSE; if (!d->dirty) return TRUE; gconf_log (GCL_DEBUG, "Syncing dir \"%s\"", d->key); d->last_access = time (NULL); if (dir_useless (d)) { gconf_log (GCL_DEBUG, "Deleting useless dir \"%s\"", d->key); if (g_unlink (d->xml_filename) != 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to delete \"%s\": %s"), d->xml_filename, g_strerror (errno)); return FALSE; } if (strcmp (d->key, "/") != 0) /* don't delete root dir */ { if (g_rmdir (d->fs_dirname) != 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to delete \"%s\": %s"), d->fs_dirname, g_strerror (errno)); return FALSE; } } if (deleted) *deleted = TRUE; } else { gboolean old_existed = FALSE; gchar* tmp_filename; gchar* old_filename; FILE* outfile; /* We should have a doc if deleted is FALSE */ g_assert(d->doc != NULL); /* First make sure entry values are synced to their XML nodes */ g_hash_table_foreach(d->entry_cache, (GHFunc)entry_sync_foreach, NULL); tmp_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.tmp", NULL); old_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.old", NULL); outfile = g_fopen (tmp_filename, "w"); if (outfile == NULL) { /* Try to solve the problem by creating the FS dir */ if (!g_file_test (d->fs_dirname, G_FILE_TEST_EXISTS)) { if (create_fs_dir(d->fs_dirname, d->xml_filename, d->root_dir_len, d->dir_mode, d->file_mode, err)) outfile = g_fopen (tmp_filename, "w"); } if (outfile == NULL) { /* Don't set error if it's already set by some * earlier failure. */ if (err && *err == NULL) gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to write file `%s': %s"), tmp_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } } #ifdef HAVE_FCHMOD /* Set permissions on the new file */ if (fchmod (fileno (outfile), d->file_mode) != 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to set mode on `%s': %s"), tmp_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } #endif if (gconf_xml_doc_dump (outfile, d->doc) < 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to write XML data to `%s': %s"), tmp_filename, g_strerror (errno)); retval = FALSE; goto failed_end_of_sync; } if (fclose (outfile) < 0) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Failed to close file `%s': %s"), tmp_filename, g_strerror (errno)); retval = FALSE; outfile = NULL; goto failed_end_of_sync; } outfile = NULL; #ifndef HAVE_FCHMOD /* Set permissions on the new file */ if (chmod (tmp_filename, d->file_mode) != 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to set mode on `%s': %s"), tmp_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } #endif old_existed = g_file_test (d->xml_filename, G_FILE_TEST_EXISTS); if (old_existed) { if (g_rename(d->xml_filename, old_filename) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to rename `%s' to `%s': %s"), d->xml_filename, old_filename, g_strerror(errno)); retval = FALSE; goto failed_end_of_sync; } } if (g_rename(tmp_filename, d->xml_filename) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to rename `%s' to `%s': %s"), tmp_filename, d->xml_filename, g_strerror(errno)); /* Put the original file back, so this isn't a total disaster. */ if (g_rename(old_filename, d->xml_filename) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to restore `%s' from `%s': %s"), d->xml_filename, old_filename, g_strerror(errno)); } retval = FALSE; goto failed_end_of_sync; } if (old_existed) { if (g_unlink(old_filename) < 0) { gconf_log(GCL_WARNING, _("Failed to delete old file `%s': %s"), old_filename, g_strerror(errno)); /* Not a failure, just leaves cruft around. */ } } failed_end_of_sync: g_free(old_filename); g_free(tmp_filename); if (outfile) fclose (outfile); } if (retval) d->dirty = FALSE; return retval; }
static void dir_load_doc(Dir* d, GError** err) { gboolean xml_already_exists = TRUE; gboolean need_backup = FALSE; struct stat statbuf; g_return_if_fail(d->doc == NULL); if (stat(d->xml_filename, &statbuf) < 0) { switch (errno) { case ENOENT: xml_already_exists = FALSE; break; case ENOTDIR: #ifdef ELOOP case ELOOP: #endif case EFAULT: case EACCES: case ENOMEM: case ENAMETOOLONG: default: /* These are all fatal errors */ gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to stat `%s': %s"), d->xml_filename, g_strerror(errno)); return; break; } } if (statbuf.st_size == 0) { xml_already_exists = FALSE; } if (xml_already_exists) { GError *tmp_err; gboolean error_was_fatal; error_was_fatal = FALSE; tmp_err = NULL; d->doc = my_xml_parse_file (d->xml_filename, &tmp_err); if (tmp_err != NULL) { gconf_log (GCL_WARNING, "%s", tmp_err->message); /* file errors are assumed to be some kind of * blowup, like out of file descriptors, so * we play it safe and don't touch anything */ if (tmp_err->domain == G_FILE_ERROR) error_was_fatal = TRUE; g_error_free (tmp_err); } if (error_was_fatal) return; } /* We recover from parse errors instead of passing them up */ /* This has the potential to just blow away an entire corrupted * config file; but I think that is better than the alternatives * (disabling config for a directory because the document is mangled). * * Parse errors really should not happen from an XML file we created * ourselves anyway... */ /* Also we create empty %gconf.xml files when we create a new dir, * and those return a parse error, though they should be trapped * by the statbuf.st_size == 0 check above. */ if (d->doc == NULL) { if (xml_already_exists) need_backup = TRUE; /* rather uselessly save whatever broken stuff was in the file */ /* Create a new doc */ d->doc = xmlNewDoc((xmlChar *)"1.0"); } if (d->doc->xmlRootNode == NULL) { /* fill it in */ d->doc->xmlRootNode = xmlNewDocNode(d->doc, NULL, "gconf", NULL); } else if (strcmp((char*)d->doc->xmlRootNode->name, "gconf") != 0) { xmlFreeDoc(d->doc); d->doc = xmlNewDoc((xmlChar*)"1.0"); d->doc->xmlRootNode = xmlNewDocNode(d->doc, NULL, (xmlChar *)"gconf", NULL); need_backup = TRUE; /* save broken stuff */ } else { /* We had an initial doc with a valid root */ /* Fill child_cache from entries */ dir_fill_cache_from_doc(d); } if (need_backup) { /* Back up the file we failed to parse, if it exists, we aren't going to be able to do anything if this call fails */ gchar* backup = g_strconcat(d->xml_filename, ".bak", NULL); int fd; g_rename(d->xml_filename, backup); /* Recreate %gconf.xml to maintain our integrity and be sure all_subdirs works */ /* If we failed to rename, we just give up and truncate the file */ fd = g_open(d->xml_filename, O_CREAT | O_WRONLY | O_TRUNC, d->file_mode); if (fd >= 0) close(fd); g_free(backup); } g_assert(d->doc != NULL); g_assert(d->doc->xmlRootNode != NULL); }
static void dir_fill_cache_from_doc(Dir* d) { xmlNodePtr node; if (d->doc == NULL || d->doc->xmlRootNode == NULL || d->doc->xmlRootNode->xmlChildrenNode == NULL) { /* Empty document - just return. */ return; } node = d->doc->xmlRootNode->xmlChildrenNode; while (node != NULL) { if (node->type == XML_ELEMENT_NODE && (strcmp((xmlChar *)node->name, "entry") == 0)) { gchar* attr = my_xmlGetProp(node, "name"); if (attr != NULL) { if (g_hash_table_lookup(d->entry_cache, attr) != NULL) { gconf_log(GCL_WARNING, _("Duplicate entry `%s' in `%s', ignoring"), attr, d->xml_filename); } else { Entry* e; e = entry_new(attr); entry_set_node(e, node); entry_fill_from_node(e); safe_g_hash_table_insert(d->entry_cache, (gchar*)entry_get_name(e), e); } free(attr); } else { gconf_log(GCL_WARNING, _("Entry with no name in XML file `%s', ignoring"), d->xml_filename); } } else { if (node->type == XML_ELEMENT_NODE) gconf_log(GCL_WARNING, _("A toplevel node in XML file `%s' is <%s> rather than <entry>, ignoring"), d->xml_filename, node->name ? (char*) node->name : "unknown"); } node = node->next; } }
static void x_shutdown (GError** err) { gconf_log(GCL_DEBUG, _("Unloading XML backend module.")); }
static GConfSource* resolve_address (const gchar* address, GError** err) { struct stat statbuf; gchar* root_dir; XMLSource* xsource; GConfSource* source; gint flags = 0; GConfLock* lock = NULL; guint dir_mode = 0700; guint file_mode = 0600; gchar** address_flags; gchar** iter; gboolean force_readonly; root_dir = get_dir_from_address (address, err); if (root_dir == NULL) return NULL; if (g_stat (root_dir, &statbuf) == 0) { /* Already exists, base our dir_mode on it */ dir_mode = _gconf_mode_t_to_mode (statbuf.st_mode); /* dir_mode without search bits */ file_mode = dir_mode & (~0111); } else if (g_mkdir (root_dir, dir_mode) < 0) { /* Error out even on EEXIST - shouldn't happen anyway */ gconf_set_error (err, GCONF_ERROR_FAILED, _("Could not make directory `%s': %s"), (gchar *)root_dir, g_strerror (errno)); g_free (root_dir); return NULL; } force_readonly = FALSE; address_flags = gconf_address_flags (address); if (address_flags) { iter = address_flags; while (*iter) { if (strcmp (*iter, "readonly") == 0) { force_readonly = TRUE; break; } ++iter; } } g_strfreev (address_flags); { /* See if we're writable */ gboolean writable; int fd; gchar* testfile; writable = FALSE; if (!force_readonly) { testfile = g_strconcat(root_dir, "/.testing.writeability", NULL); fd = g_open(testfile, O_CREAT|O_WRONLY, S_IRWXU); if (fd >= 0) { writable = TRUE; close(fd); } g_unlink(testfile); g_free(testfile); } if (writable) flags |= GCONF_SOURCE_ALL_WRITEABLE; /* We only do locking if it's writable, * and if not using local locks, * which is sort of broken but close enough */ if (writable && !gconf_use_local_locks ()) { gchar* lockdir; lockdir = get_lock_dir_from_root_dir (root_dir); lock = gconf_get_lock(lockdir, err); if (lock != NULL) gconf_log(GCL_DEBUG, "Acquired lock directory `%s'", lockdir); g_free(lockdir); if (lock == NULL) { g_free(root_dir); return NULL; } } } { /* see if we're readable */ gboolean readable = FALSE; GDir* d; d = g_dir_open(root_dir, 0, NULL); if (d != NULL) { readable = TRUE; g_dir_close(d); } if (readable) flags |= GCONF_SOURCE_ALL_READABLE; } if (!(flags & GCONF_SOURCE_ALL_READABLE) && !(flags & GCONF_SOURCE_ALL_WRITEABLE)) { gconf_set_error(err, GCONF_ERROR_BAD_ADDRESS, _("Can't read from or write to the XML root directory in the address \"%s\""), address); g_free(root_dir); return NULL; } /* Create the new source */ xsource = xs_new(root_dir, dir_mode, file_mode, lock); gconf_log(GCL_DEBUG, _("Directory/file permissions for XML source at root %s are: %o/%o"), root_dir, dir_mode, file_mode); source = (GConfSource*)xsource; source->flags = flags; g_free(root_dir); return source; }
static gboolean create_fs_dir(const gchar* dir, const gchar* xml_filename, guint root_dir_len, guint dir_mode, guint file_mode, GError** err) { g_return_val_if_fail(xml_filename != NULL, FALSE); gconf_log(GCL_DEBUG, "Enter create_fs_dir: %s", dir); if (g_file_test(xml_filename, G_FILE_TEST_IS_REGULAR)) { gconf_log(GCL_DEBUG, "XML backend file %s already exists", xml_filename); return TRUE; } /* Don't create anything above the root directory */ if (strlen(dir) > root_dir_len) { gchar* parent; parent = _gconf_parent_dir (dir); gconf_log (GCL_DEBUG, "Parent dir is %s", parent); if (parent != NULL) { gchar* parent_xml = NULL; gboolean success = FALSE; if (xml_filename) parent_xml = g_strconcat(parent, "/%gconf.xml", NULL); success = create_fs_dir(parent, parent_xml, root_dir_len, dir_mode, file_mode, err); if (success) gconf_log(GCL_DEBUG, "created parent: %s", parent); else gconf_log(GCL_DEBUG, "failed parent: %s", parent); g_free(parent); g_free(parent_xml); if (!success) return FALSE; } else { gconf_log(GCL_DEBUG, "%s has no parent", dir); } } gconf_log(GCL_DEBUG, "Making directory %s", dir); if (g_mkdir(dir, dir_mode) < 0) { if (errno != EEXIST) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Could not make directory \"%s\": %s"), (gchar*)dir, g_strerror(errno)); return FALSE; } } if (xml_filename != NULL) { int fd; /* don't truncate the file, it may well already exist */ fd = g_open(xml_filename, O_CREAT | O_WRONLY, file_mode); gconf_log(GCL_DEBUG, "Creating XML file %s", xml_filename); if (fd < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to create file `%s': %s"), xml_filename, g_strerror(errno)); return FALSE; } if (close(fd) < 0) { gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to close file `%s': %s"), xml_filename, g_strerror(errno)); return FALSE; } } else { gconf_log(GCL_DEBUG, "No XML filename passed to create_fs_dir() for %s", dir); } return TRUE; }
/* this actually works on any node, not just <entry>, such as the <car> and <cdr> nodes and the <li> nodes and the <default> node */ static GConfValue* node_extract_value(xmlNodePtr node, const gchar** locales, GError** err) { GConfValue* value = NULL; gchar* type_str; GConfValueType type = GCONF_VALUE_INVALID; const gchar* default_locales[] = { "C", NULL }; if (locales == NULL) locales = default_locales; type_str = my_xmlGetProp(node, "type"); if (type_str == NULL) { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("No \"type\" attribute for <%s> node"), (node->name ? (char*)node->name : "(nil)")); return NULL; } type = gconf_value_type_from_string(type_str); xmlFree(type_str); switch (type) { case GCONF_VALUE_INVALID: { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("A node has unknown \"type\" attribute `%s', ignoring"), type_str); return NULL; } break; case GCONF_VALUE_INT: case GCONF_VALUE_BOOL: case GCONF_VALUE_FLOAT: { gchar* value_str; value_str = my_xmlGetProp(node, "value"); if (value_str == NULL) { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("No \"value\" attribute for node")); return NULL; } value = gconf_value_new_from_string(type, value_str, err); xmlFree(value_str); g_return_val_if_fail( (value != NULL) || (err == NULL) || (*err != NULL), NULL ); return value; } break; case GCONF_VALUE_STRING: { xmlNodePtr iter; iter = node->xmlChildrenNode; while (iter != NULL) { if (iter->type == XML_ELEMENT_NODE) { GConfValue* v = NULL; if (strcmp((char *)iter->name, "stringvalue") == 0) { gchar* s; s = (gchar *)xmlNodeGetContent(iter); v = gconf_value_new(GCONF_VALUE_STRING); /* strdup() caused purely by g_free()/free() difference */ gconf_value_set_string(v, s ? s : ""); if (s) xmlFree(s); return v; } else { /* What the hell is this? */ gconf_log(GCL_WARNING, _("Didn't understand XML node <%s> inside an XML list node"), iter->name ? iter->name : (guchar*)"???"); } } iter = iter->next; } return NULL; } break; case GCONF_VALUE_SCHEMA: return schema_node_extract_value(node, locales); break; case GCONF_VALUE_LIST: { xmlNodePtr iter; GSList* values = NULL; GConfValueType list_type = GCONF_VALUE_INVALID; { gchar* s; s = my_xmlGetProp(node, "ltype"); if (s != NULL) { list_type = gconf_value_type_from_string(s); xmlFree(s); } } switch (list_type) { case GCONF_VALUE_INVALID: case GCONF_VALUE_LIST: case GCONF_VALUE_PAIR: gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Invalid type (list, pair, or unknown) in a list node")); return NULL; default: break; } iter = node->xmlChildrenNode; while (iter != NULL) { if (iter->type == XML_ELEMENT_NODE) { GConfValue* v = NULL; if (strcmp((char*)iter->name, "li") == 0) { v = node_extract_value(iter, locales, err); if (v == NULL) { if (err && *err) { gconf_log(GCL_WARNING, _("Bad XML node: %s"), (*err)->message); /* avoid pile-ups */ g_clear_error(err); } } else if (v->type != list_type) { gconf_log(GCL_WARNING, _("List contains a badly-typed node (%s, should be %s)"), gconf_value_type_to_string(v->type), gconf_value_type_to_string(list_type)); gconf_value_free(v); v = NULL; } } else { /* What the hell is this? */ gconf_log(GCL_WARNING, _("Didn't understand XML node <%s> inside an XML list node"), iter->name ? iter->name : (guchar*)"???"); } if (v != NULL) values = g_slist_prepend(values, v); } iter = iter->next; } /* put them in order, set the value */ values = g_slist_reverse(values); value = gconf_value_new(GCONF_VALUE_LIST); gconf_value_set_list_type(value, list_type); gconf_value_set_list_nocopy(value, values); return value; } break; case GCONF_VALUE_PAIR: { GConfValue* car = NULL; GConfValue* cdr = NULL; xmlNodePtr iter; iter = node->xmlChildrenNode; while (iter != NULL) { if (iter->type == XML_ELEMENT_NODE) { if (car == NULL && strcmp((char *)iter->name, "car") == 0) { car = node_extract_value(iter, locales, err); if (car == NULL) { if (err && *err) { gconf_log(GCL_WARNING, _("Ignoring bad car from XML pair: %s"), (*err)->message); /* prevent pile-ups */ g_clear_error(err); } } else if (car->type == GCONF_VALUE_LIST || car->type == GCONF_VALUE_PAIR) { gconf_log(GCL_WARNING, _("parsing XML file: lists and pairs may not be placed inside a pair")); gconf_value_free(car); car = NULL; } } else if (cdr == NULL && strcmp((char *)iter->name, "cdr") == 0) { cdr = node_extract_value(iter, locales, err); if (cdr == NULL) { if (err && *err) { gconf_log(GCL_WARNING, _("Ignoring bad cdr from XML pair: %s"), (*err)->message); /* avoid pile-ups */ g_clear_error(err); } } else if (cdr->type == GCONF_VALUE_LIST || cdr->type == GCONF_VALUE_PAIR) { gconf_log(GCL_WARNING, _("parsing XML file: lists and pairs may not be placed inside a pair")); gconf_value_free(cdr); cdr = NULL; } } else { /* What the hell is this? */ gconf_log(GCL_WARNING, _("Didn't understand XML node <%s> inside an XML pair node"), iter->name ? (gchar*)iter->name : "???"); } } iter = iter->next; } /* Return the pair if we got both halves */ if (car && cdr) { value = gconf_value_new(GCONF_VALUE_PAIR); gconf_value_set_car_nocopy(value, car); gconf_value_set_cdr_nocopy(value, cdr); return value; } else { gconf_log(GCL_WARNING, _("Didn't find car and cdr for XML pair node")); if (car) { g_assert(cdr == NULL); gconf_value_free(car); gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Missing cdr from pair of values in XML file")); } else if (cdr) { g_assert(car == NULL); gconf_value_free(cdr); gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Missing car from pair of values in XML file")); } else { gconf_set_error(err, GCONF_ERROR_PARSE_ERROR, _("Missing both car and cdr values from pair in XML file")); } return NULL; } } break; default: g_assert_not_reached(); return NULL; break; } }
Dir* dir_load (const gchar* key, const gchar* xml_root_dir, GError** err) { Dir* d; gchar* fs_dirname; gchar* xml_filename; guint dir_mode = 0700; guint file_mode = 0600; g_return_val_if_fail(gconf_valid_key(key, NULL), NULL); fs_dirname = gconf_concat_dir_and_key(xml_root_dir, key); xml_filename = g_strconcat(fs_dirname, "/%gconf.xml", NULL); { struct stat s; gboolean notfound = FALSE; if (g_stat(xml_filename, &s) != 0) { if (errno != ENOENT) { gconf_set_error (err, GCONF_ERROR_FAILED, _("Could not stat `%s': %s"), xml_filename, g_strerror(errno)); } notfound = TRUE; } else if (S_ISDIR(s.st_mode)) { gconf_set_error (err, GCONF_ERROR_FAILED, _("XML filename `%s' is a directory"), xml_filename); notfound = TRUE; } if (notfound) { gconf_log(GCL_DEBUG, "dir file %s not found", xml_filename); g_free(fs_dirname); g_free(xml_filename); return NULL; } else { /* Take directory mode from the xml_root_dir, if possible */ if (g_stat (xml_root_dir, &s) == 0) { dir_mode = _gconf_mode_t_to_mode (s.st_mode); } file_mode = dir_mode & ~0111; /* turn off search bits */ } } d = dir_blank(key); /* sync with dir_new() */ d->fs_dirname = fs_dirname; d->xml_filename = xml_filename; d->root_dir_len = strlen(xml_root_dir); d->dir_mode = dir_mode; d->file_mode = file_mode; gconf_log (GCL_DEBUG, "loaded dir %s", fs_dirname); return d; }