summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKarel Klic <kklic@redhat.com>2011-05-13 18:32:06 +0200
committerKarel Klic <kklic@redhat.com>2011-05-13 18:32:06 +0200
commite16a562d8616d2ac55f58b091f95acd5b3174229 (patch)
treebf5c97a4ab4d4f792c436d4ed93a1c36cd381c3b /src
parent15473d2a5ecfd20bd8cc3982e703fcedd51289f8 (diff)
parente123c5f3b4bdd10f3b495a4a948f6c452ed6205f (diff)
merge
Diffstat (limited to 'src')
-rw-r--r--src/applet/applet.c2
-rw-r--r--src/btparser/normalize.c20
-rw-r--r--src/cli/Makefile.am49
-rw-r--r--src/cli/abrt-cli.158
-rw-r--r--src/cli/abrt-cli.txt75
-rw-r--r--src/cli/abrt-handle-crashdump.c (renamed from src/daemon/abrt-handle-crashdump.c)16
-rw-r--r--src/cli/cli.c299
-rw-r--r--src/cli/report.c82
-rw-r--r--src/daemon/Makefile.am26
-rw-r--r--src/daemon/abrt-action-save-package-data.c14
-rw-r--r--src/daemon/abrt-server.c15
-rw-r--r--src/daemon/abrtd.c19
-rw-r--r--src/daemon/smart_event.conf38
-rw-r--r--src/gui-gtk/abrt-gtk.c12
-rw-r--r--src/gui-gtk/main.c44
-rw-r--r--src/gui-wizard-gtk/main.c41
-rw-r--r--src/gui-wizard-gtk/wizard.c275
-rw-r--r--src/gui-wizard-gtk/wizard.glade98
-rw-r--r--src/gui-wizard-gtk/wizard.h2
-rw-r--r--src/include/abrtlib.h3
-rw-r--r--src/include/report/event_config.h1
-rw-r--r--src/lib/Makefile.am2
-rw-r--r--src/lib/abrt_curl.c50
-rw-r--r--src/lib/abrt_xmlrpc.c137
-rw-r--r--src/lib/abrt_xmlrpc.cpp102
-rw-r--r--src/lib/abrt_xmlrpc.h39
-rw-r--r--src/lib/event_config.c1
-rw-r--r--src/lib/event_xml_parser.c24
-rw-r--r--src/lib/hooklib.c2
-rw-r--r--src/lib/hooklib.h2
-rw-r--r--src/lib/parse_options.c51
-rw-r--r--src/lib/parse_options.h17
-rw-r--r--src/lib/read_write.c2
-rw-r--r--src/plugins/Makefile.am42
-rw-r--r--src/plugins/abrt-Bugzilla.743
-rw-r--r--src/plugins/abrt-Logger.744
-rw-r--r--src/plugins/abrt-Mailx.757
-rw-r--r--src/plugins/abrt-action-analyze-backtrace.c14
-rw-r--r--src/plugins/abrt-action-analyze-backtrace.txt53
-rw-r--r--src/plugins/abrt-action-analyze-c.c14
-rwxr-xr-xsrc/plugins/abrt-action-analyze-core.py6
-rw-r--r--src/plugins/abrt-action-analyze-oops.c14
-rw-r--r--src/plugins/abrt-action-analyze-python.c14
-rw-r--r--src/plugins/abrt-action-bugzilla.c340
-rw-r--r--src/plugins/abrt-action-bugzilla.cpp966
-rw-r--r--src/plugins/abrt-action-bugzilla.txt96
-rw-r--r--src/plugins/abrt-action-generate-backtrace.c18
-rw-r--r--src/plugins/abrt-action-generate-backtrace.txt47
-rw-r--r--src/plugins/abrt-action-install-debuginfo.c42
-rwxr-xr-xsrc/plugins/abrt-action-install-debuginfo.py135
-rw-r--r--src/plugins/abrt-action-kerneloops.c14
-rwxr-xr-xsrc/plugins/abrt-action-list-dsos.py29
-rw-r--r--src/plugins/abrt-action-list-dsos.txt44
-rw-r--r--src/plugins/abrt-action-mailx.c16
-rw-r--r--src/plugins/abrt-action-mailx.txt91
-rw-r--r--src/plugins/abrt-action-print.c14
-rw-r--r--src/plugins/abrt-action-print.txt63
-rw-r--r--src/plugins/abrt-action-rhtsupport.c14
-rw-r--r--src/plugins/abrt-action-trim-files.c15
-rw-r--r--src/plugins/abrt-action-trim-files.txt30
-rw-r--r--src/plugins/abrt-action-upload.c14
-rw-r--r--src/plugins/abrt-dump-oops.c18
-rw-r--r--src/plugins/abrt-retrace-client.c159
-rw-r--r--src/plugins/abrt_rh_support.c2
-rw-r--r--src/plugins/analyze_LocalGDB.xml.in1
-rw-r--r--src/plugins/analyze_RetraceServer.xml.in3
-rw-r--r--src/plugins/analyze_xsession_errors.xml.in10
-rw-r--r--src/plugins/ccpp_events.conf56
-rw-r--r--src/plugins/rhbz.c482
-rw-r--r--src/plugins/rhbz.h100
-rw-r--r--src/report-python/reportmodule.c2
-rw-r--r--src/retrace/create.wsgi2
-rw-r--r--src/retrace/retrace.py17
-rw-r--r--src/retrace/worker.c16
-rwxr-xr-xsrc/retrace/worker.py5
75 files changed, 2726 insertions, 2054 deletions
diff --git a/src/applet/applet.c b/src/applet/applet.c
index e965a3ba..aff82841 100644
--- a/src/applet/applet.c
+++ b/src/applet/applet.c
@@ -221,7 +221,7 @@ int main(int argc, char** argv)
/* Parse options */
int opt;
- while ((opt = getopt(argc, argv, "dv")) != -1)
+ while ((opt = getopt(argc, argv, "v")) != -1)
{
switch (opt)
{
diff --git a/src/btparser/normalize.c b/src/btparser/normalize.c
index 4bbee99c..64ccada7 100644
--- a/src/btparser/normalize.c
+++ b/src/btparser/normalize.c
@@ -34,6 +34,26 @@ btp_normalize_thread(struct btp_thread *thread)
btp_normalize_linux_thread(thread);
btp_normalize_xorg_thread(thread);
+ /* If the first frame has address 0x0000 and its name is '??', it
+ * is a dereferenced null, and we remove it. This frame is not
+ * really invalid, and it affects backtrace quality rating. See
+ * Red Hat Bugzilla bug #639038.
+ * @code
+ * #0 0x0000000000000000 in ?? ()
+ * No symbol table info available.
+ * #1 0x0000000000422648 in main (argc=1, argv=0x7fffa57cf0d8) at totem.c:242
+ * error = 0x0
+ * totem = 0xdee070 [TotemObject]
+ * @endcode
+ */
+ if (thread->frames &&
+ thread->frames->address == 0x0000 &&
+ thread->frames->function_name &&
+ 0 == strcmp(thread->frames->function_name, "??"))
+ {
+ btp_thread_remove_frame(thread, thread->frames);
+ }
+
/* If the last frame has address 0x0000 and its name is '??',
* remove it. This frame is not really invalid, and it affects
* backtrace quality rating. See Red Hat Bugzilla bug #592523.
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am
index d6cd5ef2..bc02236e 100644
--- a/src/cli/Makefile.am
+++ b/src/cli/Makefile.am
@@ -1,4 +1,8 @@
-bin_PROGRAMS = abrt-cli
+-include ../../config.mak
+
+bin_PROGRAMS = \
+ abrt-handle-crashdump \
+ abrt-cli
abrt_cli_SOURCES = \
cli.c \
@@ -19,10 +23,49 @@ abrt_cli_LDADD = \
../lib/libabrt_dbus.la \
$(GLIB_LIBS)
-man_MANS = abrt-cli.1
-EXTRA_DIST = $(man_MANS)
+abrt_handle_crashdump_SOURCES = \
+ abrt-handle-crashdump.c
+abrt_handle_crashdump_CPPFLAGS = \
+ -I$(srcdir)/../include/report -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DLIBEXEC_DIR=\"$(LIBEXEC_DIR)\" \
+ $(GLIB_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings -Werror
+abrt_handle_crashdump_LDADD = \
+ ../lib/libreport.la
+
+MAN_TXT = \
+ abrt-cli.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 \
+ abrt-action-analyze-core.py
completiondir = $(sysconfdir)/bash_completion.d
dist_completion_DATA = abrt-cli.bash
DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+EXTRA_DIST = $(MAN_TXT)
+
diff --git a/src/cli/abrt-cli.1 b/src/cli/abrt-cli.1
deleted file mode 100644
index 9e0a630e..00000000
--- a/src/cli/abrt-cli.1
+++ /dev/null
@@ -1,58 +0,0 @@
-.TH abrt\-cli "1" "12 Oct 2009" ""
-.SH NAME
-abrt\-cli \- a command line interface to abrt
-.SH SYNOPSIS
-.B abrt\-cli
-[option]
-.SH DESCRIPTION
-.I abrt\-cli
-is a command line tool that manages application crashes catched by
-.I abrtd
-daemon. It enables access to problem data, and allows to report
-problems depending on active abrt plugins.
-.SH OPTIONS
-.B Basic startup options
-.IP "\-V, \-\-version"
-Displays version of abrt\-cli.
-.IP "\-?, \-\-help"
-Print a help message describing all of abrt-cli’s command-line options.
-
-.PP
-.B Crash action options
-.IP "\-l, \-\-list"
-Prints list of crashes which are not reported yet.
-.IP "\-r, \-\-report \fIDUMPDIR\fR"
-Creates a crash report and then the text editor is invoked on that
-report. When you are done with editing the report just exit the editor
-and then you will be asked if you want to send the report.
-.IP "\-d, \-\-delete \fIDUMPDIR\fR"
-Removes data about particular crash.
-.IP "\-i, \-\-info \fIDUMPDIR\fR"
-Prints detailed information about particular crash.
-
-.PP
-.B Listing options
-.IP "\-f, \-\-full"
-List all crashes, including already reported.
-
-.PP
-.B Report options
-.IP "\-y, \-\-always"
-Creates and sends the crash report automatically, without asking
-any questions.
-
-.PP
-.B Info options
-.IP "\-b, \-\-backtrace"
-Includes the crash backtrace in the info output if the backtrace is
-available.
-
-.SH ENVIRONMENT VARIABLES
-The editor used to edit the crash report is chosen from the
-ABRT_EDITOR environment variable, the VISUAL environment variable, or
-the EDITOR environment variable, in that order.
-
-.SH SEE ALSO
-.IR abrtd (8),
-.IR abrt.conf (5),
-.IR abrt-plugins (7)
diff --git a/src/cli/abrt-cli.txt b/src/cli/abrt-cli.txt
new file mode 100644
index 00000000..ea3111c8
--- /dev/null
+++ b/src/cli/abrt-cli.txt
@@ -0,0 +1,75 @@
+abrt-cli(1)
+===========
+
+NAME
+----
+abrt-cli - Work with ABRT dump directories from command line.
+
+SYNOPSIS
+--------
+'abrt-cli' [-vsp] -l[f] [-D BASE_DIR]...
+
+'abrt-cli' [-vsp] -i[f] DUMP_DIR
+
+'abrt-cli' [-vsp] -L[PREFIX] [DUMP_DIR]
+
+'abrt-cli' [-vsp] -e EVENT DUMP_DIR
+
+'abrt-cli' [-vsp] -a[y] DUMP_DIR
+
+'abrt-cli' [-vsp] -r[y] DUMP_DIR
+
+'abrt-cli' [-vsp] -d DUMP_DIR
+
+DESCRIPTION
+-----------
+'abrt-cli' is a command line tool that manages application crashes and other problems
+catched by abrtd daemon. It enables access to, manipulation of problem data, and reporting.
+
+OPTIONS
+-------
+-l::
+ List not yet reported problems, or all problems with -f
+
+-D BASE_DIR::
+ Directory to list problems from (default: -D $HOME/.abrt/spool -D /var/spool/abrt)
+
+-i, --info::
+ Print information about DUMP_DIR (detailed with -f)
+
+-L[PREFIX]::
+ List possible events [which start with PREFIX]
+
+-e EVENT::
+ Run EVENT on DUMP_DIR
+
+-a, --analyze::
+ Run analyze event(s) on DUMP_DIR
+
+-r, --report::
+ Send a report about DUMP_DIR
+
+-d, --delete::
+ Remove DUMP_DIR
+
+-f, --full::
+ Full listing
+
+-y, --always::
+ Noninteractive: don't ask questions, assume positive answer to all of them
+
+-v, --verbose::
+ Be verbose
+
+-s::
+ Log to syslog
+
+-p::
+ Add program names to log
+
+-V, --version::
+ Display version and exit
+
+AUTHORS
+-------
+* ABRT team
diff --git a/src/daemon/abrt-handle-crashdump.c b/src/cli/abrt-handle-crashdump.c
index 3c3e433f..d04e4fef 100644
--- a/src/daemon/abrt-handle-crashdump.c
+++ b/src/cli/abrt-handle-crashdump.c
@@ -19,8 +19,6 @@
#include "abrtlib.h"
#include "parse_options.h"
-#define PROGNAME "abrt-handle-crashdump"
-
static const char *dump_dir_name = NULL;
//static const char *conf_filename = CONF_DIR"/abrt_event.conf";
static const char *event;
@@ -34,14 +32,12 @@ static char *do_log(char *log_line, void *param)
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" [-vs]" /*" [-c CONFFILE]"*/ " -d DIR -e EVENT\n"
- " or: "PROGNAME" [-vs]" /*" [-c CONFFILE]"*/ " [-d DIR] -l[PFX]\n"
+ "\b [-vs]" /*" [-c CONFFILE]"*/ " -d DIR -e EVENT\n"
+ " or: \b [-vs]" /*" [-c CONFFILE]"*/ " [-d DIR] -l[PFX]\n"
"\n"
"Handles dump directory DIR according to rules in abrt_event.conf"
);
@@ -69,10 +65,8 @@ int main(int argc, char **argv)
if (!(opts & (OPT_e|OPT_l)))
show_usage_and_die(program_usage_string, program_options);
- putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- msg_prefix = PROGNAME;
- if (opts & OPT_p)
- putenv((char*)"ABRT_PROG_PREFIX=1");
+ export_abrt_envvars(opts & OPT_p);
+
if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
diff --git a/src/cli/cli.c b/src/cli/cli.c
index 1a84b1a5..3981c1bc 100644
--- a/src/cli/cli.c
+++ b/src/cli/cli.c
@@ -120,92 +120,15 @@ static void print_crash_info(problem_data_t *problem_data, bool show_multiline)
free(desc);
}
-/* Program options */
-enum
+static char *do_log(char *log_line, void *param)
{
- OPT_GET_LIST,
- OPT_REPORT,
- OPT_DELETE,
- OPT_INFO
-};
-
-/**
- * Long options.
- * Do not use the has_arg field. Arguments are handled after parsing all options.
- * The reason is that we want to use all the following combinations:
- * --report ID
- * --report ID --always
- * --report --always ID
- */
-static const struct option longopts[] =
-{
- /* name, has_arg, flag, val */
- { "help" , no_argument, NULL, '?' },
- { "verbose" , no_argument, NULL, 'v' },
- { "version" , no_argument, NULL, 'V' },
- { "list" , no_argument, NULL, 'l' },
- { "full" , no_argument, NULL, 'f' },
- { "always" , no_argument, NULL, 'y' },
- { "report" , no_argument, NULL, 'r' },
- { "delete" , no_argument, NULL, 'd' },
- { "info" , no_argument, NULL, 'i' },
- { 0, 0, 0, 0 } /* prevents crashes for unknown options*/
-};
-
-/* Gets the program name from the first command line argument. */
-static const char *progname(const char *argv0)
-{
- const char* name = strrchr(argv0, '/');
- if (name)
- return ++name;
- return argv0;
-}
-
-/**
- * Prints abrt-cli version and some help text.
- * Then exits the program with return value 1.
- */
-static void print_usage_and_die(char *argv0)
-{
- const char *name = progname(argv0);
- printf("%s "VERSION"\n\n", name);
-
- /* Message has embedded tabs. */
- printf(_(
- "Usage: %s -l[f] [-D BASE_DIR]...]\n"
- " or: %s -r[y] CRASH_DIR\n"
- " or: %s -i[b] CRASH_DIR\n"
- " or: %s -d CRASH_DIR\n"
- "\n"
- " -l, --list List not yet reported problems\n"
- " -f, --full List all problems\n"
- " -D BASE_DIR Directory to list problems from\n"
- " (default: -D $HOME/.abrt/spool -D %s)\n"
- "\n"
- " -r, --report Send a report about CRASH_DIR\n"
- " -y, --always ...without editing and asking\n"
- " -i, --info Print detailed information about CRASH_DIR\n"
- " -f, --full ...including multi-line entries\n"
- " Note: -if will run analyzers\n"
- " (if this CRASH_DIR have defined analyzers)\n"
- " -d, --delete Remove CRASH_DIR\n"
- "\n"
- " -V, --version Display version and exit\n"
- " -v, --verbose Be verbose\n"
- ),
- name, name, name, name,
- DEBUG_DUMPS_DIR
- );
- exit(1);
+ log("%s", log_line);
+ return log_line;
}
int main(int argc, char** argv)
{
- GList *D_list = NULL;
- char *dump_dir_name = NULL;
- int op = -1;
- bool full = false;
- bool always = false;
+ abrt_init(argv);
setlocale(LC_ALL, "");
#if ENABLE_NLS
@@ -213,41 +136,91 @@ int main(int argc, char** argv)
textdomain(PACKAGE);
#endif
- while (1)
- {
- /* Do not use colons, arguments are handled after parsing all options. */
- int c = getopt_long(argc, argv, "?Vvrdlfyib", longopts, NULL);
+ GList *D_list = NULL;
+ const char *event_name = NULL;
+ const char *pfx = "";
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-vsp] -l[f] [-D BASE_DIR]...\n"
+ "or: \b [-vsp] -i[f] DUMP_DIR\n"
+ "or: \b [-vsp] -L[PREFIX] [DUMP_DIR]\n"
+ "or: \b [-vsp] -e EVENT DUMP_DIR\n"
+ "or: \b [-vsp] -a[y] DUMP_DIR\n"
+ "or: \b [-vsp] -r[y] DUMP_DIR\n"
+ "or: \b [-vsp] -d DUMP_DIR"
+ );
+ enum {
+ OPT_list = 1 << 0,
+ OPT_D = 1 << 1,
+ OPT_info = 1 << 2,
+ OPT_list_events = 1 << 3,
+ OPT_run_event = 1 << 4,
+ OPT_analyze = 1 << 5,
+ OPT_report = 1 << 6,
+ OPT_delete = 1 << 7,
+ OPT_version = 1 << 8,
+ OPTMASK_op = OPT_list|OPT_info|OPT_list_events|OPT_run_event|OPT_analyze|OPT_report|OPT_delete|OPT_version,
+ OPTMASK_need_arg = OPT_info|OPT_run_event|OPT_analyze|OPT_report|OPT_delete,
+ OPT_f = 1 << 9,
+ OPT_y = 1 << 10,
+ OPT_v = 1 << 11,
+ OPT_s = 1 << 12,
+ OPT_p = 1 << 13,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ /* short_name long_name value parameter_name help */
+ OPT_BOOL( 'l', "list" , NULL, _("List not yet reported problems, or all with -f")),
+ OPT_LIST( 'D', NULL , &D_list, "BASE_DIR", _("Directory to list problems from (default: -D $HOME/.abrt/spool -D "DEBUG_DUMPS_DIR")")),
+ OPT_BOOL( 'i', "info" , NULL, _("Print information about DUMP_DIR (detailed with -f)")),
+ OPT_OPTSTRING('L', NULL , &pfx, "PREFIX", _("List possible events [which start with PREFIX]")),
+ OPT_STRING( 'e', NULL , &event_name, "EVENT", _("Run EVENT on DUMP_DIR")),
+ OPT_BOOL( 'a', "analyze", NULL, _("Run analyze event(s) on DUMP_DIR")),
+ OPT_BOOL( 'r', "report" , NULL, _("Send a report about DUMP_DIR")),
+ OPT_BOOL( 'd', "delete" , NULL, _("Remove DUMP_DIR")),
+ OPT_BOOL( 'V', "version", NULL, _("Display version and exit")),
+ OPT_BOOL( 'f', "full" , NULL, _("Full listing")),
+ OPT_BOOL( 'y', "always" , NULL, _("Noninteractive: don't ask questions, assume 'yes'")),
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL( 's', NULL , NULL, _("Log to syslog")),
+ OPT_BOOL( 'p', NULL , NULL, _("Add program names to log")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+ unsigned op = (opts & OPTMASK_op);
+ if (!op || ((op-1) & op))
+ /* "You must specify exactly one operation" */
+ show_usage_and_die(program_usage_string, program_options);
+ argv += optind;
+ argc -= optind;
+ if (argc > 1
+ /* dont_need_arg == have_arg? bad in both cases:
+ * TRUE == TRUE (dont need arg but have) or
+ * FALSE == FALSE (need arg but havent).
+ * OPT_list_events is an exception, it can be used in both cases.
+ */
+ || ((op != OPT_list_events) && (!(opts & OPTMASK_need_arg) == argc))
+ ) {
+ show_usage_and_die(program_usage_string, program_options);
+ }
-#define SET_OP(newop) \
- do { \
- if (op != -1 && op != newop) \
- error_msg_and_die(_("You must specify exactly one operation")); \
- op = newop; \
- } while (0)
+ if (op == OPT_version)
+ {
+ printf("%s "VERSION"\n", g_progname);
+ return 0;
+ }
- switch (c)
- {
- case -1: goto end_of_arg_parsing;
- case 'r': SET_OP(OPT_REPORT); break;
- case 'd': SET_OP(OPT_DELETE); break;
- case 'l': SET_OP(OPT_GET_LIST); break;
- case 'i': SET_OP(OPT_INFO); break;
- case 'f': full = true; break;
- case 'y': always = true; break;
- case 'v': g_verbose++; break;
- case 'D':
- D_list = g_list_append(D_list, optarg);
- break;
- case 'V':
- printf("%s "VERSION"\n", progname(argv[0]));
- return 0;
- case '?':
- default: /* some error */
- print_usage_and_die(argv[0]); /* exits app */
- }
-#undef SET_OP
+ export_abrt_envvars(opts & OPT_p);
+ if (opts & OPT_s)
+ {
+ openlog(msg_prefix, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
}
- end_of_arg_parsing: ;
+
+ char *dump_dir_name = argv[0];
+ bool full = (opts & OPT_f);
+ bool always = (opts & OPT_y);
if (!D_list)
{
@@ -257,31 +230,6 @@ int main(int argc, char** argv)
D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR);
}
- /* Handle option arguments. */
- argc -= optind;
- switch (argc)
- {
- case 0:
- if (op == OPT_REPORT || op == OPT_DELETE || op == OPT_INFO)
- print_usage_and_die(argv[0]);
- break;
- case 1:
- if (op != OPT_REPORT && op != OPT_DELETE && op != OPT_INFO)
- print_usage_and_die(argv[0]);
- dump_dir_name = argv[optind];
- break;
- default:
- print_usage_and_die(argv[0]);
- }
-
- /* Check if we have an operation.
- * Limit --full and --always to certain operations.
- */
- if ((always && op != OPT_REPORT) || op == -1)
- {
- print_usage_and_die(argv[0]);
- }
-
/* Get settings */
load_event_config_data();
@@ -289,7 +237,7 @@ int main(int argc, char** argv)
int exitcode = 0;
switch (op)
{
- case OPT_GET_LIST:
+ case OPT_list:
{
vector_of_problem_data_t *ci = new_vector_of_problem_data();
while (D_list)
@@ -302,7 +250,49 @@ int main(int argc, char** argv)
free_vector_of_problem_data(ci);
break;
}
- case OPT_REPORT:
+ case OPT_list_events: /* -L[PREFIX] */
+ {
+ /* Note that dump_dir_name may be NULL here, it means "show all
+ * possible events regardless of dir"
+ */
+ char *events = list_possible_events(NULL, dump_dir_name, pfx);
+ if (!events)
+ return 1; /* error msg is already logged */
+ fputs(events, stdout);
+ free(events);
+ break;
+ }
+ case OPT_run_event: /* -e EVENT: run event */
+ {
+ struct run_event_state *run_state = new_run_event_state();
+ run_state->logging_callback = do_log;
+ int r = run_event_on_dir_name(run_state, dump_dir_name, event_name);
+ if (r == 0 && run_state->children_count == 0)
+ error_msg_and_die("No actions are found for event '%s'", event_name);
+ free_run_event_state(run_state);
+ break;
+ }
+ case OPT_analyze:
+ {
+ /* Load problem_data from dump dir */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
+ if (!dd)
+ return 1;
+ char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze");
+ dd_close(dd);
+
+ if (analyze_events_as_lines && *analyze_events_as_lines)
+ {
+ GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n');
+ char *event = select_event_option(list_analyze_events);
+ list_free_with_free(list_analyze_events);
+ exitcode = run_analyze_event(dump_dir_name, event);
+ free(event);
+ }
+ free(analyze_events_as_lines);
+ break;
+ }
+ case OPT_report:
{
struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
if (!dd)
@@ -327,43 +317,18 @@ int main(int argc, char** argv)
error_msg_and_die("Crash '%s' not found", dump_dir_name);
break;
}
- case OPT_DELETE:
+ case OPT_delete:
{
exitcode = delete_dump_dir_possibly_using_abrtd(dump_dir_name);
break;
}
- case OPT_INFO:
+ case OPT_info:
{
/* Load problem_data from dump dir */
struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
if (!dd)
return -1;
- char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze");
-
- if (full && analyze_events_as_lines && *analyze_events_as_lines)
- {
- dd_close(dd);
-
- GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n');
- free(analyze_events_as_lines);
-
- char *event = select_event_option(list_analyze_events);
- list_free_with_free(list_analyze_events);
-
- int analyzer_result = run_analyze_event(dump_dir_name, event);
- free(event);
-
- if (analyzer_result != 0)
- return 1;
-
- /* Reload problem_data from (possibly updated by analyze) dump dir */
- dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY);
- if (!dd)
- return -1;
- } else
- free(analyze_events_as_lines);
-
problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
dd_close(dd);
diff --git a/src/cli/report.c b/src/cli/report.c
index 96bf0062..7f722480 100644
--- a/src/cli/report.c
+++ b/src/cli/report.c
@@ -422,6 +422,31 @@ static bool set_echo(bool enable)
return true;
}
+/* Returns true if the string contains the specified number. */
+static bool is_number_in_string(unsigned number, const char *str)
+{
+ const char *c;
+ char numstr[sizeof(int) * 3 + 2];
+ int len;
+
+ len = snprintf(numstr, sizeof(numstr), "%u", number);
+ for (c = str; *c; c++)
+ {
+ c = strstr(c, numstr);
+ if (!c)
+ /* no such number exists in the string */
+ return false;
+ if ((c == str || !isalnum(c[-1])) && !isalnum(c[len]))
+ /* found */
+ return true;
+
+ /* found, but it's part of another number. Continue
+ * from the next position. */
+ }
+
+ return false;
+}
+
/**
* Asks user for missing information
*/
@@ -584,12 +609,12 @@ char *select_event_option(GList *list_options)
if (!list_options)
return NULL;
- unsigned count = g_list_length(list_options) - 1;
- if (!count)
- return NULL;
+ unsigned count = g_list_length(list_options);
+ if (count == 1)
+ return xstrdup((char*)list_options->data);
- int pos = -1;
- fprintf(stdout, _("Select how you would like to analyze the problem:\n"));
+ int pos = 0;
+ fprintf(stdout, _("How you would like to analyze the problem?\n"));
for (GList *li = list_options; li; li = li->next)
{
char *opt = (char*)li->data;
@@ -605,14 +630,9 @@ char *select_event_option(GList *list_options)
unsigned ii;
for (ii = 0; ii < 3; ++ii)
{
- fprintf(stdout, _("Choose option [0 - %u]: "), count);
- fflush(NULL);
-
char answer[16];
- if (!fgets(answer, sizeof(answer), stdin))
- continue;
- answer[strlen(answer) - 1] = '\0';
+ read_from_stdin(_("Select analyzer: "), answer, sizeof(answer));
if (!*answer)
continue;
@@ -620,6 +640,7 @@ char *select_event_option(GList *list_options)
if (picked > count)
{
fprintf(stdout, _("You have chosen number out of range"));
+ fprintf(stdout, "\n");
continue;
}
@@ -629,7 +650,7 @@ char *select_event_option(GList *list_options)
if (ii == 3)
error_msg_and_die(_("Invalid input, program exiting..."));
- GList *choosen = g_list_nth(list_options, picked);
+ GList *choosen = g_list_nth(list_options, picked - 1);
return xstrdup((char*)choosen->data);
}
@@ -661,7 +682,7 @@ int report(const char *dump_dir_name, int flags)
/* Load problem_data from (possibly updated by analyze) dump dir */
struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
- return -1;
+ return -1;
char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze");
dd_close(dd);
@@ -684,7 +705,7 @@ int report(const char *dump_dir_name, int flags)
/* Load problem_data from (possibly updated by analyze) dump dir */
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
- return -1;
+ return -1;
char *report_events_as_lines = list_possible_events(dd, NULL, "report");
problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
@@ -744,30 +765,33 @@ int report(const char *dump_dir_name, int flags)
else
{
const char *rating_str = get_problem_item_content_or_NULL(problem_data, FILENAME_RATING);
- unsigned rating = rating_str ? xatou(rating_str) : 4;
+ unsigned i, rating = rating_str ? xatou(rating_str) : 4;
+ GList *li;
+ char wanted_reporters[255];
- /* For every reporter, ask if user really wants to report using it. */
- for (GList *li = report_events; li; li = li->next)
+ puts(_("How would you like to report the problem?"));
+ /* Print list of reporters and ask the user which should be used. */
+ for (li = report_events, i = 1; li; li = li->next, i++)
{
char *reporter_name = (char *) li->data;
event_config_t *config = get_event_config(reporter_name);
- char question[255];
- char *show_reporter_name;
- if (config)
- show_reporter_name = (config->screen_name) ? config->screen_name : reporter_name;
- else
- show_reporter_name = reporter_name;
- snprintf(question, sizeof(question), _("Report using %s?"), show_reporter_name);
+ printf(" %d) %s\n", i, (config && config->screen_name) ? config->screen_name : reporter_name);
+ }
+
+ read_from_stdin(_("Select reporter(s): "), wanted_reporters, sizeof(wanted_reporters));
+
+ for (li = report_events, i = 1; li; li = li->next, i++)
+ {
+ char *reporter_name = (char *) li->data;
+ event_config_t *config = get_event_config(reporter_name);
if (!config)
VERB1 log("No configuration file found for '%s' reporter", reporter_name);
-
- if (!ask_yesno(question))
- {
- puts(_("Skipping..."));
+
+ /* Was this reporter requested? */
+ if (!is_number_in_string(i, wanted_reporters))
continue;
- }
/* TODO: npajkovs; not implemented yet */
//const char *rating_required = get_map_string_item_or_NULL(single_plugin_settings, "RatingRequired");
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 63d1d78a..7d586796 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -1,8 +1,12 @@
+eventsconfdir = $(EVENTS_CONF_DIR)
+
+dist_eventsconf_DATA = \
+ smart_event.conf
+
bin_SCRIPTS = \
abrt-handle-upload
bin_PROGRAMS = \
- abrt-handle-crashdump \
abrt-action-save-package-data
sbin_PROGRAMS = \
@@ -52,26 +56,6 @@ abrt_server_CPPFLAGS = \
abrt_server_LDADD = \
../lib/libreport.la
-abrt_handle_crashdump_SOURCES = \
- abrt-handle-crashdump.c
-abrt_handle_crashdump_CPPFLAGS = \
- -I$(srcdir)/../include/report -I$(srcdir)/../include \
- -I$(srcdir)/../lib \
- -DBIN_DIR=\"$(bindir)\" \
- -DVAR_RUN=\"$(VAR_RUN)\" \
- -DCONF_DIR=\"$(CONF_DIR)\" \
- -DLOCALSTATEDIR='"$(localstatedir)"' \
- -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
- -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
- -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
- -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
- -DLIBEXEC_DIR=\"$(LIBEXEC_DIR)\" \
- $(GLIB_CFLAGS) \
- -D_GNU_SOURCE \
- -Wall -Wwrite-strings -Werror
-abrt_handle_crashdump_LDADD = \
- ../lib/libreport.la
-
abrt_action_save_package_data_SOURCES = \
rpm.h rpm.c \
abrt-action-save-package-data.c
diff --git a/src/daemon/abrt-action-save-package-data.c b/src/daemon/abrt-action-save-package-data.c
index 93350994..df8de7b9 100644
--- a/src/daemon/abrt-action-save-package-data.c
+++ b/src/daemon/abrt-action-save-package-data.c
@@ -21,8 +21,6 @@
#include "rpm.h"
#include "parse_options.h"
-#define PROGNAME "abrt-action-save-package-data"
-
// TODO: convert g_settings_foo usage to command-line switches
/**
@@ -243,15 +241,13 @@ static int SavePackageDescriptionToDebugDump(const char *dump_dir_name)
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"
"Query package database and save package name, component, and description"
);
@@ -267,11 +263,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);
VERB1 log("Loading settings");
if (load_abrt_conf() != 0)
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
index 53bb5fba..49b7e0ce 100644
--- a/src/daemon/abrt-server.c
+++ b/src/daemon/abrt-server.c
@@ -18,8 +18,6 @@
#include "abrtlib.h"
#include "parse_options.h"
-#define PROGNAME "abrt-server"
-
/* Maximal length of backtrace. */
#define MAX_BACKTRACE_SIZE (1024*1024)
/* Amount of data received from one client for a message before reporting error. */
@@ -280,13 +278,11 @@ static void dummy_handler(int sig_unused) {}
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]"
+ "\b [options]"
);
enum {
OPT_v = 1 << 0,
@@ -304,10 +300,9 @@ 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 = xasprintf(PROGNAME"[%u]", getpid());
- if (opts & OPT_p)
- putenv((char*)"ABRT_PROG_PREFIX=1");
+ export_abrt_envvars(opts & OPT_p);
+
+ msg_prefix = xasprintf("%s[%u]", g_progname, getpid());
if (opts & OPT_s)
{
openlog(msg_prefix, 0, LOG_DAEMON);
diff --git a/src/daemon/abrtd.c b/src/daemon/abrtd.c
index ca0e0d2e..7a1ab318 100644
--- a/src/daemon/abrtd.c
+++ b/src/daemon/abrtd.c
@@ -30,8 +30,6 @@
#include "MiddleWare.h"
#include "parse_options.h"
-#define PROGNAME "abrtd"
-
#define VAR_RUN_PIDFILE VAR_RUN"/abrtd.pid"
#define SOCKET_FILE VAR_RUN"/abrt/abrt.socket"
@@ -451,7 +449,7 @@ static void start_syslog_logging()
* Otherwise fprintf(stderr) dumps messages into random fds, etc. */
xdup2(STDIN_FILENO, STDOUT_FILENO);
xdup2(STDIN_FILENO, STDERR_FILENO);
- openlog(PROGNAME, 0, LOG_DAEMON);
+ openlog(g_progname, 0, LOG_DAEMON);
logmode = LOGMODE_SYSLOG;
putenv((char*)"ABRT_SYSLOG=1");
}
@@ -490,6 +488,8 @@ static void sanitize_dump_dir_rights()
int main(int argc, char** argv)
{
+ abrt_init(argv);
+
int parent_pid = getpid();
setlocale(LC_ALL, "");
@@ -502,12 +502,8 @@ int main(int argc, char** argv)
if (getuid() != 0)
error_msg_and_die("ABRT daemon must be run as root");
- char *env_verbose = getenv("ABRT_VERBOSE");
- if (env_verbose)
- g_verbose = atoi(env_verbose);
-
const char *program_usage_string = _(
- PROGNAME" [options]"
+ "\b [options]"
);
enum {
OPT_v = 1 << 0,
@@ -527,6 +523,8 @@ int main(int argc, char** argv)
};
unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+ export_abrt_envvars(opts & OPT_p);
+
/* When dbus daemon starts us, it doesn't set PATH
* (I saw it set only DBUS_STARTER_ADDRESS and DBUS_STARTER_BUS_TYPE).
* In this case, set something sane:
@@ -536,10 +534,7 @@ int main(int argc, char** argv)
putenv((char*)"PATH=/usr/sbin:/usr/bin:/sbin:/bin");
unsetenv("ABRT_SYSLOG");
- putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- msg_prefix = PROGNAME; /* for log(), error_msg() and such */
- if (opts & OPT_p)
- putenv((char*)"ABRT_PROG_PREFIX=1");
+ msg_prefix = g_progname; /* for log(), error_msg() and such */
if (opts & OPT_s)
start_syslog_logging();
diff --git a/src/daemon/smart_event.conf b/src/daemon/smart_event.conf
new file mode 100644
index 00000000..e4b96b91
--- /dev/null
+++ b/src/daemon/smart_event.conf
@@ -0,0 +1,38 @@
+# Access to /dev/sda usually requires root.
+# skdump is usually in /usr/sbin.
+# Therefore we run it as post-create event, thus: under root.
+#
+# The code is identical for all three packages.
+#
+# To be moved to the packages, so that it is (de)installed
+# together with these packages.
+
+# FIXME: not working yet, because program selection code
+# looks at dump dir BEFORE running programs,
+# thus, component is not created yet
+# (it is created by 1st program in main .conf file)
+# when program selection code looks at this file.
+
+EVENT=post-create component=gnome-disk-utility
+ which skdump 2>/dev/null || exit 0
+ for f in /dev/[sh]d[a-z]; do
+ test -e "$f" || continue
+ skdump "$f"
+ echo
+ done >smart_data
+
+EVENT=post-create component=libatasmart
+ which skdump 2>/dev/null || exit 0
+ for f in /dev/[sh]d[a-z]; do
+ test -e "$f" || continue
+ skdump "$f"
+ echo
+ done >smart_data
+
+EVENT=post-create component=udisks
+ which skdump 2>/dev/null || exit 0
+ for f in /dev/[sh]d[a-z]; do
+ test -e "$f" || continue
+ skdump "$f"
+ echo
+ done >smart_data
diff --git a/src/gui-gtk/abrt-gtk.c b/src/gui-gtk/abrt-gtk.c
index 2e30d0eb..61c5c4f9 100644
--- a/src/gui-gtk/abrt-gtk.c
+++ b/src/gui-gtk/abrt-gtk.c
@@ -44,7 +44,14 @@ enum
void add_directory_to_dirlist(const char *dirname)
{
+ /* Silently ignore *any* errors, not only EACCES.
+ * We saw "lock file is locked by process PID" error
+ * when we raced with wizard.
+ */
+ int sv_logmode = logmode;
+ logmode = 0;
struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY | DD_FAIL_QUIETLY_EACCES);
+ logmode = sv_logmode;
if (!dd)
return;
@@ -379,6 +386,11 @@ GtkWidget *create_main_window(void)
G_TYPE_STRING, /* dump dir path */
G_TYPE_STRING);/* row background */
+ //FIXME: configurable!!
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_dumps_list_store),
+ COLUMN_LATEST_CRASH,
+ GTK_SORT_DESCENDING);
+
gtk_tree_view_set_model(GTK_TREE_VIEW(s_treeview), GTK_TREE_MODEL(s_dumps_list_store));
/* buttons are homogenous so set size only for one button and it will
diff --git a/src/gui-gtk/main.c b/src/gui-gtk/main.c
index ec2904b1..bb57ce7d 100644
--- a/src/gui-gtk/main.c
+++ b/src/gui-gtk/main.c
@@ -25,8 +25,6 @@
# include <locale.h>
#endif
-#define PROGNAME "abrt-gui"
-
static int inotify_fd = -1;
static GIOChannel *channel_inotify;
static int channel_inotify_event_id = -1;
@@ -94,6 +92,7 @@ static void init_notify(void)
else
{
close_on_exec_on(inotify_fd);
+ ndelay_on(inotify_fd);
VERB1 log("Adding inotify watch to glib main loop");
channel_inotify = g_io_channel_unix_new(inotify_fd);
channel_inotify_event_id = g_io_add_watch(channel_inotify,
@@ -103,6 +102,7 @@ static void init_notify(void)
}
}
+#if 0 // UNUSED
static void close_notify(void)
{
if (inotify_fd >= 0)
@@ -117,15 +117,25 @@ static void close_notify(void)
//VERB1 log("Done");
}
}
+#endif
/* Inotify handler */
static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr_unused)
{
- /* We don't bother reading inotify fd. We simply close and reopen it.
- * This happens rarely enough to not bother making it efficient.
+ /* Since dump dir creation usually involves directory rename as a last step,
+ * we end up rescanning twice. A small wait after first inotify event
+ * usually allows to avoid this.
*/
- close_notify();
- init_notify();
+ usleep(10*1000);
+
+ /* We read inotify events, but don't analyze them */
+ gchar buf[sizeof(struct inotify_event) + PATH_MAX + 64];
+ gsize bytes_read;
+ while (g_io_channel_read(gio, buf, sizeof(buf), &bytes_read) == G_IO_ERROR_NONE
+ && bytes_read > 0
+ ) {
+ continue;
+ }
rescan_and_refresh();
@@ -176,11 +186,13 @@ void scan_dirs_and_add_to_dirlist(void)
{
char **argv = s_dirs;
while (*argv)
- scan_directory_and_add_to_dirlist(*argv++);
+ scan_directory_and_add_to_dirlist(*argv++);
}
int main(int argc, char **argv)
{
+ abrt_init(argv);
+
/* I18n */
setlocale(LC_ALL, "");
#if ENABLE_NLS
@@ -193,15 +205,12 @@ int main(int argc, char **argv)
* trac#180
*/
g_set_prgname("abrt");
- gtk_init(&argc, &argv);
- char *env_verbose = getenv("ABRT_VERBOSE");
- if (env_verbose)
- g_verbose = atoi(env_verbose);
+ gtk_init(&argc, &argv);
/* Can't keep these strings/structs static: _() doesn't support that */
const char *program_usage_string = _(
- PROGNAME" [-vp] [DIR]...\n"
+ "\b [-vp] [DIR]...\n"
"\n"
"Shows list of ABRT dump directories in specified DIR(s)\n"
"(default DIRs: "DEBUG_DUMPS_DIR" $HOME/.abrt/spool)"
@@ -218,12 +227,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));
- if (opts & OPT_p)
- {
- msg_prefix = PROGNAME;
- putenv((char*)"ABRT_PROG_PREFIX=1");
- }
+ export_abrt_envvars(opts & OPT_p);
GtkWidget *main_window = create_main_window();
@@ -254,8 +258,8 @@ int main(int argc, char **argv)
xpipe(s_signal_pipe);
close_on_exec_on(s_signal_pipe[0]);
close_on_exec_on(s_signal_pipe[1]);
- ndelay_off(s_signal_pipe[0]);
- ndelay_off(s_signal_pipe[1]);
+ ndelay_on(s_signal_pipe[0]);
+ ndelay_on(s_signal_pipe[1]);
signal(SIGCHLD, handle_signal);
g_io_add_watch(g_io_channel_unix_new(s_signal_pipe[0]),
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
diff --git a/src/gui-wizard-gtk/main.c b/src/gui-wizard-gtk/main.c
index 2d036085..cd100ac8 100644
--- a/src/gui-wizard-gtk/main.c
+++ b/src/gui-wizard-gtk/main.c
@@ -25,13 +25,11 @@
# include <locale.h>
#endif
-#define PROGNAME "bug-reporting-wizard"
-
char *g_glade_file = NULL;
char *g_dump_dir_name = NULL;
char *g_analyze_events = NULL;
-char *g_reanalyze_events = NULL;
char *g_report_events = NULL;
+int g_report_only = false;
problem_data_t *g_cd;
@@ -39,7 +37,6 @@ void reload_problem_data_from_dump_dir(void)
{
free_problem_data(g_cd);
free(g_analyze_events);
- free(g_reanalyze_events);
free(g_report_events);
struct dump_dir *dd = dd_opendir(g_dump_dir_name, DD_OPEN_READONLY);
@@ -50,7 +47,6 @@ void reload_problem_data_from_dump_dir(void)
add_to_problem_data_ext(g_cd, CD_DUMPDIR, g_dump_dir_name, CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
g_analyze_events = list_possible_events(dd, NULL, "analyze");
- g_reanalyze_events = list_possible_events(dd, NULL, "reanalyze");
g_report_events = list_possible_events(dd, NULL, "report");
dd_close(dd);
@@ -62,6 +58,9 @@ void reload_problem_data_from_dump_dir(void)
int main(int argc, char **argv)
{
+ const char *prgname = "abrt";
+ abrt_init(argv);
+
/* I18n */
setlocale(LC_ALL, "");
#if ENABLE_NLS
@@ -69,16 +68,11 @@ int main(int argc, char **argv)
textdomain(PACKAGE);
#endif
- g_set_prgname("abrt");
gtk_init(&argc, &argv);
- char *env_verbose = getenv("ABRT_VERBOSE");
- if (env_verbose)
- g_verbose = atoi(env_verbose);
-
/* Can't keep these strings/structs static: _() doesn't support that */
const char *program_usage_string = _(
- PROGNAME" [-vp] [-g GUI_FILE] DIR\n"
+ "\b [-vp] [-g GUI_FILE] [-o|--report-only] [-n|--prgname] DIR\n"
"\n"
"GUI tool to analyze and report problem saved in specified DIR"
);
@@ -86,28 +80,35 @@ int main(int argc, char **argv)
OPT_v = 1 << 0,
OPT_g = 1 << 1,
OPT_p = 1 << 2,
+ OPT_o = 1 << 3, // report only
+ OPT_n = 1 << 4, // prgname
};
/* Keep enum above and order of options below in sync! */
struct options program_options[] = {
OPT__VERBOSE(&g_verbose),
OPT_STRING('g', NULL, &g_glade_file, "FILE" , _("Alternate GUI file")),
OPT_BOOL( 'p', NULL, NULL , _("Add program names to log")),
+ /* for use from 3rd party apps to show just a reporter selector */
+ OPT_BOOL( 'o', "report-only", &g_report_only , _("Use wizard to report pre-filled problem data")),
+ /* override the default prgname, so it's the same as the application
+ which is calling us
+ */
+ OPT_STRING( 'n', "prgname", &prgname, "NAME" , _("Override the default prgname")),
OPT_END()
};
-
unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
-
- putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- if (opts & OPT_p)
- {
- msg_prefix = PROGNAME;
- putenv((char*)"ABRT_PROG_PREFIX=1");
- }
-
argv += optind;
if (!argv[0] || argv[1]) /* zero or >1 arguments */
show_usage_and_die(program_usage_string, program_options);
+ /* without this the name is set to argv[0] which confuses
+ * desktops which uses the name to find the corresponding .desktop file
+ * trac#180
+ */
+ g_set_prgname(prgname);
+
+ export_abrt_envvars(opts & OPT_p);
+
g_dump_dir_name = xstrdup(argv[0]);
/* load /etc/abrt/events/foo.{conf,xml} stuff
diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c
index ee6c3c39..23d9141d 100644
--- a/src/gui-wizard-gtk/wizard.c
+++ b/src/gui-wizard-gtk/wizard.c
@@ -36,6 +36,7 @@ typedef struct event_gui_data_t
static GtkAssistant *g_assistant;
static char *g_analyze_event_selected;
+static unsigned g_black_event_count = 0;
static GtkBox *g_box_analyzers;
/* List of event_gui_data's */
@@ -55,6 +56,7 @@ static GtkContainer *g_container_details2;
static GtkLabel *g_lbl_cd_reason;
static GtkTextView *g_tv_backtrace;
static GtkTextView *g_tv_comment;
+static GtkEventBox *g_eb_comment;
static GtkTreeView *g_tv_details;
static GtkWidget *g_widget_warnings_area;
static GtkBox *g_box_warning_labels;
@@ -110,10 +112,10 @@ static const gchar PAGE_SUMMARY[] = "page_0";
static const gchar PAGE_COMMENT[] = "page_1";
static const gchar PAGE_ANALYZE_SELECTOR[] = "page_2";
static const gchar PAGE_ANALYZE_PROGRESS[] = "page_3";
-static const gchar PAGE_REPORTER_SELECTOR[] = "page_4";
+static const gchar PAGE_REPORTER_SELECTOR[] = "page_4_report";
static const gchar PAGE_BACKTRACE_APPROVAL[] = "page_5";
-static const gchar PAGE_REPORT[] = "page_6";
-static const gchar PAGE_REPORT_PROGRESS[] = "page_7";
+static const gchar PAGE_REPORT[] = "page_6_report";
+static const gchar PAGE_REPORT_PROGRESS[] = "page_7_report";
static const gchar *const page_names[] =
{
@@ -161,6 +163,8 @@ static page_obj_t pages[] =
{ NULL }
};
+/* hardcoded 10 pages limit */
+page_obj_t *added_pages[10];
/* Utility functions */
@@ -401,11 +405,12 @@ static gint find_by_button(gconstpointer a, gconstpointer button)
static void analyze_rb_was_toggled(GtkButton *button, gpointer user_data)
{
+ free(g_analyze_event_selected);
+ g_analyze_event_selected = NULL;
GList *found = g_list_find_custom(g_list_analyzers, button, find_by_button);
if (found)
{
event_gui_data_t *evdata = found->data;
- free(g_analyze_event_selected);
g_analyze_event_selected = xstrdup(evdata->event_name);
}
}
@@ -443,15 +448,13 @@ static void report_tb_was_toggled(GtkButton *button_unused, gpointer user_data_u
* Add new {radio/check}buttons to GtkBox for each EVENTn (type depends on bool radio).
* Remember them in GList **p_event_list (list of event_gui_data_t's).
* Set "toggled" callback on each button to given GCallback if it's not NULL.
- * If prev_selected == EVENTn, set this button as active. In this case return NULL.
- * Else return 1st button created (or NULL if none created).
+ * Return active button (or NULL if none created).
*/
static event_gui_data_t *add_event_buttons(GtkBox *box,
GList **p_event_list,
char *event_name,
GCallback func,
- bool radio,
- const char *prev_selected)
+ bool radio)
{
//VERB2 log("removing all buttons from box %p", box);
gtk_container_foreach(GTK_CONTAINER(box), &remove_child_widget, NULL);
@@ -459,13 +462,18 @@ static event_gui_data_t *add_event_buttons(GtkBox *box,
g_list_free(*p_event_list);
*p_event_list = NULL;
- bool have_activated_btn = false;
+ if (radio)
+ g_black_event_count = 0;
+
event_gui_data_t *first_button = NULL;
+ event_gui_data_t *active_button = NULL;
while (event_name[0])
{
char *event_name_end = strchr(event_name, '\n');
*event_name_end = '\0';
+ event_config_t *cfg = get_event_config(event_name);
+
/* Form a pretty text representation of event */
/* By default, use event name, just strip "foo_" prefix if it exists: */
const char *event_screen_name = strchr(event_name, '_');
@@ -473,15 +481,27 @@ static event_gui_data_t *add_event_buttons(GtkBox *box,
event_screen_name++;
else
event_screen_name = event_name;
+
const char *event_description = NULL;
- event_config_t *cfg = get_event_config(event_name);
+ char *tmp_description = NULL;
+ bool green_choice = false;
if (cfg)
{
/* .xml has (presumably) prettier description, use it: */
if (cfg->screen_name)
event_screen_name = cfg->screen_name;
event_description = cfg->description;
+ if (cfg->creates_elements)
+ {
+ if (get_problem_data_item_or_NULL(g_cd, cfg->creates_elements))
+ {
+ green_choice = true;
+ event_description = tmp_description = xasprintf(_("(not needed, '%s' already exists)"), cfg->creates_elements);
+ }
+ }
}
+ if (radio && !green_choice)
+ g_black_event_count++;
//VERB2 log("adding button '%s' to box %p", event_name, box);
char *event_label = xasprintf("%s%s%s",
@@ -489,6 +509,8 @@ static event_gui_data_t *add_event_buttons(GtkBox *box,
(event_description ? " - " : ""),
event_description ? event_description : ""
);
+ free(tmp_description);
+
GtkWidget *button = radio
? gtk_radio_button_new_with_label_from_widget(
(first_button ? GTK_RADIO_BUTTON(first_button->toggle_button) : NULL),
@@ -496,33 +518,76 @@ static event_gui_data_t *add_event_buttons(GtkBox *box,
)
: gtk_check_button_new_with_label(event_label);
free(event_label);
+
+ if (green_choice)
+ {
+ //static const GdkColor red = { .red = 0xffff };
+ //gtk_widget_modify_text(button, GTK_STATE_NORMAL, &red);
+ GtkWidget *child = gtk_bin_get_child(GTK_BIN(button));
+ if (child)
+ {
+ static const GdkColor green = { .green = 0x7fff };
+ gtk_widget_modify_fg(child, GTK_STATE_NORMAL, &green);
+ gtk_widget_modify_fg(child, GTK_STATE_ACTIVE, &green);
+ gtk_widget_modify_fg(child, GTK_STATE_PRELIGHT, &green);
+ }
+ }
+
if (func)
g_signal_connect(G_OBJECT(button), "toggled", func, NULL);
- if (cfg->long_descr)
+ if (cfg && cfg->long_descr)
gtk_widget_set_tooltip_text(button, cfg->long_descr);
event_gui_data_t *event_gui_data = new_event_gui_data_t();
event_gui_data->event_name = xstrdup(event_name);
event_gui_data->toggle_button = GTK_TOGGLE_BUTTON(button);
- *p_event_list = g_list_append(*p_event_list, event_gui_data);
+ *p_event_list = g_list_append(*p_event_list, event_gui_data);
if (!first_button)
first_button = event_gui_data;
- if (prev_selected && strcmp(prev_selected, event_name) == 0)
+ if (radio && !green_choice && !active_button)
{
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true);
- have_activated_btn = true;
- prev_selected = NULL;
+ active_button = event_gui_data;
}
*event_name_end = '\n';
event_name = event_name_end + 1;
gtk_box_pack_start(box, button, /*expand*/ false, /*fill*/ false, /*padding*/ 0);
+ }
+
+ if (radio)
+ {
+ const char *msg_proceed_to_reporting = _("Go to reporting step");
+ GtkWidget *button = radio
+ ? gtk_radio_button_new_with_label_from_widget(
+ (first_button ? GTK_RADIO_BUTTON(first_button->toggle_button) : NULL),
+ msg_proceed_to_reporting
+ )
+ : gtk_check_button_new_with_label(msg_proceed_to_reporting);
+ if (func)
+ g_signal_connect(G_OBJECT(button), "toggled", func, NULL);
+ event_gui_data_t *event_gui_data = new_event_gui_data_t();
+ event_gui_data->event_name = xstrdup("");
+ event_gui_data->toggle_button = GTK_TOGGLE_BUTTON(button);
+ *p_event_list = g_list_append(*p_event_list, event_gui_data);
+
+ //if (!first_button)
+ // first_button = event_gui_data;
+
+ if (!active_button)
+ {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true);
+ active_button = event_gui_data;
+ }
+
+ gtk_box_pack_start(box, button, /*expand*/ false, /*fill*/ false, /*padding*/ 0);
}
- return (have_activated_btn ? NULL : first_button);
+
+ return active_button;
}
struct cd_stats {
@@ -596,15 +661,16 @@ void update_gui_state_from_problem_data(void)
load_text_to_text_view(g_tv_comment, FILENAME_COMMENT);
/* Update analyze radio buttons */
- event_gui_data_t *first_rb = add_event_buttons(g_box_analyzers, &g_list_analyzers,
+ event_gui_data_t *active_button = add_event_buttons(g_box_analyzers, &g_list_analyzers,
g_analyze_events, G_CALLBACK(analyze_rb_was_toggled),
- /*radio:*/ true, /*prev:*/ g_analyze_event_selected
+ /*radio:*/ true
);
/* Update the value of currently selected analyzer */
- if (first_rb)
+ if (active_button)
{
free(g_analyze_event_selected);
- g_analyze_event_selected = xstrdup(first_rb->event_name);
+ g_analyze_event_selected = xstrdup(active_button->event_name);
+ VERB2 log("g_analyze_event_selected='%s'", g_analyze_event_selected);
}
/* Update reporter checkboxes */
@@ -623,7 +689,7 @@ void update_gui_state_from_problem_data(void)
/* Delete old checkboxes and create new ones */
add_event_buttons(g_box_reporters, &g_list_reporters,
g_report_events, /*callback:*/ G_CALLBACK(report_tb_was_toggled),
- /*radio:*/ false, /*prev:*/ NULL
+ /*radio:*/ false
);
/* Re-select new reporters which were selected before we deleted them */
GList *li_new = g_list_reporters;
@@ -650,7 +716,7 @@ void update_gui_state_from_problem_data(void)
*/
gtk_widget_show_all(GTK_WIDGET(g_assistant));
- if (g_reanalyze_events[0])
+ if (g_analyze_events[0])
gtk_widget_show(GTK_WIDGET(g_btn_refresh));
else
gtk_widget_hide(GTK_WIDGET(g_btn_refresh));
@@ -829,9 +895,6 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g
}
}
- /* Inform abrt-gui that it is a good idea to rescan the directory */
- kill(getppid(), SIGCHLD);
-
/* Stop if exit code is not 0, or no more commands */
if (retval != 0
|| spawn_next_command_in_evd(evd) < 0
@@ -839,6 +902,9 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g
VERB1 log("done running event on '%s': %d", g_dump_dir_name, retval);
//append_to_textview(evd->tv_log, msg);
+ /* Inform abrt-gui that it is a good idea to rescan the directory */
+ kill(getppid(), SIGCHLD);
+
for (;;)
{
if (!evd->more_events)
@@ -849,9 +915,14 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition condition, g
/* Unfreeze assistant
* we can't allow user to continue if analyze action fails
* i.e: if gdb fails to generate backtrace
- */
+//TODO: generic solution instead of special-casing on event name!
+ */
if (retval == 0 || (strncmp(evd->event_name, "analyze", strlen("analyze")) != 0))
+ {
gtk_assistant_set_page_complete(g_assistant, evd->page_widget, true);
+ }
+ /* Enable (un-gray out) navigation buttons */
+ gtk_widget_set_sensitive(GTK_WIDGET(g_assistant), true);
/*g_source_remove(evd->event_source_id);*/
close(evd->fd);
@@ -960,6 +1031,8 @@ static void start_event_run(const char *event_name,
/* Freeze assistant so it can't move away from the page until event run is done */
gtk_assistant_set_page_complete(g_assistant, page, false);
+ /* Disable (gray out) navigation buttons */
+ gtk_widget_set_sensitive(GTK_WIDGET(g_assistant), false);
}
@@ -1033,24 +1106,33 @@ static void on_bt_approve_toggle(GtkToggleButton *togglebutton, gpointer user_da
check_backtrace_and_allow_send();
}
+static void on_comment_changed(GtkTextBuffer *buffer, gpointer user_data)
+{
+ bool good = gtk_text_buffer_get_char_count(buffer) >= 10;
+
+ /* Allow next page only when the comment has at least 10 chars */
+ gtk_assistant_set_page_complete(g_assistant, pages[PAGENO_COMMENT].page_widget, good);
+
+ /* And show the eventbox with label */
+ if (good)
+ gtk_widget_hide(GTK_WIDGET(g_eb_comment));
+ else
+ gtk_widget_show(GTK_WIDGET(g_eb_comment));
+}
+
/* Refresh button handling */
static void on_btn_refresh_clicked(GtkButton *button)
{
- if (g_reanalyze_events[0])
- {
- /* Save backtrace text if changed */
- save_text_from_text_view(g_tv_backtrace, FILENAME_BACKTRACE);
+ /* Save backtrace text if changed */
+ save_text_from_text_view(g_tv_backtrace, FILENAME_BACKTRACE);
- g_analyze_events = append_to_malloced_string(g_analyze_events, g_reanalyze_events);
- g_reanalyze_events[0] = '\0';
- /* Refresh GUI so that we see new analyze+reanalyze buttons */
- update_gui_state_from_problem_data();
+ /* Refresh GUI so that we see new analyze buttons */
+ update_gui_state_from_problem_data();
- /* Change page to analyzer selector - let user play with them */
- gtk_assistant_set_current_page(g_assistant, PAGENO_ANALYZE_SELECTOR);
- }
+ /* Change page to analyzer selector - let user play with them */
+ gtk_assistant_set_current_page(g_assistant, PAGENO_ANALYZE_SELECTOR);
}
@@ -1059,25 +1141,29 @@ static void on_btn_refresh_clicked(GtkButton *button)
static void next_page(GtkAssistant *assistant, gpointer user_data)
{
/* page_no is actually the previous page, because this
- * function is called before assistant goes to the next_page
+ * function is called before assistant goes to the next page
*/
int page_no = gtk_assistant_get_current_page(assistant);
VERB2 log("page_no:%d", page_no);
- if (page_no == PAGENO_ANALYZE_SELECTOR
- && g_analyze_event_selected != NULL
- ) {
- start_event_run(g_analyze_event_selected,
- NULL,
- pages[PAGENO_ANALYZE_PROGRESS].page_widget,
- g_tv_analyze_log,
- g_lbl_analyze_log,
- _("Analyzing..."),
- _("Analyzing finished with exit code %d")
- );
+ if (added_pages[page_no]->name == PAGE_ANALYZE_SELECTOR)
+ {
+ VERB2 log("g_analyze_event_selected:'%s'", g_analyze_event_selected);
+ if (g_analyze_event_selected
+ && g_analyze_event_selected[0]
+ ) {
+ start_event_run(g_analyze_event_selected,
+ NULL,
+ pages[PAGENO_ANALYZE_PROGRESS].page_widget,
+ g_tv_analyze_log,
+ g_lbl_analyze_log,
+ _("Analyzing..."),
+ _("Analyzing finished with exit code %d")
+ );
+ }
}
- if (page_no == PAGENO_REPORT)
+ if (added_pages[page_no]->name == PAGE_REPORT)
{
GList *reporters = NULL;
GList *li = g_list_reporters;
@@ -1133,36 +1219,69 @@ static void on_page_prepare(GtkAssistant *assistant, GtkWidget *page, gpointer u
w
);
}
+
+ if (pages[PAGENO_COMMENT].page_widget == page)
+ on_comment_changed(gtk_text_view_get_buffer(g_tv_comment), NULL);
}
-static gint next_page_no(gint current_page_no, gpointer data)
+static gint select_next_page_no(gint current_page_no, gpointer data)
{
+ /* we don't need any magic here if we're in only-report mode */
+ if (g_report_only)
+ return current_page_no + 1;
+
+ gint prev_page_no = current_page_no;
+
again:
current_page_no++;
+
switch (current_page_no)
{
#if 0
case PAGENO_COMMENT:
if (get_problem_item_content_or_NULL(g_cd, FILENAME_COMMENT))
- break;
- goto again; /* no comment, skip next page */
+ goto again; /* no comment, skip this page */
+ break;
#endif
case PAGENO_BACKTRACE_APPROVAL:
- if (get_problem_item_content_or_NULL(g_cd, FILENAME_BACKTRACE))
- break;
- goto again; /* no backtrace, skip next page */
+ if (!get_problem_item_content_or_NULL(g_cd, FILENAME_BACKTRACE))
+ goto again; /* no backtrace, skip this page */
+ break;
case PAGENO_ANALYZE_SELECTOR:
- if (!g_analyze_events[0])
+ if (!g_analyze_events[0] || g_black_event_count == 0)
{
- //TODO: if (!g_reporter_events[0]) /* no reporters available */ then what?
- return PAGENO_REPORTER_SELECTOR; /* skip analyze pages */
+ /* skip analyze selector page and analyze log page */
+ current_page_no = PAGENO_REPORTER_SELECTOR-1;
+ goto again;
+ }
+ break;
+
+ case PAGENO_ANALYZE_PROGRESS:
+ VERB2 log("%s: ANALYZE_PROGRESS: g_analyze_event_selected:'%s'",
+ __func__, g_analyze_event_selected);
+ if (!g_analyze_event_selected || !g_analyze_event_selected[0])
+ goto again; /* skip this page */
+ break;
+
+ case PAGENO_REPORTER_SELECTOR:
+ VERB2 log("%s: REPORTER_SELECTOR: g_black_event_count:%d",
+ __func__, g_black_event_count);
+ /* if we _did_ run an event (didn't skip it)
+ * and still have analyzers which didn't run
+ */
+ if (prev_page_no == PAGENO_ANALYZE_PROGRESS
+ && g_black_event_count != 0
+ ) {
+ /* Go back to analyzer selectors */
+ current_page_no = PAGENO_ANALYZE_SELECTOR-1;
+ goto again;
}
break;
}
- VERB2 log("next page_no:%d", current_page_no);
+ VERB2 log("%s: selected page #%d", __func__, current_page_no);
return current_page_no;
}
@@ -1246,7 +1365,7 @@ static void create_details_treeview()
/* wizard.glade file as a string WIZARD_GLADE_CONTENTS: */
#include "wizard_glade.c"
-static void add_pages(void)
+static void add_pages()
{
GError *error = NULL;
if (!g_glade_file)
@@ -1268,13 +1387,24 @@ static void add_pages(void)
}
int i;
+ int page_no = 0;
for (i = 0; page_names[i] != NULL; i++)
{
+ char *delim = strrchr(page_names[i], '_');
+ if(delim != NULL)
+ {
+ if (g_report_only && (strncmp(delim+1, "report", strlen("report"))) != 0)
+ {
+ pages[i].page_widget = NULL;
+ continue;
+ }
+ }
GtkWidget *page = GTK_WIDGET(gtk_builder_get_object(builder, page_names[i]));
if (page == NULL)
continue;
pages[i].page_widget = page;
+ added_pages[page_no++] = &pages[i];
gtk_assistant_append_page(g_assistant, page);
/* If we set all pages to complete the wizard thinks there is nothing
@@ -1299,6 +1429,7 @@ static void add_pages(void)
g_tv_report_log = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_report_log"));
g_tv_backtrace = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_backtrace"));
g_tv_comment = GTK_TEXT_VIEW( gtk_builder_get_object(builder, "tv_comment"));
+ g_eb_comment = GTK_EVENT_BOX( gtk_builder_get_object(builder, "eb_comment"));
g_tv_details = GTK_TREE_VIEW( gtk_builder_get_object(builder, "tv_details"));
g_box_warning_labels = GTK_BOX( gtk_builder_get_object(builder, "box_warning_labels"));
g_tb_approve_bt = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "cb_approve_bt"));
@@ -1320,8 +1451,9 @@ static void add_pages(void)
//gtk_widget_hide(g_widget_warnings_area);
//gtk_assistant_set_page_complete(g_assistant, pages[PAGENO_REPORTER_SELECTOR].page_widget, false);
- gtk_assistant_set_page_complete(g_assistant, pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
- gtk_toggle_button_get_active(g_tb_approve_bt));
+ if (pages[PAGENO_BACKTRACE_APPROVAL].page_widget != NULL)
+ gtk_assistant_set_page_complete(g_assistant, pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
+ gtk_toggle_button_get_active(g_tb_approve_bt));
/* configure btn on select analyzers page */
GtkWidget *config_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button_cfg1"));
@@ -1332,9 +1464,14 @@ static void add_pages(void)
config_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button_cfg2"));
if (config_btn)
g_signal_connect(G_OBJECT(config_btn), "clicked", G_CALLBACK(on_show_event_list_cb), NULL);
+
+ /* Set color of the comment evenbox */
+ GdkColor color;
+ gdk_color_parse("#CC3333", &color);
+ gtk_widget_modify_bg(GTK_WIDGET(g_eb_comment), GTK_STATE_NORMAL, &color);
}
-void create_assistant()
+void create_assistant(void)
{
monospace_font = pango_font_description_from_string("monospace");
@@ -1342,7 +1479,7 @@ void create_assistant()
g_assistant = GTK_ASSISTANT(gtk_assistant_new());
- gtk_assistant_set_forward_page_func(g_assistant, next_page_no, NULL, NULL);
+ gtk_assistant_set_forward_page_func(g_assistant, select_next_page_no, NULL, NULL);
GtkWindow *wnd_assistant = GTK_WINDOW(g_assistant);
g_parent_window = wnd_assistant;
@@ -1368,6 +1505,7 @@ void create_assistant()
g_signal_connect(g_tb_approve_bt, "toggled", G_CALLBACK(on_bt_approve_toggle), NULL);
g_signal_connect(g_btn_refresh, "clicked", G_CALLBACK(on_btn_refresh_clicked), NULL);
+ g_signal_connect(gtk_text_view_get_buffer(g_tv_comment), "changed", G_CALLBACK(on_comment_changed), NULL);
g_signal_connect(g_tv_details, "row-activated", G_CALLBACK(tv_details_row_activated), NULL);
/* [Enter] on a row: g_signal_connect(g_tv_details, "select-cursor-row", G_CALLBACK(tv_details_select_cursor_row), NULL); */
g_signal_connect(g_tv_details, "cursor-changed", G_CALLBACK(tv_details_cursor_changed), NULL);
@@ -1378,8 +1516,9 @@ void create_assistant()
gtk_text_buffer_create_tag(backtrace_buf, "search_result_bg", "background", "red", NULL);
g_signal_connect(g_search_entry_bt, "changed", G_CALLBACK(search_timeout), NULL);
- gtk_assistant_set_page_complete(g_assistant,
- pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
- gtk_toggle_button_get_active(g_tb_approve_bt)
+ if (pages[PAGENO_BACKTRACE_APPROVAL].page_widget != NULL)
+ gtk_assistant_set_page_complete(g_assistant,
+ pages[PAGENO_BACKTRACE_APPROVAL].page_widget,
+ gtk_toggle_button_get_active(g_tb_approve_bt)
);
}
diff --git a/src/gui-wizard-gtk/wizard.glade b/src/gui-wizard-gtk/wizard.glade
index 301bca68..f79950ee 100644
--- a/src/gui-wizard-gtk/wizard.glade
+++ b/src/gui-wizard-gtk/wizard.glade
@@ -34,9 +34,8 @@
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
- <property name="ypad">0</property>
- <property name="wrap">True</property>
<property name="label" translatable="yes">On the following screens, you will be asked to describe how the problem occurred, to choose how to analyze the problem (if needed), to review collected data, and to choose where the problem should be reported. Click 'Forward' to proceed.</property>
+ <property name="wrap">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -51,8 +50,8 @@
<child>
<object class="GtkScrolledWindow" id="container_details1">
<property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="can_focus">True</property>
+ <property name="shadow_type">out</property>
<child>
<object class="GtkTreeView" id="tv_details">
<property name="visible">True</property>
@@ -85,14 +84,15 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
- <object class="GtkLabel" id="label2">
+ <object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
- <property name="ypad">5</property>
- <property name="label" translatable="yes">How did this problem happen (step-by-step)? How can it be reproduced?</property>
+ <property name="label" translatable="yes">How did this problem happen (step-by-step)? How can it be reproduced? Any additional comments useful for diagnosing the problem?</property>
+ <property name="wrap">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -101,37 +101,52 @@
</packing>
</child>
<child>
- <object class="GtkLabel" id="label1">
+ <object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="ypad">5</property>
- <property name="label" translatable="yes">Any additional comments useful for diagnosing the problem?</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow4">
- <property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
- <property name="can_focus">True</property>
<child>
- <object class="GtkTextView" id="tv_comment">
+ <object class="GtkScrolledWindow" id="scrolledwindow4">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="wrap_mode">word</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">GTK_SHADOW_OUT</property>
+ <child>
+ <object class="GtkTextView" id="tv_comment">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="wrap_mode">word</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEventBox" id="eb_comment">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">You need to fill the how to before you can proceed...</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ </child>
</object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
@@ -140,14 +155,14 @@
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
- <property name="ypad">5</property>
<property name="label" translatable="yes">&lt;b&gt;Your comments are not private.&lt;/b&gt; They may be included into publicly visible problem reports.</property>
<property name="use_markup">True</property>
+ <property name="wrap">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">3</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
@@ -160,6 +175,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
<object class="GtkLabel" id="lbl_page5">
<property name="visible">True</property>
@@ -199,8 +215,8 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
- <property name="image_position">right</property>
<property name="use_underline">True</property>
+ <property name="image_position">right</property>
</object>
<packing>
<property name="expand">False</property>
@@ -239,12 +255,12 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
<object class="GtkLabel" id="lbl_analyze_log">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
- <property name="ypad">5</property>
<property name="label" translatable="yes">Analyzing did not start yet</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
@@ -258,8 +274,8 @@
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="can_focus">True</property>
+ <property name="shadow_type">out</property>
<child>
<object class="GtkTextView" id="tv_analyze_log">
<property name="visible">True</property>
@@ -281,10 +297,11 @@
<object class="GtkWindow" id="window4">
<property name="can_focus">False</property>
<child>
- <object class="GtkVBox" id="page_4">
+ <object class="GtkVBox" id="page_4_report">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
<object class="GtkLabel" id="lbl_page3">
<property name="visible">True</property>
@@ -326,8 +343,8 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
- <property name="image_position">right</property>
<property name="use_underline">True</property>
+ <property name="image_position">right</property>
</object>
<packing>
<property name="expand">False</property>
@@ -367,13 +384,13 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
<object class="GtkLabel" id="lbl_page4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
- <property name="ypad">0</property>
<property name="label" translatable="yes">Backtrace provides developers with details of the crash, helping them track down the source of the problem. Please review it and remove any sensitive data you would rather not share:</property>
<property name="wrap">True</property>
</object>
@@ -386,8 +403,8 @@
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="can_focus">True</property>
+ <property name="shadow_type">out</property>
<child>
<object class="GtkTextView" id="tv_backtrace">
<property name="visible">True</property>
@@ -561,10 +578,11 @@
<object class="GtkWindow" id="window6">
<property name="can_focus">False</property>
<child>
- <object class="GtkVBox" id="page_6">
+ <object class="GtkVBox" id="page_6_report">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
<object class="GtkLabel" id="lbl_page6">
<property name="visible">True</property>
@@ -655,8 +673,8 @@
<child>
<object class="GtkScrolledWindow" id="container_details2">
<property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="can_focus">True</property>
+ <property name="shadow_type">out</property>
<child>
<placeholder/>
</child>
@@ -673,16 +691,16 @@
<object class="GtkWindow" id="window7">
<property name="can_focus">False</property>
<child>
- <object class="GtkVBox" id="page_7">
+ <object class="GtkVBox" id="page_7_report">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
+ <property name="spacing">3</property>
<child>
<object class="GtkLabel" id="lbl_report_log">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
- <property name="ypad">5</property>
<property name="label" translatable="yes">Reporting did not start yet</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
@@ -696,8 +714,8 @@
<child>
<object class="GtkScrolledWindow" id="scrolledwindow6">
<property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
<property name="can_focus">True</property>
+ <property name="shadow_type">out</property>
<child>
<object class="GtkTextView" id="tv_report_log">
<property name="visible">True</property>
diff --git a/src/gui-wizard-gtk/wizard.h b/src/gui-wizard-gtk/wizard.h
index 5e341e34..772fcc05 100644
--- a/src/gui-wizard-gtk/wizard.h
+++ b/src/gui-wizard-gtk/wizard.h
@@ -24,7 +24,7 @@ void show_error_as_msgbox(const char *msg);
extern char *g_glade_file;
extern char *g_dump_dir_name;
extern char *g_analyze_events;
-extern char *g_reanalyze_events;
extern char *g_report_events;
extern problem_data_t *g_cd;
+extern int g_report_only;
void reload_problem_data_from_dump_dir(void);
diff --git a/src/include/abrtlib.h b/src/include/abrtlib.h
index 85bbb289..294afdda 100644
--- a/src/include/abrtlib.h
+++ b/src/include/abrtlib.h
@@ -76,6 +76,7 @@ int vdprintf(int d, const char *format, va_list ap);
#include "read_write.h"
#include "strbuf.h"
#include "hash_sha1.h"
+#include "parse_options.h"
#include "abrt_problem_data.h"
#include "abrt_types.h"
@@ -117,7 +118,7 @@ char *xmalloc_fgetline(FILE *file);
/* On error, copyfd_XX prints error messages and returns -1 */
enum {
- COPYFD_SPARSE = 1 << 0,
+ COPYFD_SPARSE = 1 << 0,
};
#define copyfd_eof abrt_copyfd_eof
off_t copyfd_eof(int src_fd, int dst_fd, int flags);
diff --git a/src/include/report/event_config.h b/src/include/report/event_config.h
index 5c898d9a..2e2783de 100644
--- a/src/include/report/event_config.h
+++ b/src/include/report/event_config.h
@@ -71,6 +71,7 @@ typedef struct
//char *action; //action description to show in gui like: Upload report to the Red Hat bugzilla"
char *description; // "Report to..."/"Save to file". Should be one sentence, not long
char *long_descr; // Long(er) explanation, if needed
+ char *creates_elements;
GList *options;
} event_config_t;
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 88671f54..8fb147ac 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -87,7 +87,7 @@ libabrt_dbus_la_LIBADD = \
libabrt_web_la_SOURCES = \
abrt_curl.h abrt_curl.c \
- abrt_xmlrpc.h abrt_xmlrpc.cpp
+ abrt_xmlrpc.h abrt_xmlrpc.c
libabrt_web_la_CPPFLAGS = \
-Wall -Wwrite-strings -Werror \
-I$(srcdir)/../include/report -I$(srcdir)/../include \
diff --git a/src/lib/abrt_curl.c b/src/lib/abrt_curl.c
index 1cb9391d..0802a49b 100644
--- a/src/lib/abrt_curl.c
+++ b/src/lib/abrt_curl.c
@@ -175,6 +175,40 @@ static size_t fread_with_reporting(void *ptr, size_t size, size_t nmemb, void *u
return fread(ptr, size, nmemb, fp);
}
+static int curl_debug(CURL *handle, curl_infotype it, char *buf, size_t bufsize, void *unused)
+{
+ if (logmode == 0)
+ return 0;
+
+ switch (it) {
+ case CURLINFO_TEXT: /* The data is informational text. */
+ log("curl: %.*s", (int) bufsize, buf);
+ break;
+ case CURLINFO_HEADER_IN: /* The data is header (or header-like) data received from the peer. */
+ log("curl rcvd header: '%.*s'", (int) bufsize, buf);
+ break;
+ case CURLINFO_HEADER_OUT: /* The data is header (or header-like) data sent to the peer. */
+ log("curl sent header: '%.*s'", (int) bufsize, buf);
+ break;
+ case CURLINFO_DATA_IN: /* The data is protocol data received from the peer. */
+ if (g_verbose >= 3)
+ log("curl rcvd data: '%.*s'", (int) bufsize, buf);
+ else
+ log("curl rcvd data %u bytes", (int) bufsize);
+ break;
+ case CURLINFO_DATA_OUT: /* The data is protocol data sent to the peer. */
+ if (g_verbose >= 3)
+ log("curl sent data: '%.*s'", (int) bufsize, buf);
+ else
+ log("curl sent data %u bytes", (int) bufsize);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
int
abrt_post(abrt_post_state_t *state,
const char *url,
@@ -203,14 +237,18 @@ abrt_post(abrt_post_state_t *state,
// curl will need it until curl_easy_cleanup.
state->errmsg[0] = '\0';
xcurl_easy_setopt_ptr(handle, CURLOPT_ERRORBUFFER, state->errmsg);
- // "Display a lot of verbose information about its operations.
- // Very useful for libcurl and/or protocol debugging and understanding.
- // The verbose information will be sent to stderr, or the stream set
- // with CURLOPT_STDERR"
- //xcurl_easy_setopt_long(handle, CURLOPT_VERBOSE, 1);
// Shut off the built-in progress meter completely
xcurl_easy_setopt_long(handle, CURLOPT_NOPROGRESS, 1);
+ if (g_verbose >= 2) {
+ // "Display a lot of verbose information about its operations.
+ // Very useful for libcurl and/or protocol debugging and understanding.
+ // The verbose information will be sent to stderr, or the stream set
+ // with CURLOPT_STDERR"
+ xcurl_easy_setopt_long(handle, CURLOPT_VERBOSE, 1);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_DEBUGFUNCTION, curl_debug);
+ }
+
// TODO: do we need to check for CURLE_URL_MALFORMAT error *here*,
// not in curl_easy_perform?
xcurl_easy_setopt_ptr(handle, CURLOPT_URL, url);
@@ -246,7 +284,7 @@ abrt_post(abrt_post_state_t *state,
if (basename) basename++;
else basename = data;
#if 0
- // Simple way, without custom reader function
+ // Simple way, without custom reader function
CURLFORMcode curlform_err = curl_formadd(&post, &last,
CURLFORM_PTRNAME, "file", // element name
CURLFORM_FILE, data, // filename to read from
diff --git a/src/lib/abrt_xmlrpc.c b/src/lib/abrt_xmlrpc.c
new file mode 100644
index 00000000..28d42325
--- /dev/null
+++ b/src/lib/abrt_xmlrpc.c
@@ -0,0 +1,137 @@
+/*
+ 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"
+
+void abrt_xmlrpc_die(xmlrpc_env *env)
+{
+ error_msg_and_die("fatal: XML-RPC(%d): %s", env->fault_code, env->fault_string);
+}
+
+void abrt_xmlrpc_error(xmlrpc_env *env)
+{
+ error_msg("error: XML-RPC (%d): %s", env->fault_code, env->fault_string);
+}
+
+struct abrt_xmlrpc *abrt_xmlrpc_new_client(const char *url, int ssl_verify)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ struct abrt_xmlrpc *ax = xzalloc(sizeof(struct abrt_xmlrpc));
+
+ /* This should be done at program startup, once. We do it in main */
+ /* xmlrpc_client_setup_global_const(&env); */
+
+ /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of
+ * XML-RPC response from server
+ *
+ * By default, XML data from the network may be no larger than 512K.
+ * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h
+ *
+ * Users reported trouble with 733402 byte long responses, hope raising the
+ * limit to 2*512k is enough
+ */
+ xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT);
+
+ struct xmlrpc_curl_xportparms curl_parms;
+ memset(&curl_parms, 0, sizeof(curl_parms));
+ /* curlParms.network_interface = NULL; - done by memset */
+ curl_parms.no_ssl_verifypeer = !ssl_verify;
+ curl_parms.no_ssl_verifyhost = !ssl_verify;
+#ifdef VERSION
+ curl_parms.user_agent = PACKAGE_NAME"/"VERSION;
+#else
+ curl_parms.user_agent = "abrt";
+#endif
+
+ struct xmlrpc_clientparms client_parms;
+ memset(&client_parms, 0, sizeof(client_parms));
+ client_parms.transport = "curl";
+ client_parms.transportparmsP = &curl_parms;
+ client_parms.transportparm_size = XMLRPC_CXPSIZE(user_agent);
+
+ xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS,
+ PACKAGE_NAME, VERSION,
+ &client_parms, XMLRPC_CPSIZE(transportparm_size),
+ &ax->ax_client);
+
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ ax->ax_server_info = xmlrpc_server_info_new(&env, url);
+ if (env.fault_occurred)
+ {
+ xmlrpc_client_destroy(ax->ax_client);
+ abrt_xmlrpc_die(&env);
+ }
+
+ return ax;
+}
+
+void abrt_xmlrpc_free_client(struct abrt_xmlrpc *ax)
+{
+ if (!ax)
+ return;
+
+ if (ax->ax_server_info)
+ xmlrpc_server_info_free(ax->ax_server_info);
+
+ if (ax->ax_client)
+ xmlrpc_client_destroy(ax->ax_client);
+
+ free(ax);
+}
+
+/* die or return expected results */
+xmlrpc_value *abrt_xmlrpc_call(struct abrt_xmlrpc *ax,
+ const char* method, const char* format, ...)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ xmlrpc_value* param = NULL;
+ const char* suffix;
+ va_list args;
+
+ va_start(args, format);
+ xmlrpc_build_value_va(&env, format, args, &param, &suffix);
+ va_end(args);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ xmlrpc_value* result = NULL;
+ if (*suffix != '\0')
+ {
+ xmlrpc_env_set_fault_formatted(
+ &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+ "specifier: '%s'. There must be exactly one argument.",
+ suffix);
+ }
+ else
+ {
+ xmlrpc_client_call2(&env, ax->ax_client, ax->ax_server_info, method,
+ param, &result);
+ }
+ xmlrpc_DECREF(param);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ return result;
+}
diff --git a/src/lib/abrt_xmlrpc.cpp b/src/lib/abrt_xmlrpc.cpp
deleted file mode 100644
index ae75a47f..00000000
--- a/src/lib/abrt_xmlrpc.cpp
+++ /dev/null
@@ -1,102 +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"
-
-void throw_xml_fault(xmlrpc_env *env)
-{
- error_msg_and_die("XML-RPC Fault(%d): %s", env->fault_code, env->fault_string);
-}
-
-void throw_if_xml_fault_occurred(xmlrpc_env *env)
-{
- if (env->fault_occurred)
- {
- throw_xml_fault(env);
- }
-}
-
-void abrt_xmlrpc_conn::new_xmlrpc_client(const char* url, bool ssl_verify)
-{
- m_pClient = NULL;
- m_pServer_info = NULL;
-
- xmlrpc_env env;
- xmlrpc_env_init(&env);
-
- /* This should be done at program startup, once. We do it in main */
- /* xmlrpc_client_setup_global_const(&env); */
-
- /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of
- * XML-RPC response from server
- *
- * By default, XML data from the network may be no larger than 512K.
- * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h
- *
- * Users reported trouble with 733402 byte long responses, hope raising the
- * limit to 2*512k is enough
- */
- xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT);
-
- struct xmlrpc_curl_xportparms curlParms;
- memset(&curlParms, 0, sizeof(curlParms));
- /* curlParms.network_interface = NULL; - done by memset */
- curlParms.no_ssl_verifypeer = !ssl_verify;
- curlParms.no_ssl_verifyhost = !ssl_verify;
-#ifdef VERSION
- curlParms.user_agent = PACKAGE_NAME"/"VERSION;
-#else
- curlParms.user_agent = "abrt";
-#endif
-
- struct xmlrpc_clientparms clientParms;
- memset(&clientParms, 0, sizeof(clientParms));
- clientParms.transport = "curl";
- clientParms.transportparmsP = &curlParms;
- clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent);
-
- xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS,
- PACKAGE_NAME, VERSION,
- &clientParms, XMLRPC_CPSIZE(transportparm_size),
- &m_pClient);
- if (env.fault_occurred)
- throw_xml_fault(&env);
-
- m_pServer_info = xmlrpc_server_info_new(&env, url);
- if (env.fault_occurred)
- {
- xmlrpc_client_destroy(m_pClient);
- m_pClient = NULL;
- throw_xml_fault(&env);
- }
-}
-
-void abrt_xmlrpc_conn::destroy_xmlrpc_client()
-{
- if (m_pServer_info)
- {
- xmlrpc_server_info_free(m_pServer_info);
- m_pServer_info = NULL;
- }
- if (m_pClient)
- {
- xmlrpc_client_destroy(m_pClient);
- m_pClient = NULL;
- }
-}
diff --git a/src/lib/abrt_xmlrpc.h b/src/lib/abrt_xmlrpc.h
index 93c5a9d6..5c94360f 100644
--- a/src/lib/abrt_xmlrpc.h
+++ b/src/lib/abrt_xmlrpc.h
@@ -19,37 +19,30 @@
#ifndef ABRT_XMLRPC_H_
#define ABRT_XMLRPC_H_ 1
-#include <curl/curl.h>
+/* include/stdint.h: typedef int int32_t;
+ * include/xmlrpc-c/base.h: typedef int32_t xmlrpc_int32;
+ */
+
#include <xmlrpc-c/base.h>
#include <xmlrpc-c/client.h>
#ifdef __cplusplus
-/*
- * Simple class holding XMLRPC connection data.
- * Used mainly to ensure we always destroy xmlrpc client and server_info
- * on return or throw.
- */
-struct abrt_xmlrpc_conn {
- xmlrpc_client* m_pClient;
- xmlrpc_server_info* m_pServer_info;
-
- abrt_xmlrpc_conn(const char* url, bool ssl_verify) { new_xmlrpc_client(url, ssl_verify); }
- /* this never throws exceptions - calls C functions only */
- ~abrt_xmlrpc_conn() { destroy_xmlrpc_client(); }
-
- void new_xmlrpc_client(const char* url, bool ssl_verify);
- void destroy_xmlrpc_client();
-};
+extern "C" {
#endif
+struct abrt_xmlrpc {
+ xmlrpc_client *ax_client;
+ xmlrpc_server_info *ax_server_info;
+};
-#ifdef __cplusplus
-extern "C" {
-#endif
+struct abrt_xmlrpc *abrt_xmlrpc_new_client(const char *url, int ssl_verify);
+void abrt_xmlrpc_free_client(struct abrt_xmlrpc *ax);
+void abrt_xmlrpc_die(xmlrpc_env *env) __attribute__((noreturn));
+void abrt_xmlrpc_error(xmlrpc_env *env);
-/* Utility functions */
-void throw_xml_fault(xmlrpc_env *env);
-void throw_if_xml_fault_occurred(xmlrpc_env *env);
+/* die or return expected results */
+xmlrpc_value *abrt_xmlrpc_call(struct abrt_xmlrpc *ax,
+ const char *method, const char *format, ...);
#ifdef __cplusplus
}
diff --git a/src/lib/event_config.c b/src/lib/event_config.c
index 66f9beba..9f48af54 100644
--- a/src/lib/event_config.c
+++ b/src/lib/event_config.c
@@ -55,6 +55,7 @@ void free_event_config(event_config_t *p)
//free(p->action);
free(p->description);
free(p->long_descr);
+ free(p->creates_elements);
for (opt = p->options; opt; opt = opt->next)
free_event_option(opt->data);
g_list_free(p->options);
diff --git a/src/lib/event_xml_parser.c b/src/lib/event_xml_parser.c
index 17e8a72e..5bf5f411 100644
--- a/src/lib/event_xml_parser.c
+++ b/src/lib/event_xml_parser.c
@@ -25,6 +25,7 @@
#define LONG_DESCR_ELEMENT "long-description"
#define ALLOW_EMPTY_ELEMENT "allow-empty"
#define NOTE_HTML_ELEMENT "note-html"
+#define CREATES_ELEMENT "creates-elements"
#define OPTION_ELEMENT "option"
//#define ACTION_ELEMENT "action"
#define NAME_ELEMENT "name"
@@ -55,16 +56,16 @@ static const char *const option_types[] =
static char *get_element_lang(struct my_parse_data *parse_data, const gchar **att_names, const gchar **att_values)
{
char *short_locale_end = strchr(parse_data->cur_locale, '_');
- VERB2 log("locale: %s", parse_data->cur_locale);
+ VERB3 log("locale: %s", parse_data->cur_locale);
int i;
for (i = 0; att_names[i] != NULL; ++i)
{
- VERB2 log("attr: %s:%s", att_names[i], att_values[i]);
+ VERB3 log("attr: %s:%s", att_names[i], att_values[i]);
if (strcmp(att_names[i], "xml:lang") == 0)
{
if (strcmp(att_values[i], parse_data->cur_locale) == 0)
{
- VERB2 log("found translation for: %s", parse_data->cur_locale);
+ VERB3 log("found translation for: %s", parse_data->cur_locale);
return xstrdup(att_values[i]);
}
@@ -74,7 +75,7 @@ static char *get_element_lang(struct my_parse_data *parse_data, const gchar **at
if (short_locale_end
&& strncmp(att_values[i], parse_data->cur_locale, short_locale_end - parse_data->cur_locale) == 0
) {
- VERB2 log("found translation for shortlocale: %s", parse_data->cur_locale);
+ VERB3 log("found translation for shortlocale: %s", parse_data->cur_locale);
return xstrndup(att_values[i], short_locale_end - parse_data->cur_locale);
}
}
@@ -297,11 +298,18 @@ static void text(GMarkupParseContext *context,
if (strcmp(inner_element, ACTION_ELEMENT) == 0)
{
VERB2 log("action description:'%s'", text_copy);
- free(ui->eo_action);
- ui->eo_action = text_copy;
+ free(ui->action);
+ ui->action = text_copy;
return;
}
*/
+ if (strcmp(inner_element, CREATES_ELEMENT) == 0)
+ {
+ VERB2 log("creates_elements:'%s'", text_copy);
+ free(ui->creates_elements);
+ ui->creates_elements = text_copy;
+ return;
+ }
if (strcmp(inner_element, NAME_ELEMENT) == 0)
{
if (parse_data->attribute_lang != NULL) /* if it isn't for other locale */
@@ -321,7 +329,7 @@ static void text(GMarkupParseContext *context,
}
if (strcmp(inner_element, DESCRIPTION_ELEMENT) == 0)
{
- VERB2 log("event description:'%s'", text_copy);
+ VERB3 log("event description:'%s'", text_copy);
if (parse_data->attribute_lang != NULL) /* if it isn't for other locale */
{
@@ -339,7 +347,7 @@ static void text(GMarkupParseContext *context,
}
if (strcmp(inner_element, LONG_DESCR_ELEMENT) == 0)
{
- VERB2 log("event long description:'%s'", text_copy);
+ VERB3 log("event long description:'%s'", text_copy);
if (parse_data->attribute_lang != NULL) /* if it isn't for other locale */
{
diff --git a/src/lib/hooklib.c b/src/lib/hooklib.c
index 3bde4dfa..804a2394 100644
--- a/src/lib/hooklib.c
+++ b/src/lib/hooklib.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2009 RedHat inc.
+ Copyright (C) 2009 RedHat inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/lib/hooklib.h b/src/lib/hooklib.h
index c140f951..1add7d09 100644
--- a/src/lib/hooklib.h
+++ b/src/lib/hooklib.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2009 RedHat inc.
+ Copyright (C) 2009 RedHat inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/lib/parse_options.c b/src/lib/parse_options.c
index d9ce8a2f..4e4a3e08 100644
--- a/src/lib/parse_options.c
+++ b/src/lib/parse_options.c
@@ -24,12 +24,55 @@
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-void show_usage_and_die(const char *usage, const struct options *opt)
+const char *g_progname;
+
+const char *abrt_init(char **argv)
{
- fprintf(stderr, _("Usage: %s\n"), usage);
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ g_progname = strrchr(argv[0], '/');
+ if (g_progname)
+ g_progname++;
+ else
+ g_progname = argv[0];
+
+ char *pfx = getenv("ABRT_PROG_PREFIX");
+ if (pfx && string_to_bool(pfx))
+ msg_prefix = g_progname;
+
+ return g_progname;
+}
- if (opt->type != OPTION_GROUP)
- fputc('\n', stderr);
+void export_abrt_envvars(int pfx)
+{
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+ if (pfx)
+ {
+ putenv((char*)"ABRT_PROG_PREFIX=1");
+ msg_prefix = g_progname;
+ }
+}
+
+void show_usage_and_die(const char *usage, const struct options *opt)
+{
+ fputs(_("Usage: "), stderr);
+ while (*usage)
+ {
+ int len = strchrnul(usage, '\b') - usage;
+ if (len > 0)
+ {
+ fprintf(stderr, "%.*s", len, usage);
+ usage += len;
+ }
+ if (*usage == '\b')
+ {
+ fputs(g_progname, stderr);
+ usage++;
+ }
+ }
+ fputs("\n\n", stderr);
for (; opt->type != OPTION_END; opt++)
{
diff --git a/src/lib/parse_options.h b/src/lib/parse_options.h
index 8c784b96..d86662e2 100644
--- a/src/lib/parse_options.h
+++ b/src/lib/parse_options.h
@@ -23,6 +23,13 @@
extern "C" {
#endif
+const char *abrt_init(char **argv);
+#define export_abrt_envvars abrt_export_abrt_envvars
+void export_abrt_envvars(int pfx);
+#define g_progname abrt_g_progname
+extern const char *g_progname;
+
+
enum parse_opt_type {
OPTION_BOOL,
OPTION_GROUP,
@@ -50,12 +57,12 @@ struct options {
* h - help
*/
#define OPT_END() { OPTION_END }
-#define OPT_BOOL(s, l, v, h) { OPTION_BOOL, (s), (l), (v), NULL, (h) }
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
-#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), "NUM", (h) }
-#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
-#define OPT_OPTSTRING(s, l, v, a, h) { OPTION_OPTSTRING, (s), (l), (v), (a), (h) }
-#define OPT_LIST(s, l, v, a, h) { OPTION_LIST, (s), (l), (v), (a), (h) }
+#define OPT_BOOL( s, l, v, h) { OPTION_BOOL , (s), (l), (v), NULL , (h) }
+#define OPT_INTEGER( s, l, v, h) { OPTION_INTEGER , (s), (l), (v), "NUM", (h) }
+#define OPT_STRING( s, l, v, a, h) { OPTION_STRING , (s), (l), (v), (a) , (h) }
+#define OPT_OPTSTRING(s, l, v, a, h) { OPTION_OPTSTRING, (s), (l), (v), (a) , (h) }
+#define OPT_LIST( s, l, v, a, h) { OPTION_LIST , (s), (l), (v), (a) , (h) }
#define OPT__VERBOSE(v) OPT_BOOL('v', "verbose", (v), _("Be verbose"))
diff --git a/src/lib/read_write.c b/src/lib/read_write.c
index da067f78..fe85fcfb 100644
--- a/src/lib/read_write.c
+++ b/src/lib/read_write.c
@@ -88,7 +88,7 @@ ssize_t full_write(int fd, const void *buf, size_t len)
/* user can do another write to know the error code */
return total;
}
- return cc; /* write() returns -1 on failure. */
+ return cc; /* write() returns -1 on failure. */
}
total += cc;
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, &param, &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
diff --git a/src/report-python/reportmodule.c b/src/report-python/reportmodule.c
index c40cfaf6..92f435ce 100644
--- a/src/report-python/reportmodule.c
+++ b/src/report-python/reportmodule.c
@@ -30,7 +30,7 @@ static PyMethodDef module_methods[] = {
{ NULL }
};
-#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
diff --git a/src/retrace/create.wsgi b/src/retrace/create.wsgi
index e26ae0cc..7c5f81b4 100644
--- a/src/retrace/create.wsgi
+++ b/src/retrace/create.wsgi
@@ -104,7 +104,7 @@ def application(environ, start_response):
return response(start_response, "403 Forbidden",
"Required file \"%s\" is missing" % required_file)
- Popen(["/usr/bin/abrt-retrace-worker", "%d" % taskid])
+ call(["/usr/bin/abrt-retrace-worker", "%d" % taskid])
return response(start_response, "201 Created", "",
[("X-Task-Id", "%d" % taskid),
diff --git a/src/retrace/retrace.py b/src/retrace/retrace.py
index 39588d90..56bfd46e 100644
--- a/src/retrace/retrace.py
+++ b/src/retrace/retrace.py
@@ -21,7 +21,7 @@ PACKAGE_PARSER = re.compile("^(.+)-([0-9]+(\.[0-9]+)*-[0-9]+)\.([^-]+)$")
DF_OUTPUT_PARSER = re.compile("^([^ ^\t]*)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+%)[ \t]+(.*)$")
DU_OUTPUT_PARSER = re.compile("^([0-9]+)")
URL_PARSER = re.compile("^/([0-9]+)/?")
-WORKER_RUNNING_PARSER = re.compile("^([0-9]+)[ \t]+[0-9]+[ \t]+([^ ^\t]+)[ \t]+.*abrt-retrace-worker ([0-9]+)$")
+WORKER_RUNNING_PARSER = re.compile("^[ \t]*([0-9]+)[ \t]+[0-9]+[ \t]+([^ ^\t]+)[ \t]+.*abrt-retrace-worker ([0-9]+)$")
HANDLE_ARCHIVE = {
"application/x-xz-compressed-tar": {
@@ -315,7 +315,7 @@ def kill_process_and_childs(process_id, ps_output=None):
return result
-def cleanup_task(taskid):
+def cleanup_task(taskid, gc=True):
null = open("/dev/null", "w")
savedir = "%s/%d" % (CONFIG["SaveDir"], taskid)
@@ -334,12 +334,13 @@ def cleanup_task(taskid):
except:
pass
- try:
- log = open(newlog, "a")
- log.write("Killed by garbage collector\n")
- log.close()
- except:
- pass
+ if gc:
+ try:
+ log = open(newlog, "a")
+ log.write("Killed by garbage collector\n")
+ log.close()
+ except:
+ pass
null.close()
diff --git a/src/retrace/worker.c b/src/retrace/worker.c
index 83773e61..a49f74c3 100644
--- a/src/retrace/worker.c
+++ b/src/retrace/worker.c
@@ -16,6 +16,7 @@ int main(int argc, char **argv)
int i;
struct passwd *apache_user;
const char *apache_username = "apache";
+ pid_t pid;
if (argc != 2)
{
@@ -50,7 +51,20 @@ int main(int argc, char **argv)
/* required by mock to be able to write into result directory */
setenv("SUDO_GID", "0", 1);
- /* launch worker.py */
+ /* fork and launch worker.py */
+ pid = fork();
+
+ if (pid < 0)
+ {
+ fputs("Unable to fork.", stderr);
+ return 6;
+ }
+
+ /* parent - exit */
+ if (pid > 0)
+ return 0;
+
+ /* child */
sprintf(command, "/usr/bin/python /usr/share/abrt-retrace/worker.py \"%s\"", argv[1]);
pipe = popen(command, "r");
if (pipe == NULL)
diff --git a/src/retrace/worker.py b/src/retrace/worker.py
index ad2badf1..24defa66 100755
--- a/src/retrace/worker.py
+++ b/src/retrace/worker.py
@@ -27,8 +27,9 @@ def set_status(statusid):
def fail(exitcode):
"Kills script with given exitcode"
- LOG.close()
set_status(STATUS_FAIL)
+ LOG.close()
+ cleanup_task(int(taskid), False)
sys.exit(exitcode)
def retrace_run(errorcode, cmd):
@@ -287,7 +288,7 @@ if __name__ == "__main__":
else:
LOG.write("OK\n")
- # publish bactkrace and log
+ # publish backtrace and log
set_status(STATUS_FINISHING)
try: