diff options
| author | Nikola Pajkovsky <npajkovs@redhat.com> | 2010-02-15 18:09:55 +0100 |
|---|---|---|
| committer | Nikola Pajkovsky <npajkovs@redhat.com> | 2010-02-15 18:09:55 +0100 |
| commit | d93fc21129f08a149d7a1bb042179942485fcedb (patch) | |
| tree | 72ec4eb636b15d8e2385f068881f86a6aa88db2b /src | |
| parent | deef343e0372b0a167f1d35f9ef9d18694aa9a0e (diff) | |
| parent | 3a0729e697b24d4d30e3a1a008f83ca605aaad5d (diff) | |
| download | abrt-d93fc21129f08a149d7a1bb042179942485fcedb.tar.gz abrt-d93fc21129f08a149d7a1bb042179942485fcedb.tar.xz abrt-d93fc21129f08a149d7a1bb042179942485fcedb.zip | |
Merge branch 'master' into bugzilla
Diffstat (limited to 'src')
| -rwxr-xr-x | src/Backtrace/abrt-bz-dupchecker | 127 | ||||
| -rw-r--r-- | src/CLI/report.cpp | 309 | ||||
| -rw-r--r-- | src/Daemon/PluginManager.cpp | 54 | ||||
| -rw-r--r-- | src/Daemon/PluginManager.h | 9 | ||||
| -rw-r--r-- | src/Daemon/Settings.cpp | 12 | ||||
| -rwxr-xr-x | src/Daemon/abrt-debuginfo-install | 83 | ||||
| -rw-r--r-- | src/Gui/CCMainWindow.py | 43 | ||||
| -rw-r--r-- | src/Gui/CCReporterDialog.py | 10 | ||||
| -rw-r--r-- | src/Gui/CC_gui_functions.py | 3 | ||||
| -rw-r--r-- | src/Gui/PluginsSettingsDialog.py | 16 | ||||
| -rw-r--r-- | src/Gui/ccgui.glade | 4 | ||||
| -rw-r--r-- | src/Gui/report.glade | 4 | ||||
| -rw-r--r-- | src/Hooks/abrt-hook-ccpp.cpp | 60 | ||||
| -rw-r--r-- | src/Hooks/abrt-hook-python.cpp | 15 | ||||
| -rw-r--r-- | src/Hooks/abrt_exception_handler.py.in | 14 |
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 <pcon@fedoraproject.org></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 <pcon@fedoraproject.org></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 <pcon@fedoraproject.org></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]' |
