/*
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 "internal_libreport.h"
static bool rejected_name(const char *name, char **v, int flags)
{
bool r = is_in_string_list(name, (const char *const *)v);
if (flags & MAKEDESC_WHITELIST)
r = !r;
return r;
}
static
char *make_description_item_multiline(const char *name, const char *content)
{
char *eol = strchr(content, '\n');
if (!eol)
return NULL;
struct strbuf *buf = strbuf_new();
strbuf_append_str(buf, name);
strbuf_append_str(buf, ":\n");
for (;;)
{
eol = strchrnul(content, '\n');
strbuf_append_strf(buf, ":%.*s\n", (int)(eol - content), content);
if (*eol == '\0' || eol[1] == '\0')
break;
content = eol + 1;
}
return strbuf_free_nobuf(buf);
}
static int list_cmp(const char *s1, const char *s2)
{
static const char *const list_order[] = {
FILENAME_REASON ,
FILENAME_TIME ,
FILENAME_CMDLINE ,
FILENAME_PACKAGE ,
FILENAME_UID ,
FILENAME_COUNT ,
NULL
};
int s1_index = index_of_string_in_list(s1, list_order);
int s2_index = index_of_string_in_list(s2, list_order);
if(s1_index < 0 && s2_index < 0)
return strcmp(s1, s2);
if(s1_index < 0)
return 1;
if(s2_index < 0)
return -1;
return s1_index - s2_index;
}
char *make_description(problem_data_t *problem_data, char **names_to_skip,
unsigned max_text_size, unsigned desc_flags)
{
INITIALIZE_LIBREPORT();
struct strbuf *buf_dsc = strbuf_new();
const char *type = problem_data_get_content_or_NULL(problem_data,
FILENAME_TYPE);
GList *list = g_hash_table_get_keys(problem_data);
list = g_list_sort(list, (GCompareFunc)list_cmp);
GList *l;
/* Print one-liners. Format:
* NAME1: <maybe more spaces>VALUE1
* NAME2: <maybe more spaces>VALUE2
*/
bool empty = true;
l = list;
while (l)
{
const char *key = l->data;
l = l->next;
/* Skip items we are not interested in */
//TODO: optimize by doing this once, not 3 times:
if (names_to_skip
&& rejected_name(key, names_to_skip, desc_flags))
continue;
struct problem_item *item = g_hash_table_lookup(problem_data, key);
if (!item)
continue;
if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST))
continue;
if ((item->flags & CD_FLAG_TXT)
&& !strchr(item->content, '\n')
) {
char *formatted = problem_item_format(item);
char *output = formatted ? formatted : item->content;
int pad = 16 - (strlen(key) + 2);
if (pad < 0) pad = 0;
bool done = false;
if (strcmp(FILENAME_REASON, key) == 0)
{
const char *crash_func = problem_data_get_content_or_NULL(problem_data,
FILENAME_CRASH_FUNCTION);
if((done = (bool)crash_func))
strbuf_append_strf(buf_dsc, "%s: %*s%s(): %s\n", key, pad, "", crash_func, output);
}
else if (strcmp(FILENAME_UID, key) == 0)
{
const char *username = problem_data_get_content_or_NULL(problem_data,
FILENAME_USERNAME);
if((done = (bool)username))
strbuf_append_strf(buf_dsc, "%s: %*s%s (%s)\n", key, pad, "", output, username);
}
if (!done)
strbuf_append_strf(buf_dsc, "%s: %*s%s\n", key, pad, "", output);
empty = false;
free(formatted);
}
}
if (desc_flags & MAKEDESC_SHOW_URLS)
{
if (problem_data_get_content_or_NULL(problem_data, FILENAME_NOT_REPORTABLE) != NULL)
strbuf_append_strf(buf_dsc, "%s%*s%s\n", _("Reported:"), 16 - strlen(_("Reported:")), "" , _("cannot be reported"));
else
{
const char *reported_to = problem_data_get_content_or_NULL(problem_data, FILENAME_REPORTED_TO);
if (reported_to != NULL)
{
GList *reports = read_entire_reported_to_data(reported_to);
/* The value part begins on 17th column */
/* 0123456789ABCDEF*/
const char *pad_prefix = " ";
char *first_prefix = xasprintf("%s%*s", _("Reported:"), 16 - strlen(_("Reported:")), "");
const char *prefix = first_prefix;
for (GList *iter = reports; iter != NULL; iter = g_list_next(iter))
{
const report_result_t *const report = (report_result_t *)iter->data;
if (report->url == NULL)
continue;
strbuf_append_strf(buf_dsc, "%s%s\n", prefix, report->url);
if (prefix == first_prefix)
{ /* Only the first URL is prefixed by 'Reported:' */
empty = false;
prefix = pad_prefix;
}
}
free(first_prefix);
g_list_free_full(reports, (GDestroyNotify)free_report_result);
}
}
}
bool append_empty_line = !empty;
if (desc_flags & MAKEDESC_SHOW_FILES)
{
/* Print file info. Format:
* <empty line if needed>
* NAME1: <maybe more spaces>Binary file, NNN bytes
* NAME2: <maybe more spaces>Text file, NNN bytes
*
* In many cases, it is useful to know how big binary files are
* (for example, helps with diagnosing bug upload problems)
*/
l = list;
while (l)
{
const char *key = l->data;
l = l->next;
/* Skip items we are not interested in */
if (names_to_skip
&& rejected_name(key, names_to_skip, desc_flags))
continue;
struct problem_item *item = g_hash_table_lookup(problem_data, key);
if (!item)
continue;
if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST))
continue;
if ((item->flags & CD_FLAG_BIN)
|| ((item->flags & CD_FLAG_TXT) && strlen(item->content) > max_text_size)
) {
if (append_empty_line)
strbuf_append_char(buf_dsc, '\n');
append_empty_line = false;
unsigned long size = 0;
int stat_err = problem_item_get_size(item, &size);
/* We don't print item->content for CD_FLAG_BIN, as it is
* always "/path/to/dump/dir/KEY" - not informative.
*/
int pad = 16 - (strlen(key) + 2);
if (pad < 0) pad = 0;
strbuf_append_strf(buf_dsc,
(!stat_err ? "%s: %*s%s file, %lu bytes\n" : "%s: %*s%s file\n"),
key,
pad, "",
((item->flags & CD_FLAG_BIN) ? "Binary" : "Text"),
size
);
empty = false;
}
}
}
if (desc_flags & MAKEDESC_SHOW_MULTILINE)
{
/* Print multi-liners. Format:
* <empty line if needed>
* NAME:
* :LINE1
* :LINE2
* :LINE3
*/
l = list;
while (l)
{
const char *key = l->data;
l = l->next;
/* Skip items we are not interested in */
if (names_to_skip
&& rejected_name(key, names_to_skip, desc_flags))
continue;
struct problem_item *item = g_hash_table_lookup(problem_data, key);
if (!item)
continue;
if ((desc_flags & MAKEDESC_SHOW_ONLY_LIST) && !(item->flags & CD_FLAG_LIST))
continue;
if ((item->flags & CD_FLAG_TXT)
&& (strlen(item->content) <= max_text_size
|| (!strcmp(type, "Kerneloops") && !strcmp(key, FILENAME_BACKTRACE))))
{
char *formatted = problem_item_format(item);
char *output = make_description_item_multiline(key, formatted ? formatted : item->content);
if (output)
{
if (!empty)
strbuf_append_str(buf_dsc, "\n");
strbuf_append_str(buf_dsc, output);
empty = false;
free(output);
}
free(formatted);
}
}
}
g_list_free(list);
return strbuf_free_nobuf(buf_dsc);
}
/* Items we don't want to include to bz / logger */
static const char *const blacklisted_items[] = {
CD_DUMPDIR ,
FILENAME_ANALYZER ,
FILENAME_TYPE ,
FILENAME_COREDUMP ,
FILENAME_HOSTNAME ,
FILENAME_DUPHASH ,
FILENAME_UUID ,
FILENAME_COUNT ,
FILENAME_TAINTED_SHORT,
FILENAME_ARCHITECTURE,
FILENAME_PACKAGE,
FILENAME_OS_RELEASE,
FILENAME_OS_INFO,
FILENAME_COMPONENT,
FILENAME_REASON,
NULL
};
char* make_description_logger(problem_data_t *problem_data, unsigned max_text_size)
{
return make_description(
problem_data,
(char**)blacklisted_items,
max_text_size,
MAKEDESC_SHOW_FILES | MAKEDESC_SHOW_MULTILINE
);
}