#include #include "abrt-polkit.h" #include "libabrt.h" #define ABRT_CONF_DBUS_NAME "com.redhat.problems.configuration" #define ABRT_CONF_IFACE_PFX ABRT_CONF_DBUS_NAME #ifndef PROBLEMS_CONFIG_INTERFACES_DIR # error "Undefined PROBLEMS_CONFIG_INTERFACES_DIR" #endif #define ANNOTATION_NAME_CONF_FILE "com.redhat.problems.ConfFile" #define ANNOTATION_NAME_DEF_CONF_FILE "com.redhat.problems.DefaultConfFile" #define ABRT_FILE_ACCESS_ERROR abrt_file_access_error_quark() #define ABRT_FILE_ACCESS_ERROR_CODE ENOENT static GQuark abrt_file_access_error_quark() { return g_quark_from_static_string("abrt-file-access-error"); } #define ABRT_REFLECTION_UNSUPPORTED_TYPE_ERROR abrt_reflection_unsupported_type_error_quark() #define ABRT_REFLECTION_UNSUPPORTED_TYPE_ERROR_CODE (129) static GQuark abrt_reflection_unsupported_type_error_quark() { return g_quark_from_static_string("abrt-reflection-unsupported-type-error"); } #define ABRT_REFLECTION_MISSING_MEMBER_ERROR abrt_reflection_missing_member_error_quark() #define ABRT_REFLECTION_MISSING_MEMBER_ERROR_CODE (130) static GQuark abrt_reflection_missing_member_error_quark() { return g_quark_from_static_string("abrt-reflection-missing-member-error"); } #define ABRT_FILE_FORMAT_ERROR abrt_file_fromat_error_quark() #define ABRT_FILE_FORMAT_ERROR_CODE (131) static GQuark abrt_file_fromat_error_quark() { return g_quark_from_static_string("abrt-file-format-error"); } #define ABRT_AUTHORIZATION_ERROR abrt_authorization_error_quark() #define ABRT_AUTHORIZATION_ERROR_CODE (132) static GQuark abrt_authorization_error_quark() { return g_quark_from_static_string("abrt-authorization-error"); } const char *g_default_xml = ""; GDBusNodeInfo *g_default_node; guint g_timeout_source; int g_timeout_value = 10; GMainLoop *g_loop; static gboolean on_timeout_cb(gpointer user_data) { log_info("Inactivity timeout was reached"); g_main_loop_quit(g_loop); return TRUE; } static void reset_timeout(void) { if (g_timeout_source > 0) { log_info("Removing timeout"); g_source_remove(g_timeout_source); } log_info("Setting a new timeout"); g_timeout_source = g_timeout_add_seconds(g_timeout_value, on_timeout_cb, NULL); } /* A configuration entity * * Each configuration has the working file and the default file. It is * expected that the default configuration file remains unchanged while the * program is running, so the parsed file can be cached. But the working file * can be modified from various source, therefore we must parse the file upon * each request. */ typedef struct _configuration { char *file_path; ///< Path to the working file char *def_file_path; ///< Path to the default file map_string_t *def; ///< The default configuration } configuration_t; typedef GVariant *(*configuration_getter_fn)(configuration_t *conf, const char *option, GError **error); typedef bool (*configuration_setter_fn)(configuration_t *conf, const char *option, GVariant *variant, GError **error); static configuration_t *configuration_new(const char *file_path, const char *def_file_path) { configuration_t *self = xmalloc(sizeof(*self)); self->file_path = xstrdup(file_path); self->def_file_path = xstrdup(def_file_path); self->def = NULL; return self; } /* A helper function for equality comparison. * * Checks if the configuration files equals to the paths in array */ static int configuration_compare_paths(configuration_t *self, const char *paths[2]) { if (strcmp(self->file_path, paths[0]) != 0) return -1; if (strcmp(self->def_file_path, paths[1]) != 0) return 1; return 0; } static void configuration_free(configuration_t *self) { if (self == NULL) return; free(self->file_path); self->file_path = NULL; free(self->def_file_path); self->def_file_path = NULL; free_map_string(self->def); self->def = NULL; free(self); } static map_string_t *configuration_load_file(const char *file_path, map_string_t *preloaded, bool fail_on_noent, GError **error) { map_string_t *conf = preloaded; if (conf == NULL) conf = new_map_string(); if (!load_conf_file(file_path, conf, /*skip w/o values*/false) && (errno != ENOENT || fail_on_noent)) { if (conf != preloaded) free_map_string(conf); conf = NULL; g_set_error(error, ABRT_FILE_ACCESS_ERROR, ABRT_FILE_ACCESS_ERROR_CODE, "Could not load configuration from '%s'", file_path); } return conf; } static bool configuration_save_file(const char *file_path, map_string_t *conf, GError **error) { const bool retval = save_conf_file(file_path, conf); if (!retval) g_set_error(error, ABRT_FILE_ACCESS_ERROR, ABRT_FILE_ACCESS_ERROR_CODE, "Could not save configuration file '%s'", file_path); return retval; } static map_string_t *configuration_get_default(configuration_t *self, GError **error) { if (self->def == NULL) self->def = configuration_load_file(self->def_file_path, /*No preloaded configuration*/NULL, /*Fail on ENOENT*/true, error); return self->def; } /* * Default value */ static bool configuration_set_default(configuration_t *self, const char *option, GError **error) { map_string_t *const def_conf = configuration_get_default(self, error); if (def_conf == NULL) return false; const char *const def_value = get_map_string_item_or_NULL(def_conf, option); map_string_t *const conf = configuration_load_file(self->file_path, /*No preloaded configuration*/NULL, /*Fail on ENOENT*/true, error); if (conf == NULL) /* If the configuration file does not exist, the default value is used */ return errno == ENOENT; const char *const cur_value = get_map_string_item_or_NULL(conf, option); bool retval = true; /* Avoid saving the configuration if nothing has changed */ if (!((def_value == NULL && cur_value == NULL) || (def_value != NULL && cur_value != NULL && strcmp(def_value, cur_value) == 0))) { if (def_value != NULL) replace_map_string_item(conf, xstrdup(option), xstrdup(def_value)); else remove_map_string_item(conf, option); retval = configuration_save_file(self->file_path, conf, error); } free_map_string(conf); return retval; } /* * Setters */ /* Stores value of GVariant argument in the configuration under key 'option'. * * Converts the GVariant to the underlaying type via 'transform' function. */ static bool configuration_set_gvariant(configuration_t *self, const char *option, GVariant *value, void (*transform)(map_string_t *, const char *, GVariant *), GError **error) { map_string_t *const conf = configuration_load_file(self->file_path, /*No preloaded configuration*/NULL, /*Fail on ENOENT*/false, error); if (conf == NULL) return false; transform(conf, option, value); const bool retval = configuration_save_file(self->file_path, conf, error); free_map_string(conf); return retval; } static void bool_from_gvariant(map_string_t *conf, const char *option, GVariant *value) { const bool raw_value = g_variant_get_boolean(value); log_debug("Save boolean option '%s':%d", option, raw_value); set_map_string_item_from_bool(conf, option, raw_value); } static void int32_from_gvariant(map_string_t *conf, const char *option, GVariant *value) { const int raw_value = g_variant_get_int32(value); log_debug("Save int32 option '%s':%d", option, raw_value); set_map_string_item_from_int(conf, option, raw_value); } static void string_from_gvariant(map_string_t *conf, const char *option, GVariant *value) { const char *const raw_value = g_variant_get_string(value, /*length*/ NULL); log_debug("Save string option '%s':%s", option, raw_value); set_map_string_item_from_string(conf, option, raw_value); } static void string_vector_from_gvariant(map_string_t *conf, const char *option, GVariant *value) { const gchar **const raw_value = g_variant_get_strv(value, /*lenght -> request NULL terminated vector*/ NULL); log_debug("Save string vector option '%s'", option); set_map_string_item_from_string_vector(conf, option, (string_vector_ptr_t)raw_value); g_free(raw_value); } static bool configuration_set_string(configuration_t *self, const char *option, GVariant *value, GError **error) { return configuration_set_gvariant(self, option, value, string_from_gvariant, error); } static bool configuration_set_string_vector(configuration_t *self, const char *option, GVariant *value, GError **error) { return configuration_set_gvariant(self, option, value, string_vector_from_gvariant, error); } static bool configuration_set_bool(configuration_t *self, const char *option, GVariant *value, GError **error) { return configuration_set_gvariant(self, option, value, bool_from_gvariant, error); } static bool configuration_set_int(configuration_t *self, const char *option, GVariant *value, GError **error) { return configuration_set_gvariant(self, option, value, int32_from_gvariant, error); } static configuration_setter_fn configuration_setter_factory(GVariantType *type) { if (g_variant_type_equal(G_VARIANT_TYPE_BOOLEAN, type)) return configuration_set_bool; else if (g_variant_type_equal(G_VARIANT_TYPE_INT32, type)) return configuration_set_int; else if (g_variant_type_equal(G_VARIANT_TYPE_STRING, type)) return configuration_set_string; else if (g_variant_type_equal(G_VARIANT_TYPE_STRING_ARRAY, type)) return configuration_set_string_vector; return NULL; } /* * Getters */ /* Gets the configuration option's value as GVariant * * Converts the GVariant to the underlaying type via 'transform' function. */ static GVariant *configuration_get_gvariant(configuration_t *self, const char *option, GVariant *(*transform)(map_string_t *conf, const char *option), GError **error) { map_string_t *const def_conf = configuration_get_default(self, error); if (def_conf == NULL) return false; /* BEGIN: clone_map_string() */ map_string_t *conf = new_map_string(); map_string_iter_t iter; init_map_string_iter(&iter, def_conf); const char *key=NULL; const char *value=NULL; while(next_map_string_iter(&iter, &key, &value)) replace_map_string_item(conf, xstrdup(key), xstrdup(value)); /* END: clone_map_string() */ GError *working_error = NULL; if (!configuration_load_file(self->file_path, conf, /*Fail on ENOENT*/true, &working_error)) { log_debug("Error while loading working configuration: %s", working_error->message); g_error_free(working_error); } GVariant *const retval = transform(conf, option); free_map_string(conf); if (!retval) { g_set_error(error, ABRT_FILE_FORMAT_ERROR, ABRT_FILE_FORMAT_ERROR_CODE, "Option '%s' has invalid value in file '%s'", option, self->file_path); log_warning("Option '%s' has invalid value in file '%s'", option, self->file_path); } return retval; } static GVariant *int32_to_gvariant(map_string_t *conf, const char *key) { int value = 0; if (!try_get_map_string_item_as_int(conf, key, &value)) return NULL; return g_variant_new_int32(value); } static GVariant *bool_to_gvariant(map_string_t *conf, const char *key) { int value = 0; if (!try_get_map_string_item_as_bool(conf, key, &value)) return NULL; return g_variant_new_boolean(value); } static GVariant *string_to_gvariant(map_string_t *conf, const char *key) { char *value = NULL; if (!try_get_map_string_item_as_string(conf, key, &value)) return NULL; GVariant *retval = g_variant_new_string(value); free(value); return retval; } static GVariant *string_vector_to_gvariant(map_string_t *conf, const char *key) { string_vector_ptr_t value = NULL; if (!try_get_map_string_item_as_string_vector(conf, key, &value)) return NULL; GVariant *retval = g_variant_new_strv((const gchar *const *)value, /*NULL terminated*/-1); string_vector_free(value); return retval; } static GVariant *configuration_get_string(configuration_t *self, const char *option, GError **error) { return configuration_get_gvariant(self, option, string_to_gvariant, error); } static GVariant *configuration_get_int32(configuration_t *self, const char *option, GError **error) { return configuration_get_gvariant(self, option, int32_to_gvariant, error); } static GVariant *configuration_get_bool(configuration_t *self, const char *option, GError **error) { return configuration_get_gvariant(self, option, bool_to_gvariant, error); } static GVariant *configuration_get_string_vector(configuration_t *self, const char *option, GError **error) { return configuration_get_gvariant(self, option, string_vector_to_gvariant, error); } static configuration_getter_fn configuration_getter_factory(GVariantType *type) { if (g_variant_type_equal(G_VARIANT_TYPE_BOOLEAN, type)) return configuration_get_bool; else if (g_variant_type_equal(G_VARIANT_TYPE_INT32, type)) return configuration_get_int32; else if (g_variant_type_equal(G_VARIANT_TYPE_STRING, type)) return configuration_get_string; else if (g_variant_type_equal(G_VARIANT_TYPE_STRING_ARRAY, type)) return configuration_get_string_vector; return NULL; } /* A single property entity * * This structure provides mapping between a D-Bus property and a configuration option. */ typedef struct _property { char *name; ///< Property Name in the XML file and Option Name in configuration GVariantType *type; ///< GLib's type char *type_string; ///< GLib's type string configuration_t *conf; ///< A configuration which contains this option (Not owned) configuration_getter_fn getter; ///< Getter function configuration_setter_fn setter; ///< Setter function } property_t; static property_t *property_new(const char *name, GVariantType *type, configuration_t *conf, configuration_getter_fn getter, configuration_setter_fn setter) { property_t *self = xmalloc(sizeof(*self)); self->name = xstrdup(name); self->type = type; self->type_string = NULL; self->conf = conf; self->getter = getter; self->setter = setter; return self; } static void property_free(property_t *self) { if (self == NULL) return; free(self->name); self->name = NULL; g_variant_type_free(self->type); self->type = NULL; g_free(self->type_string); self->type_string = NULL; } static const char *property_get_type_string(property_t *self) { if (self->type_string == NULL) self->type_string = g_variant_type_dup_string(self->type); return self->type_string; } static bool property_set(property_t *self, GVariant *args, GError **error) { if (self->setter) return self->setter(self->conf, self->name, args, error); g_set_error(error, ABRT_REFLECTION_UNSUPPORTED_TYPE_ERROR, ABRT_REFLECTION_UNSUPPORTED_TYPE_ERROR_CODE, "Type with signature '%s' is not supported", property_get_type_string(self)); return false; } static GVariant *property_get(property_t *self, GError **error) { if (self->getter) { GVariant *retval = self->getter(self->conf, self->name, error); assert((retval != NULL || *error != NULL) || !"GError must be set if bool option cannot be returned."); return retval; } g_set_error(error, ABRT_REFLECTION_UNSUPPORTED_TYPE_ERROR, ABRT_REFLECTION_UNSUPPORTED_TYPE_ERROR_CODE, "Type with signature '%s' is not supported", property_get_type_string(self)); return NULL; } static bool property_reset(property_t *self, GError **error) { return configuration_set_default(self->conf, self->name, error); } /* A single D-Bus node */ typedef struct _dbus_conf_node { GDBusNodeInfo *node; ///< Parsed XML file GSList *configurations; ///< List of all configurations (configuration_t) GHashTable *properties; ///< List of properties (property_t) } dbus_conf_node_t; static dbus_conf_node_t *dbus_conf_node_new(GDBusNodeInfo *node, GSList *configurations, GHashTable *properties) { if (node->path == NULL) { error_msg("Node misses 'name' attribute"); return NULL; } if (node->interfaces[0] == NULL) { error_msg("Node has no interface defined"); return NULL; } dbus_conf_node_t *self = xmalloc(sizeof(*self)); self->node = node; self->configurations = configurations; self->properties = properties; return self; } static void dbus_conf_node_free(dbus_conf_node_t *self) { if (self == NULL) return; g_dbus_node_info_unref(self->node); self->node = NULL; g_slist_free_full(self->configurations, (GDestroyNotify)configuration_free); self->configurations = NULL; g_hash_table_destroy(self->properties); self->properties = NULL; } static const char* dbus_conf_node_get_path(dbus_conf_node_t *self) { return self->node->path; } static GDBusInterfaceInfo *dbus_conf_node_get_interface(dbus_conf_node_t *self) { return self->node->interfaces[0]; } static property_t *dbus_conf_node_get_property(dbus_conf_node_t *self, const char *property_name, GError **error) { gpointer property = g_hash_table_lookup(self->properties, property_name); if (property == NULL) g_set_error(error, ABRT_REFLECTION_MISSING_MEMBER_ERROR, ABRT_REFLECTION_MISSING_MEMBER_ERROR_CODE, "Could find property '%s'", property_name); return (property_t *)property; } /* SetDefault D-Bus method handler */ static void dbus_conf_node_handle_configuration_method_call(GDBusConnection *connection, const gchar *caller, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { log_debug("Set Default Property"); reset_timeout(); if (polkit_check_authorization_dname(caller, "com.redhat.problems.configuration.update") != PolkitYes) { log_notice("not authorized"); g_dbus_method_invocation_return_dbus_error(invocation, "com.redhat.problems.configuration.AuthFailure", _("Not Authorized")); return; } const char *property_name = NULL; g_variant_get(parameters, "(&s)", &property_name); log_debug("Going to reset value of '%s'", property_name); GError *error = NULL; property_t *prop = dbus_conf_node_get_property((dbus_conf_node_t *)user_data, property_name, &error); if (prop == NULL || !property_reset(prop, &error)) { g_dbus_method_invocation_return_gerror(invocation, error); g_error_free(error); return; } g_dbus_method_invocation_return_value(invocation, NULL); } /* Get D-Bus property handler */ static GVariant *dbus_conf_node_handle_get_property(GDBusConnection *connection, const gchar *caller, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { log_debug("Get Property '%s'", property_name); reset_timeout(); property_t *prop = dbus_conf_node_get_property((dbus_conf_node_t *)user_data, property_name, error); /* Paranoia: this should never happen*/ if (prop == NULL) return NULL; return property_get(prop, error); } /* Set D-Bus property handler */ static gboolean dbus_conf_node_handle_set_property(GDBusConnection *connection, const gchar *caller, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *args, GError **error, gpointer user_data) { log_debug("Set Property '%s'", property_name); reset_timeout(); if (polkit_check_authorization_dname(caller, "com.redhat.problems.configuration.update") != PolkitYes) { log_notice("not authorized"); g_set_error(error, ABRT_AUTHORIZATION_ERROR, ABRT_AUTHORIZATION_ERROR_CODE, _("Not Authorized")); return FALSE; } property_t *prop = dbus_conf_node_get_property((dbus_conf_node_t *)user_data, property_name, error); /* Paranoia: this should never happen*/ if (prop == NULL) return false; return property_set(prop, args, error); } static GDBusInterfaceVTable *dbus_conf_node_get_vtable(dbus_conf_node_t *node) { static GDBusInterfaceVTable default_vtable = { .method_call = NULL, .get_property = dbus_conf_node_handle_get_property, .set_property = dbus_conf_node_handle_set_property, }; return &default_vtable; } static GDBusInterfaceVTable *dbus_conf_node_get_configuration_vtable(dbus_conf_node_t *node) { static GDBusInterfaceVTable default_vtable = { .method_call = dbus_conf_node_handle_configuration_method_call, .get_property = NULL, .set_property = NULL, }; return &default_vtable; } /* Helpers for Annotation parsing */ struct annot { const char *name; const char *value; }; static void parse_annotations(GDBusAnnotationInfo **annotations, struct annot annots[], size_t count) { GDBusAnnotationInfo **an_iter = annotations; while(*an_iter != NULL) { for (size_t i = 0; i < count; ++i) if (strcmp(annots[i].name, (*an_iter)->key) == 0) annots[i].value = (*an_iter)->value; ++an_iter; } } /* Builds the internal representation of configuration node from a D-Bus XML interface file. */ static dbus_conf_node_t *dbus_conf_node_from_node(GDBusNodeInfo *node) { if (node->interfaces[0] == NULL) { error_msg("Node has no interface defined"); return NULL; } struct annot annots[] = { { .name=ANNOTATION_NAME_CONF_FILE, .value=NULL }, { .name=ANNOTATION_NAME_DEF_CONF_FILE, .value=NULL }, }; /* Parse the implicit file paths. * The implicit paths are stored as child annotation elements of * element. These paths are use when a property does not have * its own file paths. * * Both of them are required. This is simplification because this rule does * not make sense if all of the properties have its own file paths. */ parse_annotations(node->annotations, annots, sizeof(annots)/sizeof(annots[0])); bool misses_annot = false; for (size_t i = 0; i < ARRAY_SIZE(annots); ++i) { if (annots[i].value == NULL) { error_msg("Configuration node '%s' misses annotation '%s'", node->path, annots[i].name); misses_annot = true; } } if (misses_annot) return NULL; /* The following two variable can be omitted but the configuraion_new() call */ /* would be less understandable. */ const char *const conf_file = annots[0].value; const char *const def_conf_file = annots[1].value; configuration_t * conf = configuration_new(conf_file, def_conf_file); /* List of known configuration file paths pairs (file, default file) */ GSList *configurations = g_slist_prepend(NULL, conf); GHashTable *properties = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)NULL, (GDestroyNotify)property_free); for(GDBusPropertyInfo **prop_iter = node->interfaces[0]->properties; *prop_iter != NULL; ++prop_iter) { /* Use the node's default configuration file pair */ configuration_t *prop_conf = conf; annots[0].value = NULL; annots[1].value = NULL; /* Check whether the current property has configuration paths annotations. * It must have either both or none! */ parse_annotations((*prop_iter)->annotations, annots, ARRAY_SIZE(annots)); if (annots[0].value != NULL && annots[1].value != NULL) { const char *paths[2] = { annots[0].value, annots[1].value }; /* Try to find a configuration which equals to this configuration files pair */ GSList *lst_item = g_slist_find_custom(configurations, (gpointer)paths, (GCompareFunc)configuration_compare_paths); /* If such configuration object does not exist yet, create a new one. */ if (lst_item == NULL) { /* The following two variable can be omitted but the configuraion_new() */ /* call would be less understandable. */ const char *const prop_conf_file = annots[0].value; const char *const prop_def_conf_file = annots[1].value; prop_conf = configuration_new(prop_conf_file, prop_def_conf_file); configurations = g_slist_prepend(configurations, prop_conf); } else prop_conf = (configuration_t *)lst_item->data; } else if (annots[0].value == NULL && annots[1].value != NULL) { error_msg("Property '%s' misses annotation '%s'", (*prop_iter)->name, annots[0].name); continue; } else if (annots[0].value != NULL && annots[1].value == NULL) { error_msg("Property '%s' misses annotation '%s'", (*prop_iter)->name, annots[1].name); continue; } GVariantType *prop_type = g_variant_type_new((*prop_iter)->signature); configuration_getter_fn prop_get = configuration_getter_factory(prop_type); configuration_setter_fn prop_set = configuration_setter_factory(prop_type); /* We don't mind if the property's type is not supported. We still want * to provide the property, because hiding it would be more confusing. */ if (prop_get == NULL) error_msg("Property '%s' has unsupported getter type", (*prop_iter)->name); if (prop_set == NULL) error_msg("Property '%s' has unsupported setter type", (*prop_iter)->name); property_t *prop = property_new((*prop_iter)->name, prop_type, prop_conf, prop_get, prop_set); g_hash_table_replace(properties, (*prop_iter)->name, prop); } return dbus_conf_node_new(node, configurations, properties); } static dbus_conf_node_t *dbus_conf_node_from_file(const char *iface_file_path) { char *xmldata = xmalloc_open_read_close(iface_file_path, /*maxsize*/NULL); if (xmldata == NULL) { error_msg("Cannot create configuration node from file '%s'", iface_file_path); return NULL; } GError *error = NULL; GDBusNodeInfo *node = g_dbus_node_info_new_for_xml(xmldata, &error); if (error) { free(xmldata); error_msg("Could not parse interface file '%s': %s", iface_file_path, error->message); g_error_free(error); return NULL; } dbus_conf_node_t *conf_node = dbus_conf_node_from_node(node); /* Failed to create a configuration node, node is unchanged and must be released */ if (conf_node == NULL) g_dbus_node_info_unref(node); free(xmldata); return conf_node; } /* Go through files within the directory and try to convert them to a D-Bus * configuration nodes. */ static GList *load_configurators(const char *file_dir) { log_debug("Loading configuration XML interfaces from '%s'", file_dir); GList *conf_files = get_file_list(file_dir, "xml"); GList *result = NULL; for (GList *iter = conf_files; iter != NULL; iter = g_list_next(iter)) { file_obj_t *const file = (file_obj_t *)iter->data; const char *const filename = fo_get_filename(file); if (prefixcmp(filename, ABRT_CONF_IFACE_PFX) != 0) /* Skipping the current file because it is not Problems Configuration iface */ continue; /* The non-default interfaces has a short string between ABRT_CONF_IFACE_PFX prefix */ /* and ".xml" suffix (get_file_list() chops the sfx (.xml))*/ if (filename[strlen(ABRT_CONF_IFACE_PFX)] == '\0') /* Skipping the default configuration iface */ continue; const char *const fullpath = fo_get_fullpath(file); log_debug("Processing interface file '%s'", fullpath); dbus_conf_node_t *const conf_node = dbus_conf_node_from_file(fullpath); if (conf_node != NULL) result = g_list_prepend(result, conf_node); } free_file_list(conf_files); return result; } static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { log_debug("Going to register the configuration objects on bus '%s'", name); for (GList *iter = (GList *)user_data; iter != NULL; iter = g_list_next(iter)) { dbus_conf_node_t *node = (dbus_conf_node_t *)iter->data; log_debug("Registering dbus object '%s'", dbus_conf_node_get_path(node)); GError *error = NULL; /* Register the interface parsed from a XML file */ guint registration_id = g_dbus_connection_register_object(connection, dbus_conf_node_get_path(node), dbus_conf_node_get_interface(node), dbus_conf_node_get_vtable(node), node, /*destroy notify*/NULL, &error); if (registration_id == 0) { error_msg("Could not register object '%s': %s", dbus_conf_node_get_path(node), error->message); g_error_free(error); } /* Register interface for SetDefault() method */ registration_id = g_dbus_connection_register_object(connection, dbus_conf_node_get_path(node), g_default_node->interfaces[0], dbus_conf_node_get_configuration_vtable(node), node, /*destroy notify*/NULL, &error); if (registration_id == 0) { error_msg("Could not register object '%s': %s", dbus_conf_node_get_path(node), error->message); g_error_free(error); } } reset_timeout(); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { log_debug("Acquired the name '%s' on the system bus", name); } static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data) { log_warning(_("The name '%s' has been lost, please check if other " "service owning the name is not running.\n"), name); exit(1); } int main(int argc, char *argv[]) { /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif guint owner_id; glib_init(); abrt_init(argv); const char *program_usage_string = _( "& [options]" ); enum { OPT_v = 1 << 0, OPT_t = 1 << 1, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_INTEGER('t', NULL, &g_timeout_value, _("Exit after NUM seconds of inactivity")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); msg_prefix = "abrt-configuration"; /* for log_warning(), error_msg() and such */ if (getuid() != 0) error_msg_and_die(_("This program must be run as root.")); GError *error = NULL; g_default_node = g_dbus_node_info_new_for_xml(g_default_xml, &error); if (error != NULL) error_msg_and_die("Could not parse the default internface: %s", error->message); GList *conf_nodes = load_configurators(PROBLEMS_CONFIG_INTERFACES_DIR); if (conf_nodes == NULL) error_msg_and_die("No configuration interface file loaded. Exiting"); owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, ABRT_CONF_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, conf_nodes, (GDestroyNotify)NULL); g_loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(g_loop); log_notice("Cleaning up"); g_bus_unown_name(owner_id); g_list_free_full(conf_nodes, (GDestroyNotify)dbus_conf_node_free); g_dbus_node_info_unref(g_default_node); free_abrt_conf_data(); return 0; }