|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Copyright (C) 2014 ABRT team
|
|
Packit |
8ea169 |
* Copyright (C) 2014 RedHat Inc
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8ea169 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8ea169 |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
8ea169 |
* (at your option) any later version.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8ea169 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8ea169 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8ea169 |
* GNU General Public License for more details.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
#include <satyr/stacktrace.h>
|
|
Packit |
8ea169 |
#include <satyr/koops/stacktrace.h>
|
|
Packit |
8ea169 |
#include <satyr/thread.h>
|
|
Packit |
8ea169 |
#include <satyr/koops/frame.h>
|
|
Packit |
8ea169 |
#include <satyr/frame.h>
|
|
Packit |
8ea169 |
#include <satyr/normalize.h>
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#include "oops-utils.h"
|
|
Packit |
8ea169 |
#include "libabrt.h"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int abrt_oops_process_list(GList *oops_list, const char *dump_location, const char *analyzer, int flags)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
unsigned errors = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int oops_cnt = g_list_length(oops_list);
|
|
Packit |
8ea169 |
if (oops_cnt != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Found oopses: %d", oops_cnt);
|
|
Packit |
8ea169 |
if ((flags & ABRT_OOPS_PRINT_STDOUT))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
int i = 0;
|
|
Packit |
8ea169 |
while (i < oops_cnt)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *kernel_bt = (char*)g_list_nth_data(oops_list, i++);
|
|
Packit |
8ea169 |
char *tainted_short = kernel_tainted_short(kernel_bt);
|
|
Packit |
8ea169 |
if (tainted_short)
|
|
Packit |
8ea169 |
log_warning("Kernel is tainted '%s'", tainted_short);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(tainted_short);
|
|
Packit |
8ea169 |
printf("\nVersion: %s", kernel_bt);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (dump_location != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Creating problem directories");
|
|
Packit |
8ea169 |
errors = abrt_oops_create_dump_dirs(oops_list, dump_location, analyzer, flags);
|
|
Packit |
8ea169 |
if (errors)
|
|
Packit |
8ea169 |
log_warning("%d errors while dumping oopses", errors);
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* This marker in syslog file prevents us from
|
|
Packit |
8ea169 |
* re-parsing old oopses. The only problem is that we
|
|
Packit |
8ea169 |
* can't be sure here that the file we are watching
|
|
Packit |
8ea169 |
* is the same file where syslog(xxx) stuff ends up.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
syslog(LOG_WARNING,
|
|
Packit |
8ea169 |
"Reported %u kernel oopses to Abrt",
|
|
Packit |
8ea169 |
oops_cnt
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* If we are run by a log watcher, this delays log rescan
|
|
Packit |
8ea169 |
* (because log watcher waits to us to terminate)
|
|
Packit |
8ea169 |
* and possibly prevents dreaded "abrt storm".
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
int unreported_cnt = oops_cnt - ABRT_OOPS_MAX_DUMPED_COUNT;
|
|
Packit |
8ea169 |
if (g_abrt_oops_sleep_woke_up_on_signal <= 0 &&
|
|
Packit |
8ea169 |
(unreported_cnt > 0 && (flags & ABRT_OOPS_THROTTLE_CREATION)))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Quadratic throttle time growth, but careful to not overflow in "n*n" */
|
|
Packit |
8ea169 |
int n = unreported_cnt > 30 ? 30 : unreported_cnt;
|
|
Packit |
8ea169 |
n = n * n;
|
|
Packit |
8ea169 |
if (n > 9)
|
|
Packit |
8ea169 |
log_warning(_("Sleeping for %d seconds"), n);
|
|
Packit |
8ea169 |
abrt_oops_signaled_sleep(n); /* max 15 mins */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return errors;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* returns number of errors */
|
|
Packit |
8ea169 |
unsigned abrt_oops_create_dump_dirs(GList *oops_list, const char *dump_location, const char *analyzer, int flags)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const int oops_cnt = g_list_length(oops_list);
|
|
Packit |
8ea169 |
unsigned countdown = ABRT_OOPS_MAX_DUMPED_COUNT; /* do not report hundreds of oopses */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_notice("Saving %u oopses as problem dirs", oops_cnt >= countdown ? countdown : oops_cnt);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *cmdline_str = xmalloc_fopen_fgetline_fclose("/proc/cmdline");
|
|
Packit |
8ea169 |
char *fips_enabled = xmalloc_fopen_fgetline_fclose("/proc/sys/crypto/fips_enabled");
|
|
Packit |
8ea169 |
char *proc_modules = xmalloc_open_read_close("/proc/modules", /*maxsize:*/ NULL);
|
|
Packit |
8ea169 |
char *suspend_stats = xmalloc_open_read_close("/sys/kernel/debug/suspend_stats", /*maxsize:*/ NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
time_t t = time(NULL);
|
|
Packit |
8ea169 |
const char *iso_date = iso_date_string(&t);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
pid_t my_pid = getpid();
|
|
Packit |
8ea169 |
unsigned idx = 0;
|
|
Packit |
8ea169 |
unsigned errors = 0;
|
|
Packit |
8ea169 |
while (idx < oops_cnt)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char base[sizeof("oops-YYYY-MM-DD-hh:mm:ss-%lu-%lu") + 2 * sizeof(long)*3];
|
|
Packit |
8ea169 |
sprintf(base, "oops-%s-%lu-%lu", iso_date, (long)my_pid, (long)idx);
|
|
Packit |
8ea169 |
char *path = concat_path_file(dump_location, base);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_create(path, /*fs owner*/0, DEFAULT_DUMP_DIR_MODE);
|
|
Packit |
8ea169 |
if (dd)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_create_basic_files(dd, /*no uid*/(uid_t)-1L, NULL);
|
|
Packit |
8ea169 |
abrt_oops_save_data_in_dump_dir(dd, (char*)g_list_nth_data(oops_list, idx++), proc_modules);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ANALYZER, "abrt-oops");
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_TYPE, "Kerneloops");
|
|
Packit |
8ea169 |
if (cmdline_str)
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CMDLINE, cmdline_str);
|
|
Packit |
8ea169 |
if (proc_modules)
|
|
Packit |
8ea169 |
dd_save_text(dd, "proc_modules", proc_modules);
|
|
Packit |
8ea169 |
if (fips_enabled && strcmp(fips_enabled, "0") != 0)
|
|
Packit |
8ea169 |
dd_save_text(dd, "fips_enabled", fips_enabled);
|
|
Packit |
8ea169 |
if (suspend_stats)
|
|
Packit |
8ea169 |
dd_save_text(dd, "suspend_stats", suspend_stats);
|
|
Packit |
8ea169 |
if ((flags & ABRT_OOPS_WORLD_READABLE))
|
|
Packit |
8ea169 |
dd_set_no_owner(dd);
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
notify_new_path(path);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
errors++;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (--countdown == 0)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (dd && (flags & ABRT_OOPS_THROTTLE_CREATION))
|
|
Packit |
8ea169 |
if (abrt_oops_signaled_sleep(1) > 0)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(cmdline_str);
|
|
Packit |
8ea169 |
free(proc_modules);
|
|
Packit |
8ea169 |
free(fips_enabled);
|
|
Packit |
8ea169 |
free(suspend_stats);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return errors;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static char *abrt_oops_list_of_tainted_modules(const char *proc_modules)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct strbuf *result = strbuf_new();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *p = proc_modules;
|
|
Packit |
8ea169 |
for (;;)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const char *end = strchrnul(p, '\n');
|
|
Packit |
8ea169 |
const char *paren = strchrnul(p, '(');
|
|
Packit |
8ea169 |
/* We look for a line with this format:
|
|
Packit |
8ea169 |
* "kvm_intel 126289 0 - Live 0xf829e000 (taint_flags)"
|
|
Packit |
8ea169 |
* where taint_flags have letters
|
|
Packit |
8ea169 |
* (flags '+' and '-' indicate (un)loading, we must ignore them).
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
while (++paren < end)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if ((unsigned)(toupper(*paren) - 'A') <= 'Z'-'A')
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
strbuf_append_strf(result, result->len == 0 ? "%.*s" : ",%.*s",
|
|
Packit |
8ea169 |
(int)(strchrnul(p,' ') - p), p
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (*paren == ')')
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (*end == '\0')
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
p = end + 1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (result->len == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
strbuf_free(result);
|
|
Packit |
8ea169 |
return NULL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
return strbuf_free_nobuf(result);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
void abrt_oops_save_data_in_dump_dir(struct dump_dir *dd, char *oops, const char *proc_modules)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *first_line = oops;
|
|
Packit |
8ea169 |
char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */
|
|
Packit |
8ea169 |
*second_line++ = '\0';
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (first_line[0])
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_KERNEL, first_line);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_BACKTRACE, second_line);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* save crash_function into dumpdir */
|
|
Packit |
8ea169 |
char *error_message = NULL;
|
|
Packit |
8ea169 |
struct sr_stacktrace *stacktrace = sr_stacktrace_parse(SR_REPORT_KERNELOOPS,
|
|
Packit |
8ea169 |
(const char *)second_line, &error_message);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (stacktrace)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
sr_normalize_koops_stacktrace((struct sr_koops_stacktrace *)stacktrace);
|
|
Packit |
8ea169 |
/* stacktrace is the same as thread, there is no need to check return value */
|
|
Packit |
8ea169 |
struct sr_thread *thread = sr_stacktrace_find_crash_thread(stacktrace);
|
|
Packit |
8ea169 |
struct sr_koops_frame *frame = (struct sr_koops_frame *)sr_thread_frames(thread);
|
|
Packit |
8ea169 |
if (frame && frame->function_name)
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CRASH_FUNCTION, frame->function_name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
sr_stacktrace_free(stacktrace);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Can't parse stacktrace: %s", error_message);
|
|
Packit |
8ea169 |
free(error_message);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* check if trace doesn't have line: 'Your BIOS is broken' */
|
|
Packit |
8ea169 |
if (strstr(second_line, "Your BIOS is broken"))
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_NOT_REPORTABLE,
|
|
Packit |
8ea169 |
_("A kernel problem occurred because of broken BIOS. "
|
|
Packit |
8ea169 |
"Unfortunately, such problems are not fixable by kernel maintainers."));
|
|
Packit |
8ea169 |
/* check if trace doesn't have line: 'Your hardware is unsupported' */
|
|
Packit |
8ea169 |
else if (strstr(second_line, "Your hardware is unsupported"))
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_NOT_REPORTABLE,
|
|
Packit |
8ea169 |
_("A kernel problem occurred, but your hardware is unsupported, "
|
|
Packit |
8ea169 |
"therefore kernel maintainers are unable to fix this problem."));
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *tainted_short = kernel_tainted_short(second_line);
|
|
Packit |
8ea169 |
if (tainted_short)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_notice("Kernel is tainted '%s'", tainted_short);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_TAINTED_SHORT, tainted_short);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *tnt_long = kernel_tainted_long(tainted_short);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_TAINTED_LONG, tnt_long);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct strbuf *reason = strbuf_new();
|
|
Packit |
8ea169 |
const char *fmt = _("A kernel problem occurred, but your kernel has been "
|
|
Packit |
8ea169 |
"tainted (flags:%s). Explanation:\n%s"
|
|
Packit |
8ea169 |
"Kernel maintainers are unable to diagnose tainted reports.");
|
|
Packit |
8ea169 |
strbuf_append_strf(reason, fmt, tainted_short, tnt_long);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *modlist = !proc_modules ? NULL : abrt_oops_list_of_tainted_modules(proc_modules);
|
|
Packit |
8ea169 |
if (modlist)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
strbuf_append_strf(reason, _(" Tainted modules: %s."), modlist);
|
|
Packit |
8ea169 |
free(modlist);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_NOT_REPORTABLE, reason->buf);
|
|
Packit |
8ea169 |
strbuf_free(reason);
|
|
Packit |
8ea169 |
free(tainted_short);
|
|
Packit |
8ea169 |
free(tnt_long);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
// TODO: add "Kernel oops: " prefix, so that all oopses have recognizable FILENAME_REASON?
|
|
Packit |
8ea169 |
// kernel oops 1st line may look quite puzzling otherwise...
|
|
Packit |
8ea169 |
char *reason_pretty = NULL;
|
|
Packit |
8ea169 |
char *error = NULL;
|
|
Packit |
8ea169 |
struct sr_stacktrace *trace = sr_stacktrace_parse(SR_REPORT_KERNELOOPS, second_line, &error);
|
|
Packit |
8ea169 |
if (trace)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
reason_pretty = sr_stacktrace_get_reason(trace);
|
|
Packit |
8ea169 |
sr_stacktrace_free(trace);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
free(error);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (reason_pretty)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_REASON, reason_pretty);
|
|
Packit |
8ea169 |
free(reason_pretty);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_REASON, second_line);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int abrt_oops_signaled_sleep(int seconds)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
sigset_t set;
|
|
Packit |
8ea169 |
sigemptyset(&set);
|
|
Packit |
8ea169 |
sigaddset(&set, SIGTERM);
|
|
Packit |
8ea169 |
sigaddset(&set, SIGINT);
|
|
Packit |
8ea169 |
sigaddset(&set, SIGHUP);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct timespec timeout;
|
|
Packit |
8ea169 |
timeout.tv_sec = seconds;
|
|
Packit |
8ea169 |
timeout.tv_nsec = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return g_abrt_oops_sleep_woke_up_on_signal = sigtimedwait(&set, NULL, &timeout);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *abrt_oops_string_filter_regex(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
map_string_t *settings = new_map_string();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
load_abrt_plugin_conf_file("oops.conf", settings);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int only_fatal_mce = 1;
|
|
Packit |
8ea169 |
try_get_map_string_item_as_bool(settings, "OnlyFatalMCE", &only_fatal_mce);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free_map_string(settings);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (only_fatal_mce)
|
|
Packit |
8ea169 |
return xstrdup("^Machine .*$");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return NULL;
|
|
Packit |
8ea169 |
}
|