/* * Copyright 2007, Intel Corporation * Copyright 2009, Red Hat Inc. * * This file is part of Abrt. * * This program file 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; version 2 of the License. * * 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 in a file named COPYING; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * Authors: * Anton Arapov * Arjan van de Ven */ #include #include #include /* __NR_syslog */ #include "abrtlib.h" #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" #include "KerneloopsSysLog.h" #include "KerneloopsScanner.h" // TODO: https://fedorahosted.org/abrt/ticket/78 CKerneloopsScanner::CKerneloopsScanner() { int cnt_FoundOopses; /* Scan dmesg, on first call only */ cnt_FoundOopses = ScanDmesg(); if (cnt_FoundOopses > 0) SaveOopsToDebugDump(); } void CKerneloopsScanner::Run(const char *pActionDir, const char *pArgs, int force) { const char *syslog_file = "/var/log/messages"; map_plugin_settings_t::const_iterator it = m_pSettings.find("SysLogFile"); if (it != m_pSettings.end()) { syslog_file = it->second.c_str(); } int cnt_FoundOopses = ScanSysLogFile(syslog_file); if (cnt_FoundOopses > 0) { SaveOopsToDebugDump(); /* * This marker in syslog file prevents us from * re-parsing old oopses (any oops before it is * ignored by ScanSysLogFile()). The only problem * is that we can't be sure here that syslog_file * is the file where syslog(xxx) stuff ends up. */ openlog("abrt", 0, LOG_KERN); syslog( LOG_WARNING, "Kerneloops: Reported %u kernel oopses to Abrt", cnt_FoundOopses ); closelog(); } } void CKerneloopsScanner::SaveOopsToDebugDump() { update_client(_("Creating kernel oops crash reports...")); int countdown = 16; /* do not report hundreds of oopses */ time_t t = time(NULL); vector_string_t oopsList = m_pOopsList; m_pOopsList.clear(); while (!oopsList.empty() && --countdown != 0) { char path[sizeof(DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu") + 2 * sizeof(long)*3]; sprintf(path, DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu", (long)t, (long)oopsList.size()); std::string oops = oopsList.back(); const char *first_line = oops.c_str(); char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */ *second_line++ = '\0'; try { CDebugDump dd; dd.Create(path, 0); dd.SaveText(FILENAME_ANALYZER, "Kerneloops"); dd.SaveText(FILENAME_EXECUTABLE, "kernel"); dd.SaveText(FILENAME_KERNEL, first_line); dd.SaveText(FILENAME_PACKAGE, "not_applicable"); dd.SaveText(FILENAME_CMDLINE, "not_applicable"); dd.SaveText(FILENAME_COMPONENT, "kernel"); dd.SaveText(FILENAME_KERNELOOPS, second_line); } catch (CABRTException& e) { throw CABRTException(EXCEP_PLUGIN, "%s: %s", __func__, e.what()); } oopsList.pop_back(); } } int CKerneloopsScanner::ScanDmesg() { VERB1 log("Scanning dmesg..."); int cnt_FoundOopses; char *buffer; long pagesz = sysconf(_SC_PAGESIZE); buffer = (char*)xzalloc(pagesz + 1); syscall(__NR_syslog, 3, buffer, pagesz); m_pOopsList.clear(); cnt_FoundOopses = extract_oopses(m_pOopsList, buffer, strlen(buffer)); free(buffer); return cnt_FoundOopses; } int CKerneloopsScanner::ScanSysLogFile(const char *filename) { VERB1 log("Scanning syslog..."); char *buffer; struct stat statb; int fd; int cnt_FoundOopses; ssize_t sz; fd = open(filename, O_RDONLY); if (fd < 0) return 0; statb.st_size = 0; /* paranoia */ if (fstat(fd, &statb) != 0 || statb.st_size < 1) { close(fd); return 0; } /* * in theory there's a race here, since someone could spew * to /var/log/messages before we read it in... we try to * deal with it by reading at most 1023 bytes extra. If there's * more than that.. any oops will be in dmesg anyway. * Do not try to allocate an absurd amount of memory; ignore * older log messages because they are unlikely to have * sufficiently recent data to be useful. 32MB is more * than enough; it's not worth looping through more log * if the log is larger than that. */ sz = statb.st_size + 1024; if (statb.st_size > (32*1024*1024 - 1024)) { xlseek(fd, statb.st_size - (32*1024*1024 - 1024), SEEK_SET); sz = 32*1024*1024; } buffer = (char*)xzalloc(sz); sz = full_read(fd, buffer, sz); close(fd); cnt_FoundOopses = 0; if (sz > 0) { m_pOopsList.clear(); cnt_FoundOopses = extract_oopses(m_pOopsList, buffer, sz); } free(buffer); return cnt_FoundOopses; } PLUGIN_INFO(ACTION, CKerneloopsScanner, "KerneloopsScanner", "0.0.1", "Periodically scans for and saves kernel oopses", "anton@redhat.com", "http://people.redhat.com/aarapov", ""); /* For "dumpoops" tool */ extern "C" { int scan_syslog_file(CKerneloopsScanner *This, const char *filename) { return This->ScanSysLogFile(filename); } void save_oops_to_debug_dump(CKerneloopsScanner *This) { This->SaveOopsToDebugDump(); } } /* extern "C" */