diff options
author | Karel Klic <kklic@redhat.com> | 2011-05-13 18:32:06 +0200 |
---|---|---|
committer | Karel Klic <kklic@redhat.com> | 2011-05-13 18:32:06 +0200 |
commit | e16a562d8616d2ac55f58b091f95acd5b3174229 (patch) | |
tree | bf5c97a4ab4d4f792c436d4ed93a1c36cd381c3b /src/plugins | |
parent | 15473d2a5ecfd20bd8cc3982e703fcedd51289f8 (diff) | |
parent | e123c5f3b4bdd10f3b495a4a948f6c452ed6205f (diff) | |
download | abrt-e16a562d8616d2ac55f58b091f95acd5b3174229.tar.gz abrt-e16a562d8616d2ac55f58b091f95acd5b3174229.tar.xz abrt-e16a562d8616d2ac55f58b091f95acd5b3174229.zip |
merge
Diffstat (limited to 'src/plugins')
37 files changed, 1704 insertions, 1416 deletions
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 52006b5e..df0ad944 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -1,3 +1,5 @@ +-include ../../config.mak + pluginslibdir = $(PLUGINS_LIB_DIR) bin_SCRIPTS = \ @@ -38,6 +40,7 @@ dist_events_DATA = \ report_Logger.conf \ analyze_LocalGDB.xml \ analyze_RetraceServer.xml \ + analyze_xsession_errors.xml \ report_Mailx.xml \ report_RHTSupport.xml \ report_Kerneloops.xml @@ -53,13 +56,31 @@ dist_eventsconf_DATA = \ rhtsupport_events.conf man_MANS = \ - abrt-Bugzilla.7 \ abrt-KerneloopsReporter.7 \ - abrt-Logger.7 \ - abrt-Mailx.7 \ abrt-Upload.7 \ abrt-plugins.7 +MAN_TXT = \ + abrt-action-print.txt \ + abrt-action-trim-files.txt \ + abrt-action-generate-backtrace.txt \ + abrt-action-analyze-backtrace.txt \ + abrt-action-mailx.txt \ + abrt-action-bugzilla.txt \ + abrt-action-list-dsos.txt + +# Manual pages are generated from .txt via Docbook +man1_MANS = ${MAN_TXT:%.txt=%.1} + +%.1 %.5 %.7: %.xml + $(XMLTO_SILENT) xmlto man $< 2>&1 | sed '/Note/d' + +%.xml: %.txt ../../asciidoc.conf + $(ASCIIDOC_SILENT) asciidoc --backend=docbook --doctype=manpage --conf-file ../../asciidoc.conf -aabrt_version=$(PACKAGE_VERSION) -o $@ $< + + +CLEANFILES = $(man1_MANS) + PYTHON_FILES = \ abrt-action-install-debuginfo.py \ abrt-action-list-dsos.py \ @@ -67,12 +88,15 @@ PYTHON_FILES = \ EXTRA_DIST = \ $(man_MANS) \ + $(MAN_TXT) \ $(PYTHON_FILES) \ + $(man1_MANS) \ + analyze_xsession_errors.xml.in \ + analyze_LocalGDB.xml.in \ + analyze_RetraceServer.xml.in \ report_Bugzilla.xml.in \ report_Bugzilla.conf \ report_Logger.conf \ - analyze_LocalGDB.xml.in \ - analyze_RetraceServer.xml.in \ report_Mailx.xml.in \ report_RHTSupport.xml.in \ report_Kerneloops.xml.in @@ -80,10 +104,6 @@ EXTRA_DIST = \ $(DESTDIR)/$(DEBUG_INFO_DIR): $(mkdir_p) '$@' -install-data-hook: $(DESTDIR)/$(DEBUG_INFO_DIR) - $(LN_S) -f analyze_RetraceServer.xml $(DESTDIR)$(eventsdir)/reanalyze_RetraceServer.xml - $(LN_S) -f analyze_LocalGDB.xml $(DESTDIR)$(eventsdir)/reanalyze_LocalGDB.xml - abrt_dump_oops_SOURCES = \ abrt-dump-oops.c abrt_dump_oops_CPPFLAGS = \ @@ -219,7 +239,7 @@ abrt_action_analyze_backtrace_LDADD = \ ../btparser/libbtparser.la abrt_action_bugzilla_SOURCES = \ - abrt-action-bugzilla.cpp + abrt-action-bugzilla.c rhbz.c rhbz.h abrt_action_bugzilla_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ -I$(srcdir)/../lib \ @@ -233,7 +253,7 @@ abrt_action_bugzilla_CPPFLAGS = \ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ $(GLIB_CFLAGS) \ -D_GNU_SOURCE \ - -Wall -Wwrite-strings -Werror + -Wall -Wwrite-strings abrt_action_bugzilla_LDADD = \ $(GLIB_LIBS) \ ../lib/libabrt_web.la \ diff --git a/src/plugins/abrt-Bugzilla.7 b/src/plugins/abrt-Bugzilla.7 deleted file mode 100644 index 708695c7..00000000 --- a/src/plugins/abrt-Bugzilla.7 +++ /dev/null @@ -1,43 +0,0 @@ -.TH abrt "7" "1 Jun 2009" "" -.SH NAME -Bugzilla plugin for abrt(8) -.SH DESCRIPTION -.P -.I abrt -is a daemon that watches for application crashes. When a crash occurs, -it collects the problem data and takes action according to -its configuration. This manual page describes the \fIBugzilla\fP plugin -for \fIabrt\fP. -.P -This plugin is used to report the crash to a Bugzilla instance. The -plugin will determine the package name and distribution version. The -problem data is attached to the bug report. -.SH INVOCATION -The plugin is invoked in the \fIabrt.conf\fP configuration file. -No parameters are necessary. -.SH CONFIGURATION -The \fIBugzilla.conf\fP configuration file contains several -entries in the format "Option = Value". The options are: -.SS BugzillaURL -The URL of the Bugzilla instance that you want to use, including the -path to the xmlrpc. The default is https://bugzilla.redhat.com/xmlrpc.cgi -.SS Login -Your Bugzilla login. If you have no Bugzilla account, you cannot -use the plugin. -.SS Password -Your Bugzilla password. -.SH EXAMPLES -.P -This is a snippet from the \fIabrt.conf\fP configuration file. -When something crashes, use the Bugzilla plugin: -.P -[common] -.br -ActionsAndReporters = Bugzilla -.SH "SEE ALSO" -.IR abrt (8), -.IR abrt.conf (5), -.IR abrt-plugins (7) -.SH AUTHOR -Written by Zdenek Prikryl <zprikryl@redhat.com>. -Manual page written by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-Logger.7 b/src/plugins/abrt-Logger.7 deleted file mode 100644 index a9fbae09..00000000 --- a/src/plugins/abrt-Logger.7 +++ /dev/null @@ -1,44 +0,0 @@ -.TH abrt "7" "1 Jun 2009" "" -.SH NAME -Logger plugin for abrt(8) -.SH DESCRIPTION -.P -.I abrt -is a daemon that watches for application crashes. When a crash occurs, -it collects the problem data and takes action according to -its configuration. This manual page describes the \fILogger\fP plugin -for \fIabrt\fP. -.P -This plugin is used to log the crash to a file. -.P -The log will contain all the file names as well as their -content. It also contains "duplicity check": the ID -of the crash, which is used to tell whether the same -crash has happened previously. -.SH INVOCATION -The plugin is invoked in the \fIabrt.conf\fP configuration file. -No parameters are necessary. -.SH CONFIGURATION -The \fILogger.conf\fP configuration file contains -several entries in a format "Option = Value". The options are: -.SS LogPath -The path to the log file. -.SS AppendLogs -If set to "yes" (the default) \fILogger\fP will append -the report to the file, otherwise it will overwrite the file (so -only the last crash will be stored). -.SH EXAMPLES -.P -This is a snippet from the \fIabrt.conf\fP configuration file. -Log all the C/C++ application crashes: -.P -[AnalyzerActionsAndReporters] -.br -CCpp = Logger -.SH "SEE ALSO" -.IR abrt (8), -.IR abrt.conf (5), -.IR abrt-plugins (7) -.SH AUTHOR -Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual -page by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-Mailx.7 b/src/plugins/abrt-Mailx.7 deleted file mode 100644 index 1f2f08fc..00000000 --- a/src/plugins/abrt-Mailx.7 +++ /dev/null @@ -1,57 +0,0 @@ -.TH abrt "7" "1 Jun 2009" "" -.SH NAME -Mailx plugin for abrt(8) -.SH DESCRIPTION -.P -.I abrt -is a daemon that watches for application crashes. When a crash occurs, -it collects the problem data and takes action according to -its configuration. This manual page describes the \fIMailx\fP plugin -for \fIabrt\fP. -.P -This plugin is used to mail the data about the crash -to a specified mail address. -.SH INVOCATION -The plugin is invoked in the \fIabrt.conf\fP configuration file. It can take -one parameter, a subject of the mail (if it differs from the -one specified in the \fIMailx.conf\fP configuration file). -.SH CONFIGURATION -The \fIMailx.conf\fP configuration file contains -several entries in a format "Option = Value". The options are: -.SS Subject -The subject of the mail. -.SS Parameters -The \fIMailx\fP plugin executes the external "mailx" command to -send the mail. This option defines some additional command line -parameters, which should be added to the program invocation, if any. -.SS EmailFrom -The address from which the email is sent. -.SS EmailTo -The address to which the email is sent. -.SS SendBinaryData -Can be "yes" or "no". If set to "yes", the email will also -contain the binary files associated with the crash. Warning: -this can cause the emails to be large! (several MB) -.SH EXAMPLES -.P -These are snippets from the \fIabrt.conf\fP configuration file. -.P -1) Each time a crash happens, a mail is sent -.PP -[common] -.br -ActionsAndReporters = Mailx("[abrt] a crash occurs") -.P -2) When a program in a specific package (in this case "httpd") crashes, -send a mail about it. -.PP -[AnalyzerActionsAndReporters] -.br -CCpp:httpd = Mailx("[abrt] Apache crash") -.SH "SEE ALSO" -.IR abrt (8), -.IR abrt.conf (5), -.IR abrt-plugins (7) -.SH AUTHOR -Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual -page by Daniel Novotny <dnovotny@redhat.com>. diff --git a/src/plugins/abrt-action-analyze-backtrace.c b/src/plugins/abrt-action-analyze-backtrace.c index f25f379c..5d8c77f6 100644 --- a/src/plugins/abrt-action-analyze-backtrace.c +++ b/src/plugins/abrt-action-analyze-backtrace.c @@ -21,8 +21,6 @@ #include "../btparser/location.h" #include "parse_options.h" -#define PROGNAME "abrt-action-generate-backtrace" - static const char *dump_dir_name = "."; @@ -50,13 +48,11 @@ static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [options] -d DIR\n" + "\b [options] -d DIR\n" "\n" "Analyzes C/C++ backtrace, generates duplication hash, backtrace rating, and identifies crash function in dump directory DIR" ); @@ -72,11 +68,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) diff --git a/src/plugins/abrt-action-analyze-backtrace.txt b/src/plugins/abrt-action-analyze-backtrace.txt new file mode 100644 index 00000000..7a3f096c --- /dev/null +++ b/src/plugins/abrt-action-analyze-backtrace.txt @@ -0,0 +1,53 @@ +abrt-action-analyze-backtrace(1) +================================ + +NAME +---- +abrt-action-analyze-backtrace - Analyzes C/C++ backtrace, generates +duplication hash, backtrace rating, and identifies crash function +in dump directory DIR. + + +SYNOPSIS +-------- +'abrt-action-analyze-backtrace' [-v] [-d DIR] + +DESCRIPTION +----------- +The tool reads a file named 'backtrace' from problem dump directory, +generates duplication hash, backtrace rating, and identifies +crash function. Then it saves this data as new elements 'global_uuid', +'rating', 'crash_function' in this dump directory. + +Integration with ABRT events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +'abrt-action-analyze-backtrace' can be used as a secondary analyzer, +after backtrace has been generated. The data generated by +'abrt-action-analyze-backtrace' is useful for reporting the crash +to bug databases: rating makes it possible to prevent reporting of bugs +with low quality (non-informative) backtraces, duplication hash +is used to find already filed bugs about similar crashes. + +Example usage in abrt_event.conf: + +------------ +EVENT=analyze analyzer=CCpp + abrt-action-generate-backtrace || exit $? + abrt-action-analyze-backtrace +------------ + +OPTIONS +------- +-d DIR:: + Path to dump directory. + +-v:: + Be more verbose. Can be given multiple times. + +AUTHORS +------- +* ABRT team + +SEE ALSO +-------- +abrt-action-generate-backtrace diff --git a/src/plugins/abrt-action-analyze-c.c b/src/plugins/abrt-action-analyze-c.c index e3fe71d3..487fb8f7 100644 --- a/src/plugins/abrt-action-analyze-c.c +++ b/src/plugins/abrt-action-analyze-c.c @@ -19,8 +19,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-analyze-c" - static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput) { unsigned char hash_bytes[SHA1_RESULT_LEN]; @@ -155,15 +153,13 @@ static void trim_unstrip_output(char *result, const char *unstrip_n_output) int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); const char *dump_dir_name = "."; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] -d DIR\n" + "\b [-v] -d DIR\n" "\n" "Calculates and saves UUID of coredump in dump directory DIR" ); @@ -179,11 +175,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); /* Run unstrip -n and trim its output, leaving only sizes and build ids */ diff --git a/src/plugins/abrt-action-analyze-core.py b/src/plugins/abrt-action-analyze-core.py index 949519cc..06473c40 100755 --- a/src/plugins/abrt-action-analyze-core.py +++ b/src/plugins/abrt-action-analyze-core.py @@ -33,13 +33,13 @@ def error_msg(s): def error_msg_and_die(s): sys.stderr.write("%s\n" % s) - os.exit(1) + sys.exit(1) def xopen(name, mode): try: r = open(name, mode) - except IOError, e: - error_msg_and_die("Can't open '%s': %s" % (name, e)); + except IOError, ex: + error_msg_and_die("Can't open '%s': %s" % (name, ex)) return r diff --git a/src/plugins/abrt-action-analyze-oops.c b/src/plugins/abrt-action-analyze-oops.c index 8fca109d..9485d3c8 100644 --- a/src/plugins/abrt-action-analyze-oops.c +++ b/src/plugins/abrt-action-analyze-oops.c @@ -19,8 +19,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-analyze-oops" - static void hash_oops_str(char hash_str[SHA1_RESULT_LEN*2 + 1], char *oops_buf, const char *oops_ptr) { unsigned char old_c; @@ -137,15 +135,13 @@ static void hash_oops_str(char hash_str[SHA1_RESULT_LEN*2 + 1], char *oops_buf, int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); const char *dump_dir_name = "."; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-vs] -d DIR\n" + "\b [-vs] -d DIR\n" "\n" "Calculates and saves UUID and DUPHASH for oops dump directory DIR" ); @@ -161,11 +157,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) diff --git a/src/plugins/abrt-action-analyze-python.c b/src/plugins/abrt-action-analyze-python.c index 07f14a32..5a33fb8d 100644 --- a/src/plugins/abrt-action-analyze-python.c +++ b/src/plugins/abrt-action-analyze-python.c @@ -19,19 +19,15 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-analyze-python" - int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); const char *dump_dir_name = "."; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] -d DIR\n" + "\b [-v] -d DIR\n" "\n" "Calculates and saves UUID and DUPHASH of python crash dumps" ); @@ -47,11 +43,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) diff --git a/src/plugins/abrt-action-bugzilla.c b/src/plugins/abrt-action-bugzilla.c new file mode 100644 index 00000000..91bc26f8 --- /dev/null +++ b/src/plugins/abrt-action-bugzilla.c @@ -0,0 +1,340 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 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 "abrtlib.h" +#include "abrt_problem_data.h" +#include "parse_options.h" +#include "abrt_xmlrpc.h" +#include "rhbz.h" + +#define XML_RPC_SUFFIX "/xmlrpc.cgi" + +/* From RHEL6 kernel/panic.c: + * { TAINT_PROPRIETARY_MODULE, 'P', 'G' }, + * { TAINT_FORCED_MODULE, 'F', ' ' }, + * { TAINT_UNSAFE_SMP, 'S', ' ' }, + * { TAINT_FORCED_RMMOD, 'R', ' ' }, + * { TAINT_MACHINE_CHECK, 'M', ' ' }, + * { TAINT_BAD_PAGE, 'B', ' ' }, + * { TAINT_USER, 'U', ' ' }, + * { TAINT_DIE, 'D', ' ' }, + * { TAINT_OVERRIDDEN_ACPI_TABLE, 'A', ' ' }, + * { TAINT_WARN, 'W', ' ' }, + * { TAINT_CRAP, 'C', ' ' }, + * { TAINT_FIRMWARE_WORKAROUND, 'I', ' ' }, + * entries 12 - 27 are unused + * { TAINT_HARDWARE_UNSUPPORTED, 'H', ' ' }, + * entries 29 - 31 are unused + */ + +static const char * const taint_warnings[] = { + "Proprietary Module", + "Forced Module", + "Unsafe SMP", + "Forced rmmod", + "Machine Check", + "Bad Page", + "User", + "Die", + "Overriden ACPI Table", + "Warning Issued", + "Experimental Module Loaded", + "Firmware Workaround", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Hardware Unsupported", + NULL, + NULL, +}; + +/* TODO: npajkovs: fix tainted string */ +static const char *tainted_string(unsigned tainted) +{ + unsigned idx = 0; + while ((tainted >>= 1) != 0) + idx++; + + return taint_warnings[idx]; +} + +static void report_to_bugzilla(const char *dump_dir_name, map_string_h *settings) +{ + struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + if (!dd) + xfunc_die(); /* dd_opendir already emitted error msg */ + problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); + dd_close(dd); + + const char *env; + const char *login; + const char *password; + const char *bugzilla_xmlrpc; + const char *bugzilla_url; + bool ssl_verify; + + env = getenv("Bugzilla_Login"); + login = env ? env : get_map_string_item_or_empty(settings, "Login"); + env = getenv("Bugzilla_Password"); + password = env ? env : get_map_string_item_or_empty(settings, "Password"); + if (!login[0] || !password[0]) + error_msg_and_die(_("Empty login or password, please check your configuration")); + + env = getenv("Bugzilla_BugzillaURL"); + bugzilla_url = env ? env : get_map_string_item_or_empty(settings, "BugzillaURL"); + if (!bugzilla_url[0]) + bugzilla_url = "https://bugzilla.redhat.com"; + bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url); + + env = getenv("Bugzilla_SSLVerify"); + ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify")); + + const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT); + const char *duphash = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH); + if (!duphash) + error_msg_and_die(_("Essential file '%s' is missing, can't continue.."), + FILENAME_DUPHASH); + + if (!*duphash) + error_msg_and_die(_("Essential file '%s' is empty, can't continue.."), + FILENAME_DUPHASH); + + const char *release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE); + if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */ + release = get_problem_item_content_or_NULL(problem_data, "release"); + + struct abrt_xmlrpc *client = abrt_xmlrpc_new_client(bugzilla_xmlrpc, ssl_verify); + + log(_("Logging into Bugzilla at %s"), bugzilla_url); + rhbz_login(client, login, password); + + log(_("Checking for duplicates")); + char *product = NULL; + char *version = NULL; + parse_release_for_bz(release, &product, &version); + free(version); + + xmlrpc_value *result; + if (strcmp(product, "Fedora") == 0) + result = rhbz_search_duphash(client, component, product, duphash); + else + result = rhbz_search_duphash(client, component, NULL, duphash); + + xmlrpc_value *all_bugs = rhbz_get_member("bugs", result); + xmlrpc_DECREF(result); + + if (!all_bugs) + error_msg_and_die(_("Missing mandatory member 'bugs'")); + + int all_bugs_size = rhbz_array_size(all_bugs); + // When someone clones bug it has same duphash, so we can find more than 1. + // Need to be checked if component is same. + VERB3 log("Bugzilla has %i reports with same duphash '%s'", + all_bugs_size, duphash); + + int bug_id = -1, dependent_bug = -1; + struct bug_info *bz = NULL; + if (all_bugs_size > 0) + { + bug_id = rhbz_bug_id(all_bugs); + xmlrpc_DECREF(all_bugs); + bz = rhbz_bug_info(client, bug_id); + + if (strcmp(bz->bi_product, product) != 0) + { + dependent_bug = bug_id; + /* found something, but its a different product */ + free_bug_info(bz); + + xmlrpc_value *result = rhbz_search_duphash(client, component, + product, duphash); + xmlrpc_value *all_bugs = rhbz_get_member("bugs", result); + xmlrpc_DECREF(result); + + all_bugs_size = rhbz_array_size(all_bugs); + if (all_bugs_size > 0) + { + bug_id = rhbz_bug_id(all_bugs); + bz = rhbz_bug_info(client, bug_id); + } + xmlrpc_DECREF(all_bugs); + } + + } + free(product); + + if (all_bugs_size == 0) // Create new bug + { + log(_("Creating a new bug")); + bug_id = rhbz_new_bug(client, problem_data, bug_id); + + log("Adding attachments to bug %i", bug_id); + char bug_id_str[sizeof(int)*3 + 2]; + sprintf(bug_id_str, "%i", bug_id); + + rhbz_attachments(client, bug_id_str, problem_data); + + log(_("Logging out")); + rhbz_logout(client); + + log("Status: NEW %s/show_bug.cgi?id=%u", bugzilla_url, bug_id); + abrt_xmlrpc_free_client(client); + return; + } + + // decision based on state + log(_("Bug is already reported: %i"), bz->bi_id); + if ((strcmp(bz->bi_status, "CLOSED") == 0) + && (strcmp(bz->bi_resolution, "DUPLICATE") == 0)) + { + struct bug_info *origin; + origin = rhbz_find_origin_bug_closed_duplicate(client, bz); + if (origin) + { + free_bug_info(bz); + bz = origin; + } + } + + if (strcmp(bz->bi_status, "CLOSED") != 0) + { + if ((strcmp(bz->bi_reporter, login) != 0) + && (!g_list_find_custom(bz->bi_cc_list, login, (GCompareFunc)g_strcmp0))) + { + log(_("Add %s to CC list"), login); + rhbz_mail_to_cc(client, bz->bi_id, login); + } + + char *dsc = make_description_comment(problem_data); + if (dsc) + { + const char *package = get_problem_item_content_or_NULL(problem_data, + FILENAME_PACKAGE); + const char *release = get_problem_item_content_or_NULL(problem_data, + FILENAME_OS_RELEASE); + if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */ + release = get_problem_item_content_or_NULL(problem_data, "release"); + const char *arch = get_problem_item_content_or_NULL(problem_data, + FILENAME_ARCHITECTURE); + const char *is_private = get_problem_item_content_or_NULL(problem_data, + "is_private"); + + char *full_dsc = xasprintf("Package: %s\n" + "Architecture: %s\n" + "OS Release: %s\n" + "%s", package, arch, release, dsc); + + log(_("Adding new comment to bug %d"), bz->bi_id); + free(dsc); + + int is_priv = is_private && string_to_bool(is_private); + rhbz_add_comment(client, bz->bi_id, full_dsc, is_priv); + free(full_dsc); + } + } + + log(_("Logging out")); + rhbz_logout(client); + + log("Status: %s%s%s %s/show_bug.cgi?id=%u", + bz->bi_status, + bz->bi_resolution ? " " : "", + bz->bi_resolution ? bz->bi_resolution : "", + bugzilla_url, + bz->bi_id); + + dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + if (dd) + { + char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", bugzilla_url, bz->bi_id); + add_reported_to(dd, msg); + free(msg); + dd_close(dd); + } + + free_problem_data(problem_data); + free_bug_info(bz); + abrt_xmlrpc_free_client(client); +} + +int main(int argc, char **argv) +{ + abrt_init(argv); + + map_string_h *settings = new_map_string(); + const char *dump_dir_name = "."; + GList *conf_file = NULL; + + /* Can't keep these strings/structs static: _() doesn't support that */ + const char *program_usage_string = _( + "\b [-v] -c CONFFILE -d DIR\n" + "\n" + "Reports problem to Bugzilla" + ); + enum { + OPT_v = 1 << 0, + OPT_d = 1 << 1, + OPT_c = 1 << 2, + }; + /* Keep enum above and order of options below in sync! */ + struct options program_options[] = { + OPT__VERBOSE(&g_verbose), + OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")), + OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")), + OPT_END() + }; + /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); + + export_abrt_envvars(0); + + while (conf_file) + { + char *fn = (char *)conf_file->data; + VERB1 log("Loading settings from '%s'", fn); + load_conf_file(fn, settings, /*skip key w/o values:*/ true); + VERB3 log("Loaded '%s'", fn); + conf_file = g_list_remove(conf_file, fn); + } + + VERB1 log("Initializing XML-RPC library"); + xmlrpc_env env; + xmlrpc_env_init(&env); + xmlrpc_client_setup_global_const(&env); + if (env.fault_occurred) + error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code); + xmlrpc_env_clean(&env); + + report_to_bugzilla(dump_dir_name, settings); + + free_map_string(settings); + return 0; +} diff --git a/src/plugins/abrt-action-bugzilla.cpp b/src/plugins/abrt-action-bugzilla.cpp deleted file mode 100644 index 6f551e2c..00000000 --- a/src/plugins/abrt-action-bugzilla.cpp +++ /dev/null @@ -1,966 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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 "abrtlib.h" -#include "abrt_xmlrpc.h" -#include "abrt_problem_data.h" -#include "parse_options.h" - -#define PROGNAME "abrt-action-bugzilla" - -#define XML_RPC_SUFFIX "/xmlrpc.cgi" -#define MAX_HOPS 5 - -/* - * TODO: npajkovs: better deallocation of xmlrpc value - * npajkovs: better gathering function which collects all information from bugzilla - * npajkovs: figure out how to deal with cloning bugs - * npajkovs: check if attachment was uploaded successul an if not try it again(max 3 times) - * and if it still fails. retrun successful, but mention that attaching failed - * npajkovs: add option to set comment privat - */ - -struct bug_info { - const char* bug_status; - const char* bug_resolution; - const char* bug_reporter; - const char* bug_product; - xmlrpc_int32 bug_dup_id; - GList* bug_cc; -}; - -/* xzalloc */ -static void bug_info_init(struct bug_info* bz) -{ - bz->bug_status = NULL; - bz->bug_resolution = NULL; - bz->bug_reporter = NULL; - bz->bug_product = NULL; - bz->bug_dup_id = -1; - bz->bug_cc = NULL; -} - -static void bug_info_destroy(struct bug_info* bz) -{ - free((void*)bz->bug_status); - free((void*)bz->bug_resolution); - free((void*)bz->bug_reporter); - free((void*)bz->bug_product); - - list_free_with_free(bz->bug_cc); -} - -/* - * Static namespace for xmlrpc stuff. - * Used mainly to ensure we always destroy xmlrpc client and server_info. - */ - -namespace { - -struct ctx: public abrt_xmlrpc_conn { - xmlrpc_env env; - - ctx(const char* url, bool ssl_verify): abrt_xmlrpc_conn(url, ssl_verify) - { xmlrpc_env_init(&env); } - ~ctx() { xmlrpc_env_clean(&env); } - - void login(const char* login, const char* passwd); - void logout(); - - const char* get_bug_status(xmlrpc_value* result_xml); - const char* get_bug_resolution(xmlrpc_value* result_xml); - const char* get_bug_reporter(xmlrpc_value* result_xml); - const char* get_bug_product(xmlrpc_value* relult_xml); - - xmlrpc_value* call_quicksearch_duphash(const char* component, const char* release, const char* duphash); - xmlrpc_value* get_cc_member(xmlrpc_value* result_xml); - xmlrpc_value* get_member(const char* member, xmlrpc_value* result_xml); - - int get_array_size(xmlrpc_value* result_xml); - xmlrpc_int32 get_bug_id(xmlrpc_value* result_xml); - xmlrpc_int32 get_bug_dup_id(xmlrpc_value* result_xml); - void get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz); - int add_plus_one_cc(xmlrpc_int32 bug_id, const char* login); - xmlrpc_int32 new_bug(problem_data_t *problem_data, int depend_on_bugno); - int add_attachments(const char* bug_id_str, problem_data_t *problem_data); - int get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id); - int add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private); - - xmlrpc_value* call(const char* method, const char* format, ...); -}; - -xmlrpc_value* ctx::call(const char* method, const char* format, ...) -{ - xmlrpc_value* result = NULL; - - if (!env.fault_occurred) - { - xmlrpc_value* param = NULL; - va_list args; - const char* suffix; - - va_start(args, format); - xmlrpc_build_value_va(&env, format, args, ¶m, &suffix); - va_end(args); - - if (*suffix != '\0') - { - xmlrpc_env_set_fault_formatted( - &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument " - "specifier: '%s'. There must be exactly one arument.", - suffix); - } - else - { - xmlrpc_client_call2(&env, m_pClient, m_pServer_info, method, param, &result); - } - xmlrpc_DECREF(param); - if (env.fault_occurred) - return NULL; - } - - return result; -} - -xmlrpc_value* ctx::get_member(const char* member, xmlrpc_value* result_xml) -{ - xmlrpc_value* cc_member = NULL; - xmlrpc_struct_find_value(&env, result_xml, member, &cc_member); - if (env.fault_occurred) - return NULL; - - return cc_member; -} - -int ctx::get_array_size(xmlrpc_value* result_xml) -{ - int size = xmlrpc_array_size(&env, result_xml); - if (env.fault_occurred) - return -1; - - return size; -} - -xmlrpc_int32 ctx::get_bug_dup_id(xmlrpc_value* result_xml) -{ - xmlrpc_value* dup_id = get_member("dup_id", result_xml); - if (!dup_id) - return -1; - - xmlrpc_int32 dup_id_int = -1; - xmlrpc_read_int(&env, dup_id, &dup_id_int); - xmlrpc_DECREF(dup_id); - if (env.fault_occurred) - return -1; - - VERB3 log("got dup_id: %i", dup_id_int); - return dup_id_int; -} - -const char* ctx::get_bug_product(xmlrpc_value* result_xml) -{ - xmlrpc_value* product_member = get_member("product", result_xml); - if (!product_member) //should never happend. Each bug has to set up product - return NULL; - - const char* product = NULL; - xmlrpc_read_string(&env, product_member, &product); - xmlrpc_DECREF(product_member); - if (env.fault_occurred) - return NULL; - - if (*product != '\0') - { - VERB3 log("got bug product: %s", product); - return product; - } - - free((void*)product); - return NULL; -} - -const char* ctx::get_bug_reporter(xmlrpc_value* result_xml) -{ - xmlrpc_value* reporter_member = get_member("reporter", result_xml); - if (!reporter_member) - return NULL; - - const char* reporter = NULL; - xmlrpc_read_string(&env, reporter_member, &reporter); - xmlrpc_DECREF(reporter_member); - if (env.fault_occurred) - return NULL; - - if (*reporter != '\0') - { - VERB3 log("got bug reporter: %s", reporter); - return reporter; - } - free((void*)reporter); - return NULL; -} - -const char* ctx::get_bug_resolution(xmlrpc_value* result_xml) -{ - xmlrpc_value* bug_resolution = get_member("resolution", result_xml); - if (!bug_resolution) - return NULL; - - const char* resolution_str = NULL; - xmlrpc_read_string(&env, bug_resolution, &resolution_str); - xmlrpc_DECREF(bug_resolution); - if (env.fault_occurred) - return NULL; - - if (*resolution_str != '\0') - { - VERB3 log("got resolution: %s", resolution_str); - return resolution_str; - } - free((void*)resolution_str); - return NULL; -} - -const char* ctx::get_bug_status(xmlrpc_value* result_xml) -{ - xmlrpc_value* bug_status = get_member("bug_status", result_xml); - if (!bug_status) - return NULL; - - const char* status_str = NULL; - xmlrpc_read_string(&env, bug_status, &status_str); - xmlrpc_DECREF(bug_status); - if (env.fault_occurred) - return NULL; - - if (*status_str != '\0') - { - VERB3 log("got bug_status: %s", status_str); - return status_str; - } - free((void*)status_str); - return NULL; -} - -void ctx::get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz) -{ - xmlrpc_value* cc_member = get_member("cc", result_xml); - if (!cc_member) - return; - - int array_size = xmlrpc_array_size(&env, cc_member); - if (array_size == -1) - return; - - VERB3 log("count members on cc %i", array_size); - - for (int i = 0; i < array_size; i++) - { - xmlrpc_value* item = NULL; - xmlrpc_array_read_item(&env, cc_member, i, &item); - if (env.fault_occurred) - return; - - if (item) - { - const char* cc = NULL; - xmlrpc_read_string(&env, item, &cc); - xmlrpc_DECREF(item); - if (env.fault_occurred) - { - xmlrpc_DECREF(cc_member); - return; - } - - if (*cc != '\0') - { - bz->bug_cc = g_list_append(bz->bug_cc, (char*)cc); - VERB3 log("member on cc is %s", cc); - continue; - } - free((char*)cc); - } - } - xmlrpc_DECREF(cc_member); - return; -} - -xmlrpc_value* ctx::call_quicksearch_duphash(const char* component, - const char* release, const char* duphash) -{ - char *query = NULL; - if (!release) - query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\"", component, duphash); - else - { - char *product = NULL; - char *version = NULL; - parse_release_for_bz(release, &product, &version); - query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"", - component, duphash, product - ); - free(product); - free(version); - } - - VERB3 log("quicksearch for `%s'", query); - xmlrpc_value *ret = call("Bug.search", "({s:s})", "quicksearch", query); - free(query); - return ret; -} - -xmlrpc_int32 ctx::get_bug_id(xmlrpc_value* result_xml) -{ - xmlrpc_value* item = NULL; - xmlrpc_array_read_item(&env, result_xml, 0, &item); - if (env.fault_occurred) - return -1; - - xmlrpc_value* bug = get_member("bug_id", item); - xmlrpc_DECREF(item); - if (!bug) - return -1; - - xmlrpc_int32 bug_id = -1; - xmlrpc_read_int(&env, bug, &bug_id); - xmlrpc_DECREF(bug); - if (env.fault_occurred) - return -1; - - VERB3 log("got bug_id %d", (int)bug_id); - return bug_id; -} - -int ctx::add_plus_one_cc(xmlrpc_int32 bug_id, const char* login) -{ - xmlrpc_value* result = call("Bug.update", "({s:i,s:{s:(s)}})", "ids", (int)bug_id, "updates", "add_cc", login); - if (result) - xmlrpc_DECREF(result); - return result ? 0 : -1; -} - -int ctx::add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private) -{ - xmlrpc_value* result = call("Bug.add_comment", "({s:i,s:s,s:b})", "id", (int)bug_id, - "comment", comment, - "private", is_private); - if (result) - xmlrpc_DECREF(result); - return result ? 0 : -1; -} - -/* From RHEL6 kernel/panic.c: - * { TAINT_PROPRIETARY_MODULE, 'P', 'G' }, - * { TAINT_FORCED_MODULE, 'F', ' ' }, - * { TAINT_UNSAFE_SMP, 'S', ' ' }, - * { TAINT_FORCED_RMMOD, 'R', ' ' }, - * { TAINT_MACHINE_CHECK, 'M', ' ' }, - * { TAINT_BAD_PAGE, 'B', ' ' }, - * { TAINT_USER, 'U', ' ' }, - * { TAINT_DIE, 'D', ' ' }, - * { TAINT_OVERRIDDEN_ACPI_TABLE, 'A', ' ' }, - * { TAINT_WARN, 'W', ' ' }, - * { TAINT_CRAP, 'C', ' ' }, - * { TAINT_FIRMWARE_WORKAROUND, 'I', ' ' }, - * entries 12 - 27 are unused - * { TAINT_HARDWARE_UNSUPPORTED, 'H', ' ' }, - * entries 29 - 31 are unused - */ - -static const char * const taint_warnings[] = { - "Proprietary Module", - "Forced Module", - "Unsafe SMP", - "Forced rmmod", - "Machine Check", - "Bad Page", - "User", - "Die", - "Overriden ACPI Table", - "Warning Issued", - "Experimental Module Loaded", - "Firmware Workaround", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "Hardware Unsupported", - NULL, - NULL, -}; - -static const char *tainted_string(unsigned tainted) -{ - unsigned idx = 0; - while ((tainted >>= 1) != 0) - idx++; - - return taint_warnings[idx]; -} - -xmlrpc_int32 ctx::new_bug(problem_data_t *problem_data, int depend_on_bugno) -{ - const char *package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE); - const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT); - const char *release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE); - if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */ - release = get_problem_item_content_or_NULL(problem_data, "release"); - const char *arch = get_problem_item_content_or_NULL(problem_data, FILENAME_ARCHITECTURE); - const char *duphash = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH); - const char *reason = get_problem_item_content_or_NULL(problem_data, FILENAME_REASON); - const char *function = get_problem_item_content_or_NULL(problem_data, FILENAME_CRASH_FUNCTION); - const char *analyzer = get_problem_item_content_or_NULL(problem_data, FILENAME_ANALYZER); - const char *tainted_str = get_problem_item_content_or_NULL(problem_data, FILENAME_TAINTED); - - struct strbuf *buf_summary = strbuf_new(); - strbuf_append_strf(buf_summary, "[abrt] %s", package); - - if (function != NULL && strlen(function) < 30) - strbuf_append_strf(buf_summary, ": %s", function); - - if (reason != NULL) - strbuf_append_strf(buf_summary, ": %s", reason); - - if (tainted_str && analyzer - && (strcmp(analyzer, "Kerneloops") == 0) - ) { - unsigned long tainted = xatoi_positive(tainted_str); - const char *tainted_warning = tainted_string(tainted); - if (tainted_warning) - strbuf_append_strf(buf_summary, ": TAINTED %s", tainted_warning); - } - - char *status_whiteboard = xasprintf("abrt_hash:%s", duphash); - - char *bz_dsc = make_description_bz(problem_data); - char *full_dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc); - free(bz_dsc); - - char *product = NULL; - char *version = NULL; - parse_release_for_bz(release, &product, &version); - - xmlrpc_value* result = NULL; - char *summary = strbuf_free_nobuf(buf_summary); - if (depend_on_bugno > -1) - { - result = call("Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:i})", - "product", product, - "component", component, - "version", version, - "summary", summary, - "description", full_dsc, - "status_whiteboard", status_whiteboard, - "platform", arch, - "dependson", depend_on_bugno - ); - } - else - { - result = call("Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})", - "product", product, - "component", component, - "version", version, - "summary", summary, - "description", full_dsc, - "status_whiteboard", status_whiteboard, - "platform", arch - ); - } - free(status_whiteboard); - free(product); - free(version); - free(summary); - free(full_dsc); - - if (!result) - return -1; - - xmlrpc_value* id = get_member("id", result); - xmlrpc_DECREF(result); - if (!id) - return -1; - - xmlrpc_int32 bug_id = -1; - xmlrpc_read_int(&env, id, &bug_id); - xmlrpc_DECREF(id); - if (env.fault_occurred) - return -1; - - log(_("New bug id: %i"), (int)bug_id); - - return bug_id; -} - -int ctx::add_attachments(const char* bug_id_str, problem_data_t *problem_data) -{ - GHashTableIter iter; - char *name; - struct problem_item *value; - g_hash_table_iter_init(&iter, problem_data); - while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) - { - const char *content = value->content; - - // We were special-casing FILENAME_BACKTRACE here, but karel says - // he can retrieve it in inlined form from comments too. - if ((value->flags & CD_FLAG_TXT) - && (strlen(content) > CD_TEXT_ATT_SIZE /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/) - ) { - char *encoded64 = encode_base64(content, strlen(content)); - char *filename = xasprintf("File: %s", name); - xmlrpc_value* result = call("bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s})", bug_id_str, - "description", filename, - "filename", name, - "contenttype", "text/plain", - "data", encoded64 - ); - free(encoded64); - free(filename); - if (!result) - return -1; - - xmlrpc_DECREF(result); - } - } - return 0; -} - -int ctx::get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id) -{ - char bug_id_str[sizeof(long)*3 + 2]; - sprintf(bug_id_str, "%lu", (long)bug_id); - xmlrpc_value* result = call("bugzilla.getBug", "(s)", bug_id_str); - if (!result) - return -1; - - bz->bug_product = get_bug_product(result); - if (bz->bug_product == NULL) - return -1; - - bz->bug_status = get_bug_status(result); - if (bz->bug_status == NULL) - return -1; - - bz->bug_reporter = get_bug_reporter(result); - if (bz->bug_reporter == NULL) - return -1; - - // mandatory when bug status is CLOSED - if (strcmp(bz->bug_status, "CLOSED") == 0) - { - bz->bug_resolution = get_bug_resolution(result); - if ((env.fault_occurred) && (bz->bug_resolution == NULL)) - return -1; - } - - // mandatory when bug status is CLOSED and resolution is DUPLICATE - if ((strcmp(bz->bug_status, "CLOSED") == 0) - && (strcmp(bz->bug_resolution, "DUPLICATE") == 0) - ) { - bz->bug_dup_id = get_bug_dup_id(result); - if (env.fault_occurred) - return -1; - } - - get_bug_cc(result, bz); - if (env.fault_occurred) - return -1; - - xmlrpc_DECREF(result); - return 0; -} - -void ctx::login(const char* login, const char* passwd) -{ - xmlrpc_value* result = call("User.login", "({s:s,s:s})", "login", login, "password", passwd); -//TODO: with URL like http://bugzilla.redhat.com (that is, with http: instead of https:) -//we are getting this error: -//Logging into Bugzilla at http://bugzilla.redhat.com -//Can't login. Server said: HTTP response code is 301, not 200 -//But this is a 301 redirect! We _can_ follow it if we configure curl to understand that! - if (!result) - error_msg_and_die("Can't login. Server said: %s", env.fault_string); - xmlrpc_DECREF(result); -} - -void ctx::logout() -{ - xmlrpc_value* result = call("User.logout", "(s)", ""); - if (result) - xmlrpc_DECREF(result); - - throw_if_xml_fault_occurred(&env); -} - -} /* namespace */ - - -static void report_to_bugzilla( - const char *dump_dir_name, - map_string_h *settings) -{ - struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); - if (!dd) - xfunc_die(); /* dd_opendir already emitted error msg */ - problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); - dd_close(dd); - - const char *env; - const char *login; - const char *password; - const char *bugzilla_xmlrpc; - const char *bugzilla_url; - bool ssl_verify; - - env = getenv("Bugzilla_Login"); - login = env ? env : get_map_string_item_or_empty(settings, "Login"); - env = getenv("Bugzilla_Password"); - password = env ? env : get_map_string_item_or_empty(settings, "Password"); - if (!login[0] || !password[0]) - error_msg_and_die(_("Empty login or password, please check your configuration")); - - env = getenv("Bugzilla_BugzillaURL"); - bugzilla_url = env ? env : get_map_string_item_or_empty(settings, "BugzillaURL"); - if (!bugzilla_url[0]) - bugzilla_url = "https://bugzilla.redhat.com"; - bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url); - - env = getenv("Bugzilla_SSLVerify"); - ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify")); - - const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT); - const char *duphash = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH); - if (!duphash) - error_msg_and_die(_("Essential file '%s' is missing, can't continue.."), - FILENAME_DUPHASH); - - if (!*duphash) - error_msg_and_die(_("Essential file '%s' is empty, can't continue.."), - FILENAME_DUPHASH); - - const char *release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE); - if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */ - release = get_problem_item_content_or_NULL(problem_data, "release"); - - ctx bz_server(bugzilla_xmlrpc, ssl_verify); - - log(_("Logging into Bugzilla at %s"), bugzilla_url); - bz_server.login(login, password); - - log(_("Checking for duplicates")); - - char *product = NULL; - char *version = NULL; - parse_release_for_bz(release, &product, &version); - free(version); - - xmlrpc_value *result; - if (strcmp(product, "Fedora") == 0) - result = bz_server.call_quicksearch_duphash(component, product, duphash); - else - result = bz_server.call_quicksearch_duphash(component, NULL, duphash); - - if (!result) - throw_if_xml_fault_occurred(&bz_server.env); - - xmlrpc_value *all_bugs = bz_server.get_member("bugs", result); - xmlrpc_DECREF(result); - - if (!all_bugs) - { - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("Missing mandatory member 'bugs'")); - } - - xmlrpc_int32 bug_id = -1; - int all_bugs_size = bz_server.get_array_size(all_bugs); - struct bug_info bz; - int depend_on_bugno = -1; - if (all_bugs_size > 0) - { - bug_id = bz_server.get_bug_id(all_bugs); - xmlrpc_DECREF(all_bugs); - if (bug_id == -1) - throw_if_xml_fault_occurred(&bz_server.env); - - bug_info_init(&bz); - if (bz_server.get_bug_info(&bz, bug_id) == -1) - { - bug_info_destroy(&bz); - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information")); - } - - if (strcmp(bz.bug_product, product) != 0) - { - depend_on_bugno = bug_id; - bug_info_destroy(&bz); - result = bz_server.call_quicksearch_duphash(component, release, duphash); - if (!result) - throw_if_xml_fault_occurred(&bz_server.env); - - all_bugs = bz_server.get_member("bugs", result); - xmlrpc_DECREF(result); - - if (!all_bugs) - { - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("Missing mandatory member 'bugs'")); - } - - all_bugs_size = bz_server.get_array_size(all_bugs); - if (all_bugs_size > 0) - { - bug_id = bz_server.get_bug_id(all_bugs); - xmlrpc_DECREF(all_bugs); - if (bug_id == -1) - throw_if_xml_fault_occurred(&bz_server.env); - - bug_info_init(&bz); - if (bz_server.get_bug_info(&bz, bug_id) == -1) - { - bug_info_destroy(&bz); - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information")); - } - } - else - xmlrpc_DECREF(all_bugs); - } - } - free(product); - - if (all_bugs_size < 0) - { - throw_if_xml_fault_occurred(&bz_server.env); - } - else if (all_bugs_size == 0) // Create new bug - { - log(_("Creating a new bug")); - bug_id = bz_server.new_bug(problem_data, depend_on_bugno); - if (bug_id < 0) - { - throw_if_xml_fault_occurred(&bz_server.env); - error_msg_and_die(_("Bugzilla entry creation failed")); - } - - log("Adding attachments to bug %ld", (long)bug_id); - char bug_id_str[sizeof(long)*3 + 2]; - sprintf(bug_id_str, "%ld", (long) bug_id); - int ret = bz_server.add_attachments(bug_id_str, problem_data); - if (ret == -1) - { - throw_if_xml_fault_occurred(&bz_server.env); - } - - log(_("Logging out")); - bz_server.logout(); - - log("Status: NEW %s/show_bug.cgi?id=%u", - bugzilla_url, - (int)bug_id - ); - return; - } - - if (all_bugs_size > 1) - { - // When someone clones bug it has same duphash, so we can find more than 1. - // Need to be checked if component is same. - VERB3 log("Bugzilla has %u reports with same duphash '%s'", all_bugs_size, duphash); - } - - // decision based on state - log(_("Bug is already reported: %i"), bug_id); - - xmlrpc_int32 original_bug_id = bug_id; - if ((strcmp(bz.bug_status, "CLOSED") == 0) && (strcmp(bz.bug_resolution, "DUPLICATE") == 0)) - { - for (int ii = 0; ii <= MAX_HOPS; ii++) - { - if (ii == MAX_HOPS) - { - VERB3 log("Bugzilla could not find a parent of bug %d", (int)original_bug_id); - bug_info_destroy(&bz); - error_msg_and_die(_("Bugzilla couldn't find parent of bug %d"), (int)original_bug_id); - } - - log("Bug %d is a duplicate, using parent bug %d", bug_id, (int)bz.bug_dup_id); - bug_id = bz.bug_dup_id; - bug_info_destroy(&bz); - bug_info_init(&bz); - - if (bz_server.get_bug_info(&bz, bug_id) == -1) - { - bug_info_destroy(&bz); - if (bz_server.env.fault_occurred) - { - throw_if_xml_fault_occurred(&bz_server.env); - } - error_msg_and_die(_("get_bug_info() failed. Could not collect all mandatory information")); - } - - // found a bug which is not CLOSED as DUPLICATE - if (bz.bug_dup_id == -1) - break; - } - } - - if (strcmp(bz.bug_status, "CLOSED") != 0) - { - int status = 0; - if ((strcmp(bz.bug_reporter, login) != 0) - && (g_list_find(bz.bug_cc, login))) - { - log(_("Add %s to CC list"), login); - status = bz_server.add_plus_one_cc(bug_id, login); - } - - if (status == -1) - { - bug_info_destroy(&bz); - throw_if_xml_fault_occurred(&bz_server.env); - } - - char *dsc = make_description_comment(problem_data); - if (dsc) - { - const char* package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE); - const char* release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE); - if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */ - release = get_problem_item_content_or_NULL(problem_data, "release"); - const char* arch = get_problem_item_content_or_NULL(problem_data, FILENAME_ARCHITECTURE); - const char* is_private = get_problem_item_content_or_NULL(problem_data, "is_private"); - - char *full_dsc = xasprintf("Package: %s\n" - "Architecture: %s\n" - "OS Release: %s\n" - "%s", package, arch, release, dsc - ); - - log(_("Adding new comment to bug %d"), (int)bug_id); - - free(dsc); - - bool is_priv = is_private && string_to_bool(is_private); - if (bz_server.add_comment(bug_id, full_dsc, is_priv) == -1) - { - free(full_dsc); - bug_info_destroy(&bz); - throw_xml_fault(&bz_server.env); - } - free(full_dsc); - } - } - - log(_("Logging out")); - bz_server.logout(); - - log("Status: %s%s%s %s/show_bug.cgi?id=%u", - bz.bug_status, - bz.bug_resolution ? " " : "", - bz.bug_resolution ? bz.bug_resolution : "", - bugzilla_url, - (int)bug_id - ); - - dd = dd_opendir(dump_dir_name, /*flags:*/ 0); - if (dd) - { - char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", bugzilla_url, (int)bug_id); - add_reported_to(dd, msg); - free(msg); - dd_close(dd); - } - - free_problem_data(problem_data); - bug_info_destroy(&bz); -} - -int main(int argc, char **argv) -{ - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); - - map_string_h *settings = new_map_string(); - const char *dump_dir_name = "."; - GList *conf_file = NULL; - - /* Can't keep these strings/structs static: _() doesn't support that */ - const char *program_usage_string = _( - PROGNAME" [-v] -c CONFFILE -d DIR\n" - "\n" - "Reports problem to Bugzilla" - ); - enum { - OPT_v = 1 << 0, - OPT_d = 1 << 1, - OPT_c = 1 << 2, - }; - /* Keep enum above and order of options below in sync! */ - struct options program_options[] = { - OPT__VERBOSE(&g_verbose), - OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")), - OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")), - OPT_END() - }; - /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; - - while (conf_file) - { - char *fn = (char *)conf_file->data; - VERB1 log("Loading settings from '%s'", fn); - load_conf_file(fn, settings, /*skip key w/o values:*/ true); - VERB3 log("Loaded '%s'", fn); - conf_file = g_list_remove(conf_file, fn); - } - - VERB1 log("Initializing XML-RPC library"); - xmlrpc_env env; - xmlrpc_env_init(&env); - xmlrpc_client_setup_global_const(&env); - if (env.fault_occurred) - error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code); - xmlrpc_env_clean(&env); - - report_to_bugzilla(dump_dir_name, settings); - - free_map_string(settings); - return 0; -} diff --git a/src/plugins/abrt-action-bugzilla.txt b/src/plugins/abrt-action-bugzilla.txt new file mode 100644 index 00000000..313e5b19 --- /dev/null +++ b/src/plugins/abrt-action-bugzilla.txt @@ -0,0 +1,96 @@ +abrt-action-buzilla(1) +==================== + +NAME +---- +abrt-action-bugzilla - Sends problem information to bugzilla. + +SYNOPSIS +-------- +'abrt-action-bugzilla' [-v] [-c CONFFILE] -d DIR + +DESCRIPTION +----------- +The tool reads a problem dump direcotry. Firstly, the tool is trying to find +duplication of bug. If duplication is 'not' found, then a new bug is created. +Otherwise if duplication 'is' found and bug is closed as duplication of the +other bug, than, so called, 'hoping' begin. It means, that tool is going to try +track the origin bug. + +Example of bug stages are: +------------ +CLOSED DUPLICATE -> CLOSED DUPLICATE -> NEW +------------ +Third one is the origin of the same problem. The tool adds a new comment to bug, +logouts from bugzilla and return link to bug. + +Properties of bugzilla can be specified in a configuration file, +and via environment variables. + +Configuration file +~~~~~~~~~~~~~~~~~~ +Configuration file contains entries in a format "Option = Value". + +The options are: + +'Login':: + Login to Bugzilla account. + +'Password':: + Password to Bugzilla account. + +'BugzillaURL':: + Bugzilla http address. (default: https://bugzilla.redht.com) + +'SSLVerify':: + Use yes/true/on/1 to verify Bugzilla ssl certificate. (default: yes) + +Integration with ABRT events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +'abrt-action-bugzilla' can be used as a reporter, to allow users report +problems to bugzilla when they decide to do it. This usage is +pre-configured in /etc/abrt/events.d/ccpp_events.conf and +/etc/abrt/abrt_events.conf. + +For python problems (/etc/abrt/abrt_events.conf) +------------ +EVENT=report_Bugzilla analyzer=Python + abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf +------------ + +For C/C++ problems (/etc/abrt/events.d/ccpp_events.conf) +------------ +EVENT=report_Bugzilla analyzer=CCpp + abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf +------------ + +OPTIONS +------- +-d DIR:: + Path to dump directory. + +-c CONFFILE:: + Path to configration file. When used in ABRT event system, the file + contains site-wide configuration. Users can change the values via + environment variables. + +ENVIRONMENT VARIABLES +--------------------- +Environment variables take precedence over values provided in +the configuration file. + +'Bugzilla_Login':: + Login to Bugzilla account. + +'Bugzilla_Password':: + Password to Bugzilla account. + +'Bugzilla_BugzillaURL':: + Bugzilla http address. (default: https://bugzilla.redht.com) + +'Bugzilla_SSLVerify':: + Use yes/true/on/1 to verify Bugzilla ssl certificate. (default: yes) + +AUTHORS +------- +* ABRT team diff --git a/src/plugins/abrt-action-generate-backtrace.c b/src/plugins/abrt-action-generate-backtrace.c index 561b2165..1d06871a 100644 --- a/src/plugins/abrt-action-generate-backtrace.c +++ b/src/plugins/abrt-action-generate-backtrace.c @@ -23,8 +23,6 @@ #include "parse_options.h" -#define PROGNAME "abrt-action-generate-backtrace" - #define DEBUGINFO_CACHE_DIR LOCALSTATEDIR"/cache/abrt-di" static const char *dump_dir_name = "."; @@ -232,17 +230,15 @@ static char *get_backtrace(struct dump_dir *dd) int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); char *i_opt = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [options] -d DIR\n" + "\b [options] -d DIR\n" "\n" - "Generates and saves backtrace for coredump in dump directory DIR" + "Analyzes coredump in dump directory DIR, generates and saves backtrace" ); enum { OPT_v = 1 << 0, @@ -255,16 +251,12 @@ int main(int argc, char **argv) OPT__VERBOSE(&g_verbose), OPT_STRING( 'd', NULL, &dump_dir_name , "DIR" , _("Dump directory")), OPT_STRING( 'i', NULL, &i_opt , "DIR1[:DIR2]...", _("Additional debuginfo directories")), - OPT_INTEGER('t', NULL, &exec_timeout_sec, _("Kill gdb if it runs for more than N seconds")), + OPT_INTEGER('t', NULL, &exec_timeout_sec, _("Kill gdb if it runs for more than NUM seconds")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); if (i_opt) { diff --git a/src/plugins/abrt-action-generate-backtrace.txt b/src/plugins/abrt-action-generate-backtrace.txt new file mode 100644 index 00000000..fb90f158 --- /dev/null +++ b/src/plugins/abrt-action-generate-backtrace.txt @@ -0,0 +1,47 @@ +abrt-action-generate-backtrace(1) +================================= + +NAME +---- +abrt-action-generate-backtrace - Analyzes coredump, generates and saves backtrace. + +SYNOPSIS +-------- +'abrt-action-generate-backtrace' [-v] [-d DIR] [-i DIR1[:DIR2]...] [-t NUM] + +DESCRIPTION +----------- +This tool runs gdb(1) on a file named 'coredump' in dump directory DIR. +gdb(1) generates backtrace and other diagnostic information about the state +of the application at the moment when coredump was generated. +Then the tool saves it as new element 'backtrace' in this dump directory. + +Integration with ABRT events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +'abrt-action-generate-backtrace' can be used as an analyzer for +application crashes which dump core. + +Example usage in abrt_event.conf: + +------------ +EVENT=analyze analyzer=CCpp + abrt-action-generate-backtrace +------------ + +OPTIONS +------- +-d DIR:: + Path to dump directory. + +-v:: + Be more verbose. Can be given multiple times. + +-i DIR1[:DIR2]:: + Additional debuginfo directories + +-t NUM:: + Kill gdb if it runs for more than NUM seconds + +AUTHORS +------- +* ABRT team diff --git a/src/plugins/abrt-action-install-debuginfo.c b/src/plugins/abrt-action-install-debuginfo.c index 357762ee..112620b4 100644 --- a/src/plugins/abrt-action-install-debuginfo.c +++ b/src/plugins/abrt-action-install-debuginfo.c @@ -21,8 +21,7 @@ #include <stdlib.h> #include <string.h> -// TODO: honor configure --prefix here: -#define EXECUTABLE "/usr/bin/abrt-action-install-debuginfo.py" +#define EXECUTABLE "abrt-action-install-debuginfo.py" static void error_msg_and_die(const char *msg, const char *arg) { @@ -30,7 +29,7 @@ static void error_msg_and_die(const char *msg, const char *arg) if (arg) { write(2, " '", 2); - write(2, msg, strlen(msg)); + write(2, arg, strlen(arg)); write(2, "'", 1); } write(2, "\n", 1); @@ -47,7 +46,7 @@ int main(int argc, char **argv) { /* * We disallow passing of arguments which point to writable dirs. - * This way, the script will always use default arguments. + * This way, the script will always use default values for these arguments. */ char **pp = argv; char *arg; @@ -57,7 +56,7 @@ int main(int argc, char **argv) error_msg_and_die("bad option", arg); if (strncmp(arg, "--tmpdir", 8) == 0) error_msg_and_die("bad option", arg); - if (strncmp(arg, "-i", 2) == 0) + if (strncmp(arg, "--ids", 5) == 0) error_msg_and_die("bad option", arg); } @@ -70,12 +69,35 @@ int main(int argc, char **argv) setregid(g, g); uid_t u = geteuid(); if (u != getuid()) + { setreuid(u, u); + /* We are suid'ed! */ + /* Prevent malicious user from messing up with suid'ed process: */ + /* Set safe PATH */ +// TODO: honor configure --prefix here by adding it to PATH +// (otherwise abrt-action-install-debuginfo.py would fail to spawn abrt-action-trim-files): + if (u == 0) + putenv((char*) "PATH=/usr/sbin:/sbin:/usr/bin:/bin"); + else + putenv((char*) "PATH=/usr/bin:/bin"); + /* Clear dangerous stuff from env */ + static const char forbid[] = + "LD_LIBRARY_PATH" "\0" + "LD_PRELOAD" "\0" + "LD_TRACE_LOADED_OBJECTS" "\0" + "LD_BIND_NOW" "\0" + "LD_AOUT_LIBRARY_PATH" "\0" + "LD_AOUT_PRELOAD" "\0" + "LD_NOWARN" "\0" + "LD_KEEPDIR" "\0" + ; + const char *p = forbid; + do { + unsetenv(p); + p += strlen(p) + 1; + } while (*p); + } - /* We use full path, and execv instead of execvp in order to - * disallow user to execute his own abrt-action-install-debuginfo.py - * in his dir by setting up corresponding malicious $PATH. - */ - execv(EXECUTABLE, argv); + execvp(EXECUTABLE, argv); error_msg_and_die("Can't execute", EXECUTABLE); } diff --git a/src/plugins/abrt-action-install-debuginfo.py b/src/plugins/abrt-action-install-debuginfo.py index ab131f7c..e15fc12b 100755 --- a/src/plugins/abrt-action-install-debuginfo.py +++ b/src/plugins/abrt-action-install-debuginfo.py @@ -1,8 +1,10 @@ #! /usr/bin/python -u # -*- coding: utf-8 -*- - # WARNING: python -u means unbuffered I/O without it the messages are -# passed to the parent asynchronously which looks bad in clients.. +# passed to the parent asynchronously which looks bad in clients. + +PROGNAME = "abrt-action-install-debuginfo.py" + from subprocess import Popen, PIPE import sys import os @@ -35,9 +37,26 @@ def init_gettext(): gettext.textdomain(GETTEXT_PROGNAME) -def error_msg_and_die(s): - sys.stderr.write("%s\n" % s) - os.exit(1) +verbose = 0 +def log(fmt, *args): + sys.stderr.write("%s\n" % (fmt % args)) + +def log1(fmt, *args): + """ prints log message if verbosity >= 1 """ + if verbose >= 1: + sys.stderr.write("%s\n" % (fmt % args)) + +def log2(fmt, *args): + """ prints log message if verbosity >= 2 """ + if verbose >= 2: + sys.stderr.write("%s\n" % (fmt % args)) + +def error_msg(fmt, *args): + sys.stderr.write("%s\n" % (fmt % args)) + +def error_msg_and_die(fmt, *args): + sys.stderr.write("%s\n" % (fmt % args)) + sys.exit(1) old_stdout = -1 @@ -60,10 +79,10 @@ def ask_yes_no(prompt, retries=4): response = raw_input(prompt) except EOFError: log1("got eof, probably executed from helper, assuming - yes") - response = 'y' - if response in ('y', 'Y'): return True - if response in ('n', 'N', ''): + if response in (_("y")): # for translators -> y/Y as yes + return True + if response in ("", _("n")): # for translators -> N/n as no return False retries = retries - 1 if retries < 0: @@ -76,8 +95,8 @@ def ask_yes_no(prompt, retries=4): def unpack_rpm(package_nevra, files, tmp_dir, destdir, keeprpm): package_name = package_nevra + ".rpm" package_full_path = tmp_dir + "/" + package_name - log1("Extracting %s to %s" % (package_full_path, destdir)) - log2(files) + log1("Extracting %s to %s", package_full_path, destdir) + log2("%s", files) print _("Extracting cpio from %s") % (package_full_path) unpacked_cpio_path = tmp_dir + "/unpacked.cpio" try: @@ -92,7 +111,7 @@ def unpack_rpm(package_nevra, files, tmp_dir, destdir, keeprpm): if retcode == 0: log1("cpio written OK") if not keeprpm: - log1("keeprpms = False, removing %s" % package_full_path) + log1("keeprpms = False, removing %s", package_full_path) #print _("Removing temporary rpm file") os.unlink(package_full_path) else: @@ -106,7 +125,7 @@ def unpack_rpm(package_nevra, files, tmp_dir, destdir, keeprpm): unpacked_cpio = open(unpacked_cpio_path, 'rb') print _("Caching files from %s made from %s") % ("unpacked.cpio", package_name) - cpio = Popen(["cpio","-i", "-d", "--quiet"], + cpio = Popen(["cpio", "-idu", "--quiet"], stdin=unpacked_cpio, cwd=destdir, bufsize=-1) retcode = cpio.wait() @@ -208,7 +227,7 @@ class DebugInfoDownload(YumBase): #print repo repo.enable() rid = self.repos.enableRepo(repo.id) - log1("enabled repo %s" % rid) + log1("enabled repo %s", rid) setattr(repo, "skip_if_unavailable", True) self.repos.doSetup() @@ -241,7 +260,7 @@ class DebugInfoDownload(YumBase): not_found = [] package_files_dict = {} for debuginfo_path in files: - log2("yum whatprovides %s" % debuginfo_path) + log2("yum whatprovides %s", debuginfo_path) pkg = self.pkgSack.searchFiles(debuginfo_path) # sometimes one file is provided by more rpms, we can use either of # them, so let's use the first match @@ -254,9 +273,9 @@ class DebugInfoDownload(YumBase): installed_size += float(pkg[0].installedsize) total_pkgs += 1 - log2("found pkg for %s: %s" % (debuginfo_path, pkg[0])) + log2("found pkg for %s: %s", debuginfo_path, pkg[0]) else: - log2("not found pkg for %s" % debuginfo_path) + log2("not found pkg for %s", debuginfo_path) not_found.append(debuginfo_path) # connect our progress update callback @@ -317,17 +336,6 @@ class DebugInfoDownload(YumBase): except OSError: print _("Can't remove %s, probably contains an error log") % self.tmpdir -verbose = 0 -def log1(message): - """ prints log message if verbosity > 0 """ - if verbose > 0: - print "LOG1:", message - -def log2(message): - """ prints log message if verbosity > 1 """ - if verbose > 1: - print "LOG2:", message - def build_ids_to_path(build_ids): """ build_id1=${build_id:0:2} @@ -344,14 +352,14 @@ def filter_installed_debuginfos(build_ids, cache_dir): files = build_ids_to_path(build_ids) for debuginfo_path in files: cache_debuginfo_path = cache_dir + debuginfo_path - log2("checking path: %s" % debuginfo_path) + log2("checking path: %s", debuginfo_path) if os.path.exists(debuginfo_path): - log2("found: %s" % debuginfo_path) + log2("found: %s", debuginfo_path) continue if os.path.exists(cache_debuginfo_path): - log2("found: %s" % cache_debuginfo_path) + log2("found: %s", cache_debuginfo_path) continue - log2("not found: %s" % (cache_debuginfo_path)) + log2("not found: %s", cache_debuginfo_path) missing_di.append(debuginfo_path) return missing_di @@ -380,6 +388,7 @@ if __name__ == "__main__": signal.signal(signal.SIGINT, sigint_handler) fbuild_ids = "build_ids" cachedir = None + size_mb = 4096 tmpdir = None keeprpms = False noninteractive = False @@ -395,12 +404,29 @@ if __name__ == "__main__": except: pass - progname = os.path.basename(sys.argv[0]) - help_text = _("Usage: %s [-i <build_ids_file>] [--tmpdir=TMPDIR] " - "[--cache=CACHEDIR] [--keeprpms]") % progname + PROGNAME = os.path.basename(sys.argv[0]) + # ____________________________________________________________________________________ 7 + # ______01234567890123456789012345678901234567890123456789012345678901234567890123456789 + help_text = _( + "Usage: %s [-vy] [--ids=BUILD_IDS_FILE]\n" + " [--tmpdir=TMPDIR] [--cache=CACHEDIR] [--size_mb=SIZE]\n" + "\n" + "Installs debuginfos for all build-ids listed in BUILD_IDS_FILE\n" + "to CACHEDIR, using TMPDIR as temporary staging area.\n" + "Old files in CACHEDIR are deleted until it is smaller than SIZE.\n" + "\n" + " -v Be verbose\n" + " -y Noninteractive, assume 'Yes' to all questions\n" + " --ids Default: build_ids\n" + " --tmpdir Default: /tmp/abrt-tmp-debuginfo-RANDOM_SUFFIX\n" + " --cache Default: /var/cache/abrt-di\n" + " --size_mb Default: 4096\n" + # --keeprpms is not documented yet because it's a NOP so far + ) % PROGNAME + try: - opts, args = getopt.getopt(sys.argv[1:], "vyhi:", ["help", "cache=", - "tmpdir=","keeprpms"]) + opts, args = getopt.getopt(sys.argv[1:], "vyh", + ["help", "ids=", "cache=", "size_mb=", "tmpdir=", "keeprpms"]) except getopt.GetoptError, err: print str(err) # prints something like "option -a not recognized" exit(RETURN_FAILURE) @@ -413,13 +439,18 @@ if __name__ == "__main__": verbose += 1 elif opt == "-y": noninteractive = True - elif opt == "-i": + elif opt == "--ids": fbuild_ids = arg - elif opt in ("--cache"): + elif opt == "--cache": cachedir = arg - elif opt in ("--tmpdir"): + elif opt == "--size_mb": + try: + size_mb = int(arg) + except: + pass + elif opt == "--tmpdir": tmpdir = arg - elif opt in ("--keeprpms"): + elif opt == "--keeprpms": keeprpms = True if not cachedir: @@ -440,15 +471,37 @@ if __name__ == "__main__": if not b_ids: exit(RETURN_FAILURE) + # Delete oldest/biggest files from cachedir. + # (Note that we need to do it before we check for missing debuginfos) + # + # We can do it as a separate step in abrt_event.conf, but this + # would require setuid'ing abrt-action-trim-files to abrt:abrt. + # Since we (abrt-action-install-debuginfo) are already running setuid, + # it makes sense to NOT setuid abrt-action-trim-files too, + # but instead run it as our child: + sys.stdout.flush() + try: + pid = os.fork() + if pid == 0: + log1("abrt-action-trim-files -f %um:%s", size_mb, cachedir); + os.execlp("abrt-action-trim-files", "abrt-action-trim-files", "-f", "%um:%s" % (size_mb, cachedir)); + if pid > 0: + os.waitpid(pid, 0); + except Exception, e: + error_msg("Can't execute abrt-action-trim-files: %s", e); + missing = filter_installed_debuginfos(b_ids, cachedir) if missing: - log2(missing) + log2("%s", missing) print _("Coredump references %u debuginfo files, %u of them are not installed") % (len(b_ids), len(missing)) + + # TODO: should we pass keep_rpms=keeprpms to DebugInfoDownload here?? downloader = DebugInfoDownload(cache=cachedir, tmp=tmpdir) try: result = downloader.download(missing) except Exception, ex: error_msg_and_die("Can't download debuginfos: %s", ex) + missing = filter_installed_debuginfos(b_ids, cachedir) for bid in missing: print _("Missing debuginfo file: %s") % bid diff --git a/src/plugins/abrt-action-kerneloops.c b/src/plugins/abrt-action-kerneloops.c index 7b054a2e..99b2fea9 100644 --- a/src/plugins/abrt-action-kerneloops.c +++ b/src/plugins/abrt-action-kerneloops.c @@ -21,8 +21,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-kerneloops" - /* helpers */ static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) { @@ -129,9 +127,7 @@ static void report_to_kerneloops( int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); map_string_h *settings = new_map_string(); const char *dump_dir_name = "."; @@ -139,7 +135,7 @@ int main(int argc, char **argv) /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] [-c CONFFILE]... -d DIR\n" + "\b [-v] [-c CONFFILE]... -d DIR\n" "\n" "Reports kernel oops to kerneloops.org (or similar) site" ); @@ -157,11 +153,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); while (conf_file) { diff --git a/src/plugins/abrt-action-list-dsos.py b/src/plugins/abrt-action-list-dsos.py index 8b8a06ab..81a99276 100755 --- a/src/plugins/abrt-action-list-dsos.py +++ b/src/plugins/abrt-action-list-dsos.py @@ -7,13 +7,6 @@ import os import getopt import rpm -GETTEXT_PROGNAME = "abrt" -import locale -import gettext - -_ = lambda x: gettext.lgettext(x) - - def log(s): sys.stderr.write("%s\n" % s) @@ -22,26 +15,27 @@ def error_msg(s): def error_msg_and_die(s): sys.stderr.write("%s\n" % s) - os.exit(1) + sys.exit(1) def xopen(name, mode): try: r = open(name, mode) except IOError, e: - error_msg_and_die("Can't open '%s': %s" % (name, e)); + error_msg_and_die("Can't open '%s': %s" % (name, e)) return r def parse_maps(maps_path): try: f = xopen(maps_path, "r") - return [x.strip()[x.find('/'):] for x in f.readlines() if x.find('/') > -1] + # set() -> uniqifies the list of filenames + return set([x.strip()[x.find('/'):] for x in f.readlines() if x.find('/') > -1]) except IOError, e: - error_msg_and_die("Can't read '%s': %s" % (name, e)); + error_msg_and_die("Can't read '%s': %s" % (maps_path, e)) if __name__ == "__main__": progname = os.path.basename(sys.argv[0]) - help_text = _("Usage: %s [-o OUTFILE] -m PROC_PID_MAP_FILE") % progname + help_text = ("Usage: %s [-o OUTFILE] -m PROC_PID_MAP_FILE") % progname try: opts, args = getopt.getopt(sys.argv[1:], "o:m:h", ["help"]) except getopt.GetoptError, err: @@ -63,7 +57,7 @@ if __name__ == "__main__": memfile = arg if not memfile: - error_msg(_("MAP_FILE is not specified")) + error_msg("MAP_FILE is not specified") error_msg_and_die(help_text) try: @@ -81,10 +75,15 @@ if __name__ == "__main__": if outname: outfile = xopen(outname, "w") outname = None - outfile.write("%s %s (%s)\n" % (path, h[rpm.RPMTAG_NEVRA], h[rpm.RPMTAG_VENDOR])) + outfile.write("%s %s (%s) %s\n" % + (path, + h[rpm.RPMTAG_NEVRA], + h[rpm.RPMTAG_VENDOR], + h[rpm.RPMTAG_INSTALLTIME]) + ) except Exception, ex: - error_msg_and_die("Can't get the DSO list: %s", ex) + error_msg_and_die("Can't get the DSO list: %s" % ex) outfile.close() except: diff --git a/src/plugins/abrt-action-list-dsos.txt b/src/plugins/abrt-action-list-dsos.txt new file mode 100644 index 00000000..18d10708 --- /dev/null +++ b/src/plugins/abrt-action-list-dsos.txt @@ -0,0 +1,44 @@ +abrt-action-list-dsos(1) +====================== + +NAME +---- +abrt-action-list-dsos - Prints out DSO from mapped memory regions. + +SYNOPSIS +-------- +'abrt-action-list-dsos' [-o OUTFILE] -m PROC_PID_MAP_FILE + +DESCRIPTION +----------- +The tool reads a file containing the mapped memory regions. +Output is printed to 'stdout' or 'file'. + +Output format: + +------------ +%p %n %v %i +------------ +where + +'%p':: + path to library or binary file, which is mapped in memory region +'%n':: + name, version, release, architecture of package, where '%p' belongs +'%v':: + vendor of package +'%i':: + installation time + +OPTIONS +------- +-o OUTFILE:: + Output file, if not specified, it is printed to 'stdout' + +-m PROC_PID_MAP_FILE:: + File containing the mapped memory regions + +AUTHORS +------- +* ABRT team + diff --git a/src/plugins/abrt-action-mailx.c b/src/plugins/abrt-action-mailx.c index a4ee31cd..21180854 100644 --- a/src/plugins/abrt-action-mailx.c +++ b/src/plugins/abrt-action-mailx.c @@ -20,8 +20,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-mailx" - static void exec_and_feed_input(const char* text, char **args) { int pipein[2]; @@ -131,18 +129,16 @@ static void create_and_send_email( int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); const char *dump_dir_name = "."; const char *conf_file = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] -d DIR [-c CONFFILE]\n" + "\b [-v] -d DIR [-c CONFFILE]\n" "\n" - "Sends compressed tarball of dump directory DIR via email" + "Sends contents of a dump directory DIR via email" ); enum { OPT_v = 1 << 0, @@ -158,11 +154,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); map_string_h *settings = new_map_string(); if (conf_file) diff --git a/src/plugins/abrt-action-mailx.txt b/src/plugins/abrt-action-mailx.txt new file mode 100644 index 00000000..a12c2bf1 --- /dev/null +++ b/src/plugins/abrt-action-mailx.txt @@ -0,0 +1,91 @@ +abrt-action-mailx(1) +==================== + +NAME +---- +abrt-action-mailx - Sends contents of a dump directory via email. + +SYNOPSIS +-------- +'abrt-action-mailx' [-v] -d DIR [-c CONFFILE] + +DESCRIPTION +----------- +The tool reads a problem dump directory, composes an email message +from the directory contents, and uses mailx to send the message to +specified recipient. + +Properties of email messages can be specified in a configuration file, +and via environment variables. + +Configuration file +~~~~~~~~~~~~~~~~~~ +Configuration file contains entries in a format "Option = Value". + +The options are: + +'Subject':: + The subject of the email message. + +'EmailFrom':: + The sender of the email message. + +'EmailTo':: + The recipient of the email message. + +'SendBinaryData':: + Use yes/true/on/1 to attach all binary files from the dump + directory to the email. This can cause the emails to be very + large. + +Integration with ABRT events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +'abrt-action-mailx' can be used as a reporter, to allow users report +problems via email when they decide to do it. This usage is +pre-configured in /etc/abrt/events.d/mailx_events.conf: + +------------ +EVENT=report_Mailx abrt-action-mailx +------------ + +It can also be used to notify administrator automatically when a +problem happens. When this is desired, modify the event configuration +file to run the tool on the 'post-create' event: + +------------ +EVENT=post-create abrt-action-mailx +------------ + +OPTIONS +------- +-d DIR:: + Path to dump directory. + +-c CONFFILE:: + Path to configration file. When used in ABRT event system, the file + contains site-wide configuration. Users can change the values via + environment variables. + +ENVIRONMENT VARIABLES +--------------------- +Environment variables take precedence over values provided in +the configuration file. + +'Mailx_Subject':: + Subject of the email message. + +'Mailx_EmailFrom':: + Sender of the email message. + +'Mailx_EmailTo':: + Recipient of the email message. If nor the environment variable + nor the corresponding option is defined, the message is send to + "root@localhost". + +'Mailx_SendBinaryData':: + Use yes/true/on/1 to attach all binary files from the dump + directory to the email. + +AUTHORS +------- +* ABRT team diff --git a/src/plugins/abrt-action-print.c b/src/plugins/abrt-action-print.c index f76abf86..d433fa52 100644 --- a/src/plugins/abrt-action-print.c +++ b/src/plugins/abrt-action-print.c @@ -21,8 +21,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-print" - static const char *dump_dir_name = "."; static const char *output_file = NULL; static const char *append = "no"; @@ -30,13 +28,11 @@ static const char *open_mode = "w"; int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] -d DIR [-o FILE] [-a yes/no]\n" + "\b [-v] -d DIR [-o FILE] [-a yes/no] [-r]\n" "\n" "Prints problem information to standard output or FILE" ); @@ -58,11 +54,7 @@ int main(int argc, char **argv) }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); if (output_file) { diff --git a/src/plugins/abrt-action-print.txt b/src/plugins/abrt-action-print.txt new file mode 100644 index 00000000..200ea671 --- /dev/null +++ b/src/plugins/abrt-action-print.txt @@ -0,0 +1,63 @@ +abrt-action-print(1) +==================== + +NAME +---- +abrt-action-print - Prints problem information to standard output or FILE. + +SYNOPSIS +-------- +'abrt-action-print' [-v] [-d DIR] [-o FILE] [-a yes/no] [-r] + +DESCRIPTION +----------- +The tool reads dump directory DIR and prints its text representation +to stdout or to a specified FILE. + +Integration with ABRT events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +'abrt-action-print' can be used as a reporter, to allow users +to report problems by writinf them to a file. Example: + +------------ +EVENT=report abrt-action-print -o "${Log_File:-/tmp/abrt.log}" +------------ + +OPTIONS +------- +-d DIR:: + Path to dump directory. + +-v:: + Be more verbose. Can be given multiple times. + +-o FILE:: + Output file + +-a yes/no:: + If -o FILE is specified, controls whether FILE is appended to, or overwritten. + +-r:: + Add a recor to 'reported_to' in DIR which specifies that this problem + was reported. Some tools use this to differentiate between problems + which were and weren't yet reported. + +Output format +~~~~~~~~~~~~~ +The output is designed to be machine-parseable. The elements which have only +one line are printed in the form + + NAME:<whitespace>VALUE + +Elements which have more than one line are printed in the form + + NAME: + :LINE1 + :LINE2 + :LINE3 + +Output may contain empty lines for better readability. + +AUTHORS +------- +* ABRT team diff --git a/src/plugins/abrt-action-rhtsupport.c b/src/plugins/abrt-action-rhtsupport.c index fbdc7f7e..4616be3f 100644 --- a/src/plugins/abrt-action-rhtsupport.c +++ b/src/plugins/abrt-action-rhtsupport.c @@ -24,8 +24,6 @@ #include "abrt_rh_support.h" #include "parse_options.h" -#define PROGNAME "abrt-action-rhtsupport" - static void report_to_rhtsupport( const char *dump_dir_name, map_string_h *settings) @@ -266,9 +264,7 @@ static void report_to_rhtsupport( int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); map_string_h *settings = new_map_string(); const char *dump_dir_name = "."; @@ -276,7 +272,7 @@ int main(int argc, char **argv) /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] -c CONFFILE -d DIR\n" + "\b [-v] -c CONFFILE -d DIR\n" "\n" "Reports a problem to RHTSupport" ); @@ -294,11 +290,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); while (conf_file) { diff --git a/src/plugins/abrt-action-trim-files.c b/src/plugins/abrt-action-trim-files.c index dd7366ed..9f15038c 100644 --- a/src/plugins/abrt-action-trim-files.c +++ b/src/plugins/abrt-action-trim-files.c @@ -19,8 +19,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-trim-files" - static double get_dir_size(const char *dirname, char **worst_file, double *worst_file_size) @@ -141,9 +139,7 @@ static void delete_files(gpointer data, gpointer user_data_unused) int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); GList *dir_list = NULL; GList *file_list = NULL; @@ -151,7 +147,7 @@ int main(int argc, char **argv) /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] [-d SIZE:DIR]... [-f SIZE:DIR]... [-p DIR]\n" + "\b [-v] [-d SIZE:DIR]... [-f SIZE:DIR]... [-p DIR]\n" "\n" "Deletes dump dirs (-d) or files (-f) in DIRs until they are smaller than SIZE" ); @@ -169,17 +165,12 @@ int main(int argc, char **argv) OPT_STRING('p', NULL, &preserve , "DIR" , _("Preserve this dump dir")), OPT_END() }; - /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); argv += optind; if (argv[0] || !(dir_list || file_list)) show_usage_and_die(program_usage_string, program_options); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); g_list_foreach(dir_list, delete_dirs, preserve); g_list_foreach(file_list, delete_files, NULL); diff --git a/src/plugins/abrt-action-trim-files.txt b/src/plugins/abrt-action-trim-files.txt new file mode 100644 index 00000000..a8d8a50e --- /dev/null +++ b/src/plugins/abrt-action-trim-files.txt @@ -0,0 +1,30 @@ +abrt-action-trim-files(1) +========================= + +NAME +---- +abrt-action-trim-files - Deletes old dump directories or files +in specified directories until they are smaller than specified size. + +SYNOPSIS +-------- +'abrt-action-trim-files' [-v] [-d SIZE:DIR]... [-f SIZE:DIR]... [-p DIR] + +OPTIONS +------- +-v:: + Be more verbose. Can be given multiple times. + +-d SIZE:DIR:: + Delete dump directories in DIR + SIZE can be suffixed by k,m,g,t to specify kilo,mega,giga,terabytes. + +-f SIZE:DIR:: + Delete files in DIR + +-p DIR:: + Preserve this dump directory (never consider it for deletion) + +AUTHORS +------- +* ABRT team diff --git a/src/plugins/abrt-action-upload.c b/src/plugins/abrt-action-upload.c index 79347a18..06ae8b20 100644 --- a/src/plugins/abrt-action-upload.c +++ b/src/plugins/abrt-action-upload.c @@ -22,8 +22,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-action-upload" - //TODO: use this for better logging #if 0 /* "read local data from a file" callback */ @@ -232,9 +230,7 @@ static int create_and_upload_archive( int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); const char *dump_dir_name = "."; const char *conf_file = NULL; @@ -242,7 +238,7 @@ int main(int argc, char **argv) /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-v] -d DIR [-c CONFFILE] [-u URL]\n" + "\b [-v] -d DIR [-c CONFFILE] [-u URL]\n" "\n" "Uploads compressed tarball of dump directory DIR" ); @@ -262,11 +258,7 @@ int main(int argc, char **argv) }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - - char *pfx = getenv("ABRT_PROG_PREFIX"); - if (pfx && string_to_bool(pfx)) - msg_prefix = PROGNAME; + export_abrt_envvars(0); map_string_h *settings = new_map_string(); if (url) diff --git a/src/plugins/abrt-dump-oops.c b/src/plugins/abrt-dump-oops.c index f716c3db..c2879caa 100644 --- a/src/plugins/abrt-dump-oops.c +++ b/src/plugins/abrt-dump-oops.c @@ -23,8 +23,6 @@ #include "abrtlib.h" #include "parse_options.h" -#define PROGNAME "abrt-dump-oops" - static bool world_readable_dump = false; static const char *debug_dumps_dir = "."; @@ -560,13 +558,11 @@ static unsigned save_oops_to_dump_dir(GList *oops_list, unsigned oops_cnt) int main(int argc, char **argv) { - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); + abrt_init(argv); /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( - PROGNAME" [-vsrowx] [-d DIR] FILE\n" + "\b [-vsrowx] [-d DIR] FILE\n" "\n" "Extract oops from syslog/dmesg file" ); @@ -595,11 +591,11 @@ int main(int argc, char **argv) }; unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); - putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); - msg_prefix = PROGNAME; - if ((opts & OPT_s) - || getenv("ABRT_SYSLOG") - ) { + export_abrt_envvars(0); + + msg_prefix = g_progname; + if ((opts & OPT_s) || getenv("ABRT_SYSLOG")) + { openlog(msg_prefix, 0, LOG_DAEMON); logmode = LOGMODE_SYSLOG; } diff --git a/src/plugins/abrt-retrace-client.c b/src/plugins/abrt-retrace-client.c index d49f9cfa..524fa186 100644 --- a/src/plugins/abrt-retrace-client.c +++ b/src/plugins/abrt-retrace-client.c @@ -25,6 +25,7 @@ #include <sslproto.h> #include <sslerr.h> #include <secerr.h> +#include <secmod.h> struct retrace_settings { @@ -38,7 +39,7 @@ struct retrace_settings static const char *dump_dir_name = NULL; static const char *coredump = NULL; -static const char *url = "retrace01.fedoraproject.org"; +static const char *url = "retrace.fedoraproject.org"; static const char *required_files[] = { FILENAME_COREDUMP, FILENAME_EXECUTABLE, FILENAME_PACKAGE, @@ -102,7 +103,7 @@ static int create_archive(bool unlink_temp) xmove_fd(tar_xz_pipe[0], STDIN_FILENO); xdup2(tempfd, STDOUT_FILENO); execvp(xz_args[0], (char * const*)xz_args); - perror_msg("Can't execute '%s'", xz_args[0]); + perror_msg("Can't execute '%s'", xz_args[0]); } close(tar_xz_pipe[0]); @@ -129,7 +130,7 @@ static int create_archive(bool unlink_temp) xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO); xmove_fd(tar_xz_pipe[1], STDOUT_FILENO); execvp(tar_args[0], (char * const*)tar_args); - perror_msg("Can't execute '%s'", tar_args[0]); + perror_msg("Can't execute '%s'", tar_args[0]); } close(tar_xz_pipe[1]); @@ -208,6 +209,28 @@ static const char *ssl_get_configdir() return NULL; } +static PK11GenericObject *nss_load_cacert(const char *filename) +{ + PK11SlotInfo *slot = PK11_FindSlotByName("PEM Token #0"); + if (!slot) + error_msg_and_die("Failed to get slot 'PEM Token #0': %d.", PORT_GetError()); + + CK_ATTRIBUTE template[4]; + CK_OBJECT_CLASS class = CKO_CERTIFICATE; + +#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ + (x)->pValue=(v); (x)->ulValueLen = (l) + + PK11_SETATTRS(&template[0], CKA_CLASS, &class, sizeof(class)); + CK_BBOOL cktrue = CK_TRUE; + PK11_SETATTRS(&template[1], CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); + PK11_SETATTRS(&template[2], CKA_LABEL, (unsigned char*)filename, strlen(filename)+1); + PK11_SETATTRS(&template[3], CKA_TRUST, &cktrue, sizeof(CK_BBOOL)); + PK11GenericObject *cert = PK11_CreateGenericObject(slot, template, 4, PR_FALSE); + PK11_FreeSlot(slot); + return cert; +} + static char *ssl_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) { return NULL; @@ -216,13 +239,6 @@ static char *ssl_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) static void ssl_connect(PRFileDesc **tcp_sock, PRFileDesc **ssl_sock) { - const char *configdir = ssl_get_configdir(); - if (configdir) - NSS_Initialize(configdir, "", "", "", NSS_INIT_READONLY); - else - NSS_NoDB_Init(NULL); - PK11_SetPasswordFunc(ssl_get_password); - NSS_SetDomesticPolicy(); *tcp_sock = PR_NewTCPSocket(); if (!*tcp_sock) error_msg_and_die("Failed to create a TCP socket"); @@ -253,6 +269,7 @@ static void ssl_connect(PRFileDesc **tcp_sock, error_msg_and_die("Failed to enable client handshake to SSL socket."); if (SECSuccess != SSL_OptionSet(*ssl_sock, SSL_ENABLE_TLS, PR_TRUE)) error_msg_and_die("Failed to enable client handshake to SSL socket."); + sec_status = SSL_SetURL(*ssl_sock, url); if (SECSuccess != sec_status) { @@ -322,14 +339,6 @@ static void ssl_disconnect(PRFileDesc *ssl_sock) PRStatus pr_status = PR_Close(ssl_sock); if (PR_SUCCESS != pr_status) error_msg("Failed to close SSL socket."); - - SSL_ClearSessionCache(); - - SECStatus sec_status = NSS_Shutdown(); - if (SECSuccess != sec_status) - error_msg("Failed to shutdown NSS."); - - PR_Cleanup(); } /** @@ -366,10 +375,12 @@ static char *http_get_header_value(const char *message, */ static char *http_get_body(const char *message) { - char *body = strstr(message, "\r\n\r\n") + strlen("\r\n\r\n"); + char *body = strstr(message, "\r\n\r\n"); if (!body) return NULL; + body += strlen("\r\n\r\n"); + while (*body == ' ') ++body; @@ -642,11 +653,17 @@ static int create(bool delete_temp_archive, close(tempfd); /* Read the HTTP header of the response from server. */ char *http_response = tcp_read_response(tcp_sock); + char *http_body = http_get_body(http_response); + if (!http_body) + error_msg_and_die("Invalid response from server: missing HTTP message body."); if (http_show_headers) http_print_headers(stderr, http_response); int response_code = http_get_response_code(http_response); - if (response_code != 201) - error_msg_and_die("Unexpected HTTP response from server: %d\n%s", response_code, http_response); + if (response_code == 500 || response_code == 507) + error_msg_and_die("There is a problem on the server side: %s", http_body); + else if (response_code != 201) + error_msg_and_die("Unexpected HTTP response from server: %d\n%s", response_code, http_body); + free(http_body); *task_id = http_get_header_value(http_response, "X-Task-Id"); if (!*task_id) error_msg_and_die("Invalid response from server: missing X-Task-Id"); @@ -670,6 +687,7 @@ static int run_create(bool delete_temp_archive) return 0; } +/* Caller must free task_status and status_message */ static void status(const char *task_id, const char *task_password, char **task_status, @@ -695,23 +713,21 @@ static void status(const char *task_id, } strbuf_free(http_request); char *http_response = tcp_read_response(tcp_sock); + char *http_body = http_get_body(http_response); + if (!*http_body) + error_msg_and_die("Invalid response from server: missing body"); if (http_show_headers) http_print_headers(stderr, http_response); int response_code = http_get_response_code(http_response); if (response_code != 200) { error_msg_and_die("Unexpected HTTP response from server: %d\n%s", - response_code, http_response); + response_code, http_body); } *task_status = http_get_header_value(http_response, "X-Task-Status"); if (!*task_status) error_msg_and_die("Invalid response from server: missing X-Task-Status"); - *status_message = http_get_body(http_response); - if (!*status_message) - { - free(*task_status); - error_msg_and_die("Invalid response from server: missing body"); - } + *status_message = http_body; free(http_response); ssl_disconnect(ssl_sock); } @@ -726,6 +742,7 @@ static void run_status(const char *task_id, const char *task_password) free(status_message); } +/* Caller must free backtrace */ static void backtrace(const char *task_id, const char *task_password, char **backtrace) { @@ -749,30 +766,18 @@ static void backtrace(const char *task_id, const char *task_password, } strbuf_free(http_request); char *http_response = tcp_read_response(tcp_sock); + char *http_body = http_get_body(http_response); + if (!http_body) + error_msg_and_die("Invalid response from server: missing HTTP message body."); if (http_show_headers) http_print_headers(stderr, http_response); int response_code = http_get_response_code(http_response); if (response_code != 200) { error_msg_and_die("Unexpected HTTP response from server: %d\n%s", - response_code, http_response); - } - char *headers_end = strstr(http_response, "\r\n\r\n"); - if (!headers_end) - error_msg_and_die("Invalid response from server: missing HTTP message body."); - int length = strlen(http_response) + (headers_end - http_response) + strlen("\r\n\r\n"); - /* Slightly more space than needed might be allocated, because - * '\r' characters are not copied to the backtrace. */ - *backtrace = xmalloc(length); - char *b = *backtrace; - char *c; - for (c = headers_end + strlen("\r\n\r\n"); *c; ++c) - { - if (*c == '\r') - continue; - *b = *c; - ++b; + response_code, http_body); } + *backtrace = http_body; free(http_response); ssl_disconnect(ssl_sock); } @@ -807,24 +812,19 @@ static void run_log(const char *task_id, const char *task_password) } strbuf_free(http_request); char *http_response = tcp_read_response(tcp_sock); + char *http_body = http_get_body(http_response); + if (!http_body) + error_msg_and_die("Invalid response from server: missing HTTP message body."); if (http_show_headers) http_print_headers(stderr, http_response); int response_code = http_get_response_code(http_response); if (response_code != 200) { error_msg_and_die("Unexpected HTTP response from server: %d\n%s", - response_code, http_response); - } - char *headers_end = strstr(http_response, "\r\n\r\n"); - char *c; - if (!headers_end) - error_msg_and_die("Invalid response from server: missing HTTP message body."); - for (c = headers_end + 4; *c; ++c) - { - if (*c == '\r') - continue; - putc(*c, stdout); + response_code, http_body); } + puts(http_body); + free(http_body); free(http_response); ssl_disconnect(ssl_sock); } @@ -844,6 +844,7 @@ static int run_batch(bool delete_temp_archive) sleep(10); status(task_id, task_password, &task_status, &status_message); puts(status_message); + fflush(stdout); } if (0 == strcmp(task_status, "FINISHED_SUCCESS")) { @@ -875,6 +876,8 @@ static int run_batch(bool delete_temp_archive) int main(int argc, char **argv) { + abrt_init(argv); + const char *task_id = NULL; const char *task_password = NULL; @@ -922,9 +925,6 @@ int main(int argc, char **argv) const char usage[] = "abrt-retrace-client <operation> [options]\n" "Operations: create/status/backtrace/log/batch"; - char *env_verbose = getenv("ABRT_VERBOSE"); - if (env_verbose) - g_verbose = atoi(env_verbose); char *env_url = getenv("RETRACE_SERVER_URL"); if (env_url) url = env_url; @@ -942,17 +942,40 @@ int main(int argc, char **argv) show_usage_and_die(usage, options); ssl_allow_insecure = opts & OPT_insecure; http_show_headers = opts & OPT_headers; + + /* Initialize NSS */ + SECStatus sec_status; + const char *configdir = ssl_get_configdir(); + if (configdir) + sec_status = NSS_Initialize(configdir, "", "", "", NSS_INIT_READONLY); + else + sec_status = NSS_NoDB_Init(NULL); + if (SECSuccess != sec_status) + error_msg_and_die("Failed to initialize NSS."); + + char *user_module = xstrdup("library=libnsspem.so name=PEM"); + SECMODModule* mod = SECMOD_LoadUserModule(user_module, NULL, PR_FALSE); + free(user_module); + if (!mod || !mod->loaded) + error_msg_and_die("Failed to initialize security module."); + + PK11GenericObject *cert = nss_load_cacert("/etc/pki/tls/certs/ca-bundle.crt"); + PK11_SetPasswordFunc(ssl_get_password); + NSS_SetDomesticPolicy(); + + /* Run the desired operation. */ + int result = 0; if (0 == strcasecmp(operation, "create")) { if (!dump_dir_name && !coredump) error_msg_and_die("Either dump directory or coredump is needed."); - return run_create(0 == (opts & OPT_no_unlink)); + result = run_create(0 == (opts & OPT_no_unlink)); } else if (0 == strcasecmp(operation, "batch")) { if (!dump_dir_name && !coredump) error_msg_and_die("Either dump directory or coredump is needed."); - return run_batch(0 == (opts & OPT_no_unlink)); + result = run_batch(0 == (opts & OPT_no_unlink)); } else if (0 == strcasecmp(operation, "status")) { @@ -980,5 +1003,17 @@ int main(int argc, char **argv) } else error_msg_and_die("Unknown operation: %s", operation); - return 0; + + /* Shutdown NSS. */ + SSL_ClearSessionCache(); + PK11_DestroyGenericObject(cert); + SECMOD_UnloadUserModule(mod); + SECMOD_DestroyModule(mod); + sec_status = NSS_Shutdown(); + if (SECSuccess != sec_status) + error_msg("Failed to shutdown NSS."); + + PR_Cleanup(); + + return result; } diff --git a/src/plugins/abrt_rh_support.c b/src/plugins/abrt_rh_support.c index 9a48485b..b83f041e 100644 --- a/src/plugins/abrt_rh_support.c +++ b/src/plugins/abrt_rh_support.c @@ -421,7 +421,7 @@ send_report_to_new_case(const char* baseURL, atch_state->http_resp_code, errmsg ? ": " : "", errmsg ? errmsg : "" - ); + ); break; case 200: diff --git a/src/plugins/analyze_LocalGDB.xml.in b/src/plugins/analyze_LocalGDB.xml.in index 925f2c41..4f7ccce1 100644 --- a/src/plugins/analyze_LocalGDB.xml.in +++ b/src/plugins/analyze_LocalGDB.xml.in @@ -5,4 +5,5 @@ <_long-description>Needs to downloads debuginfo packages, which might take significant time, and take up disk space. However, unlike RetraceServer, doesn't send coredump to remote machines. </_long-description> + <creates-elements>backtrace</creates-elements> </event> diff --git a/src/plugins/analyze_RetraceServer.xml.in b/src/plugins/analyze_RetraceServer.xml.in index f91bf763..d2072db5 100644 --- a/src/plugins/analyze_RetraceServer.xml.in +++ b/src/plugins/analyze_RetraceServer.xml.in @@ -6,10 +6,11 @@ Pros: no need for debuginfo downloads. Retrace server's database of debuginfos is more complete. Retrace server may generate better backtraces. Cons: coredump you upload contains all the data from the crashed program, including your private data, if any. </_long-description> + <creates-elements>backtrace</creates-elements> <options> <option type="text" name="RETRACE_SERVER_URL"> <_label>Retrace server URL</_label> - <default-value>retrace01.fedoraproject.org</default-value> + <default-value>retrace.fedoraproject.org</default-value> <allow-empty>no</allow-empty> <_description>Address of the retrace server</_description> </option> diff --git a/src/plugins/analyze_xsession_errors.xml.in b/src/plugins/analyze_xsession_errors.xml.in new file mode 100644 index 00000000..9f7a46bf --- /dev/null +++ b/src/plugins/analyze_xsession_errors.xml.in @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<event> + <name>Collect .xsession-errors</name> + <_description>Save relevant lines from ~/.xsession-errors file</_description> + <_long-description> + Scans through ~/.xsession-errors file and saves those lines which contain executable's name. + The result is saved as 'xsession_errors' element. + </_long-description> + <creates-elements>xsession_errors</creates-elements> +</event> diff --git a/src/plugins/ccpp_events.conf b/src/plugins/ccpp_events.conf index 1e111f04..b56601e9 100644 --- a/src/plugins/ccpp_events.conf +++ b/src/plugins/ccpp_events.conf @@ -1,33 +1,37 @@ EVENT=post-create analyzer=CCpp - abrt-action-analyze-c && - abrt-action-list-dsos.py -m maps -o dsos + abrt-action-analyze-c && + abrt-action-list-dsos.py -m maps -o dsos && + ( + # Try to save relevant log lines. + # Can't do it as analyzer step, non-root can't read log. + # It's not an error if /var/log/messages isn't readable: + test -f /var/log/messages || exit 0 + test -r /var/log/messages || exit 0 + executable=`cat executable` && + base_executable=${executable##*/} && + grep -F -e "$base_executable" /var/log/messages | tail -999 >var_log_messages && + echo "Element 'var_log_messages' saved" + ) + +EVENT=analyze_xsession_errors analyzer=CCpp + test -f ~/.xsession-errors || { echo "No ~/.xsession-errors"; exit 1; } + test -r ~/.xsession-errors || { echo "Can't read ~/.xsession-errors"; exit 1; } + executable=`cat executable` && + base_executable=${executable##*/} && + grep -F -e "$base_executable" ~/.xsession-errors | tail -999 >xsession_errors && + echo "Element 'xsession_errors' saved" -# We run analyze_foo steps only if backtrace is empty (not yet generated): # TODO: can we still specify additional directories to search for debuginfos, # or was this ability lost with move to python installer? -EVENT=analyze_LocalGDB analyzer=CCpp backtrace= - abrt-action-trim-files -f 4096m:/var/cache/abrt-di && - abrt-action-analyze-core.py --core=coredump -o build_ids && - abrt-action-install-debuginfo && - abrt-action-generate-backtrace && - abrt-action-analyze-backtrace - -EVENT=analyze_RetraceServer analyzer=CCpp backtrace= - abrt-retrace-client batch -k --dir "$DUMP_DIR" && - abrt-action-analyze-backtrace - -# Same as "analyze", but executed when user requests "refresh" in GUI -# It doesn't check that backtrace is empty: -EVENT=reanalyze_LocalGDB analyzer=CCpp - abrt-action-trim-files -f 4096m:/var/cache/abrt-di && - abrt-action-analyze-core.py --core=coredump -o build_ids && - abrt-action-install-debuginfo && - abrt-action-generate-backtrace && - abrt-action-analyze-backtrace +EVENT=analyze_LocalGDB analyzer=CCpp + abrt-action-analyze-core.py --core=coredump -o build_ids && + abrt-action-install-debuginfo --size_mb=4096 && + abrt-action-generate-backtrace && + abrt-action-analyze-backtrace -EVENT=reanalyze_RetraceServer analyzer=CCpp - abrt-retrace-client batch -k --dir "$DUMP_DIR" && - abrt-action-analyze-backtrace +EVENT=analyze_RetraceServer analyzer=CCpp + abrt-retrace-client batch --dir "$DUMP_DIR" && + abrt-action-analyze-backtrace EVENT=report_Bugzilla analyzer=CCpp - abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf + abrt-action-bugzilla -c /etc/abrt/plugins/Bugzilla.conf diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c new file mode 100644 index 00000000..90587e5e --- /dev/null +++ b/src/plugins/rhbz.c @@ -0,0 +1,482 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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 "abrtlib.h" +#include "rhbz.h" + +#define MAX_HOPS 5 + +struct bug_info *new_bug_info() +{ + struct bug_info *bi = xzalloc(sizeof(struct bug_info)); + bi->bi_dup_id = -1; + + return bi; +} + +void free_bug_info(struct bug_info *bi) +{ + if (!bi) + return; + + free((void*)bi->bi_status); + free((void*)bi->bi_resolution); + free((void*)bi->bi_reporter); + free((void*)bi->bi_product); + + list_free_with_free(bi->bi_cc_list); + + bi->bi_status = NULL; + bi->bi_resolution = NULL; + bi->bi_reporter = NULL; + bi->bi_product = NULL; + + bi->bi_cc_list = NULL; + + free(bi); +} + +void rhbz_login(struct abrt_xmlrpc *ax, const char* login, const char* passwd) +{ + xmlrpc_value* result = abrt_xmlrpc_call(ax, "User.login", "({s:s,s:s})", + "login", login, "password", passwd); + +//TODO: with URL like http://bugzilla.redhat.com (that is, with http: instead of https:) +//we are getting this error: +//Logging into Bugzilla at http://bugzilla.redhat.com +//Can't login. Server said: HTTP response code is 301, not 200 +//But this is a 301 redirect! We _can_ follow it if we configure curl to understand that! + xmlrpc_DECREF(result); +} + +xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *component, + const char *product, const char *duphash) +{ + char *query = NULL; + if (!product) + query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\"", component, duphash); + else + query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"", + component, duphash, product); + + VERB3 log("search for '%s'", query); + xmlrpc_value *ret = abrt_xmlrpc_call(ax, "Bug.search", "({s:s})", + "quicksearch", query); + free(query); + return ret; +} + +xmlrpc_value *rhbz_get_member(const char *member, xmlrpc_value *xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value *value = NULL; + /* The xmlrpc_struct_find_value functions consider "not found" to be + * a normal result. If a member of the structure with the specified key + * exists, it returns it as a handle to an xmlrpc_value. If not, it returns + * NULL in place of that handle. + */ + xmlrpc_struct_find_value(&env, xml, member, &value); + if (env.fault_occurred) + abrt_xmlrpc_error(&env); + + return value; +} + +/* The only way this can fail is if arrayP is not actually an array XML-RPC + * value. So it is usually not worth checking *envP. + * die or return size of array + */ +int rhbz_array_size(xmlrpc_value *xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + int size = xmlrpc_array_size(&env, xml); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + return size; +} + +/* die or return bug id; each bug must have bug id otherwise xml is corrupted */ +int rhbz_bug_id(xmlrpc_value* xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value *item = NULL; + xmlrpc_value *bug = NULL; + int bug_id = -1;; + + xmlrpc_array_read_item(&env, xml, 0, &item); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + bug = rhbz_get_member("bug_id", item); + xmlrpc_DECREF(item); + if (!bug) + abrt_xmlrpc_die(&env); + + xmlrpc_read_int(&env, bug, &bug_id); + xmlrpc_DECREF(bug); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + VERB3 log("found bug_id %i", bug_id); + return bug_id; +} + +/* die when mandatory value is missing (set flag RHBZ_MANDATORY_MEMB) + * or return appropriate string or NULL when fail; + */ +// TODO: npajkovs: add flag to read xmlrpc_read_array_item first +void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value *member = rhbz_get_member(memb, xml); + + const char *string = NULL; + + if (!member) + goto die; + + if (IS_READ_STR(flags)) + { + xmlrpc_read_string(&env, member, &string); + xmlrpc_DECREF(member); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + if (!*string) + goto die; + + VERB3 log("found %s: '%s'", memb, string); + return (void*)string; + } + + { + if (IS_READ_INT(flags)) + { + int *integer = xmalloc(sizeof(int)); + xmlrpc_read_int(&env, member, integer); + xmlrpc_DECREF(member); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + VERB3 log("found %s: '%i'", memb, *integer); + return (void*)integer; + } + } +die: + free((void*)string); + if (IS_MANDATORY(flags)) + error_msg_and_die(_("Looks like corrupted xml response, because '%s'" + " member is missing."), memb); + + return NULL; +} + +GList *rhbz_bug_cc(xmlrpc_value* result_xml) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* cc_member = rhbz_get_member("cc", result_xml); + if (!cc_member) + return NULL; + + int array_size = rhbz_array_size(cc_member); + + VERB3 log("count members on cc %i", array_size); + GList *cc_list = NULL; + + for (int i = 0; i < array_size; ++i) + { + xmlrpc_value* item = NULL; + xmlrpc_array_read_item(&env, cc_member, i, &item); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + if (!item) + continue; + + const char* cc = NULL; + xmlrpc_read_string(&env, item, &cc); + xmlrpc_DECREF(item); + if (env.fault_occurred) + abrt_xmlrpc_die(&env); + + if (*cc != '\0') + { + cc_list = g_list_append(cc_list, (char*)cc); + VERB3 log("member on cc is %s", cc); + continue; + } + free((char*)cc); + } + xmlrpc_DECREF(cc_member); + return cc_list; +} + +struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id) +{ + struct bug_info *bz = new_bug_info(); + xmlrpc_value *xml_bug_response = abrt_xmlrpc_call(ax, "bugzilla.getBug", + "(i)", bug_id); + + int *ret = (int*)rhbz_bug_read_item("bug_id", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_INT); + bz->bi_id = *ret; + free(ret); + bz->bi_product = rhbz_bug_read_item("product", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); + bz->bi_reporter = rhbz_bug_read_item("reporter", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); + bz->bi_status = rhbz_bug_read_item("bug_status", xml_bug_response, + RHBZ_MANDATORY_MEMB | RHBZ_READ_STR); + bz->bi_resolution = rhbz_bug_read_item("resolution", xml_bug_response, + RHBZ_READ_STR); + + if (strcmp(bz->bi_status, "CLOSED") == 0 && !bz->bi_resolution) + error_msg_and_die(_("Bug %i is CLOSED, but it has no RESOLUTION"), bz->bi_id); + + ret = (int*)rhbz_bug_read_item("dup_id", xml_bug_response, + RHBZ_READ_INT); + if (strcmp(bz->bi_status, "CLOSED") == 0 + && strcmp(bz->bi_resolution, "DUPLICATE") == 0 + && !ret) + { + error_msg_and_die(_("Bug %i is CLOSED as DUPLICATE, but it has no DUP_ID"), + bz->bi_id); + } + + bz->bi_dup_id = (ret) ? *ret: -1; + free(ret); + + bz->bi_cc_list = rhbz_bug_cc(xml_bug_response); + + xmlrpc_DECREF(xml_bug_response); + + return bz; +} + +/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */ +int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data, + int depend_on_bug) +{ + const char *package = get_problem_item_content_or_NULL(problem_data, + FILENAME_PACKAGE); + const char *component = get_problem_item_content_or_NULL(problem_data, + FILENAME_COMPONENT); + const char *release = get_problem_item_content_or_NULL(problem_data, + FILENAME_OS_RELEASE); + if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */ + release = get_problem_item_content_or_NULL(problem_data, "release"); + const char *arch = get_problem_item_content_or_NULL(problem_data, + FILENAME_ARCHITECTURE); + const char *duphash = get_problem_item_content_or_NULL(problem_data, + FILENAME_DUPHASH); + const char *reason = get_problem_item_content_or_NULL(problem_data, + FILENAME_REASON); + const char *function = get_problem_item_content_or_NULL(problem_data, + FILENAME_CRASH_FUNCTION); + const char *analyzer = get_problem_item_content_or_NULL(problem_data, + FILENAME_ANALYZER); + const char *tainted_str = get_problem_item_content_or_NULL(problem_data, + FILENAME_TAINTED); + + struct strbuf *buf_summary = strbuf_new(); + strbuf_append_strf(buf_summary, "[abrt] %s", package); + + if (function != NULL && strlen(function) < 30) + strbuf_append_strf(buf_summary, ": %s", function); + + if (reason != NULL) + strbuf_append_strf(buf_summary, ": %s", reason); + + if (tainted_str && analyzer + && (strcmp(analyzer, "Kerneloops") == 0) + ) { + //TODO: fix me; basically it doesn't work as it suppose to work + // I will fix it immediately when this patch land into abrt git + /* + unsigned long tainted = xatoi_positive(tainted_str); + const char *tainted_warning = tainted_string(tainted); + if (tainted_warning) + strbuf_append_strf(buf_summary, ": TAINTED %s", tainted_warning); + */ + } + + char *status_whiteboard = xasprintf("abrt_hash:%s", duphash); + + char *bz_dsc = make_description_bz(problem_data); + char *full_dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc); + free(bz_dsc); + + char *product = NULL; + char *version = NULL; + parse_release_for_bz(release, &product, &version); + + xmlrpc_value* result = NULL; + char *summary = strbuf_free_nobuf(buf_summary); + if (depend_on_bug > -1) + { + result = abrt_xmlrpc_call(ax, "Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:i})", + "product", product, + "component", component, + "version", version, + "summary", summary, + "description", full_dsc, + "status_whiteboard", status_whiteboard, + "platform", arch, + "dependson", depend_on_bug); + } + else + { + result = abrt_xmlrpc_call(ax, "Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})", + "product", product, + "component", component, + "version", version, + "summary", summary, + "description", full_dsc, + "status_whiteboard", status_whiteboard, + "platform", arch); + } + free(status_whiteboard); + free(product); + free(version); + free(summary); + free(full_dsc); + + if (!result) + return -1; + + int *r = rhbz_bug_read_item("id", result, RHBZ_MANDATORY_MEMB | RHBZ_READ_INT); + xmlrpc_DECREF(result); + int new_bug_id = *r; + free(r); + + log(_("New bug id: %i"), new_bug_id); + return new_bug_id; +} + +/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */ +int rhbz_attachment(struct abrt_xmlrpc *ax, const char *filename, + const char *bug_id, const char *data) +{ + char *encoded64 = encode_base64(data, strlen(data)); + char *fn = xasprintf("File: %s", filename); + xmlrpc_value* result; + result= abrt_xmlrpc_call(ax, "bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s})", + bug_id, + "description", fn, + "filename", filename, + "contenttype", "text/plain", + "data", encoded64); + free(encoded64); + free(fn); + if (!result) + return -1; + + xmlrpc_DECREF(result); + + return 0; +} + +/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */ +int rhbz_attachments(struct abrt_xmlrpc *ax, const char *bug_id, + problem_data_t *problem_data) +{ + GHashTableIter iter; + char *name; + struct problem_item *value; + g_hash_table_iter_init(&iter, problem_data); + while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) + { + const char *content = value->content; + + // We were special-casing FILENAME_BACKTRACE here, but karel says + // he can retrieve it in inlined form from comments too. + if ((value->flags & CD_FLAG_TXT) + && (strlen(content) > CD_TEXT_ATT_SIZE /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/) + ) { + /* check if the attachment failed and try it once more */ + rhbz_attachment(ax, name, bug_id, content); + } + } + + return 0; +} + +void rhbz_logout(struct abrt_xmlrpc *ax) +{ + xmlrpc_value* result = abrt_xmlrpc_call(ax, "User.logout", "(s)", ""); + if (result) + xmlrpc_DECREF(result); +} + +struct bug_info *rhbz_find_origin_bug_closed_duplicate(struct abrt_xmlrpc *ax, + struct bug_info *bi) +{ + struct bug_info *bi_tmp = new_bug_info(); + bi_tmp->bi_id = bi->bi_id; + bi_tmp->bi_dup_id = bi->bi_dup_id; + + for (int ii = 0; ii <= MAX_HOPS; ii++) + { + if (ii == MAX_HOPS) + error_msg_and_die(_("Bugzilla couldn't find parent of bug %d"), bi->bi_id); + + log("Bug %d is a duplicate, using parent bug %d", bi_tmp->bi_id, bi_tmp->bi_dup_id); + int bug_id = bi_tmp->bi_dup_id; + + free_bug_info(bi_tmp); + bi_tmp = rhbz_bug_info(ax, bug_id); + + // found a bug which is not CLOSED as DUPLICATE + if (bi_tmp->bi_dup_id == -1) + break; + } + + return bi_tmp; +} + +/* suppress mail notify by {s:i} (nomail:1) */ +void rhbz_mail_to_cc(struct abrt_xmlrpc *ax, int bug_id, const char *mail) +{ + xmlrpc_value *result = abrt_xmlrpc_call(ax, "Bug.update", "({s:i,s:{s:(s)}})", + "ids", bug_id, "updates", "add_cc", mail); + if (result) + xmlrpc_DECREF(result); +} + +void rhbz_add_comment(struct abrt_xmlrpc *ax, int bug_id, const char *comment, + int is_private) +{ + xmlrpc_value *result = abrt_xmlrpc_call(ax, "Bug.add_comment", "({s:i,s:s,s:b})", + "id", bug_id, + "comment", comment, + "private", is_private); + if (result) + xmlrpc_DECREF(result); +} diff --git a/src/plugins/rhbz.h b/src/plugins/rhbz.h new file mode 100644 index 00000000..73d76f0a --- /dev/null +++ b/src/plugins/rhbz.h @@ -0,0 +1,100 @@ +/* + Copyright (C) 2011 ABRT team + Copyright (C) 2011 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. +*/ + +#ifndef RHBZ_H +#define RHBZ_H + +/* include/stdint.h: typedef int int32_t; + * include/xmlrpc-c/base.h: typedef int32_t xmlrpc_int32; + */ + +#include "abrt_xmlrpc.h" +#include "abrt_problem_data.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + RHBZ_MANDATORY_MEMB = (1 << 0), + RHBZ_READ_STR = (1 << 1), + RHBZ_READ_INT = (1 << 2), +}; + +#define IS_MANDATORY(flags) ((flags) & RHBZ_MANDATORY_MEMB) +#define IS_READ_STR(flags) ((flags) & RHBZ_READ_STR) +#define IS_READ_INT(flags) ((flags) & RHBZ_READ_INT) + +struct bug_info { + int bi_id; + int bi_dup_id; + + const char *bi_status; + const char *bi_resolution; + const char *bi_reporter; + const char *bi_product; + + GList *bi_cc_list; +}; + +struct bug_info *new_bug_info(); +void free_bug_info(struct bug_info *bz); + +void rhbz_login(struct abrt_xmlrpc *ax, const char *login, const char *passwd); + +void rhbz_mail_to_cc(struct abrt_xmlrpc *ax, int bug_id, const char *mail); + +void rhbz_add_comment(struct abrt_xmlrpc *ax, int bug_id, const char *comment, + int is_private); + +void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags); + +void rhbz_logout(struct abrt_xmlrpc *ax); + +xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *component, + const char *release, const char *duphash); + +xmlrpc_value *rhbz_get_member(const char *member, xmlrpc_value *xml); + +int rhbz_array_size(xmlrpc_value *xml); + +int rhbz_bug_id(xmlrpc_value *xml); + +int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data, + int depend_on_bug); + +int rhbz_attachments(struct abrt_xmlrpc *ax, const char *bug_id, + problem_data_t *problem_data); + +int rhbz_attachment(struct abrt_xmlrpc *ax, const char *filename, + const char *bug_id, const char *data); + +GList *rhbz_bug_cc(xmlrpc_value *result_xml); + +struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id); + + +struct bug_info *rhbz_find_origin_bug_closed_duplicate(struct abrt_xmlrpc *ax, + struct bug_info *bi); + +#ifdef __cplusplus +} +#endif + +#endif |