diff options
author | Karel Klic <kklic@redhat.com> | 2010-03-18 11:18:19 +0100 |
---|---|---|
committer | Karel Klic <kklic@redhat.com> | 2010-03-18 11:18:19 +0100 |
commit | e2d79ab74c2bfa798a3ec9772eb57bc4bcc7a7b8 (patch) | |
tree | 05e4aeede1499a548d02304f1d68276e21630612 | |
parent | f916f9dc8938cd59fa8a119f245e6e61d1adf496 (diff) | |
download | abrt-e2d79ab74c2bfa798a3ec9772eb57bc4bcc7a7b8.tar.gz abrt-e2d79ab74c2bfa798a3ec9772eb57bc4bcc7a7b8.tar.xz abrt-e2d79ab74c2bfa798a3ec9772eb57bc4bcc7a7b8.zip |
Allow user to select which reporter he wants to use to report a crash using CLI.
The daemon skips reporters which are not in the list of reporters provided via Report() dbus call.
Reviewed by: Jiri Moskovcak <jmoskovc@redhat.com>
Reviewed by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | src/CLI/dbus.cpp | 20 | ||||
-rw-r--r-- | src/CLI/dbus.h | 17 | ||||
-rw-r--r-- | src/CLI/report.cpp | 199 | ||||
-rw-r--r-- | src/Daemon/CommLayerServerDBus.cpp | 6 | ||||
-rw-r--r-- | src/Daemon/MiddleWare.cpp | 43 | ||||
-rw-r--r-- | src/Daemon/MiddleWare.h | 18 |
6 files changed, 218 insertions, 85 deletions
diff --git a/src/CLI/dbus.cpp b/src/CLI/dbus.cpp index 600d8556..9dd5bff7 100644 --- a/src/CLI/dbus.cpp +++ b/src/CLI/dbus.cpp @@ -160,6 +160,7 @@ map_crash_data_t call_CreateReport(const char* crash_id) } report_status_t call_Report(const map_crash_data_t& report, + const vector_string_t& reporters, const map_map_string_t &plugins) { DBusMessage* msg = new_call_msg(__func__ + 5); @@ -169,8 +170,7 @@ report_status_t call_Report(const map_crash_data_t& report, /* parameter #1: report data */ store_val(&out_iter, report); /* parameter #2: reporters to use */ - vector_string_t reporters; - store_val(&out_iter, reporters); /* unused by daemon so far */ + store_val(&out_iter, reporters); /* parameter #3 (opt): plugin config */ if (!plugins.empty()) store_val(&out_iter, plugins); @@ -248,6 +248,22 @@ map_plugin_settings_t call_GetPluginSettings(const char *name) return argout; } +map_map_string_t call_GetSettings() +{ + DBusMessage *msg = new_call_msg(__func__ + 5); + DBusMessage *reply = send_get_reply_and_unref(msg); + + DBusMessageIter in_iter; + dbus_message_iter_init(reply, &in_iter); + map_map_string_t argout; + int r = load_val(&in_iter, argout); + if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ + error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5); + + dbus_message_unref(reply); + return argout; +} + void handle_dbus_err(bool error_flag, DBusError *err) { if (dbus_error_is_set(err)) diff --git a/src/CLI/dbus.h b/src/CLI/dbus.h index be6b9615..4aaa4e96 100644 --- a/src/CLI/dbus.h +++ b/src/CLI/dbus.h @@ -24,21 +24,30 @@ extern DBusConnection* s_dbus_conn; vector_map_crash_data_t call_GetCrashInfos(); + map_crash_data_t call_CreateReport(const char *crash_id); /** Sends report using enabled Reporter plugins. + * @param report + * The report sent to Reporter plugins. + * @param reporters + * List of names of Reporters which should be called. * @param plugins - * Optional settings for plugins, can be empty. + * Optional settings for Reporter plugins, can be empty. * Format: plugins["PluginName"]["SettingsKey"] = "SettingsValue" * If it contains settings for some plugin, it must contain _all fields_ * obtained by call_GetPluginSettings, otherwise the plugin might ignore * the settings. */ report_status_t call_Report(const map_crash_data_t& report, + const vector_string_t& reporters, const map_map_string_t &plugins); + int32_t call_DeleteDebugDump(const char* crash_id); /* Gets basic data about all installed plugins. + * @todo + * Return more semantically structured output - maybe a struct instead of a map. */ map_map_string_t call_GetPluginsInfo(); @@ -48,6 +57,12 @@ map_map_string_t call_GetPluginsInfo(); */ map_plugin_settings_t call_GetPluginSettings(const char *name); +/** Gets global daemon settings. + * @todo + * Return more semantically structured output - maybe a struct instead of a map. + */ +map_map_string_t call_GetSettings(); + void handle_dbus_err(bool error_flag, DBusError *err); #endif diff --git a/src/CLI/report.cpp b/src/CLI/report.cpp index 2d79d38d..60ed3c85 100644 --- a/src/CLI/report.cpp +++ b/src/CLI/report.cpp @@ -22,6 +22,8 @@ #include "DebugDump.h" #include "CrashTypes.h" // FILENAME_* defines #include "Plugin.h" // LoadPluginSettings +#include <cassert> +#include <algorithm> #if HAVE_CONFIG_H # include <config.h> #endif @@ -423,26 +425,82 @@ static int run_report_editor(map_crash_data_t &cr) */ static void read_from_stdin(const char *question, char *result, int result_size) { + assert(result_size > 1); printf("%s", question); fflush(NULL); - fgets(result, result_size, stdin); + if (NULL == fgets(result, result_size, stdin)) + result[0] = '\0'; // Remove the newline from the login. char *newline = strchr(result, '\n'); if (newline) *newline = '\0'; } -/** - * Gets reporter plugin settings. - * @param ask_user - * If it's set to true and some reporter plugin settings are found to be missing - * (like login name or password), user is asked to provide the missing parts. - * @param settings - * A structure filled with reporter plugin settings. +/** Splits a string into substrings using chosen delimiters. + * @param delim + * Specifies a set of characters that delimit the + * tokens in the parsed string */ -static void get_reporter_plugin_settings(map_map_string_t &settings, bool ask_user) +static vector_string_t split(const std::string &s, const char *delim) { - /* First of all, load system-wide report plugin settings. */ + std::vector<std::string> elems; + char str[s.length() + 1]; + /* str is modified by the following strtok_r, + so s.c_str() cannot be used directly */ + strcpy(str, s.c_str()); + char *saveptr, *token; + token = strtok_r(str, delim, &saveptr); + while (token != NULL) + { + elems.push_back(token); + token = strtok_r(NULL, delim, &saveptr); + } + return elems; +} + +/** Returns a list of enabled Reporter plugins, that are used to report + * a particular crash. + * @todo + * Very similar code is used in the GUI, and also in the Daemon. + * It should be shared. + */ +static vector_string_t get_enabled_reporters(map_crash_data_t &crash_data) +{ + vector_string_t result; + + /* Get global daemon settings. Analyzer->Reporters mapping is stored there. */ + map_map_string_t settings = call_GetSettings(); + /* Reporters are separated by comma in the following map. */ + map_string_t &analyzer_to_reporters = settings["AnalyzerActionsAndReporters"]; + + /* Get the analyzer from the crash. */ + const char *analyzer = get_crash_data_item_content_or_NULL(crash_data, FILENAME_ANALYZER); + if (!analyzer) + return result; /* No analyzer found in the crash data. */ + + /* First try to find package name dependent analyzer. + * nvr = name-version-release + * TODO: Similar code is in MiddleWare.cpp. It should not be duplicated. + */ + const char *package_nvr = get_crash_data_item_content_or_NULL(crash_data, FILENAME_PACKAGE); + if (!package_nvr) + return result; /* No package name found in the crash data. */ + std::string str_package_nvr(package_nvr); + std::string package_name = str_package_nvr.substr(0, str_package_nvr.rfind("-", str_package_nvr.rfind("-") - 1)); + // analyzer with package name (CCpp:xorg-x11-app) has higher priority + std::string package_specific_analyzer = std::string(analyzer) + ":" + package_name; + + map_string_t::const_iterator reporters_iter = analyzer_to_reporters.find(package_specific_analyzer); + if (analyzer_to_reporters.end() == reporters_iter) + { + reporters_iter = analyzer_to_reporters.find(analyzer); + if (analyzer_to_reporters.end() == reporters_iter) + return result; /* No reporters found for the analyzer. */ + } + + /* Reporters found, now parse the list. */ + vector_string_t reporter_vec = split(reporters_iter->second, ","); + // Get informations about all plugins. map_map_string_t plugins = call_GetPluginsInfo(); // Check the configuration of each enabled Reporter plugin. @@ -455,11 +513,49 @@ static void get_reporter_plugin_settings(map_map_string_t &settings, bool ask_us // Skip nonReporter plugins. if (0 != strcmp(it->second["Type"].c_str(), "Reporter")) continue; - map_string_t single_plugin_settings = call_GetPluginSettings(it->first.c_str()); + // Skip plugins not used in this particular crash. + if (reporter_vec.end() == std::find(reporter_vec.begin(), reporter_vec.end(), std::string(it->first))) + continue; + result.push_back(it->first); + } + return result; +} + +/* Asks a [y/n] question on stdin/stdout. + * Returns true if the answer is yes, false otherwise. + */ +static bool ask_yesno(const char *question) +{ + printf(question); + fflush(NULL); + char answer[16] = "n"; + fgets(answer, sizeof(answer), stdin); + /* TODO: localize 'y' */ + return ((answer[0] | 0x20) == 'y'); +} + +/** + * Gets reporter plugin settings. + * @param reporters + * List of reporter names. Settings of these reporters are handled. + * @param settings + * A structure filled with reporter plugin settings. + * @param ask_user + * If it's set to true and some reporter plugin settings are found to be missing + * (like login name or password), user is asked to provide the missing parts. + */ +static void get_reporter_plugin_settings(const vector_string_t& reporters, + map_map_string_t &settings, + bool ask_user) +{ + /* First of all, load system-wide report plugin settings. */ + for (vector_string_t::const_iterator it = reporters.begin(); it != reporters.end(); ++it) + { + map_string_t single_plugin_settings = call_GetPluginSettings(it->c_str()); // Copy the received settings as defaults. // Plugins won't work without it, if some value is missing // they use their default values for all fields. - settings[it->first] = single_plugin_settings; + settings[it->c_str()] = single_plugin_settings; } /* Second, load user-specific settings, which override @@ -468,14 +564,14 @@ static void get_reporter_plugin_settings(map_map_string_t &settings, bool ask_us const char* homedir = pw ? pw->pw_dir : NULL; if (homedir) { - itend = settings.end(); - for (it = settings.begin(); it != itend; ++it) + map_map_string_t::const_iterator itend = settings.end(); + for (map_map_string_t::iterator it = settings.begin(); it != itend; ++it) { map_string_t single_plugin_settings; std::string path = std::string(homedir) + "/.abrt/" + it->first + "."PLUGINS_CONF_EXTENSION; /* Load plugin config in the home dir. Do not skip lines with empty value (but containing a "key="), - because user may want to override password from /etc/abrt/plugins/*.conf, but he prefers to + because user may want to override password from /etc/abrt/plugins/\*.conf, but he prefers to enter it every time he reports. */ bool success = LoadPluginSettings(path.c_str(), single_plugin_settings, false); if (!success) @@ -491,8 +587,8 @@ static void get_reporter_plugin_settings(map_map_string_t &settings, bool ask_us return; /* Third, check if a login or password is missing, and ask for it. */ - itend = settings.end(); - for (it = settings.begin(); it != itend; ++it) + map_map_string_t::const_iterator itend = settings.end(); + for (map_map_string_t::iterator it = settings.begin(); it != itend; ++it) { map_string_t &single_plugin_settings = it->second; // Login information is missing. @@ -538,40 +634,55 @@ int report(const char *crash_id, bool always) return result; } - /* Read the plugin settings. */ - map_map_string_t pluginSettings; - get_reporter_plugin_settings(pluginSettings, !always); + /* Get enabled reporters associated with this particular crash. */ + vector_string_t reporters = get_enabled_reporters(cr); - /* Ask if user really wants to send the report. */ - if (!always) + int errors = 0; + int plugins = 0; + if (always) { - // Report only if the user is sure. - printf(_("Do you want to send the report? [y/N]: ")); - fflush(NULL); - char answer[16] = "n"; - fgets(answer, sizeof(answer), stdin); - if ((answer[0] | 0x20) != 'y') + map_map_string_t reporters_settings; /* to be filled on the next line */ + get_reporter_plugin_settings(reporters, reporters_settings, false); + + puts(_("Reporting...")); + report_status_t r = call_Report(cr, reporters, reporters_settings); + report_status_t::iterator it = r.begin(); + while (it != r.end()) { - puts(_("Crash report was not sent.")); - return 0; + vector_string_t &v = it->second; + printf("%s: %s\n", it->first.c_str(), v[REPORT_STATUS_IDX_MSG].c_str()); + plugins++; + if (v[REPORT_STATUS_IDX_FLAG] == "0") + errors++; + it++; } } - - int errors = 0; - int plugins = 0; - puts(_("Reporting...")); - report_status_t r = call_Report(cr, pluginSettings); - report_status_t::iterator it = r.begin(); - while (it != r.end()) + else { - vector_string_t &v = it->second; - printf("%s: %s\n", it->first.c_str(), v[REPORT_STATUS_IDX_MSG].c_str()); - plugins++; - if (v[REPORT_STATUS_IDX_FLAG] == "0") - errors++; - it++; + /* For every reporter, ask if user really wants to report using it. */ + for (vector_string_t::const_iterator it = reporters.begin(); it != reporters.end(); ++it) + { + char question[255]; + snprintf(question, 255, _("Report using %s? [y/N]: "), it->c_str()); + if (!ask_yesno(question)) + { + puts(_("Skipping...")); + continue; + } + + vector_string_t cur_reporter(1, *it); + map_map_string_t reporters_settings; /* to be filled on the next line */ + get_reporter_plugin_settings(cur_reporter, reporters_settings, true); + report_status_t r = call_Report(cr, cur_reporter, reporters_settings); + assert(r.size() == 1); /* one reporter --> one report status */ + vector_string_t &v = r.begin()->second; + printf("%s: %s\n", r.begin()->first.c_str(), v[REPORT_STATUS_IDX_MSG].c_str()); + plugins++; + if (v[REPORT_STATUS_IDX_FLAG] == "0") + errors++; + } } - printf(_("Crash reported via %d plugins (%d errors)\n"), plugins, errors); + printf(_("Crash reported via %d plugins (%d errors)\n"), plugins, errors); return errors != 0; } diff --git a/src/Daemon/CommLayerServerDBus.cpp b/src/Daemon/CommLayerServerDBus.cpp index 103f8675..d45209a3 100644 --- a/src/Daemon/CommLayerServerDBus.cpp +++ b/src/Daemon/CommLayerServerDBus.cpp @@ -247,7 +247,7 @@ static int handle_Report(DBusMessage* call, DBusMessage* reply) return 0; } - /* Second parameter: reporters to use */ + /* Second parameter: list of reporters to use */ vector_string_t reporters; r = load_val(&in_iter, reporters); if (r == ABRT_DBUS_ERROR) @@ -256,7 +256,7 @@ static int handle_Report(DBusMessage* call, DBusMessage* reply) return -1; } - /* Third parameter is optional */ + /* Third parameter (optional): configuration data for plugins */ map_map_string_t user_conf_data; if (r == ABRT_DBUS_MORE_FIELDS) { @@ -294,7 +294,7 @@ static int handle_Report(DBusMessage* call, DBusMessage* reply) report_status_t argout1; try { - argout1 = Report(argin1, user_conf_data, unix_uid); + argout1 = Report(argin1, reporters, user_conf_data, unix_uid); } catch (CABRTException &e) { diff --git a/src/Daemon/MiddleWare.cpp b/src/Daemon/MiddleWare.cpp index 3ab3ddd1..7792f5b8 100644 --- a/src/Daemon/MiddleWare.cpp +++ b/src/Daemon/MiddleWare.cpp @@ -27,6 +27,7 @@ #include "ABRTException.h" #include "CommLayerInner.h" #include "MiddleWare.h" +#include <algorithm> using namespace std; @@ -377,10 +378,11 @@ void RunActionsAndReporters(const char *pDebugDumpDir) } -// We must not trust client_report here! +// Do not trust client_report here! // dbus handler passes it from user without checking report_status_t Report(const map_crash_data_t& client_report, - map_map_string_t& pSettings, + const vector_string_t &reporters, + map_map_string_t& settings, long caller_uid) { // Get ID fields @@ -498,39 +500,20 @@ report_status_t Report(const map_crash_data_t& client_report, for (; it_r != keyPtr->second.end(); it_r++) { const char *plugin_name = it_r->first.c_str(); + + /* Check if the reporter is in the input list of allowed reporters. */ + if (reporters.end() == std::find(reporters.begin(), reporters.end(), plugin_name)) + { + continue; + } + try { if (g_pPluginManager->GetPluginType(plugin_name) == REPORTER) { CReporter* reporter = g_pPluginManager->GetReporter(plugin_name); /* can't be NULL */ -#if 0 /* Using ~user/.abrt/ is bad wrt security */ - std::string home; - map_plugin_settings_t oldSettings; - map_plugin_settings_t newSettings; - - if (pUID != "") - { - home = get_home_dir(xatoi_u(pUID.c_str())); - if (home != "") - { - oldSettings = reporter->GetSettings(); - - if (LoadPluginSettings(home + "/.abrt/" + plugin_name + "."PLUGINS_CONF_EXTENSION, newSettings)) - { - reporter->SetSettings(newSettings); - } - } - } -#endif - map_plugin_settings_t plugin_settings = pSettings[plugin_name]; + map_plugin_settings_t plugin_settings = settings[plugin_name]; std::string res = reporter->Report(stored_report, plugin_settings, it_r->second.c_str()); - -#if 0 /* Using ~user/.abrt/ is bad wrt security */ - if (home != "") - { - reporter->SetSettings(oldSettings); - } -#endif ret[plugin_name].push_back("1"); // REPORT_STATUS_IDX_FLAG ret[plugin_name].push_back(res); // REPORT_STATUS_IDX_MSG if (message != "") @@ -589,7 +572,7 @@ static bool IsDebugDumpSaved(long uid, vector_database_rows_t rows = database->GetUIDData(uid); database->DisConnect(); - int ii; + size_t ii; bool found = false; for (ii = 0; ii < rows.size(); ii++) { diff --git a/src/Daemon/MiddleWare.h b/src/Daemon/MiddleWare.h index 275d5312..4a2903c2 100644 --- a/src/Daemon/MiddleWare.h +++ b/src/Daemon/MiddleWare.h @@ -87,12 +87,20 @@ void RunActionsAndReporters(const char *pDebugDumpDir); * fails, then default config is used. If pUID is emply string, default * config is used. * ...). - * @param pCrashData A crash report. - * @param pUID An user uid - * @return A report status, which reporters ends successfuly with messages. + * @param crash_data + * A crash report. + * @param reporters + * List of allowed reporters. Which reporters will be used depends + * on the analyzer of the crash_data. Reporters missing from this list + * will not be used. + * @param caller_uid + * An user uid. + * @return + * A report status, which reporters ends successfuly with messages. */ -report_status_t Report(const map_crash_data_t& pCrashData, - map_map_string_t& pSettings, +report_status_t Report(const map_crash_data_t& crash_data, + const vector_string_t& reporters, + map_map_string_t& settings, long caller_uid); /** * Adds package name and description to debugdump dir. |