int delete_problem_dirs_over_dbus(const GList *problem_dir_paths) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return 1; GVariant *parameters = variant_from_string_list(problem_dir_paths); GError *error = NULL; g_dbus_proxy_call_sync(proxy, "DeleteProblem", parameters, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); //g_variant_unref(parameters); -- need this??? no?? why? if (error) { error_msg(_("Deleting problem directory failed: %s"), error->message); g_error_free(error); return 1; } return 0; }
problem_data_t *get_problem_data_dbus(const char *problem_dir_path) { INITIALIZE_LIBABRT(); static const char *elements[] = { FILENAME_TIME, FILENAME_REASON, FILENAME_NOT_REPORTABLE, FILENAME_COMPONENT, FILENAME_EXECUTABLE, FILENAME_REPORTED_TO, NULL, }; problem_data_t *pd = problem_data_new(); if (fill_problem_data_over_dbus(problem_dir_path, elements, pd) != 0) { error_msg(_("Can't get problem data from abrt-dbus")); problem_data_free(pd); return NULL; } return pd; }
int chown_dir_over_dbus(const char *problem_dir_path) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return 1; GError *error = NULL; g_dbus_proxy_call_sync(proxy, "ChownProblemDir", g_variant_new("(s)", problem_dir_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("Can't chown '%s': %s"), problem_dir_path, error->message); g_error_free(error); return 1; } return 0; }
int test_exist_over_dbus(const char *problem_id, const char *element_name) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return -1; GError *error = NULL; GVariant *result = g_dbus_proxy_call_sync(proxy, "TestElementExists", g_variant_new("(ss)", problem_id, element_name), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("Can't test whether the element exists over abrt-dbus: %s"), error->message); g_error_free(error); return -1; } gboolean retval; g_variant_get(result, "(b)", &retval); g_variant_unref(result); return retval; }
problem_data_t *get_problem_data_dbus(const char *problem_dir_path) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return NULL; GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("as")); g_variant_builder_add(builder, "s", FILENAME_TIME ); g_variant_builder_add(builder, "s", FILENAME_REASON ); g_variant_builder_add(builder, "s", FILENAME_NOT_REPORTABLE); g_variant_builder_add(builder, "s", FILENAME_COMPONENT ); g_variant_builder_add(builder, "s", FILENAME_EXECUTABLE ); g_variant_builder_add(builder, "s", FILENAME_REPORTED_TO ); GVariant *params = g_variant_new("(sas)", problem_dir_path, builder); g_variant_builder_unref(builder); GError *error = NULL; GVariant *result = g_dbus_proxy_call_sync(proxy, "GetInfo", params, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("Can't get problem data from abrt-dbus: %s"), error->message); g_error_free(error); return NULL; } problem_data_t *pd = problem_data_new(); char *key, *val; GVariantIter *iter; g_variant_get(result, "(a{ss})", &iter); while (g_variant_iter_loop(iter, "{ss}", &key, &val)) { problem_data_add_text_noteditable(pd, key, val); } g_variant_unref(result); return pd; }
char *load_text_over_dbus(const char *problem_id, const char *element_name) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return ERR_PTR; GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE("as")); g_variant_builder_add(builder, "s", element_name); GVariant *params = g_variant_new("(sas)", problem_id, builder); g_variant_builder_unref(builder); GError *error = NULL; GVariant *result = g_dbus_proxy_call_sync(proxy, "GetInfo", params, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("Can't get problem data from abrt-dbus: %s"), error->message); g_error_free(error); return ERR_PTR; } GVariant *values = g_variant_get_child_value(result, 0); g_variant_unref(result); char *retval = NULL; if (g_variant_n_children(values) == 1) { GVariant *contents = g_variant_get_child_value(values, 0); gchar *key; g_variant_get(contents, "{&ss}", &key, &retval); } g_variant_unref(values); return retval; }
problem_data_t *get_full_problem_data_over_dbus(const char *problem_dir_path) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return ERR_PTR; GError *error = NULL; GVariant *result = g_dbus_proxy_call_sync(proxy, "GetProblemData", g_variant_new("(s)", problem_dir_path), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("Can't get problem data from abrt-dbus: %s"), error->message); g_error_free(error); return ERR_PTR; } GVariantIter *iter = NULL; g_variant_get(result, "(a{s(its)})", &iter); gchar *name = NULL; gint flags; gulong size; gchar *value = NULL; problem_data_t *pd = problem_data_new(); while (g_variant_iter_loop(iter, "{&s(it&s)}", &name, &flags, &size, &value)) problem_data_add_ext(pd, name, value, flags, size); problem_data_add(pd, CD_DUMPDIR, problem_dir_path, CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE + CD_FLAG_LIST); g_variant_unref(result); return pd; }
int fill_problem_data_over_dbus(const char *problem_id, const char **elements, problem_data_t *problem_data) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return -1; GVariantBuilder *args_builder = g_variant_builder_new(G_VARIANT_TYPE("as")); for (const char **iter = elements; *iter; ++iter) g_variant_builder_add(args_builder, "s", *iter); GVariant *params = g_variant_new("(sas)", problem_id, args_builder); g_variant_builder_unref(args_builder); GError *error = NULL; GVariant *result = g_dbus_proxy_call_sync(proxy, "GetInfo", params, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("D-Bus GetInfo method call failed: %s"), error->message); g_error_free(error); return -2; } char *key, *val; GVariantIter *iter; g_variant_get(result, "(a{ss})", &iter); while (g_variant_iter_loop(iter, "{ss}", &key, &val)) problem_data_add_text_noteditable(problem_data, key, val); g_variant_unref(result); return 0; }
GList *get_problems_over_dbus(bool authorize) { INITIALIZE_LIBABRT(); GDBusProxy *proxy = get_dbus_proxy(); if (!proxy) return ERR_PTR; GError *error = NULL; GVariant *result = g_dbus_proxy_call_sync(proxy, authorize ? "GetAllProblems" : "GetProblems", g_variant_new("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { error_msg(_("Can't get problem list from abrt-dbus: %s"), error->message); g_error_free(error); return ERR_PTR; } GList *list = NULL; if (result) { /* Fetch "as" from "(as)" */ GVariant *array = g_variant_get_child_value(result, 0); list = string_list_from_variant(array); g_variant_unref(array); g_variant_unref(result); } return list; }
char *get_backtrace(const char *dump_dir_name, unsigned timeout_sec, const char *debuginfo_dirs) { INITIALIZE_LIBABRT(); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return NULL; char *executable = dd_load_text(dd, FILENAME_EXECUTABLE); dd_close(dd); /* Let user know what's going on */ log(_("Generating backtrace")); unsigned i = 0; char *args[25]; args[i++] = (char*)"gdb"; args[i++] = (char*)"-batch"; struct strbuf *set_debug_file_directory = strbuf_new(); unsigned auto_load_base_index = 0; if(debuginfo_dirs == NULL) { // set non-existent debug file directory to prevent resolving // function names - we need offsets for core backtrace. strbuf_append_str(set_debug_file_directory, "set debug-file-directory /"); } else { strbuf_append_str(set_debug_file_directory, "set debug-file-directory /usr/lib/debug"); struct strbuf *debug_directories = strbuf_new(); const char *p = debuginfo_dirs; while (1) { while (*p == ':') p++; if (*p == '\0') break; const char *colon_or_nul = strchrnul(p, ':'); strbuf_append_strf(debug_directories, "%s%.*s/usr/lib/debug", (debug_directories->len == 0 ? "" : ":"), (int)(colon_or_nul - p), p); p = colon_or_nul; } strbuf_append_strf(set_debug_file_directory, ":%s", debug_directories->buf); args[i++] = (char*)"-iex"; auto_load_base_index = i; args[i++] = xasprintf("add-auto-load-safe-path %s", debug_directories->buf); args[i++] = (char*)"-iex"; args[i++] = xasprintf("add-auto-load-scripts-directory %s", debug_directories->buf); strbuf_free(debug_directories); } args[i++] = (char*)"-ex"; const unsigned debug_dir_cmd_index = i++; args[debug_dir_cmd_index] = strbuf_free_nobuf(set_debug_file_directory); /* "file BINARY_FILE" is needed, without it gdb cannot properly * unwind the stack. Currently the unwind information is located * in .eh_frame which is stored only in binary, not in coredump * or debuginfo. * * Fedora GDB does not strictly need it, it will find the binary * by its build-id. But for binaries either without build-id * (= built on non-Fedora GCC) or which do not have * their debuginfo rpm installed gdb would not find BINARY_FILE * so it is still makes sense to supply "file BINARY_FILE". * * Unfortunately, "file BINARY_FILE" doesn't work well if BINARY_FILE * was deleted (as often happens during system updates): * gdb uses specified BINARY_FILE * even if it is completely unrelated to the coredump. * See https://bugzilla.redhat.com/show_bug.cgi?id=525721 * * TODO: check mtimes on COREFILE and BINARY_FILE and not supply * BINARY_FILE if it is newer (to at least avoid gdb complaining). */ args[i++] = (char*)"-ex"; const unsigned file_cmd_index = i++; args[file_cmd_index] = xasprintf("file %s", executable); free(executable); args[i++] = (char*)"-ex"; const unsigned core_cmd_index = i++; args[core_cmd_index] = xasprintf("core-file %s/"FILENAME_COREDUMP, dump_dir_name); args[i++] = (char*)"-ex"; const unsigned bt_cmd_index = i++; /*args[9] = ... see below */ args[i++] = (char*)"-ex"; args[i++] = (char*)"info sharedlib"; /* glibc's abort() stores its message in __abort_msg variable */ args[i++] = (char*)"-ex"; args[i++] = (char*)"print (char*)__abort_msg"; args[i++] = (char*)"-ex"; args[i++] = (char*)"print (char*)__glib_assert_msg"; args[i++] = (char*)"-ex"; args[i++] = (char*)"info all-registers"; args[i++] = (char*)"-ex"; const unsigned dis_cmd_index = i++; args[dis_cmd_index] = (char*)"disassemble"; args[i++] = NULL; /* Get the backtrace, but try to cap its size */ /* Limit bt depth. With no limit, gdb sometimes OOMs the machine */ unsigned bt_depth = 1024; const char *thread_apply_all = "thread apply all -ascending"; const char *full = " full"; char *bt = NULL; while (1) { args[bt_cmd_index] = xasprintf("%s backtrace %u%s", thread_apply_all, bt_depth, full); bt = exec_vp(args, /*redirect_stderr:*/ 1, timeout_sec, NULL); free(args[bt_cmd_index]); if ((bt && strnlen(bt, 256*1024) < 256*1024) || bt_depth <= 32) { break; } bt_depth /= 2; if (bt) log("Backtrace is too big (%u bytes), reducing depth to %u", (unsigned)strlen(bt), bt_depth); else /* (NB: in fact, current impl. of exec_vp() never returns NULL) */ log("Failed to generate backtrace, reducing depth to %u", bt_depth); free(bt); /* Replace -ex disassemble (which disasms entire function $pc points to) * to a version which analyzes limited, small patch of code around $pc. * (Users reported a case where bare "disassemble" attempted to process * entire .bss). * TODO: what if "$pc-N" underflows? in my test, this happens: * Dump of assembler code from 0xfffffffffffffff0 to 0x30: * End of assembler dump. * (IOW: "empty" dump) */ args[dis_cmd_index] = (char*)"disassemble $pc-20, $pc+64"; if (bt_depth <= 64 && thread_apply_all[0] != '\0') { /* This program likely has gazillion threads, dont try to bt them all */ bt_depth = 128; thread_apply_all = ""; } if (bt_depth <= 64 && full[0] != '\0') { /* Looks like there are gigantic local structures or arrays, disable "full" bt */ bt_depth = 128; full = ""; } } if (auto_load_base_index > 0) { free(args[auto_load_base_index]); free(args[auto_load_base_index + 2]); } free(args[debug_dir_cmd_index]); free(args[file_cmd_index]); free(args[core_cmd_index]); return bt; }