Blame src/plugins/oops-utils.c

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
}