summaryrefslogtreecommitdiffstats
path: root/src/Daemon/MiddleWare.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Daemon/MiddleWare.cpp')
-rw-r--r--src/Daemon/MiddleWare.cpp1132
1 files changed, 0 insertions, 1132 deletions
diff --git a/src/Daemon/MiddleWare.cpp b/src/Daemon/MiddleWare.cpp
deleted file mode 100644
index c7ed4df5..00000000
--- a/src/Daemon/MiddleWare.cpp
+++ /dev/null
@@ -1,1132 +0,0 @@
-/*
- MiddleWare.cpp
-
- Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
- Copyright (C) 2009 RedHat inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include <fnmatch.h>
-#include <algorithm>
-#include "abrtlib.h"
-#include "abrt_types.h"
-#include "Daemon.h"
-#include "Settings.h"
-#include "rpm.h"
-#include "debug_dump.h"
-#include "abrt_exception.h"
-#include "abrt_packages.h"
-#include "comm_layer_inner.h"
-#include "MiddleWare.h"
-
-using namespace std;
-
-/**
- * An instance of CPluginManager. When MiddleWare wants to do something
- * with plugins, it calls the plugin manager.
- * @see PluginManager.h
- */
-CPluginManager* g_pPluginManager;
-
-/**
- * A map, which associates particular analyzer to one or more
- * action or reporter plugins. These are activated when a crash, which
- * is maintained by particular analyzer, occurs.
- */
-typedef std::map<std::string, vector_pair_string_string_t> map_analyzer_actions_and_reporters_t;
-static map_analyzer_actions_and_reporters_t s_mapAnalyzerActionsAndReporters;
-/**
- * A vector of one or more action or reporter plugins. These are
- * activated when any crash occurs.
- */
-static vector_pair_string_string_t s_vectorActionsAndReporters;
-
-
-static void RunAnalyzerActions(const char *pAnalyzer, const char* pPackageName, const char *pDebugDumpDir, int force);
-
-
-static char* is_text_file(const char *name, ssize_t *sz)
-{
- /* We were using magic.h API to check for file being text, but it thinks
- * that file containing just "0" is not text (!!)
- * So, we do it ourself.
- */
-
- int fd = open(name, O_RDONLY);
- if (fd < 0)
- return NULL; /* it's not text (because it does not exist! :) */
-
- char *buf = (char*)xmalloc(*sz);
- ssize_t r = *sz = full_read(fd, buf, *sz);
- close(fd);
- if (r < 0)
- {
- free(buf);
- return NULL; /* it's not text (because we can't read it) */
- }
-
- /* Some files in our dump directories are known to always be textual */
- const char *base = strrchr(name, '/');
- if (base)
- {
- base++;
- if (strcmp(base, FILENAME_BACKTRACE) == 0
- || strcmp(base, FILENAME_CMDLINE) == 0
- ) {
- return buf;
- }
- }
-
- /* Every once in a while, even a text file contains a few garbled
- * or unexpected non-ASCII chars. We should not declare it "binary".
- */
- const unsigned RATIO = 50;
- unsigned total_chars = r + RATIO;
- unsigned bad_chars = 1; /* 1 prevents division by 0 later */
- while (--r >= 0)
- {
- if (buf[r] >= 0x7f
- /* among control chars, only '\t','\n' etc are allowed */
- || (buf[r] < ' ' && !isspace(buf[r]))
- ) {
- if (buf[r] == '\0')
- {
- /* We don't like NULs very much. Not text for sure! */
- free(buf);
- return NULL;
- }
- bad_chars++;
- }
- }
-
- if ((total_chars / bad_chars) >= RATIO)
- return buf; /* looks like text to me */
-
- free(buf);
- return NULL; /* it's binary */
-}
-
-static void load_crash_data_from_debug_dump(CDebugDump& dd, map_crash_data_t& data)
-{
- std::string short_name;
- std::string full_name;
-
- dd.InitGetNextFile();
- while (dd.GetNextFile(&short_name, &full_name))
- {
- ssize_t sz = 4*1024;
- char *text = NULL;
- bool editable = is_editable_file(short_name.c_str());
-
- if (!editable)
- {
- text = is_text_file(full_name.c_str(), &sz);
- if (!text)
- {
- add_to_crash_data_ext(data,
- short_name.c_str(),
- CD_BIN,
- CD_ISNOTEDITABLE,
- full_name.c_str()
- );
- continue;
- }
- }
-
- std::string content;
- if (sz < 4*1024) /* is_text_file did read entire file */
- content.assign(text, sz);
- else /* no, need to read it all */
- dd.LoadText(short_name.c_str(), content);
- free(text);
-
- add_to_crash_data_ext(data,
- short_name.c_str(),
- CD_TXT,
- editable ? CD_ISEDITABLE : CD_ISNOTEDITABLE,
- content.c_str()
- );
- }
-}
-
-/**
- * Transforms a debugdump directory to inner crash
- * report form. This form is used for later reporting.
- * @param pDebugDumpDir A debugdump dir containing all necessary data.
- * @param pCrashData A created crash report.
- */
-static void DebugDumpToCrashReport(const char *pDebugDumpDir, map_crash_data_t& pCrashData)
-{
- VERB3 log(" DebugDumpToCrashReport('%s')", pDebugDumpDir);
-
- CDebugDump dd;
- dd.Open(pDebugDumpDir);
-
- const char *const *v = must_have_files;
- while (*v)
- {
- if (!dd.Exist(*v))
- {
- throw CABRTException(EXCEP_ERROR, "DebugDumpToCrashReport(): important file '%s' is missing", *v);
- }
- v++;
- }
-
- load_crash_data_from_debug_dump(dd, pCrashData);
-}
-
-/**
- * Get a local UUID from particular analyzer plugin.
- * @param pAnalyzer A name of an analyzer plugin.
- * @param pDebugDumpDir A debugdump dir containing all necessary data.
- * @return A local UUID.
- */
-static std::string GetLocalUUID(const char *pAnalyzer, const char *pDebugDumpDir)
-{
- CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(pAnalyzer);
- if (analyzer)
- {
- return analyzer->GetLocalUUID(pDebugDumpDir);
- }
- throw CABRTException(EXCEP_PLUGIN, "Error running '%s'", pAnalyzer);
-}
-
-/**
- * Get a global UUID from particular analyzer plugin.
- * @param pAnalyzer A name of an analyzer plugin.
- * @param pDebugDumpDir A debugdump dir containing all necessary data.
- * @return A global UUID.
- */
-static std::string GetGlobalUUID(const char *pAnalyzer,
- const char *pDebugDumpDir)
-{
- CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(pAnalyzer);
- if (analyzer)
- {
- return analyzer->GetGlobalUUID(pDebugDumpDir);
- }
- throw CABRTException(EXCEP_PLUGIN, "Error running '%s'", pAnalyzer);
-}
-
-/**
- * Take care of getting all additional data needed
- * for computing UUIDs and creating a report for particular analyzer
- * plugin. This report could be send somewhere afterwards.
- * @param pAnalyzer A name of an analyzer plugin.
- * @param pDebugDumpPath A debugdump dir containing all necessary data.
- */
-static void run_analyser_CreateReport(const char *pAnalyzer,
- const char *pDebugDumpDir,
- int force)
-{
- CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(pAnalyzer);
- if (analyzer)
- {
- analyzer->CreateReport(pDebugDumpDir, force);
- }
- /* else: GetAnalyzer() already complained, no need to handle it here */
-}
-
-/*
- * Called in three cases:
- * (1) by StartJob dbus call -> CreateReportThread(), in the thread
- * (2) by CreateReport dbus call
- * (3) by daemon if AutoReportUID is set for this user's crashes
- */
-mw_result_t CreateCrashReport(const char *crash_id,
- long caller_uid,
- int force,
- map_crash_data_t& pCrashData)
-{
- VERB2 log("CreateCrashReport('%s',%ld,result)", crash_id, caller_uid);
-
- database_row_t row;
- CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase.c_str());
- database->Connect();
- row = database->GetRow(crash_id);
- database->DisConnect();
- if (row.m_sUUID == "")
- {
- error_msg("crash '%s' is not in database", crash_id);
- return MW_IN_DB_ERROR;
- }
- if (caller_uid != 0 /* not called by root */
- && row.m_sInformAll != "1"
- && to_string(caller_uid) != row.m_sUID
- ) {
- error_msg("crash '%s' can't be accessed by user with uid %ld", crash_id, caller_uid);
- return MW_IN_DB_ERROR;
- }
-
- mw_result_t r = MW_OK;
- try
- {
- {
- CDebugDump dd;
- dd.Open(row.m_sDebugDumpDir.c_str());
- load_crash_data_from_debug_dump(dd, pCrashData);
- }
-
- std::string analyzer = get_crash_data_item_content(pCrashData, FILENAME_ANALYZER);
- const char* package = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_PACKAGE);
- char* package_name = get_package_name_from_NVR_or_NULL(package);
-
- // TODO: explain what run_analyser_CreateReport and RunAnalyzerActions are expected to do.
- // Do they potentially add more files to dump dir?
- // Why we calculate dup_hash after run_analyser_CreateReport but before RunAnalyzerActions?
- // Why do we reload dump dir's data via DebugDumpToCrashReport?
-
- VERB3 log(" run_analyser_CreateReport('%s')", analyzer.c_str());
- run_analyser_CreateReport(analyzer.c_str(), row.m_sDebugDumpDir.c_str(), force);
-
- std::string dup_hash = GetGlobalUUID(analyzer.c_str(), row.m_sDebugDumpDir.c_str());
- VERB3 log(" DUPHASH:'%s'", dup_hash.c_str());
-
- VERB3 log(" RunAnalyzerActions('%s','%s','%s',force=%d)", analyzer.c_str(), package_name, row.m_sDebugDumpDir.c_str(), force);
- RunAnalyzerActions(analyzer.c_str(), package_name, row.m_sDebugDumpDir.c_str(), force);
- free(package_name);
- DebugDumpToCrashReport(row.m_sDebugDumpDir.c_str(), pCrashData);
- add_to_crash_data_ext(pCrashData, CD_UUID , CD_SYS, CD_ISNOTEDITABLE, row.m_sUUID.c_str());
- add_to_crash_data_ext(pCrashData, CD_DUPHASH, CD_TXT, CD_ISNOTEDITABLE, dup_hash.c_str());
- }
- catch (CABRTException& e)
- {
- r = MW_CORRUPTED;
- error_msg("%s", e.what());
- if (e.type() == EXCEP_DD_OPEN)
- {
- r = MW_ERROR;
- }
- else if (e.type() == EXCEP_DD_LOAD)
- {
- r = MW_FILE_ERROR;
- }
- else if (e.type() == EXCEP_PLUGIN)
- {
- r = MW_PLUGIN_ERROR;
- }
- }
-
- VERB3 log("CreateCrashReport() returns %d", r);
- return r;
-}
-
-void RunAction(const char *pActionDir,
- const char *pPluginName,
- const char *pPluginArgs)
-{
- CAction* action = g_pPluginManager->GetAction(pPluginName);
- if (!action)
- {
- /* GetAction() already complained */
- return;
- }
- try
- {
- action->Run(pActionDir, pPluginArgs, /*force:*/ 0);
- }
- catch (CABRTException& e)
- {
- error_msg("Execution of '%s' was not successful: %s", pPluginName, e.what());
- }
-}
-
-void RunActionsAndReporters(const char *pDebugDumpDir)
-{
- vector_pair_string_string_t::iterator it_ar = s_vectorActionsAndReporters.begin();
- map_plugin_settings_t plugin_settings;
- for (; it_ar != s_vectorActionsAndReporters.end(); it_ar++)
- {
- const char *plugin_name = it_ar->first.c_str();
- try
- {
- VERB3 log("RunActionsAndReporters: checking %s", plugin_name);
- plugin_type_t tp = g_pPluginManager->GetPluginType(plugin_name);
- if (tp == REPORTER)
- {
- CReporter* reporter = g_pPluginManager->GetReporter(plugin_name); /* can't be NULL */
- map_crash_data_t crashReport;
- DebugDumpToCrashReport(pDebugDumpDir, crashReport);
- VERB2 log("%s.Report(...)", plugin_name);
- reporter->Report(crashReport, plugin_settings, it_ar->second.c_str());
- }
- else if (tp == ACTION)
- {
- CAction* action = g_pPluginManager->GetAction(plugin_name); /* can't be NULL */
- VERB2 log("%s.Run('%s','%s')", plugin_name, pDebugDumpDir, it_ar->second.c_str());
- action->Run(pDebugDumpDir, it_ar->second.c_str(), /*force:*/ 0);
- }
- }
- catch (CABRTException& e)
- {
- error_msg("Activation of plugin '%s' was not successful: %s", plugin_name, e.what());
- }
- }
-}
-
-
-// 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,
- const vector_string_t &reporters,
- map_map_string_t& settings,
- long caller_uid)
-{
- // Get ID fields
- const char *UID = get_crash_data_item_content_or_NULL(client_report, CD_UID);
- const char *UUID = get_crash_data_item_content_or_NULL(client_report, CD_UUID);
- if (!UID || !UUID)
- {
- throw CABRTException(EXCEP_ERROR, "Report(): UID or UUID is missing in client's report data");
- }
- string crash_id = ssprintf("%s:%s", UID, UUID);
-
- // Retrieve corresponding stored record
- map_crash_data_t stored_report;
- mw_result_t r = FillCrashInfo(crash_id.c_str(), stored_report);
- if (r != MW_OK)
- {
- return report_status_t();
- }
-
- // Is it allowed for this user to report?
- if (caller_uid != 0 // not called by root
- && get_crash_data_item_content(stored_report, CD_INFORMALL) != "1"
- && strcmp(to_string(caller_uid).c_str(), UID) != 0
- ) {
- throw CABRTException(EXCEP_ERROR, "Report(): user with uid %ld can't report crash %s",
- caller_uid, crash_id.c_str());
- }
-
- const std::string& pDumpDir = get_crash_data_item_content(stored_report, CD_DUMPDIR);
-
- // Save comment, "how to reproduce", backtrace
-//TODO: we should iterate through stored_report and modify all
-//modifiable fields which have new data in client_report
- const char *comment = get_crash_data_item_content_or_NULL(client_report, FILENAME_COMMENT);
- const char *reproduce = get_crash_data_item_content_or_NULL(client_report, FILENAME_REPRODUCE);
- const char *backtrace = get_crash_data_item_content_or_NULL(client_report, FILENAME_BACKTRACE);
- if (comment || reproduce || backtrace)
- {
- CDebugDump dd;
- dd.Open(pDumpDir.c_str());
- if (comment)
- {
- dd.SaveText(FILENAME_COMMENT, comment);
- add_to_crash_data_ext(stored_report, FILENAME_COMMENT, CD_TXT, CD_ISEDITABLE, comment);
- }
- if (reproduce)
- {
- dd.SaveText(FILENAME_REPRODUCE, reproduce);
- add_to_crash_data_ext(stored_report, FILENAME_REPRODUCE, CD_TXT, CD_ISEDITABLE, reproduce);
- }
- if (backtrace)
- {
- dd.SaveText(FILENAME_BACKTRACE, backtrace);
- add_to_crash_data_ext(stored_report, FILENAME_BACKTRACE, CD_TXT, CD_ISEDITABLE, backtrace);
- }
- }
-
- /* Remove BIN filenames from stored_report if they are not present in client's data */
- map_crash_data_t::const_iterator its = stored_report.begin();
- while (its != stored_report.end())
- {
- if (its->second[CD_TYPE] == CD_BIN)
- {
- std::string key = its->first;
- if (get_crash_data_item_content_or_NULL(client_report, key.c_str()) == NULL)
- {
- /* client does not have it -> does not want it passed to reporters */
- VERB3 log("Won't report BIN file %s:'%s'", key.c_str(), its->second[CD_CONTENT].c_str());
- its++; /* move off the element we will erase */
- stored_report.erase(key);
- continue;
- }
- }
- its++;
- }
-
- const std::string& analyzer = get_crash_data_item_content(stored_report, FILENAME_ANALYZER);
-
- std::string dup_hash = GetGlobalUUID(analyzer.c_str(), pDumpDir.c_str());
- VERB3 log(" DUPHASH:'%s'", dup_hash.c_str());
- add_to_crash_data_ext(stored_report, CD_DUPHASH, CD_TXT, CD_ISNOTEDITABLE, dup_hash.c_str());
-
- // Run reporters
-
- VERB3 {
- log("Run reporters");
- log_map_crash_data(client_report, " client_report");
- log_map_crash_data(stored_report, " stored_report");
- }
-#define client_report client_report_must_not_be_used_below
-
- map_crash_data_t::const_iterator its_PACKAGE = stored_report.find(FILENAME_PACKAGE);
- std::string packageNVR = its_PACKAGE->second[CD_CONTENT];
- char * packageName = get_package_name_from_NVR_or_NULL(packageNVR.c_str());
-
- // analyzer with package name (CCpp:xorg-x11-app) has higher priority
- char* key = xasprintf("%s:%s",analyzer.c_str(),packageName);
- free(packageName);
- map_analyzer_actions_and_reporters_t::iterator end = s_mapAnalyzerActionsAndReporters.end();
- map_analyzer_actions_and_reporters_t::iterator keyPtr = s_mapAnalyzerActionsAndReporters.find(key);
- if (keyPtr == end)
- {
- VERB3 log("'%s' not found, looking for '%s'", key, analyzer.c_str());
- // if there is no such settings, then try default analyzer
- keyPtr = s_mapAnalyzerActionsAndReporters.find(analyzer);
- }
- free(key);
-
- bool at_least_one_reporter_succeeded = false;
- report_status_t ret;
- std::string message;
- if (keyPtr != end)
- {
- VERB2 log("Found AnalyzerActionsAndReporters for '%s'", analyzer.c_str());
-
- vector_pair_string_string_t::iterator it_r = keyPtr->second.begin();
- 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 */
- map_plugin_settings_t plugin_settings = settings[plugin_name];
- std::string res = reporter->Report(stored_report, plugin_settings, it_r->second.c_str());
- ret[plugin_name].push_back("1"); // REPORT_STATUS_IDX_FLAG
- ret[plugin_name].push_back(res); // REPORT_STATUS_IDX_MSG
- if (message != "")
- message += ";";
- message += res;
- at_least_one_reporter_succeeded = true;
- }
- }
- catch (CABRTException& e)
- {
- ret[plugin_name].push_back("0"); // REPORT_STATUS_IDX_FLAG
- ret[plugin_name].push_back(e.what()); // REPORT_STATUS_IDX_MSG
- update_client("Reporting via '%s' was not successful: %s", plugin_name, e.what());
- }
- } // for
- } // if
-
- if (at_least_one_reporter_succeeded)
- {
- CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase.c_str());
- database->Connect();
- report_status_t::iterator ret_it = ret.begin();
- while (ret_it != ret.end())
- {
- const string &plugin_name = ret_it->first;
- const vector_string_t &v = ret_it->second;
- if (v[REPORT_STATUS_IDX_FLAG] == "1")
- {
- database->SetReportedPerReporter(crash_id.c_str(), plugin_name.c_str(), v[REPORT_STATUS_IDX_MSG].c_str());
- }
- ret_it++;
- }
- database->SetReported(crash_id.c_str(), message.c_str());
- database->DisConnect();
- }
-
- return ret;
-#undef client_report
-}
-
-/**
- * Check whether particular debugdump directory is saved
- * in database. This check is done together with an UID of an user.
- * @param uid
- * An UID of an user.
- * @param debug_dump_dir
- * A debugdump dir containing all necessary data.
- * @return
- * It returns true if debugdump dir is already saved, otherwise
- * it returns false.
- * @todo
- * Use database query instead of dumping all rows and searching in them.
- */
-static bool is_debug_dump_saved(long uid, const char *debug_dump_dir)
-{
- if (g_settings_sDatabase.empty())
- error_msg_and_die(_("Database plugin not specified. Please check abrtd settings."));
-
- CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase.c_str());
- database->Connect();
- vector_database_rows_t rows = database->GetUIDData(uid);
- database->DisConnect();
-
- size_t ii;
- bool found = false;
- for (ii = 0; ii < rows.size(); ii++)
- {
- if (0 == strcmp(rows[ii].m_sDebugDumpDir.c_str(), debug_dump_dir))
- {
- found = true;
- break;
- }
- }
-
- return found;
-}
-
-void LoadOpenGPGPublicKey(const char* key)
-{
- VERB1 log("Loading GPG key '%s'", key);
- rpm_load_gpgkey(key);
-}
-
-/**
- * Returns the first full path argument in the command line or NULL.
- * Skips options are in form "-XXX".
- * Caller must delete the returned string using free().
- */
-static char *get_argv1_if_full_path(const char* cmdline)
-{
- const char *argv1 = strpbrk(cmdline, " \t");
- while (argv1 != NULL)
- {
- /* we found space in cmdline, so it might contain
- * path to some script like:
- * /usr/bin/python [-XXX] /usr/bin/system-control-network
- */
- argv1++; /* skip the space */
- if (*argv1 == '-') /* skip arguments */
- {
- /* looks like -XXX in "perl -XXX /usr/bin/script.pl", skip */
- argv1 = strpbrk(argv1, " \t");
- continue;
- }
- else if (*argv1 == ' ' || *argv1 == '\t') /* skip multiple spaces */
- continue;
- else if (*argv1 != '/')
- {
- /* if the string following the space doesn't start
- * with '/' it's probably not a full path to script
- * and we can't use it to determine the package name
- */
- break;
- }
-
- /* cut the rest of cmdline arguments */
- int len = strchrnul(argv1, ' ') - argv1;
- return xstrndup(argv1, len);
- }
- return NULL;
-}
-
-static bool is_path_blacklisted(const char *path)
-{
- set_string_t::iterator it = g_settings_setBlackListedPaths.begin();
- while (it != g_settings_setBlackListedPaths.end())
- {
- if (fnmatch(it->c_str(), path, /*flags:*/ 0) == 0)
- {
- return true;
- }
- it++;
- }
- return false;
-}
-
-
-/**
- * Get a package name from executable name and save
- * package description to particular debugdump directory of a crash.
- * @param pExecutable A name of crashed application.
- * @param pDebugDumpDir A debugdump dir containing all necessary data.
- * @return It return results of operation. See mw_result_t.
- */
-static mw_result_t SavePackageDescriptionToDebugDump(
- const char *pExecutable,
- const char *cmdline,
- bool remote,
- const char *pDebugDumpDir)
-{
- char* rpm_pkg = NULL;
- char* packageName = NULL;
- char* component = NULL;
- std::string scriptName; /* only if "interpreter /path/to/script" */
-
- if (strcmp(pExecutable, "kernel") == 0)
- {
- component = xstrdup("kenel");
- rpm_pkg = xstrdup("kernel");
- packageName = xstrdup("kernel");
- }
- else
- {
- if (is_path_blacklisted(pExecutable))
- {
- log("Blacklisted executable '%s'", pExecutable);
- return MW_BLACKLISTED;
- }
-
- rpm_pkg = rpm_get_package_nvr(pExecutable);
- if (rpm_pkg == NULL)
- {
- if (g_settings_bProcessUnpackaged || remote)
- {
- VERB2 log("Crash in unpackaged executable '%s', proceeding without packaging information", pExecutable);
- try
- {
- CDebugDump dd;
- dd.Open(pDebugDumpDir);
- dd.SaveText(FILENAME_PACKAGE, "");
- dd.SaveText(FILENAME_COMPONENT, "");
- dd.SaveText(FILENAME_DESCRIPTION, "Crashed executable does not belong to any installed package");
- return MW_OK;
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
- return MW_ERROR;
- }
- }
- else
- {
- log("Executable '%s' doesn't belong to any package", pExecutable);
- return MW_PACKAGE_ERROR;
- }
- }
-
- /* Check well-known interpreter names */
-
- const char *basename = strrchr(pExecutable, '/');
- if (basename) basename++; else basename = pExecutable;
-
- /* Add more interpreters as needed */
- if (strcmp(basename, "python") == 0
- || strcmp(basename, "perl") == 0
- ) {
-// TODO: we don't verify that python executable is not modified
-// or that python package is properly signed
-// (see CheckFingerprint/CheckHash below)
-
- /* Try to find package for the script by looking at argv[1].
- * This will work only if the cmdline contains the whole path.
- * Example: python /usr/bin/system-control-network
- */
- bool knownOrigin = false;
- char *script_name = get_argv1_if_full_path(cmdline);
- if (script_name)
- {
- char *script_pkg = rpm_get_package_nvr(script_name);
- if (script_pkg)
- {
- /* There is a well-formed script name in argv[1],
- * and it does belong to some package.
- * Replace interpreter's rpm_pkg and pExecutable
- * with data pertaining to the script.
- */
- free(rpm_pkg);
- rpm_pkg = script_pkg;
- scriptName = script_name;
- pExecutable = scriptName.c_str();
- knownOrigin = true;
- /* pExecutable has changed, check it again */
- if (is_path_blacklisted(pExecutable))
- {
- log("Blacklisted executable '%s'", pExecutable);
- return MW_BLACKLISTED;
- }
- }
- free(script_name);
- }
-
- if (!knownOrigin && !g_settings_bProcessUnpackaged && !remote)
- {
- log("Interpreter crashed, but no packaged script detected: '%s'", cmdline);
- return MW_PACKAGE_ERROR;
- }
- }
-
- packageName = get_package_name_from_NVR_or_NULL(rpm_pkg);
- VERB2 log("Package:'%s' short:'%s'", rpm_pkg, packageName);
-
- if (g_settings_setBlackListedPkgs.find(packageName) != g_settings_setBlackListedPkgs.end())
- {
- log("Blacklisted package '%s'", packageName);
- free(packageName);
- return MW_BLACKLISTED;
- }
- if (g_settings_bOpenGPGCheck && !remote)
- {
- if (rpm_chk_fingerprint(packageName))
- {
- log("Package '%s' isn't signed with proper key", packageName);
- free(packageName);
- return MW_GPG_ERROR;
- }
- /*
- Checking the MD5 sum requires to run prelink to "un-prelink" the
- binaries - this is considered potential security risk so we don't
- use it, until we find some non-intrusive way
-
- Delete?
- */
- /*
- if (!CheckHash(packageName.c_str(), pExecutable))
- {
- error_msg("Executable '%s' seems to be modified, "
- "doesn't match one from package '%s'",
- pExecutable, packageName.c_str());
- return MW_GPG_ERROR;
- }
- */
- }
- component = rpm_get_component(pExecutable);
- }
-
- char *dsc = rpm_get_description(packageName);
- free(packageName);
-
- char host[HOST_NAME_MAX + 1];
- if (!remote)
- {
- // HOST_NAME_MAX is defined in limits.h
- int ret = gethostname(host, HOST_NAME_MAX);
- host[HOST_NAME_MAX] = '\0';
- if (ret < 0)
- {
- perror_msg("gethostname");
- host[0] = '\0';
- }
- }
-
- try
- {
- CDebugDump dd;
- dd.Open(pDebugDumpDir);
- if (rpm_pkg)
- {
- dd.SaveText(FILENAME_PACKAGE, rpm_pkg);
- free(rpm_pkg);
- }
-
- if (dsc)
- {
- dd.SaveText(FILENAME_DESCRIPTION, dsc);
- free(dsc);
- }
-
- if (component)
- {
- dd.SaveText(FILENAME_COMPONENT, component);
- free(component);
- }
-
- if (!remote)
- dd.SaveText(FILENAME_HOSTNAME, host);
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
- return MW_ERROR;
- }
-
- return MW_OK;
-}
-
-bool analyzer_has_InformAllUsers(const char *analyzer_name)
-{
- CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(analyzer_name);
- if (!analyzer)
- {
- return false;
- }
- map_plugin_settings_t settings = analyzer->GetSettings();
- map_plugin_settings_t::const_iterator it = settings.find("InformAllUsers");
- if (it == settings.end())
- return false;
- return string_to_bool(it->second.c_str());
-}
-
-bool analyzer_has_AutoReportUIDs(const char *analyzer_name, const char *uid_str)
-{
- CAnalyzer* analyzer = g_pPluginManager->GetAnalyzer(analyzer_name);
- if (!analyzer)
- {
- return false;
- }
- map_plugin_settings_t settings = analyzer->GetSettings();
- map_plugin_settings_t::const_iterator it = settings.find("AutoReportUIDs");
- if (it == settings.end())
- return false;
-
- vector_string_t logins;
- parse_args(it->second.c_str(), logins);
-
- uid_t uid = xatoi_u(uid_str);
- unsigned size = logins.size();
- for (unsigned ii = 0; ii < size; ii++)
- {
- struct passwd* pw = getpwnam(logins[ii].c_str());
- if (!pw)
- continue;
- if (pw->pw_uid == uid)
- return true;
- }
-
- return false;
-}
-
-void autoreport(const pair_string_string_t& reporter_options, const map_crash_data_t& crash_report)
-{
- CReporter* reporter = g_pPluginManager->GetReporter(reporter_options.first.c_str());
- if (!reporter)
- {
- return;
- }
- map_plugin_settings_t plugin_settings;
- /*std::string res =*/ reporter->Report(crash_report, plugin_settings, reporter_options.second.c_str());
-}
-
-/**
- * Execute all action plugins, which are associated to
- * particular analyzer plugin.
- * @param pAnalyzer A name of an analyzer plugin.
- * @param pDebugDumpPath A debugdump dir containing all necessary data.
- */
-static void RunAnalyzerActions(const char *pAnalyzer, const char *pPackageName, const char *pDebugDumpDir, int force)
-{
- map_analyzer_actions_and_reporters_t::iterator analyzer;
- if (pPackageName != NULL)
- {
- /*try to find analyzer:component first*/
- char *analyzer_component = xasprintf("%s:%s", pAnalyzer, pPackageName);
- analyzer = s_mapAnalyzerActionsAndReporters.find(analyzer_component);
- /* if we didn't find an action for specific package, use the generic one */
- if (analyzer == s_mapAnalyzerActionsAndReporters.end())
- {
- VERB2 log("didn't find action for %s, trying just %s", analyzer_component, pAnalyzer);
- map_analyzer_actions_and_reporters_t::iterator analyzer = s_mapAnalyzerActionsAndReporters.find(pAnalyzer);
- }
- free(analyzer_component);
- }
- else
- {
- VERB2 log("no package name specified, trying to find action for: %s", pAnalyzer);
- analyzer = s_mapAnalyzerActionsAndReporters.find(pAnalyzer);
- }
- if (analyzer != s_mapAnalyzerActionsAndReporters.end())
- {
- vector_pair_string_string_t::iterator it_a = analyzer->second.begin();
- for (; it_a != analyzer->second.end(); it_a++)
- {
- const char *plugin_name = it_a->first.c_str();
- CAction* action = g_pPluginManager->GetAction(plugin_name, /*silent:*/ true);
- if (!action)
- {
- /* GetAction() already complained if no such plugin.
- * If plugin exists but isn't an Action, it's not an error.
- */
- continue;
- }
- try
- {
- action->Run(pDebugDumpDir, it_a->second.c_str(), force);
- }
- catch (CABRTException& e)
- {
- update_client("Action performed by '%s' was not successful: %s", plugin_name, e.what());
- }
- }
- }
-}
-
-/**
- * Save a debugdump into database. If saving is
- * successful, then crash info is filled. Otherwise the crash info is
- * not changed.
- * @param pUUID A local UUID of a crash.
- * @param pUID An UID of an user.
- * @param pTime Time when a crash occurs.
- * @param pDebugDumpPath A debugdump path.
- * @param pCrashData A filled crash info.
- * @return It return results of operation. See mw_result_t.
- */
-static mw_result_t SaveDebugDumpToDatabase(const char *crash_id,
- bool inform_all_users,
- const char *pTime,
- const char *pDebugDumpDir,
- map_crash_data_t& pCrashData)
-{
- CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase.c_str());
- database->Connect();
- /* note: if [UUID,UID] record exists, pDebugDumpDir is not updated in the record */
- database->Insert_or_Update(crash_id, inform_all_users, pDebugDumpDir, pTime);
- database_row_t row = database->GetRow(crash_id);
- database->DisConnect();
-
- mw_result_t res = FillCrashInfo(crash_id, pCrashData);
- if (res == MW_OK)
- {
- const char *first = get_crash_data_item_content(pCrashData, CD_DUMPDIR).c_str();
- if (row.m_sReported == "1")
- {
- log("Crash is in database already (dup of %s) and is reported", first);
- return MW_REPORTED;
- }
- if (row.m_sCount != "1")
- {
- log("Crash is in database already (dup of %s)", first);
- return MW_OCCURRED;
- }
- }
- return res;
-}
-
-mw_result_t SaveDebugDump(const char *pDebugDumpDir,
- map_crash_data_t& pCrashData)
-{
- std::string UID;
- std::string time;
- std::string analyzer;
- std::string executable;
- std::string cmdline;
- bool remote = false;
- try
- {
- CDebugDump dd;
- dd.Open(pDebugDumpDir);
- dd.LoadText(FILENAME_TIME, time);
- dd.LoadText(CD_UID, UID);
- dd.LoadText(FILENAME_ANALYZER, analyzer);
- dd.LoadText(FILENAME_EXECUTABLE, executable);
- dd.LoadText(FILENAME_CMDLINE, cmdline);
- if (dd.Exist(FILENAME_REMOTE))
- {
- std::string remote_str;
- dd.LoadText(FILENAME_REMOTE, remote_str);
- remote = (remote_str.find('1') != std::string::npos);
- }
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
- return MW_ERROR;
- }
-
- /* Convert UID string to number uid_num. The UID string can be modified by user or
- wrongly saved (empty or non-numeric), so xatou() cannot be used here,
- because it would kill the daemon. */
- char *endptr;
- errno = 0;
- unsigned long uid_num = strtoul(UID.c_str(), &endptr, 10);
- if (errno || UID.c_str() == endptr || *endptr != '\0' || uid_num > UINT_MAX)
- {
- error_msg("Invalid UID '%s' loaded from %s", UID.c_str(), pDebugDumpDir);
- return MW_ERROR;
- }
-
- if (is_debug_dump_saved(uid_num, pDebugDumpDir))
- return MW_IN_DB;
-
- mw_result_t res = SavePackageDescriptionToDebugDump(executable.c_str(), cmdline.c_str(), remote, pDebugDumpDir);
- if (res != MW_OK)
- return res;
-
- std::string UUID = GetLocalUUID(analyzer.c_str(), pDebugDumpDir);
- std::string crash_id = ssprintf("%s:%s", UID.c_str(), UUID.c_str());
- /* Loads pCrashData (from the *first debugdump dir* if this one is a dup)
- * Returns:
- * MW_REPORTED: "the crash is flagged as reported in DB" (which also means it's a dup)
- * MW_OCCURRED: "crash count is != 1" (iow: it is > 1 - dup)
- * MW_OK: "crash count is 1" (iow: this is a new crash, not a dup)
- * else: an error code
- */
- return SaveDebugDumpToDatabase(crash_id.c_str(),
- analyzer_has_InformAllUsers(analyzer.c_str()),
- time.c_str(),
- pDebugDumpDir,
- pCrashData);
-}
-
-mw_result_t FillCrashInfo(const char *crash_id,
- map_crash_data_t& pCrashData)
-{
- CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase.c_str());
- database->Connect();
- database_row_t row = database->GetRow(crash_id);
- database->DisConnect();
-
- std::string package;
- std::string executable;
- std::string description;
- std::string analyzer;
- try
- {
- CDebugDump dd;
- dd.Open(row.m_sDebugDumpDir.c_str());
- load_crash_data_from_debug_dump(dd, pCrashData);
- }
- catch (CABRTException& e)
- {
- error_msg("%s", e.what());
- return MW_ERROR;
- }
-
- add_to_crash_data(pCrashData, CD_UID , row.m_sUID.c_str() );
- add_to_crash_data(pCrashData, CD_UUID , row.m_sUUID.c_str() );
- add_to_crash_data(pCrashData, CD_INFORMALL , row.m_sInformAll.c_str() );
- add_to_crash_data(pCrashData, CD_COUNT , row.m_sCount.c_str() );
- add_to_crash_data(pCrashData, CD_REPORTED , row.m_sReported.c_str() );
- add_to_crash_data(pCrashData, CD_MESSAGE , row.m_sMessage.c_str() );
- add_to_crash_data(pCrashData, CD_DUMPDIR , row.m_sDebugDumpDir.c_str());
- add_to_crash_data(pCrashData, FILENAME_TIME , row.m_sTime.c_str() );
-
- return MW_OK;
-}
-
-void GetUUIDsOfCrash(long caller_uid, vector_string_t &result)
-{
- CDatabase* database = g_pPluginManager->GetDatabase(g_settings_sDatabase.c_str());
- vector_database_rows_t rows;
- database->Connect();
- rows = database->GetUIDData(caller_uid);
- database->DisConnect();
-
- unsigned ii;
- for (ii = 0; ii < rows.size(); ii++)
- {
- string crash_id = ssprintf("%s:%s", rows[ii].m_sUID.c_str(), rows[ii].m_sUUID.c_str());
- result.push_back(crash_id);
- }
-}
-
-void AddAnalyzerActionOrReporter(const char *pAnalyzer,
- const char *pAnalyzerOrReporter,
- const char *pArgs)
-{
- s_mapAnalyzerActionsAndReporters[pAnalyzer].push_back(make_pair(std::string(pAnalyzerOrReporter), std::string(pArgs)));
-}
-
-void AddActionOrReporter(const char *pActionOrReporter,
- const char *pArgs)
-{
- VERB3 log("AddActionOrReporter('%s','%s')", pActionOrReporter, pArgs);
- s_vectorActionsAndReporters.push_back(make_pair(std::string(pActionOrReporter), std::string(pArgs)));
-}