[PATCH] abrt-cli: add report-only option (trac#281)
by Miroslav Lichvar
---
src/cli/abrt-cli.txt | 5 ++++-
src/cli/cli.c | 15 ++++++++++-----
src/cli/report.c | 39 +++++++++++++++++++++------------------
src/cli/report.h | 1 +
4 files changed, 36 insertions(+), 24 deletions(-)
diff --git a/src/cli/abrt-cli.txt b/src/cli/abrt-cli.txt
index ea3111c..412f1b1 100644
--- a/src/cli/abrt-cli.txt
+++ b/src/cli/abrt-cli.txt
@@ -17,7 +17,7 @@ SYNOPSIS
'abrt-cli' [-vsp] -a[y] DUMP_DIR
-'abrt-cli' [-vsp] -r[y] DUMP_DIR
+'abrt-cli' [-vsp] -r[y|o] DUMP_DIR
'abrt-cli' [-vsp] -d DUMP_DIR
@@ -58,6 +58,9 @@ OPTIONS
-y, --always::
Noninteractive: don't ask questions, assume positive answer to all of them
+-o, --report-only::
+ Report pre-filled problem data, ask only which reporter should be used.
+
-v, --verbose::
Be verbose
diff --git a/src/cli/cli.c b/src/cli/cli.c
index 0e4ce98..bf3077d 100644
--- a/src/cli/cli.c
+++ b/src/cli/cli.c
@@ -170,7 +170,7 @@ int main(int argc, char** argv)
"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] -r[y|o] DUMP_DIR\n"
"or: \b [-vsp] -d DUMP_DIR"
);
enum {
@@ -187,9 +187,10 @@ int main(int argc, char** argv)
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,
+ OPT_o = 1 << 11,
+ OPT_v = 1 << 12,
+ OPT_s = 1 << 13,
+ OPT_p = 1 << 14,
};
/* Keep enum above and order of options below in sync! */
struct options program_options[] = {
@@ -205,6 +206,7 @@ int main(int argc, char** argv)
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_BOOL( 'o', "report-only" , NULL, _("Report pre-filled problem data")),
OPT__VERBOSE(&g_verbose),
OPT_BOOL( 's', NULL , NULL, _("Log to syslog")),
OPT_BOOL( 'p', NULL , NULL, _("Add program names to log")),
@@ -244,6 +246,7 @@ int main(int argc, char** argv)
char *dump_dir_name = argv[0];
bool full = (opts & OPT_f);
bool always = (opts & OPT_y);
+ bool report_only = (opts & OPT_o);
if (!D_list)
{
@@ -335,7 +338,9 @@ int main(int argc, char** argv)
}
}
- exitcode = report(dump_dir_name, (always ? CLI_REPORT_BATCH : 0));
+ exitcode = report(dump_dir_name,
+ (always ? CLI_REPORT_BATCH : 0) |
+ (report_only ? CLI_REPORT_ONLY : 0));
if (exitcode == -1)
error_msg_and_die("Crash '%s' not found", dump_dir_name);
break;
diff --git a/src/cli/report.c b/src/cli/report.c
index 9e0455b..1b41597 100644
--- a/src/cli/report.c
+++ b/src/cli/report.c
@@ -684,34 +684,37 @@ int report(const char *dump_dir_name, int flags)
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)
+ if (!(flags & CLI_REPORT_ONLY))
{
- GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n');
- free(analyze_events_as_lines);
+ char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze");
+ dd_close(dd);
- char *event = select_event_option(list_analyze_events);
- list_free_with_free(list_analyze_events);
+ if (analyze_events_as_lines && *analyze_events_as_lines)
+ {
+ GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n');
+ free(analyze_events_as_lines);
- int analyzer_result = run_analyze_event(dump_dir_name, event);
- free(event);
+ char *event = select_event_option(list_analyze_events);
+ list_free_with_free(list_analyze_events);
- if (analyzer_result != 0)
- return 1;
- }
+ int analyzer_result = run_analyze_event(dump_dir_name, event);
+ free(event);
- /* Load problem_data from (possibly updated by analyze) dump dir */
- dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
- if (!dd)
- return -1;
+ if (analyzer_result != 0)
+ return 1;
+ }
+
+ /* Load problem_data from (possibly updated by analyze) dump dir */
+ dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ 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);
dd_close(dd);
- if (!(flags & CLI_REPORT_BATCH))
+ if (!(flags & (CLI_REPORT_BATCH | CLI_REPORT_ONLY)))
{
/* Open text editor and give a chance to review the backtrace etc */
create_fields_for_editor(problem_data);
diff --git a/src/cli/report.h b/src/cli/report.h
index a393784..5b1fdcb 100644
--- a/src/cli/report.h
+++ b/src/cli/report.h
@@ -29,6 +29,7 @@ GList *str_to_glist(char *str, int delim);
/* Report the crash */
enum {
CLI_REPORT_BATCH = 1 << 0,
+ CLI_REPORT_ONLY = 1 << 1,
};
int report(const char *dump_dir_name, int flags);
--
1.7.4.4
12 years, 10 months
ABRT test suite and other testing efforts
by Michal Nowak
Hi -
we (David Kutalek, Izidor Matusov & myself) thought of creating
ABRT test suite and broaden and formalize overall testing efforts.
We came out with mind map (both source XML and bitmap attached)
describing what we have and what we'd like to have in the future.
Basic idea we believe in is that testing in some specific field
should fit the underlying environment. So giving an example:
Testing in Fedora should use Fedora's own infrastructure, that
means e.g. running internal test suite as part of Koji build and
AutoQA tests on Fedora-specific regressions and sanity matters.
Let me describe the mind map in detail, branch by branch:
* Fedora
+ Fedora Test Day
- Global public testing by Fedora community, should focus on
both Features (e.g. Retrace Server), infrastructure
compatibility (e.g. reporting to Bugzilla) and desktop
integration.
+ Fedora AutoQA Tests
- By use of Fedora AutoQA infrastructure we should test only
sanity integration and regressions specific to Fedora only
(e.g. bugzilla plugin worked with mozilla.org Bugzilla but
not with the Red Hat instance).
+ Release Criteria
- Update to Fedora stable release will be pushed only iff
FedoraQA tests passed & upstream test suite passed in Koji.
* RHEL - Not intended to be discussed publicly.
* Internal Test Suite
+ Unit Tests
- Some folks generally think that writing unit tests *after*
the actual code is written is more or less useless because
the test may match broken code. Although I understand possible
issues it's far superb to what we have now. Also considering
ABRT being still more or less rewritten to enhance it's design,
it's quite a good chance we do it the right way.
We'd like to see unit tests in framework of your choice for
both core and plugins.
+ Regression Tests
- Having wide range of regression tests in sources needs some
time, it may be a pain and it may make you feel like you are
coding tests and not developing the useful code instead.
However, in my opinion, it saves you the time you have to
invest in fixing regressions.
Also with regression tests you may slightly less fear
breakage and anger from users. You may also view it as a step
further in providing more stable code; for example code review
was a positive step in that direction.
+ BuildBot
- BuildBot builds ABRT when new commits we the other day, runs
tests, results sends to the mailing list and perhaps even
copies resulting RPMs to fedorapeople repo for testers
consumption.
* Infrastructure
- Just that ABRT should be installed by default in Fedora and
enable in the GUI. Similar tasks for RHEL and RH
infrastructure.
More on the implementation details:
For Fedora, AutoQA should be the choice. Upstream unit tests
- not sure, up to you. Upstream regression and sanity tests -
we'd prefer Beaker/beakerlib, since we are really familiar
with it and is easy to play with.
Let me know, what you think guys about the mind map, your
frameworks of choice, anything.
Thanks,
Michal
12 years, 10 months
py-libreport bindings
by Jiri Moskovcak
It's a really quick hack to make the example work, needs some tuning..
how I test it:
cd src/report-python/.libs
$ python
and run:
import _pyreport
pd = _pyreport.problem_data()
pd.add("foo", "bar")
pd.add("description", "python-libreport test bug")
_pyreport.report(pd)
12 years, 11 months
[PATCH] rhzb#707074 abrt dupe detection needs to ignore some parts of kernel traces
by Nikola Pajkovsky
kernel bt looks like
[<ffffffff81047d33>] __might_sleep+0xeb/0xf0
[<ffffffff81111413>] __kmalloc+0x79/0x10d
[<ffffffff811113ff>] __kmalloc+0x79/0x10d
[<ffffffff81023683>] ? alloc_ioapic_entries+0x27/0x87
[<ffffffff81023683>] alloc_ioapic_entries+0x27/0x87
[<ffffffff81021cc0>] lapic_resume+0x3a/0x24d
[<ffffffff81396db2>] ? cpufreq_resume+0x2d/0xac
^ memory skipped ^^^ ^ taken ^^^^^^^^^^
| skipped | skipped
parser strip out only functions in the middle of bt and generates hash
from top 6 functions
Signed-off-by: Nikola Pajkovsky <npajkovs(a)redhat.com>
---
examples/oops-same-as-oops4.right | 20 +++++
examples/oops4.right | 20 +++++
src/plugins/abrt-action-analyze-oops.c | 128 +++++++++++++-------------------
3 files changed, 93 insertions(+), 75 deletions(-)
create mode 100644 examples/oops-same-as-oops4.right
create mode 100644 examples/oops4.right
diff --git a/examples/oops-same-as-oops4.right b/examples/oops-same-as-oops4.right
new file mode 100644
index 0000000..584e014
--- /dev/null
+++ b/examples/oops-same-as-oops4.right
@@ -0,0 +1,20 @@
+BUG: sleeping function called from invalid context at mm/slub.c:795
+in_atomic(): 0, irqs_disabled(): 1, pid: 8811, name: pm-hibernate
+Pid: 8811, comm: pm-hibernate Tainted: P 2.6.38.5-24.fc15.x86_64 #1
+Call Trace:
+ [<ffffffff81047d33>] __might_sleep+0xeb/0xf0
+ [<ffffffff811113ff>] __kmalloc+0x79/0x10d
+ [<ffffffff81023683>] ? alloc_ioapic_entries+0x27/0x87
+ [<ffffffff81023683>] alloc_ioapic_entries+0x27/0x87
+ [<ffffffff81021cc0>] lapic_resume+0x3a/0x24d
+ [<ffffffff81396d86>] ? cpufreq_resume+0x2d/0xac
+ [<ffffffff812e4981>] __sysdev_resume+0x22/0xc2
+ [<ffffffff812e4ad4>] sysdev_resume+0xb3/0xf8
+ [<ffffffff81087670>] hibernation_snapshot+0x182/0x23e
+ [<ffffffff81087c83>] hibernate+0xc0/0x1b1
+ [<ffffffff810866b7>] state_store+0x5c/0xc5
+ [<ffffffff8122a4cb>] kobj_attr_store+0x17/0x19
+ [<ffffffff811793bd>] sysfs_write_file+0x111/0x14d
+ [<ffffffff81121815>] vfs_write+0xac/0xf3
+ [<ffffffff81121a04>] sys_write+0x4a/0x6e
+ [<ffffffff81009bc2>] system_call_fastpath+0x16/0x1b
diff --git a/examples/oops4.right b/examples/oops4.right
new file mode 100644
index 0000000..cb138c6
--- /dev/null
+++ b/examples/oops4.right
@@ -0,0 +1,20 @@
+BUG: sleeping function called from invalid context at mm/slub.c:795
+in_atomic(): 0, irqs_disabled(): 1, pid: 13398, name: pm-suspend
+Pid: 13398, comm: pm-suspend Not tainted 2.6.38.6-27.fc15.x86_64 #1
+Call Trace:
+ [<ffffffff81047d33>] __might_sleep+0xeb/0xf0
+ [<ffffffff81111413>] __kmalloc+0x79/0x10d
+ [<ffffffff81023683>] ? alloc_ioapic_entries+0x27/0x87
+ [<ffffffff81023683>] alloc_ioapic_entries+0x27/0x87
+ [<ffffffff81021cc0>] lapic_resume+0x3a/0x24d
+ [<ffffffff81396db2>] ? cpufreq_resume+0x2d/0xac
+ [<ffffffff812e49b1>] __sysdev_resume+0x22/0xc2
+ [<ffffffff812e4b04>] sysdev_resume+0xb3/0xf8
+ [<ffffffff81086f3b>] suspend_devices_and_enter+0x149/0x1d6
+ [<ffffffff8108709f>] enter_state+0xd7/0x129
+ [<ffffffff81086726>] state_store+0xaf/0xc5
+ [<ffffffff8122a4d7>] kobj_attr_store+0x17/0x19
+ [<ffffffff811793d1>] sysfs_write_file+0x111/0x14d
+ [<ffffffff81121829>] vfs_write+0xac/0xf3
+ [<ffffffff81121a18>] sys_write+0x4a/0x6e
+ [<ffffffff81009bc2>] system_call_fastpath+0x16/0x1b
diff --git a/src/plugins/abrt-action-analyze-oops.c b/src/plugins/abrt-action-analyze-oops.c
index 02ba3e9..33c6500 100644
--- a/src/plugins/abrt-action-analyze-oops.c
+++ b/src/plugins/abrt-action-analyze-oops.c
@@ -21,10 +21,55 @@
static void hash_oops_str(char hash_str[SHA1_RESULT_LEN*2 + 1], char *oops_buf, const char *oops_ptr)
{
- unsigned char old_c;
- unsigned char c = 0;
+ // Example of call trace part of oops:
+ // Call Trace:
+ // [<f88e11c7>] ? radeon_cp_resume+0x7d/0xbc [radeon]
+ // [<f88745f8>] ? drm_ioctl+0x1b0/0x225 [drm]
+ // [<f88e114a>] ? radeon_cp_resume+0x0/0xbc [radeon]
+ // [<c049b1c0>] ? vfs_ioctl+0x50/0x69
+ // [<c049b414>] ? do_vfs_ioctl+0x23b/0x247
+ // [<c0460a56>] ? audit_syscall_entry+0xf9/0x123
+ // [<c049b460>] ? sys_ioctl+0x40/0x5c
+ // [<c0403c76>] ? syscall_call+0x7/0xb
+ struct strbuf *kernel_bt = strbuf_new();
+ char *call_trace = strstr(oops_buf, "Call Trace");
+ if (call_trace)
+ {
+ call_trace += sizeof("Call Trace\n");
+ char *end_line = strchr(call_trace, '\n');
+ int i = 0;
+ while (end_line)
+ {
+ char *line = xstrndup(call_trace, end_line - call_trace);
+
+ char *p = skip_whitespace(line);
+ char *end_mem_block = strchr(p, ' ');
+ if (!end_mem_block)
+ error_msg_and_die("no [<mem>] mark");
+
+ end_mem_block = skip_whitespace(end_mem_block);
- char *dst = oops_buf;
+ /* +1 for '?' and +1 for space after '?' == +2*/
+ if (end_mem_block && *end_mem_block == '?')
+ end_mem_block += 2;
+
+ /* strip out offset +off/len */
+ char *begin_off_len = strchr(end_mem_block, '+');
+ if (!begin_off_len)
+ error_msg_and_die("no +offset/len at the end of bt");
+
+ char *function = xstrndup(end_mem_block, begin_off_len - end_mem_block);
+ strbuf_append_strf(kernel_bt, "%s\n", function);
+ free(line);
+ if (i == 5)
+ break;
+
+ ++i;
+ call_trace += end_line - call_trace + 1;
+ end_line = strchr(call_trace, '\n');
+ }
+ goto gen_hash;
+ }
/* Special-case: if the first line is of form:
* WARNING: at net/wireless/core.c:614 wdev_cleanup_work+0xe9/0x120 [cfg80211]() (Not tainted)
@@ -39,88 +84,21 @@ static void hash_oops_str(char hash_str[SHA1_RESULT_LEN*2 + 1], char *oops_buf,
p = strchrnul(p + 1, ' '); /* skip function_name+0xNN/0xNNN */
oops_ptr += sizeof("WARNING: at ")-1;
while (oops_ptr < p)
- {
- *dst++ = *oops_ptr++;
- }
- goto gen_hash;
- }
- }
-
- while (1)
- {
- old_c = c;
- c = *oops_ptr++;
- if (!c)
- break;
- if (c == '\n')
- {
- // Exclude some lines which have process name - in some oops classes
- // process name is irrelevant and changes with every oops.
- // Lines we filter out:
- // Pid: 8003, comm: Xorg Not tainted (2.6.27.9-159.fc10.i686 #1)
- // Process Xorg (pid: 8003, ti=f0a0c000 task=f2380000 task.ti=f0a0c000)
- if (strncmp(oops_ptr, "Pid: ", 5) == 0
- || strncmp(oops_ptr, "Process ", 8) == 0
- ) {
- while (*oops_ptr && *oops_ptr != '\n')
- oops_ptr++;
- continue;
- }
- }
- if (!isalnum(old_c))
- {
- if (c >= '0' && c <= '9')
- {
- // Convert all (possibly hex) numbers to just one '0'
- if (c == '0' && *oops_ptr == 'x') // "0xSOMETHING"
- oops_ptr++;
- while (isxdigit(*oops_ptr))
- oops_ptr++;
- c = '0';
- }
- else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
- {
- // This *may be* a hex number without 0x prefix: "f0a0c000"
- // Check that it indeed is, and replace with '0'
- const char *oops_ptr2 = oops_ptr;
- while (isxdigit(*oops_ptr2))
- oops_ptr2++;
- // Does it end in a letter which is not a hex digit?
- // (Example: "abcw" is not a hex number, "abc " is)
- if (!isalpha(*oops_ptr2))
- {
- // It's "abc " case. Skip the "abc" string
- oops_ptr = oops_ptr2;
- c = '0';
- }
- // else: hash the string as-is
- }
+ strbuf_append_char(kernel_bt, *oops_ptr++);
}
- // TODO: Drop call trace tail - in interrupt-driven oopses,
- // everything before interrupt is irrelevant.
- // Example of call trace part of oops:
- // Call Trace:
- // [<f88e11c7>] ? radeon_cp_resume+0x7d/0xbc [radeon]
- // [<f88745f8>] ? drm_ioctl+0x1b0/0x225 [drm]
- // [<f88e114a>] ? radeon_cp_resume+0x0/0xbc [radeon]
- // [<c049b1c0>] ? vfs_ioctl+0x50/0x69
- // [<c049b414>] ? do_vfs_ioctl+0x23b/0x247
- // [<c0460a56>] ? audit_syscall_entry+0xf9/0x123
- // [<c049b460>] ? sys_ioctl+0x40/0x5c
- // [<c0403c76>] ? syscall_call+0x7/0xb
-
- *dst++ = c;
}
- gen_hash: ;
+gen_hash: ;
char hash_bytes[SHA1_RESULT_LEN];
sha1_ctx_t sha1ctx;
sha1_begin(&sha1ctx);
- sha1_hash(&sha1ctx, oops_buf, dst - oops_buf);
+ sha1_hash(&sha1ctx, kernel_bt->buf, kernel_bt->len);
sha1_end(&sha1ctx, hash_bytes);
+ strbuf_free(kernel_bt);
bin2hex(hash_str, hash_bytes, SHA1_RESULT_LEN)[0] = '\0';
+ VERB3 log("hash: %s", hash_str);
}
int main(int argc, char **argv)
--
1.7.1
12 years, 11 months
[PATCH] report C API improvements
by Jiri Moskovcak
Added few features we found missing:
- flag to tell libreport to wait for the child so application can get
it's status code and react according to it
- added example of 3rd part reporting (abrt-gtk.c:188)
- in abrt-gui go to Help->Report problem with ABRT
- I will either remove this part or improve it if others find it usefull
- report() report_dir() generates "global_uuid" using contents of all
fields, so we at least eliminate the 100% same reports (unlikely to
happen, but won't hurt to have it and also makes bugzilla reporter happy)
NOTE: if you want to test bugzilla you need to edit conf file and add line:
EVENT=report_Bugzilla analyzer="libreport" abrt-action-bugzilla
TODO:
- standalone example (maybe not needed?)
- update python bindings
- python example using python-meh + python bindings
Please review,
Jirka
12 years, 11 months
[PATCH] retrace server documentation
by Michal Toman
Hi Karel,
I went through the Retrace server's manual and modified some minor
issues. The included patch is a summary of your latest commits into ABRT
repo and my changes in retrace-server repo. Please take a look.
Michal
12 years, 11 months
[PATCH] add additional headers into abrt_post()
by Nikola Pajkovsky
Signed-off-by: Nikola Pajkovsky <npajkovs(a)redhat.com>
---
src/lib/abrt_curl.c | 19 +++++++++++++------
src/lib/abrt_curl.h | 13 ++++++++++---
src/plugins/abrt_rh_support.c | 12 ++++++++++--
3 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/src/lib/abrt_curl.c b/src/lib/abrt_curl.c
index 0802a49..739da7d 100644
--- a/src/lib/abrt_curl.c
+++ b/src/lib/abrt_curl.c
@@ -213,6 +213,7 @@ int
abrt_post(abrt_post_state_t *state,
const char *url,
const char *content_type,
+ const char **additional_headers,
const char *data,
off_t data_size)
{
@@ -337,14 +338,20 @@ abrt_post(abrt_post_state_t *state,
if (!httpheader_list)
error_msg_and_die("out of memory");
free(content_type_header);
- xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list);
}
- // Override "Accept: text/plain": helps convince server to send plain-text
- // error messages in the body of HTTP error responses [not verified to work]
- httpheader_list = curl_slist_append(httpheader_list, "Accept: text/plain");
- if (!httpheader_list)
- error_msg_and_die("out of memory");
+ for (; additional_headers && *additional_headers; additional_headers++)
+ {
+ httpheader_list = curl_slist_append(httpheader_list, *additional_headers);
+ if (!httpheader_list)
+ error_msg_and_die("out of memory");
+ }
+
+ if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA
+ || additional_headers)
+ {
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list);
+ }
// Add User-Agent: ABRT/N.M
httpheader_list = curl_slist_append(httpheader_list, "User-Agent: ABRT/"VERSION);
diff --git a/src/lib/abrt_curl.h b/src/lib/abrt_curl.h
index 77b4605..ad2afc1 100644
--- a/src/lib/abrt_curl.h
+++ b/src/lib/abrt_curl.h
@@ -62,31 +62,38 @@ int
abrt_post(abrt_post_state_t *state,
const char *url,
const char *content_type,
+ const char **additional_headers,
const char *data,
off_t data_size);
static inline int
abrt_post_string(abrt_post_state_t *state,
const char *url,
const char *content_type,
+ const char **additional_headers,
const char *str)
{
- return abrt_post(state, url, content_type, str, ABRT_POST_DATA_STRING);
+ return abrt_post(state, url, content_type, additional_headers,
+ str, ABRT_POST_DATA_STRING);
}
static inline int
abrt_post_file(abrt_post_state_t *state,
const char *url,
const char *content_type,
+ const char **additional_headers,
const char *filename)
{
- return abrt_post(state, url, content_type, filename, ABRT_POST_DATA_FROMFILE);
+ return abrt_post(state, url, content_type, additional_headers,
+ filename, ABRT_POST_DATA_FROMFILE);
}
static inline int
abrt_post_file_as_form(abrt_post_state_t *state,
const char *url,
const char *content_type,
+ const char **additional_headers,
const char *filename)
{
- return abrt_post(state, url, content_type, filename, ABRT_POST_DATA_FROMFILE_AS_FORM_DATA);
+ return abrt_post(state, url, content_type, additional_headers,
+ filename, ABRT_POST_DATA_FROMFILE_AS_FORM_DATA);
}
#ifdef __cplusplus
diff --git a/src/plugins/abrt_rh_support.c b/src/plugins/abrt_rh_support.c
index b83f041..05b448b 100644
--- a/src/plugins/abrt_rh_support.c
+++ b/src/plugins/abrt_rh_support.c
@@ -323,7 +323,13 @@ send_report_to_new_case(const char* baseURL,
);
case_state->username = username;
case_state->password = password;
- abrt_post_string(case_state, case_url, "application/xml", case_data);
+
+ const char *headers[] = {
+ "Accept: text/plain",
+ NULL
+ };
+
+ abrt_post_string(case_state, case_url, "application/xml", headers, case_data);
char *case_location = find_header_in_abrt_post_state(case_state, "Location:");
switch (case_state->http_resp_code)
@@ -385,7 +391,9 @@ send_report_to_new_case(const char* baseURL,
);
atch_state->username = username;
atch_state->password = password;
- abrt_post_file_as_form(atch_state, atch_url, "application/binary", report_file_name);
+
+ abrt_post_file_as_form(atch_state, atch_url, "application/binary", headers,
+ report_file_name);
char *atch_location = find_header_in_abrt_post_state(atch_state, "Location:");
switch (atch_state->http_resp_code)
--
1.7.1
12 years, 11 months
Please review src/lib/run_event.c rewrite
by Denys Vlasenko
Jiri, I overhauled run_event.c in order to fix
"EVENT=post-create component=mypkg doesn't work" bug.
It compiles, now I will run-test it.
Meanwhile, can you take a look at the code?
Both at correctness, and whether code is readable?
The bulk of changes are in load_rule_list() and pop_next_command()
functions.
--
vda
/*
Copyright (C) 2009 Zdenek Prikryl (zprikryl(a)redhat.com)
Copyright (C) 2009 RedHat inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <glob.h>
#include "abrtlib.h"
struct run_event_state *new_run_event_state()
{
return xzalloc(sizeof(struct run_event_state));
}
void free_run_event_state(struct run_event_state *state)
{
if (state)
{
free_commands(state);
free(state);
}
}
/* Asyncronous command execution */
/* It is not yet clear whether we need to re-parse event config file
* and re-check the elements in dump dir after each command.
*
* Consider this config file:
*
* EVENT=e cmd1
* EVENT=e foo=bar cmd2
* EVENT=e foo=baz cmd3
*
* Imagine that element foo existed and was equal to bar at the beginning.
* After cmd1, should we execute cmd2 if element foo disappeared?
* After cmd1/2, should we execute cmd3 if element foo changed value to baz?
*
* We used to read entire config file and select a list of commands to execute,
* checking all conditions in the beginning. It is a bit more simple to code up.
*
* This proved to be bad for use cases where, for example, post-create rule
* for a specified package needs to run:
*
* EVENT=post-create
* abrt-action-save-package-data
* EVENT=post-create component=mypkg
* my_handling
*
* Problem here is that "component" element is created by
* abrt-action-save-package-data! Pre-selecting rules excludes second rule.
*
* Now we read entire config but do NOT select commands to execute,
* we check conditions of every next rule *directly before its execution*.
*
* Anyway, list of commands machinery is encapsulated in struct run_event_state,
* and public async API:
* prepare_commands(state, dir, event);
* spawn_next_command(state, dir, event);
* free_commands(state);
* does not expose the way we select rules to execute.
*/
struct rule {
GList *conditions;
char *command; /* never NULL */
};
static void free_rule_list(GList *rule_list)
{
while (rule_list)
{
struct rule *cur_rule = rule_list->data;
list_free_with_free(cur_rule->conditions);
free(cur_rule->command);
free(cur_rule);
GList *next = rule_list->next;
free(rule_list);
rule_list = next;
}
}
/* Stop-gap measure against infinite recursion */
#define MAX_recursion_depth 32
static GList *load_rule_list(GList *rule_list,
const char *conf_file_name,
unsigned recursion_depth
) {
FILE *conffile = fopen(conf_file_name, "r");
if (!conffile)
{
error_msg("Can't open '%s'", conf_file_name);
return rule_list;
}
/* Read and remember rules */
char *next_line = xmalloc_fgetline(conffile);
while (next_line)
{
/* Read and concatenate all lines in a rule */
char *line = next_line;
while (1)
{
next_line = xmalloc_fgetline(conffile);
if (!next_line || !isblank(next_line[0]))
break;
char *old_line = line;
line = xasprintf("%s\n%s", line, next_line);
free(old_line);
free(next_line);
}
char *p = skip_whitespace(line);
if (*p == '\0' || *p == '#')
goto next_line; /* empty or comment line, skip */
//VERB3 log("%s: line '%s'", __func__, p);
/* Handle "include" directive */
if (recursion_depth < MAX_recursion_depth
&& strncmp(p, "include", strlen("include")) == 0
&& isblank(p[strlen("include")])
) {
/* "include GLOB_PATTERN" */
p = skip_whitespace(p + strlen("include"));
const char *last_slash;
char *name_to_glob;
if (*p != '/'
&& (last_slash = strrchr(conf_file_name, '/')) != NULL
)
/* GLOB_PATTERN is relative, and this include is in path/to/file.conf
* Construct path/to/GLOB_PATTERN:
*/
name_to_glob = xasprintf("%.*s%s", (int)(last_slash - conf_file_name + 1), conf_file_name, p);
else
/* Either GLOB_PATTERN is absolute, or this include is in file.conf
* (no slashes in its name). Use unchanged GLOB_PATTERN:
*/
name_to_glob = xstrdup(p);
glob_t globbuf;
memset(&globbuf, 0, sizeof(globbuf));
//VERB3 log("%s: globbing '%s'", __func__, name_to_glob);
glob(name_to_glob, 0, NULL, &globbuf);
free(name_to_glob);
char **name = globbuf.gl_pathv;
if (name) while (*name)
{
//VERB3 log("%s: recursing into '%s'", __func__, *name);
rule_list = load_rule_list(rule_list, *name, recursion_depth + 1);
//VERB3 log("%s: returned from '%s'", __func__, *name);
name++;
}
globfree(&globbuf);
goto next_line;
}
/* Rule has form: [VAR=VAL]... PROG [ARGS] */
struct rule *cur_rule = xzalloc(sizeof(*cur_rule));
while (1) /* word loop */
{
char *end_word = skip_non_whitespace(p);
/* If there is no '=' in this word... */
char *line_val = strchr(p, '=');
if (!line_val || line_val >= end_word)
break; /* ...we found the start of a command */
cur_rule->conditions = g_list_append(cur_rule->conditions, xstrndup(p, end_word - p));
/* Go to next word */
p = skip_whitespace(end_word);
} /* end of word loop */
VERB1 log("Adding '%s'", p);
cur_rule->command = xstrdup(p);
rule_list = g_list_append(rule_list, cur_rule);
next_line:
free(line);
} /* end of line loop */
fclose(conffile);
return rule_list;
}
/* Note: may return NULL but leave list non-NULL.
* This happens on error, such as "dump dir can't be opened"
*/
static char* pop_next_command(GList **pp_rule_list,
char **pp_event_name, /* reports EVENT value thru this, if not NULL on entry */
struct dump_dir **pp_dd, /* use *pp_dd for access to dump dir, if non-NULL */
const char *dump_dir_name,
const char *pfx,
unsigned pfx_len
)
{
char *command = NULL;
struct dump_dir *dd = pp_dd ? *pp_dd : NULL;
GList *rule_list = *pp_rule_list;
while (rule_list)
{
struct rule *cur_rule = rule_list->data;
GList *condition = cur_rule->conditions;
while (condition)
{
const char *cond_str = condition->data;
const char *eq_sign = strchr(cond_str, '=');
/* Is it "EVENT=foo"? */
if (strncmp(cond_str, "EVENT=", 6) == 0)
{
if (strncmp(eq_sign + 1, pfx, pfx_len) != 0)
goto next_rule; /* prefix doesn't match */
if (pp_event_name)
{
free(*pp_event_name);
*pp_event_name = xstrdup(eq_sign + 1);
}
}
else
{
/* Read from dump dir and compare */
if (!dd)
{
/* Without dir to match, we assume match for all conditions */
if (!dump_dir_name)
goto next_cond;
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
{
goto ret; /* error (note: dd_opendir logged error msg) */
}
}
char *var_name = xstrndup(cond_str, eq_sign - cond_str);
char *real_val = dd_load_text_ext(dd, var_name, DD_FAIL_QUIETLY_ENOENT);
free(var_name);
int vals_differ = strcmp(real_val, eq_sign + 1);
free(real_val);
/* Do values match? */
if (vals_differ) /* no */
{
//VERB3 log("var '%s': '%.*s'!='%s', skipping line",
// p,
// (int)(strchrnul(real_val, '\n') - real_val), real_val,
// eq_sign);
goto next_rule;
}
}
next_cond:
/* We are here if current condition is satisfied */
condition = condition->next;
}
/* We are here if all conditions are satisfied */
command = cur_rule->command;
next_rule:
*pp_rule_list = rule_list->next;
free(rule_list);
list_free_with_free(cur_rule->conditions);
/*free(cur_rule->command); - WRONG! we might be returning it! */
if (command)
{
free(cur_rule);
break;
}
free(cur_rule->command); /* _now_ it is ok */
free(cur_rule);
rule_list = *pp_rule_list;
} /* while (rule_list) */
ret:
if (pp_dd)
*pp_dd = dd;
else
dd_close(dd);
return command;
}
void free_commands(struct run_event_state *state)
{
free_rule_list(state->rule_list);
state->rule_list = NULL;
state->command_out_fd = -1;
state->command_pid = 0;
}
int prepare_commands(struct run_event_state *state,
const char *dump_dir_name,
const char *event
) {
free_commands(state);
state->children_count = 0;
GList *rule_list = load_rule_list(NULL, CONF_DIR"/abrt_event.conf", /*recursion_depth:*/ 0);
state->rule_list = rule_list;
return rule_list != NULL;
}
int spawn_next_command(struct run_event_state *state,
const char *dump_dir_name,
const char *event
) {
char *cmd = pop_next_command(&state->rule_list,
NULL, /* don't return event_name */
NULL, /* NULL &dd: we match by... */
dump_dir_name, /* ...dirname */
event, strlen(event)+1 /* for this event name exactly (not prefix) */
);
if (!cmd)
return -1;
/* We count it even if fork fails. The counter isn't meant
* to count *successful* forks, it is meant to let caller know
* whether the event we run has *any* handlers configured, or not.
*/
state->children_count++;
VERB1 log("Executing '%s'", cmd);
/* Export some useful environment variables for children */
char *env_vec[3];
/* Just exporting dump_dir_name isn't always ok: it can be "."
* and some children want to cd to other directory but still
* be able to find dump directory by using $DUMP_DIR...
*/
char *full_name = realpath(dump_dir_name, NULL);
env_vec[0] = xasprintf("DUMP_DIR=%s", (full_name ? full_name : dump_dir_name));
free(full_name);
env_vec[1] = xasprintf("EVENT=%s", event);
env_vec[2] = NULL;
char *argv[4];
argv[0] = (char*)"/bin/sh"; // TODO: honor $SHELL?
argv[1] = (char*)"-c";
argv[2] = cmd;
argv[3] = NULL;
int pipefds[2];
state->command_pid = fork_execv_on_steroids(
EXECFLG_INPUT_NUL + EXECFLG_OUTPUT + EXECFLG_ERR2OUT,
argv,
pipefds,
/* env_vec: */ env_vec,
/* dir: */ dump_dir_name,
/* uid(unused): */ 0
);
state->command_out_fd = pipefds[0];
free(env_vec[0]);
free(env_vec[1]);
free(cmd);
return 0;
}
/* Synchronous command execution:
*/
int run_event_on_dir_name(struct run_event_state *state,
const char *dump_dir_name,
const char *event
) {
prepare_commands(state, dump_dir_name, event);
/* Execute every command in shell */
int retval = 0;
while (spawn_next_command(state, dump_dir_name, event) >= 0)
{
/* Consume log from stdout */
FILE *fp = fdopen(state->command_out_fd, "r");
if (!fp)
die_out_of_memory();
char *buf;
while ((buf = xmalloc_fgetline(fp)) != NULL)
{
if (state->logging_callback)
buf = state->logging_callback(buf, state->logging_param);
free(buf);
}
fclose(fp); /* Got EOF, close. This also closes state->command_out_fd */
/* Wait for child to actually exit, collect status */
int status;
waitpid(state->command_pid, &status, 0);
retval = WEXITSTATUS(status);
if (WIFSIGNALED(status))
retval = WTERMSIG(status) + 128;
if (retval != 0)
break;
if (state->post_run_callback)
{
retval = state->post_run_callback(dump_dir_name, state->post_run_param);
if (retval != 0)
break;
}
}
free_commands(state);
return retval;
}
int run_event_on_problem_data(struct run_event_state *state, problem_data_t *data, const char *event)
{
state->children_count = 0;
struct dump_dir *dd = create_dump_dir_from_problem_data(data, NULL);
if (!dd)
return -1;
char *dir_name = xstrdup(dd->dd_dirname);
dd_close(dd);
int r = run_event_on_dir_name(state, dir_name, event);
g_hash_table_remove_all(data);
dd = dd_opendir(dir_name, /*flags:*/ 0);
free(dir_name);
if (dd)
{
load_problem_data_from_dump_dir(data, dd);
dd_delete(dd);
}
return r;
}
char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char *pfx)
{
struct strbuf *result = strbuf_new();
GList *rule_list = load_rule_list(NULL, CONF_DIR"/abrt_event.conf", /*recursion_depth:*/ 0);
unsigned pfx_len = strlen(pfx);
for (;;)
{
/* Retrieve each cmd, and fetch its EVENT=foo value */
char *event_name = NULL;
char *cmd = pop_next_command(&rule_list,
&event_name, /* return event_name */
(dd ? &dd : NULL), /* match this dd... */
dump_dir_name, /* ...or if NULL, this dirname */
pfx, pfx_len /* for events with this prefix */
);
free(cmd);
if (!cmd)
break;
if (event_name)
{
/* Append "EVENT\n" - only if it is not there yet */
unsigned e_len = strlen(event_name);
char *p = result->buf;
while (p && *p)
{
if (strncmp(p, event_name, e_len) == 0 && p[e_len] == '\n')
goto skip; /* This event is already in the result */
p = strchr(p, '\n');
if (p)
p++;
}
strbuf_append_strf(result, "%s\n", event_name);
skip:
free(event_name);
}
}
free_rule_list(rule_list);
return strbuf_free_nobuf(result);
}
12 years, 11 months
[PATCH] Localize retrace server and client
by Michal Toman
Localize both server and client. You need to regenerate abrt.pot and .po
files. A server with sk_SK locale (probably) working is running on
10.34.24.254, you can try to use this one.
Please review.
Michal
12 years, 11 months