summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNikola Pajkovsky <npajkovs@redhat.com>2010-02-15 18:09:55 +0100
committerNikola Pajkovsky <npajkovs@redhat.com>2010-02-15 18:09:55 +0100
commitd93fc21129f08a149d7a1bb042179942485fcedb (patch)
tree72ec4eb636b15d8e2385f068881f86a6aa88db2b /src
parentdeef343e0372b0a167f1d35f9ef9d18694aa9a0e (diff)
parent3a0729e697b24d4d30e3a1a008f83ca605aaad5d (diff)
downloadabrt-d93fc21129f08a149d7a1bb042179942485fcedb.tar.gz
abrt-d93fc21129f08a149d7a1bb042179942485fcedb.tar.xz
abrt-d93fc21129f08a149d7a1bb042179942485fcedb.zip
Merge branch 'master' into bugzilla
Diffstat (limited to 'src')
-rwxr-xr-xsrc/Backtrace/abrt-bz-dupchecker127
-rw-r--r--src/CLI/report.cpp309
-rw-r--r--src/Daemon/PluginManager.cpp54
-rw-r--r--src/Daemon/PluginManager.h9
-rw-r--r--src/Daemon/Settings.cpp12
-rwxr-xr-xsrc/Daemon/abrt-debuginfo-install83
-rw-r--r--src/Gui/CCMainWindow.py43
-rw-r--r--src/Gui/CCReporterDialog.py10
-rw-r--r--src/Gui/CC_gui_functions.py3
-rw-r--r--src/Gui/PluginsSettingsDialog.py16
-rw-r--r--src/Gui/ccgui.glade4
-rw-r--r--src/Gui/report.glade4
-rw-r--r--src/Hooks/abrt-hook-ccpp.cpp60
-rw-r--r--src/Hooks/abrt-hook-python.cpp15
-rw-r--r--src/Hooks/abrt_exception_handler.py.in14
15 files changed, 446 insertions, 317 deletions
diff --git a/src/Backtrace/abrt-bz-dupchecker b/src/Backtrace/abrt-bz-dupchecker
index cbdafc5..dc2ef8e 100755
--- a/src/Backtrace/abrt-bz-dupchecker
+++ b/src/Backtrace/abrt-bz-dupchecker
@@ -25,16 +25,20 @@ import sys
import os.path
import subprocess
import cPickle
+import urllib
+import json
parser = OptionParser(version="%prog 1.0")
parser.add_option("-u", "--user", dest="user",
help="Bugzilla user name (REQUIRED)", metavar="USERNAME")
parser.add_option("-p", "--password", dest="password",
help="Bugzilla password (REQUIRED)", metavar="PASSWORD")
-parser.add_option("-b", "--bugzilla", dest="bugzilla",
+parser.add_option("-b", "--bugzilla", dest="bugzilla", default="https://bugzilla.redhat.com/xmlrpc.cgi",
help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL")
parser.add_option("-v", "--verbose", dest="verbose",
help="Detailed output")
+parser.add_option("-c", "--close", help="Close some of the bugs in Bugzilla (DANGEROUS)",
+ action="store_true", default=False, dest="close")
parser.add_option("-i", "--wiki", help="Generate output in wiki syntax",
action="store_true", default=False, dest="wiki")
@@ -46,9 +50,6 @@ if not options.user or len(options.user) == 0:
if not options.password or len(options.password) == 0:
parser.error("Password is required.\nTry {0} --help".format(sys.argv[0]))
-if not options.bugzilla or len(options.bugzilla) == 0:
- options.bugzilla = "https://bugzilla.redhat.com/xmlrpc.cgi"
-
bz = RHBugzilla()
bz.connect(options.bugzilla)
bz.login(options.user, options.password)
@@ -87,6 +88,7 @@ for buginfo in buginfos:
if ids.has_key(buginfo.bug_id):
continue
+
ids[buginfo.bug_id] = True
if not buginfo.bug_status in ["NEW", "ASSIGNED", "MODIFIED", "VERIFIED"]:
@@ -152,11 +154,6 @@ for buginfo in buginfos:
else:
database[backtrace] = { buginfo.component: [ bugitem ] }
-bz.logout()
-
-print "SUMMARY"
-print "=========================================================================="
-
# The number of duplicates.
dupcount = 0
# The number of duplicates that can be closed.
@@ -168,29 +165,99 @@ for backtrace, components in database.items():
map(lambda x: x["comments"],
bugitems))),
len(bugitems) - 1)
+
+# Get the component owner.
+# Sort the duplicates by the component owner, and
+# filter out those which should not be printed.
+dups = []
+for backtrace, components in database.items():
+ for component, bugitems in components.items():
+ if len(bugitems) <= 1:
+ continue
+
+ # Get the component owner
+ owner = "Failed to get component owner"
+ try:
+ component_info = json.load(urllib.urlopen("https://admin.fedoraproject.org/pkgdb/packages/name/{0}?tg_format=json".format(component)))
+ component_packages = component_info['packageListings']
+ component_f12 = filter(lambda x:x["collection"]["version"]=="12", component_packages)
+ if len(component_f12) == 1:
+ owner = component_f12[0]["owner"]
+ except KeyError:
+ pass
+
+ dups.append((component, owner, bugitems, backtrace))
+ print "."
+
+# Close all bugs where it is appropriate.
+if options.close:
+ for (component, owner, bugitems, backtrace) in dups:
+ # Find the master bug item
+ # Its the one with the most comments.
+ # Sort function sorting by comment count.
+ def commentCmp(x, y):
+ if x['comments'] < y['comments']:
+ return 1
+ elif x['comments'] == y['comments']:
+ # Sort by bug id, older bugs should became the master bug
+ if x['id'] > y['id']:
+ return 1
+ elif x['id'] == y['id']:
+ return 0
+ else:
+ return -1
+ else:
+ return -1
+
+ sorteditems = sorted(bugitems, commentCmp)
+
+ master = sorteditems[0]
+ for item in sorteditems[1:]:
+ if item['comments'] > 2:
+ continue
+
+ print "Closing bug #{0} with {1} comments as a duplicate of #{2}.".format(item['id'], item['comments'], master['id'])
+ bug = bz.getbug(int(item['id']))
+ bug.close("DUPLICATE", int(master['id']), "",
+ "This bug appears to have been filled using a buggy version of ABRT, because\n" +
+ "it contains a backtrace which is considered as a duplicate of the backtrace in #{0}." +
+ "Sorry for the inconvenience.\n\n" +
+ "Closing as a duplicate of #{0}.".format(master['id']))
+
+bz.logout()
+
+print
+print "SUMMARY"
+print "=========================================================================="
print "Total number of duplicate bugs detected: {0}".format(dupcount)
print "Number of duplicate bugs that will be closed : {0}".format(dupclosecount)
print "------------------------------"
-# Print the duplicates
-for backtrace, components in database.items():
- for component, bugitems in components.items():
- if len(bugitems) > 1:
- if options.wiki:
- print "----"
- print "* component: '''{0}'''".format(component)
- print "* duplicates: {0}".format(
- reduce(lambda x,y: x+", "+y,
- map(lambda x: "#[https://bugzilla.redhat.com/show_bug.cgi?id={0} {0}] ({1} comments)".format(x['id'],x['comments']),
- bugitems)))
- print "* backtrace:"
- for line in backtrace.replace("Thread\n", "").splitlines():
- print "*# {0}".format(line)
- else:
- print "Component: {0}".format(component)
- print "Duplicates: {0}".format(
- reduce(lambda x,y: x+", "+y,
- map(lambda x: "{0} ({1})".format(x['id'],x['comments']),
- bugitems)))
- print "Backtrace: {0}".format(backtrace)
+# Print the duplicates sorted by package owner.
+def cmp(x, y):
+ if x[1] < y[1]:
+ return -1
+ elif x[1] == y[1]:
+ return 0
+ else:
+ return 1
+
+for (component, owner, bugitems, backtrace) in sorted(dups, cmp):
+ if options.wiki:
+ print "----"
+ print "* component: '''{0}''' ({1})".format(component, owner)
+ print "* duplicates: {0}".format(
+ reduce(lambda x,y: x+", "+y,
+ map(lambda x: "#[https://bugzilla.redhat.com/show_bug.cgi?id={0} {0}] ({1} comments)".format(x['id'],x['comments']),
+ bugitems)))
+ print "* backtrace:"
+ for line in backtrace.replace("Thread\n", "").splitlines():
+ print "*# {0}".format(line)
+ else:
+ print "Component: {0} ({1})".format(component, owner)
+ print "Duplicates: {0}".format(
+ reduce(lambda x,y: x+", "+y,
+ map(lambda x: "{0} ({1})".format(x['id'],x['comments']),
+ bugitems)))
+ print "Backtrace: {0}".format(backtrace)
diff --git a/src/CLI/report.cpp b/src/CLI/report.cpp
index 7ef33ac..ebade16 100644
--- a/src/CLI/report.cpp
+++ b/src/CLI/report.cpp
@@ -21,6 +21,7 @@
#include "abrtlib.h"
#include "DebugDump.h"
#include "CrashTypes.h" // FILENAME_* defines
+#include "Plugin.h" // LoadPluginSettings
#if HAVE_CONFIG_H
# include <config.h>
#endif
@@ -300,7 +301,12 @@ static int read_crash_report(map_crash_data_t &report, const char *text)
return result;
}
-/* Runs external editor. */
+/**
+ * Runs external editor.
+ * Returns:
+ * 0 if the launch was successful
+ * 1 if it failed. The error reason is logged using error_msg()
+ */
static int launch_editor(const char *path)
{
const char *editor, *terminal;
@@ -330,76 +336,216 @@ static int launch_editor(const char *path)
return 0;
}
-/* Reports the crash with corresponding uuid over DBus. */
-int report(const char *uuid, bool always)
+/**
+ * Returns:
+ * 0 on success, crash data has been updated
+ * 2 on failure, unable to create, open, or close temporary file
+ * 3 on failure, cannot launch text editor
+ */
+static int run_report_editor(map_crash_data_t &cr)
{
- // Ask for an initial report.
- map_crash_data_t cr = call_CreateReport(uuid);
-//TODO: error check?
-
- if (!always)
+ /* Open a temporary file and write the crash report to it. */
+ char filename[] = "/tmp/abrt-report.XXXXXX";
+ int fd = mkstemp(filename);
+ if (fd == -1) /* errno is set */
{
- /* Open a temporary file and write the crash report to it. */
- char filename[] = "/tmp/abrt-report.XXXXXX";
- int fd = mkstemp(filename);
- if (fd == -1)
- {
- error_msg("can't generate temporary file name");
- return 1;
- }
+ perror_msg("can't generate temporary file name");
+ return 2;
+ }
+
+ FILE *fp = fdopen(fd, "w");
+ if (!fp) /* errno is set */
+ {
+ perror_msg("can't open '%s' to save the crash report", filename);
+ return 2;
+ }
+
+ write_crash_report(cr, fp);
+
+ if (fclose(fp)) /* errno is set */
+ {
+ perror_msg("can't close '%s'", filename);
+ return 2;
+ }
+
+ // Start a text editor on the temporary file.
+ if (launch_editor(filename) != 0)
+ return 3; /* exit with error */
+
+ // Read the file back and update the report from the file.
+ fp = fopen(filename, "r");
+ if (!fp) /* errno is set */
+ {
+ perror_msg("can't open '%s' to read the crash report", filename);
+ return 2;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ long size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ char *text = (char*)xmalloc(size + 1);
+ if (fread(text, 1, size, fp) != size)
+ {
+ error_msg("can't read '%s'", filename);
+ return 2;
+ }
+ text[size] = '\0';
+ if (fclose(fp) != 0) /* errno is set */
+ {
+ perror_msg("can't close '%s'", filename);
+ return 2;
+ }
+
+ // Delete the tempfile.
+ if (unlink(filename) == -1) /* errno is set */
+ {
+ perror_msg("can't unlink %s", filename);
+ }
- FILE *fp = fdopen(fd, "w");
- if (!fp)
- {
- error_msg("can't open '%s' to save the crash report", filename);
- return 1;
- }
+ remove_comments_and_unescape(text);
+ // Updates the crash report from the file text.
+ int report_changed = read_crash_report(cr, text);
+ free(text);
+ if (report_changed)
+ puts(_("\nThe report has been updated."));
+ else
+ puts(_("\nNo changes were detected in the report."));
+
+ return 0;
+}
- write_crash_report(cr, fp);
+/**
+ * Asks user for a text response.
+ * @param question
+ * Question displayed to user.
+ * @param result
+ * Output array.
+ * @param result_size
+ * Maximum byte count to be written.
+ */
+static void read_from_stdin(const char *question, char *result, int result_size)
+{
+ printf(question);
+ fflush(NULL);
+ fgets(result, result_size, stdin);
+ // 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.
+ */
+static void get_reporter_plugin_settings(map_map_string_t &settings, bool ask_user)
+{
+ /* First of all, load system-wide report plugin settings. */
+ // Get informations about all plugins.
+ map_map_string_t plugins = call_GetPluginsInfo();
+ // Check the configuration of each enabled Reporter plugin.
+ map_map_string_t::iterator it, itend = plugins.end();
+ for (it = plugins.begin(); it != itend; ++it)
+ {
+ // Skip disabled plugins.
+ if (0 != strcmp(it->second["Enabled"].c_str(), "yes"))
+ continue;
+ // 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());
+ // 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;
+ }
- if (fclose(fp))
+ /* Second, load user-specific settings, which override
+ the system-wide settings. */
+ struct passwd* pw = getpwuid(geteuid());
+ const char* homedir = pw ? pw->pw_dir : NULL;
+ if (homedir)
+ {
+ itend = settings.end();
+ for (it = settings.begin(); it != itend; ++it)
{
- error_msg("can't close '%s'", filename);
- return 2;
+ 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
+ enter it every time he reports. */
+ bool success = LoadPluginSettings(path.c_str(), single_plugin_settings, false);
+ if (!success)
+ continue;
+ // Merge user's plugin settings into already loaded settings.
+ map_string_t::const_iterator valit, valitend = single_plugin_settings.end();
+ for (valit = single_plugin_settings.begin(); valit != valitend; ++valit)
+ it->second[valit->first] = valit->second;
}
+ }
- // Start a text editor on the temporary file.
- launch_editor(filename);
+ if (!ask_user)
+ return;
- // Read the file back and update the report from the file.
- fp = fopen(filename, "r");
- if (!fp)
+ /* Third, check if a login or password is missing,
+ and ask for it. */
+ itend = settings.end();
+ for (it = settings.begin(); it != itend; ++it)
+ {
+ map_string_t &single_plugin_settings = it->second;
+ // Login information is missing.
+ bool loginMissing = single_plugin_settings.find("Login") != single_plugin_settings.end()
+ && 0 == strcmp(single_plugin_settings["Login"].c_str(), "");
+ bool passwordMissing = single_plugin_settings.find("Password") != single_plugin_settings.end()
+ && 0 == strcmp(single_plugin_settings["Password"].c_str(), "");
+ if (!loginMissing && !passwordMissing)
+ continue;
+
+ // Read the missing information and push it to plugin settings.
+ printf(_("Wrong settings were detected for plugin %s.\n"), it->first.c_str());
+ char result[64];
+ if (loginMissing)
{
- error_msg("can't open '%s' to read the crash report", filename);
- return 1;
+ read_from_stdin(_("Enter your login: "), result, 64);
+ single_plugin_settings["Login"] = std::string(result);
}
-
- fseek(fp, 0, SEEK_END);
- long size = ftell(fp);
- fseek(fp, 0, SEEK_SET);
-
- char *text = (char*)xmalloc(size + 1);
- if (fread(text, 1, size, fp) != size)
+ if (passwordMissing)
{
- error_msg("can't read '%s'", filename);
- return 1;
+// TODO: echo off, see http://fixunix.com/unix/84474-echo-off.html
+ read_from_stdin(_("Enter your password: "), result, 64);
+ single_plugin_settings["Password"] = std::string(result);
}
- text[size] = '\0';
- fclose(fp);
+ }
+}
- remove_comments_and_unescape(text);
- // Updates the crash report from the file text.
- int report_changed = read_crash_report(cr, text);
- if (report_changed)
- puts(_("\nThe report has been updated."));
- else
- puts(_("\nNo changes were detected in the report."));
+/* Reports the crash with corresponding uuid over DBus. */
+int report(const char *uuid, bool always)
+{
+ // Ask for an initial report.
+ map_crash_data_t cr = call_CreateReport(uuid);
+//TODO: error check?
- free(text);
+ /* Open text editor and give a chance to review the backtrace etc. */
+ if (!always)
+ {
+ int result = run_report_editor(cr);
+ if (result != 0)
+ return result;
+ }
- if (unlink(filename) != 0) // Delete the tempfile.
- perror_msg("can't unlink %s", filename);
+ /* Read the plugin settings. */
+ map_map_string_t pluginSettings;
+ get_reporter_plugin_settings(pluginSettings, !always);
+ /* Ask if user really want to send the report. */
+ if (!always)
+ {
// Report only if the user is sure.
printf(_("Do you want to send the report? [y/N]: "));
fflush(NULL);
@@ -412,59 +558,6 @@ int report(const char *uuid, bool always)
}
}
- map_map_string_t pluginSettings;
- if (!always)
- {
- // Get informations about all plugins.
- map_map_string_t plugins = call_GetPluginsInfo();
- // Check the configuration of each enabled Reporter plugin.
- map_map_string_t::iterator it, itend = plugins.end();
- for (it = plugins.begin(); it != itend; ++it)
- {
- // Skip disabled plugins.
- if (0 != strcmp(it->second["Enabled"].c_str(), "yes"))
- continue;
- // Skip nonReporter plugins.
- if (0 != strcmp(it->second["Type"].c_str(), "Reporter"))
- continue;
-
- map_string_t settings = call_GetPluginSettings(it->first.c_str());
- // Login information is missing.
- bool loginMissing = settings.find("Login") != settings.end()
- && 0 == strcmp(settings["Login"].c_str(), "");
- bool passwordMissing = settings.find("Password") != settings.end()
- && 0 == strcmp(settings["Password"].c_str(), "");
- if (!loginMissing && !passwordMissing)
- continue;
-
- // 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.
- pluginSettings[it->first] = settings;
-
- printf(_("Wrong settings were detected for plugin %s.\n"), it->second["Name"].c_str());
- if (loginMissing)
- {
- printf(_("Enter your login: "));
- fflush(NULL);
- char answer[64] = "";
- fgets(answer, sizeof(answer), stdin);
- if (strlen(answer) > 0)
- pluginSettings[it->first]["Login"] = answer;
- }
- if (passwordMissing)
- {
-// TODO: echo off, see http://fixunix.com/unix/84474-echo-off.html
- printf(_("Enter your password: "));
- fflush(NULL);
- char answer[64] = "";
- fgets(answer, sizeof(answer), stdin);
- if (strlen(answer) > 0)
- pluginSettings[it->first]["Password"] = answer;
- }
- }
- }
-
int errors = 0;
int plugins = 0;
puts(_("Reporting..."));
@@ -475,7 +568,7 @@ int report(const char *uuid, bool always)
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")
+ if (v[REPORT_STATUS_IDX_FLAG] == "0")
errors++;
it++;
}
diff --git a/src/Daemon/PluginManager.cpp b/src/Daemon/PluginManager.cpp
index e63cb3a..f01d943 100644
--- a/src/Daemon/PluginManager.cpp
+++ b/src/Daemon/PluginManager.cpp
@@ -80,60 +80,6 @@ static const char *const plugin_type_str[] = {
};
-bool LoadPluginSettings(const char *pPath, map_plugin_settings_t& pSettings)
-{
- FILE *fp = fopen(pPath, "r");
- if (!fp)
- return false;
-
- char line[512];
- while (fgets(line, sizeof(line), fp))
- {
- strchrnul(line, '\n')[0] = '\0';
- unsigned ii;
- bool is_value = false;
- bool valid = false;
- bool in_quote = false;
- string key;
- string value;
- for (ii = 0; line[ii] != '\0'; ii++)
- {
- if (line[ii] == '"')
- {
- in_quote = !in_quote;
- }
- if (isspace(line[ii]) && !in_quote)
- {
- continue;
- }
- if (line[ii] == '#' && !in_quote && key == "")
- {
- break;
- }
- if (line[ii] == '=' && !in_quote)
- {
- is_value = true;
- continue;
- }
- if (!is_value)
- {
- key += line[ii];
- }
- else
- {
- valid = true;
- value += line[ii];
- }
- }
- if (valid && !in_quote)
- {
- pSettings[key] = value;
- }
- }
- fclose(fp);
- return true;
-}
-
/**
* A function. It saves settings. On success it returns true, otherwise returns false.
* @param path A path of config file.
diff --git a/src/Daemon/PluginManager.h b/src/Daemon/PluginManager.h
index b5dcebc..bf6952f 100644
--- a/src/Daemon/PluginManager.h
+++ b/src/Daemon/PluginManager.h
@@ -154,13 +154,4 @@ class CPluginManager
map_plugin_settings_t GetPluginSettings(const char *pName);
};
-/**
- * Loads settings and stores it in second parameter. On success it
- * returns true, otherwise returns false.
- * @param path A path of config file.
- * @param settings A readed plugin's settings.
- * @return if it success it returns true, otherwise it returns false.
- */
-bool LoadPluginSettings(const char *pPath,
- map_plugin_settings_t& pSettings);
#endif /*PLUGINMANAGER_H_*/
diff --git a/src/Daemon/Settings.cpp b/src/Daemon/Settings.cpp
index 9b0376b..5644d37 100644
--- a/src/Daemon/Settings.cpp
+++ b/src/Daemon/Settings.cpp
@@ -376,8 +376,16 @@ void LoadSettings()
ParseCommon();
ParseAnalyzerActionsAndReporters();
ParseCron();
- if(g_settings_bOpenGPGCheck)
- LoadGPGKeys();
+
+ /*
+ loading gpg keys will invoke LoadOpenGPGPublicKey() from rpm.cpp
+ pgpReadPkts which makes nss to re-init and thus makes
+ bugzilla plugin work :-/
+ */
+
+ //FIXME FIXME FIXME FIXME FIXME FIXME!!!
+ //if(g_settings_bOpenGPGCheck)
+ LoadGPGKeys();
}
/* dbus call to retrieve .conf file data from daemon */
diff --git a/src/Daemon/abrt-debuginfo-install b/src/Daemon/abrt-debuginfo-install
index 35f4d6a..3bb9c41 100755
--- a/src/Daemon/abrt-debuginfo-install
+++ b/src/Daemon/abrt-debuginfo-install
@@ -145,7 +145,9 @@ print_package_names() {
fi
# when we look for debuginfo we need only -debuginfo* repos, so we can disable the rest and thus make it faster
# also we want only fedora repositories, because abrt won't work for other packages anyway
- local cmd="yum $yumopts '--disablerepo=*' '--enablerepo=fedora-debuginfo*' '--enablerepo=updates-debuginfo*' --quiet provides $missing_debuginfo_files"
+ # --showduplicates: do not just show the latest package
+ # -R2: wait two minutes max (hopefully this prevents infinite hang on yum lock)
+ local cmd="yum $yumopts '--disablerepo=*' '--enablerepo=fedora-debuginfo*' '--enablerepo=updates-debuginfo*' --showduplicates -R2 --quiet provides $missing_debuginfo_files"
echo "$cmd" >"yum_provides.$1.OUT"
# eval is needed to strip away ''s; cant remove them above and just use
# $cmd, that would perform globbing on '*'
@@ -194,53 +196,56 @@ download_packages() {
for pkg in $packages; do
echo "Download $i/$num_packages: $pkg"
echo "Download $i/$num_packages: $pkg" >>yumdownloader.OUT
- # we can't handle packages from non Fedora repos, so we look and download only
+ # We can't handle packages from non Fedora repos, so we look and download only
# from Fedora repos which makes it faster
yumdownloader --disablerepo="*" --enablerepo="fedora-debuginfo*" --enablerepo="updates-debuginfo*" --quiet $pkg >>yumdownloader.OUT 2>&1
err=$?
echo "exitcode:$err" >>yumdownloader.OUT
echo >>yumdownloader.OUT
test $err = 0 || { echo "Download of $pkg failed!"; sleep 1; }
- : $((i++))
- done
-
- for file in *.rpm; do
- # Happens if no .rpm's were downloaded (yumdownloader problem)
- # In this case, $f is the literal "*.rpm" string
- test -f "$file" || error_msg_and_die "not a rpm file: '$file'"
- echo "Unpacking: $file"
- echo "Processing: $file" >>unpack.OUT
- rpm2cpio <"$file" 2>>unpack.OUT | cpio -id >>unpack.OUT 2>&1
-#TODO: error check?
- done
-
- # Copy debuginfo files to cachedir
- if test x"$cachedir" != x"" && test -d "$cachedir"; then
- for build_id in $build_ids; do
- build_id1=${build_id:0:2}
- build_id2=${build_id:2}
-
- file="usr/lib/debug/.build-id/$build_id1/$build_id2.debug"
-
- # Do not copy it if it can be found in any of $debuginfodirs
- test -f "/$file" && continue
- if test x"$cachedir" != x""; then
- for d in $debuginfodirs; do
- test -f "$d/$file" && continue 2
+ # Process and delete the *.rpm file just downloaded
+ # We do it right after download: some users have smallish disks...
+ for file in *.rpm; do
+ # Happens if no .rpm's were downloaded (yumdownloader problem)
+ # In this case, $f is the literal "*.rpm" string
+ test -f "$file" || { echo "No rpm file downloaded"; continue; }
+ echo "Unpacking: $file"
+ echo "Processing: $file" >>unpack.OUT
+ rpm2cpio <"$file" >"unpacked.cpio" 2>>unpack.OUT || error_msg_and_die "Can't convert '$file' to cpio"
+ rm "$file"
+ cpio -id <"unpacked.cpio" >>unpack.OUT 2>&1 || error_msg_and_die "Can't unpack '$file' cpio archive"
+ rm "unpacked.cpio"
+ # Copy debuginfo files to cachedir
+ if test x"$cachedir" != x"" && test -d "$cachedir"; then
+ # For every needed debuginfo, check whether we have it
+ for build_id in $build_ids; do
+ build_id1=${build_id:0:2}
+ build_id2=${build_id:2}
+ file="usr/lib/debug/.build-id/$build_id1/$build_id2.debug"
+ # Do not copy it if it can be found in any of $debuginfodirs
+ test -f "/$file" && continue
+ if test x"$cachedir" != x""; then
+ for d in $debuginfodirs; do
+ test -f "$d/$file" && continue 2
+ done
+ fi
+ if test -f "$file"; then
+ # File is one of those we just installed, cache it
+ mkdir -p "$cachedir/usr/lib/debug/.build-id/$build_id1"
+ # Note: this does not preserve symlinks. This is intentional
+ $debug && echo Copying "$file" to "$cachedir/$file" >&2
+ echo "Caching debuginfo: $file"
+ cp --remove-destination "$file" "$cachedir/$file" || error_msg_and_die "Can't copy $file (disk full?)"
+ continue
+ fi
done
fi
-
- if test -f "$file"; then
- # File is one of those we just installed, cache it.
- mkdir -p "$cachedir/usr/lib/debug/.build-id/$build_id1"
- # Note: this does not preserve symlinks. This is intentional
- $debug && echo Copying "$file" to "$cachedir/$file" >&2
- cp --remove-destination "$file" "$cachedir/$file"
-#TODO: error check?
- continue
- fi
+ # Delete remaining files unpacked from .cpio
+ # which we didn't need after all
+ rm -r etc bin sbin usr var opt 2>/dev/null
done
- fi
+ : $((i++))
+ done
}
diff --git a/src/Gui/CCMainWindow.py b/src/Gui/CCMainWindow.py
index 0d9b0a2..dfcfaf1 100644
--- a/src/Gui/CCMainWindow.py
+++ b/src/Gui/CCMainWindow.py
@@ -2,6 +2,7 @@
import sys
import pwd
import getopt
+from glib import markup_escape_text
from abrt_utils import _, init_logging, log, log1, log2
import gobject
@@ -64,12 +65,18 @@ class MainWindow():
#init the dumps treeview
self.dlist = self.wTree.get_widget("tvDumps")
#rows of items with:
- #icon, package_name, application, date, crash_rate, user, is_reported, ?object?
- self.dumpsListStore = gtk.ListStore(gtk.gdk.Pixbuf, str,str,str,str,str,bool, object)
- # set filter
- modelfilter = self.dumpsListStore.filter_new()
- modelfilter.set_visible_func(self.filter_dumps, None)
- self.dlist.set_model(modelfilter)
+ ICON_COL = 0
+ PACKAGE_COL = 1
+ APPLICATION_COL = 2
+ TIME_STR_COL = 3
+ CRASH_RATE_COL = 4
+ USER_COL = 5
+ IS_REPORTED_COL = 6
+ UNIX_TIME_COL = 7
+ DUMP_OBJECT_COL = 8
+ #icon, package_name, application, date, crash_rate, user, is_reported, time_in_sec ?object?
+ self.dumpsListStore = gtk.ListStore(gtk.gdk.Pixbuf, str,str,str,str,str,bool, int, object)
+ self.dlist.set_model(self.dumpsListStore)
# add pixbuff separatelly
icon_column = gtk.TreeViewColumn(_("Icon"))
icon_column.cell = gtk.CellRendererPixbuf()
@@ -78,13 +85,17 @@ class MainWindow():
icon_column.pack_start(icon_column.cell, False)
icon_column.set_attributes(icon_column.cell, pixbuf=(n-1), cell_background_set=6)
# ===============================================
- columns = [None]*4
- columns[0] = gtk.TreeViewColumn(_("Package"))
- columns[1] = gtk.TreeViewColumn(_("Application"))
- columns[2] = gtk.TreeViewColumn(_("Date"))
- columns[3] = gtk.TreeViewColumn(_("Crash count"))
- column = gtk.TreeViewColumn(_("User"))
- columns.append(column)
+ columns = []
+ columns.append(gtk.TreeViewColumn(_("Package")))
+ columns[-1].set_sort_column_id(PACKAGE_COL)
+ columns.append(gtk.TreeViewColumn(_("Application")))
+ columns[-1].set_sort_column_id(APPLICATION_COL)
+ columns.append(gtk.TreeViewColumn(_("Date")))
+ columns[-1].set_sort_column_id(UNIX_TIME_COL)
+ columns.append(gtk.TreeViewColumn(_("Crash count")))
+ columns[-1].set_sort_column_id(CRASH_RATE_COL)
+ columns.append(gtk.TreeViewColumn(_("User")))
+ columns[-1].set_sort_column_id(USER_COL)
# create list
for column in columns:
n = self.dlist.append_column(column)
@@ -204,7 +215,7 @@ class MainWindow():
except Exception, ex:
user = "UID: %s" % entry.getUID()
n = self.dumpsListStore.append([icon, entry.getPackage(), entry.getExecutable(),
- entry.getTime("%c"), entry.getCount(), user, entry.isReported(), entry])
+ entry.getTime("%c"), entry.getCount(), user, entry.isReported(), entry.getTime(""), entry])
# activate the first row if any..
if n:
# we can use (0,) as path for the first row, but what if API changes?
@@ -233,8 +244,8 @@ class MainWindow():
# it is not informative (no URL to the report)
for message in dump.getMessage().split(';'):
if message:
- message_clean = message.strip()
- if "http" in message_clean[0:5] or "file:///"[0:8] in message_clean:
+ message_clean = markup_escape_text(message.strip())
+ if "http" in message_clean[0:5] or "file:///" in message_clean[0:8]:
report_message = "<a href=\"%s\">%s</a>" % (message_clean, message_clean)
else:
report_message = message_clean
diff --git a/src/Gui/CCReporterDialog.py b/src/Gui/CCReporterDialog.py
index 816164b..bc4a1e0 100644
--- a/src/Gui/CCReporterDialog.py
+++ b/src/Gui/CCReporterDialog.py
@@ -290,11 +290,11 @@ class ReporterDialog():
self.tevHowToReproduce.set_buffer(buff)
def dehydrate(self):
- # handle attachments
- vbAttachments = self.builder.get_object("vbAttachments")
- for attachment in vbAttachments.get_children():
- #print "%s file %s" % (["not sending","sending"][attachment.get_active()], attachment.get_label())
- del self.report[attachment.item]
+ ## # handle attachments
+ ## vbAttachments = self.builder.get_object("vbAttachments")
+ ## for attachment in vbAttachments.get_children():
+ ## #print "%s file %s" % (["not sending","sending"][attachment.get_active()], attachment.get_label())
+ ## del self.report[attachment.item]
# handle comment
buff = self.tvComment.get_buffer()
diff --git a/src/Gui/CC_gui_functions.py b/src/Gui/CC_gui_functions.py
index 9378de5..acfd2a5 100644
--- a/src/Gui/CC_gui_functions.py
+++ b/src/Gui/CC_gui_functions.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from glib import markup_escape_text
import gtk
import pango
import subprocess
@@ -66,7 +67,7 @@ def gui_report_dialog ( report_status_dict, parent_dialog,
# this first one is actually a fallback to set at least
# a raw text in case when set_markup() fails
status_label.set_text(report_status_dict[plugin][MESSAGE])
- status_label.set_markup("<span foreground='red'>%s</span>" % report_status_dict[plugin][MESSAGE])
+ status_label.set_markup("<span foreground='red'>%s</span>" % markup_escape_text(report_status_dict[plugin][MESSAGE]))
# if the report was not succesful then this won't pass so this runs only
# if report succeds and gets overwriten by the status message
if report_status_dict[plugin][STATUS] == '1':
diff --git a/src/Gui/PluginsSettingsDialog.py b/src/Gui/PluginsSettingsDialog.py
index 0ba390d..787faee 100644
--- a/src/Gui/PluginsSettingsDialog.py
+++ b/src/Gui/PluginsSettingsDialog.py
@@ -107,7 +107,7 @@ class PluginsSettingsDialog:
# cell_text, toggle_active, toggle_visible, group_name_visible, color, plugin
["<b>%s</b>" % PluginInfo.types[plugin_type], 0, 0, 1, "gray", None])
plugin_rows[plugin_type] = it
- group_empty[plugin_type] = 1
+ group_empty[plugin_type] = it
for entry in pluginlist:
if entry.Description:
text = "<b>%s</b>\n%s" % (entry.getName(), entry.Description)
@@ -118,13 +118,13 @@ class PluginsSettingsDialog:
self.pluginsListStore.append(plugin_rows[plugin_type],
# cell_text, toggle_active, toggle_visible, group_name_visible, color, plugin
[text, entry.Enabled == "yes", 1, 0, "white", entry])
- group_empty[plugin_type] = 0
+ if group_empty.has_key(plugin_type):
+ del group_empty[plugin_type]
# rhbz#560971 "Don't show empty 'Not loaded plugins' section"
- for plugin_type in group_empty.keys():
- if group_empty[plugin_type]:
- self.pluginsListStore.append(plugin_rows[plugin_type],
- # cell_text, toggle_active, toggle_visible, group_name_visible, color, plugin
- ["(none)", 0, 1, 0, "white", None])
+ # don't show any empty groups
+ for it in group_empty.values():
+ self.pluginsListStore.remove(it)
+
self.pluginlist.expand_all()
def dehydrate(self):
@@ -141,7 +141,7 @@ class PluginsSettingsDialog:
def on_bConfigurePlugin_clicked(self, button, pluginview):
pluginsListStore, path = pluginview.get_selection().get_selected_rows()
if not path:
- self.builder.get_object("lDescription").set_label(_("Can't get plugin description"))
+ gui_info_dialog(_("Please select a plugin from the list to edit it's options."), self.window)
return
# this should work until we keep the row object in the last position
pluginfo = pluginsListStore.get_value(pluginsListStore.get_iter(path[0]), pluginsListStore.get_n_columns()-1)
diff --git a/src/Gui/ccgui.glade b/src/Gui/ccgui.glade
index 237f23a..3050d65 100644
--- a/src/Gui/ccgui.glade
+++ b/src/Gui/ccgui.glade
@@ -208,7 +208,6 @@ Patrick Connelly &lt;pcon@fedoraproject.org&gt;</property>
<child>
<widget class="GtkToolbar" id="toolbar1">
<property name="visible">True</property>
- <property name="toolbar_style">both</property>
<child>
<widget class="GtkToolButton" id="bDelete">
<property name="visible">True</property>
@@ -225,7 +224,6 @@ Patrick Connelly &lt;pcon@fedoraproject.org&gt;</property>
<child>
<widget class="GtkToolButton" id="bReport">
<property name="visible">True</property>
- <property name="sensitive">False</property>
<property name="tooltip" translatable="yes">Report</property>
<property name="label" translatable="yes">Report</property>
<property name="stock_id">gtk-go-up</property>
@@ -267,6 +265,8 @@ Patrick Connelly &lt;pcon@fedoraproject.org&gt;</property>
<widget class="GtkTreeView" id="tvDumps">
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="reorderable">True</property>
+ <property name="search_column">1</property>
</widget>
</child>
</widget>
diff --git a/src/Gui/report.glade b/src/Gui/report.glade
index e7f37ec..7b3aac4 100644
--- a/src/Gui/report.glade
+++ b/src/Gui/report.glade
@@ -297,12 +297,12 @@
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
<child>
<object class="GtkTextView" id="tvBacktrace">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
+ <property name="accepts_tab">False</property>
</object>
</child>
</object>
@@ -378,6 +378,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="wrap_mode">word-char</property>
+ <property name="accepts_tab">False</property>
</object>
</child>
</object>
@@ -415,6 +416,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="wrap_mode">word-char</property>
+ <property name="accepts_tab">False</property>
</object>
</child>
</object>
diff --git a/src/Hooks/abrt-hook-ccpp.cpp b/src/Hooks/abrt-hook-ccpp.cpp
index 9d1dc19..d21b8e6 100644
--- a/src/Hooks/abrt-hook-ccpp.cpp
+++ b/src/Hooks/abrt-hook-ccpp.cpp
@@ -82,31 +82,52 @@ int main(int argc, char** argv)
/* set to max possible >0 value */
ulimit_c = ~((off_t)1 << (sizeof(off_t)*8-1));
}
- off_t core_size = 0;
-
if (errno || pid <= 0)
{
error_msg_and_die("pid '%s' or limit '%s' is bogus", argv[2], argv[5]);
}
+ char* executable = get_executable(pid);
+ if (executable == NULL)
+ {
+ error_msg_and_die("can't read /proc/%lu/exe link", (long)pid);
+ }
+ if (strstr(executable, "/abrt-hook-ccpp"))
+ {
+ error_msg_and_die("pid %lu is '%s', not dumping it to avoid recursion",
+ (long)pid, executable);
+ }
+
+ char *user_pwd = get_cwd(pid); /* may be NULL on error */
+
+ /* Parse abrt.conf and plugins/CCpp.conf */
+ unsigned setting_MaxCrashReportsSize = 0;
+ bool setting_MakeCompatCore = false;
+ parse_conf(CONF_DIR"/plugins/CCpp.conf", &setting_MaxCrashReportsSize, &setting_MakeCompatCore);
+
+ int core_fd = STDIN_FILENO;
+ off_t core_size = 0;
+
const char *signame = NULL;
/* Tried to use array for this but C++ does not support v[] = { [IDX] = "str" } */
switch (signal_no)
{
- case SIGQUIT: signame = "QUIT"; break;
case SIGILL : signame = "ILL" ; break;
- case SIGABRT: signame = "ABRT"; break;
case SIGFPE : signame = "FPE" ; break;
case SIGSEGV: signame = "SEGV"; break;
+ case SIGBUS : signame = "BUS" ; break; //Bus error (bad memory access)
+ case SIGABRT: signame = "ABRT"; break; //usually when abort() was called
+ //case SIGQUIT: signame = "QUIT"; break; //Quit from keyboard
+ //case SIGSYS : signame = "SYS" ; break; //Bad argument to routine (SVr4)
+ //case SIGTRAP: signame = "TRAP"; break; //Trace/breakpoint trap
+ //case SIGXCPU: signame = "XCPU"; break; //CPU time limit exceeded (4.2BSD)
+ //case SIGXFSZ: signame = "XFSZ"; break; //File size limit exceeded (4.2BSD)
}
if (signame == NULL) {
- /* not a signal we care about, exit silently */
- return 0;
+ /* not a signal we care about */
+ goto create_user_core;
}
- char *user_pwd = get_cwd(pid); /* may be NULL on error */
- int core_fd = STDIN_FILENO;
-
if (!daemon_is_ok())
{
/* not an error, exit with exitcode 0 */
@@ -119,22 +140,6 @@ int main(int argc, char** argv)
try
{
- char* executable = get_executable(pid);
- if (executable == NULL)
- {
- error_msg_and_die("can't read /proc/%lu/exe link", (long)pid);
- }
- if (strstr(executable, "/abrt-hook-ccpp"))
- {
- error_msg_and_die("pid %lu is '%s', not dumping it to avoid recursion",
- (long)pid, executable);
- }
-
- /* Parse abrt.conf and plugins/CCpp.conf */
- unsigned setting_MaxCrashReportsSize = 0;
- bool setting_MakeCompatCore = false;
- parse_conf(CONF_DIR"/plugins/CCpp.conf", &setting_MaxCrashReportsSize, &setting_MakeCompatCore);
-
if (setting_MaxCrashReportsSize > 0)
{
check_free_space(setting_MaxCrashReportsSize);
@@ -267,8 +272,6 @@ int main(int argc, char** argv)
trim_debug_dumps(setting_MaxCrashReportsSize, path);
}
- if (!setting_MakeCompatCore)
- return 0;
/* fall through to creating user core */
}
catch (CABRTException& e)
@@ -282,6 +285,9 @@ int main(int argc, char** argv)
create_user_core:
+ if (!setting_MakeCompatCore)
+ return 0;
+
/* note: core_size may be == 0 ("unknown") */
if (core_size > ulimit_c || ulimit_c == 0)
return 0;
diff --git a/src/Hooks/abrt-hook-python.cpp b/src/Hooks/abrt-hook-python.cpp
index 356174f..3445954 100644
--- a/src/Hooks/abrt-hook-python.cpp
+++ b/src/Hooks/abrt-hook-python.cpp
@@ -37,21 +37,6 @@
static char *pid;
static char *executable;
-// Note: "" will return false
-static bool isxdigit_str(const char *str)
-{
- do {
- if ((*str < '0' || *str > '9') // not a digit
- && ((*str | 0x20) < 'a' || (*str | 0x20) > 'f') // not A-F or a-f
- )
- {
- return false;
- }
- str++;
- } while (*str);
- return true;
-}
-
static bool printable_str(const char *str)
{
do {
diff --git a/src/Hooks/abrt_exception_handler.py.in b/src/Hooks/abrt_exception_handler.py.in
index 89f3013..b5e15b8 100644
--- a/src/Hooks/abrt_exception_handler.py.in
+++ b/src/Hooks/abrt_exception_handler.py.in
@@ -59,8 +59,22 @@ def handleMyException((etype, value, tb)):
return sys.__excepthook__(etype, value, tb)
try:
+ import os
import os.path
import traceback
+ import errno
+
+ # EPIPE is not a crash, it happens all the time
+ # Testcase: script.py | true, where script.py is:
+ ## #!/usr/bin/python
+ ## import os
+ ## import time
+ ## time.sleep(1)
+ ## os.write(1, "Hello\n") # print "Hello" wouldn't be the same
+ #
+ if etype == IOError or etype == OSError:
+ if value.errno == errno.EPIPE:
+ return sys.__excepthook__(etype, value, tb)
# "-c" appears in this case:
# $ python -c 'import sys; print "argv0 is:%s" % sys.argv[0]'