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