diff options
author | Anton Arapov <aarapov@redhat.com> | 2009-03-26 16:47:00 +0100 |
---|---|---|
committer | Anton Arapov <aarapov@redhat.com> | 2009-03-26 16:47:00 +0100 |
commit | 6b54fffc7873f0006c19acc96f97b9781e1402ae (patch) | |
tree | 1bdab71a53afe311260ddaee415ffcda4b0611e3 | |
parent | 4932e17099e311cb1b4bd20c807a2cfe38990d90 (diff) | |
download | abrt-6b54fffc7873f0006c19acc96f97b9781e1402ae.tar.gz abrt-6b54fffc7873f0006c19acc96f97b9781e1402ae.tar.xz abrt-6b54fffc7873f0006c19acc96f97b9781e1402ae.zip |
kerneloops - plugin: huge changeset, make things more c++-ish, configurable syslog
-rw-r--r-- | lib/Plugins/Kerneloops.conf | 4 | ||||
-rw-r--r-- | lib/Plugins/Kerneloops.cpp | 180 | ||||
-rw-r--r-- | lib/Plugins/Kerneloops.h | 14 | ||||
-rw-r--r-- | lib/Plugins/KerneloopsSysLog.cpp | 213 | ||||
-rw-r--r-- | lib/Plugins/KerneloopsSysLog.h | 32 |
5 files changed, 256 insertions, 187 deletions
diff --git a/lib/Plugins/Kerneloops.conf b/lib/Plugins/Kerneloops.conf new file mode 100644 index 00000000..0022711d --- /dev/null +++ b/lib/Plugins/Kerneloops.conf @@ -0,0 +1,4 @@ +# Kerneloops configuration. +################################################################################ + +SysLogFile = /var/log/messages diff --git a/lib/Plugins/Kerneloops.cpp b/lib/Plugins/Kerneloops.cpp index 07c8e066..8d7d5d69 100644 --- a/lib/Plugins/Kerneloops.cpp +++ b/lib/Plugins/Kerneloops.cpp @@ -22,22 +22,43 @@ * Anton Arapov <anton@redhat.com> */ +#include "Kerneloops.h" +#include "KerneloopsSysLog.h" #include "DebugDump.h" -#include "DynamicLibrary.h" +#include "Settings.h" -#include <ctype.h> #include <sstream> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <limits.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <asm/unistd.h> -#include "Kerneloops.h" -#include "KerneloopsDmesg.h" +#define MAX(A,B) ((A) > (B) ? (A) : (B)) + +CAnalyzerKerneloops::CAnalyzerKerneloops() : + m_sSysLogFile("/var/log/messages") +{} + +void CAnalyzerKerneloops::WriteSysLog(int m_nCount) +{ + if (m_nCount > 0) { + openlog("abrt", 0, LOG_KERN); + syslog(LOG_WARNING, "Kerneloops: Reported %i kernel oopses to Abrt", m_nCount); + closelog(); + } +} std::string CAnalyzerKerneloops::GetLocalUUID(const std::string& pDebugDumpDir) { std::string m_sOops; std::stringstream m_sHash; - CDebugDump dd; - dd.Open(pDebugDumpDir); - dd.LoadText(FILENAME_TEXTDATA1, m_sOops); + CDebugDump m_pDebugDump; + m_pDebugDump.Open(pDebugDumpDir); + m_pDebugDump.LoadText(FILENAME_TEXTDATA1, m_sOops); /* An algorithm proposed by Donald E. Knuth in The Art Of Computer * Programming Volume 3, under the topic of sorting and search @@ -58,7 +79,150 @@ std::string CAnalyzerKerneloops::GetGlobalUUID(const std::string& pDebugDumpDir) return GetLocalUUID(pDebugDumpDir); } +void CAnalyzerKerneloops::Report() +{ + CDebugDump m_pDebugDump; + char m_sPath[PATH_MAX]; + std::list<COops> m_pOopsList; + + time_t t = time(NULL); + if (((time_t) -1) == t) + { + fprintf(stderr, "Kerneloops: cannot get local time.\n"); + perror(""); + // TODO: throw -4 + } + + m_pOopsList = m_pSysLog.GetOopsList(); + m_pSysLog.ClearOopsList(); + while (!m_pOopsList.empty()) + { + snprintf(m_sPath, sizeof(m_sPath), "%s/kerneloops-%d-%d", DEBUG_DUMPS_DIR, t, m_pOopsList.size()); + + COops m_pOops; + m_pOops = m_pOopsList.back(); + + try + { + m_pDebugDump.Create(m_sPath); + m_pDebugDump.SaveText(FILENAME_ANALYZER, "Kerneloops"); + m_pDebugDump.SaveText(FILENAME_UID, "0"); + m_pDebugDump.SaveText(FILENAME_EXECUTABLE, "kernel"); + m_pDebugDump.SaveText(FILENAME_KERNEL, m_pOops.m_sVersion); + m_pDebugDump.SaveText(FILENAME_PACKAGE, "not_applicable"); + m_pDebugDump.SaveText(FILENAME_TEXTDATA1, m_pOops.m_sData); + m_pDebugDump.Close(); + } + catch (std::string sError) + { + fprintf(stderr, "Kerneloops: %s\n", sError.c_str()); + // TODO: throw -2 + } + m_pOopsList.pop_back(); + } +} + void CAnalyzerKerneloops::Init() { - scan_logs(); + /* hack: release Init() */ + pid_t pid = fork(); + if (pid) + return; + // TODO: throw if we can't fork() + + sched_yield(); + +#ifdef PR_SET_TIMERSLACK + /* + * Signal the kernel that we're not timing critical + */ + prctl(PR_SET_TIMERSLACK,1000*1000*1000, 0, 0, 0); +#endif + + /* we scan dmesg before /var/log/messages; dmesg is a more accurate source normally */ + ScanDmesg(); + /* during boot... don't go too fast and slow the system down */ + sleep(10); + ScanSysLogFile(m_sSysLogFile.c_str(), 1); + + while(1) { + sleep(10); + ScanDmesg(); + } +} + +void CAnalyzerKerneloops::ScanDmesg() +{ + int m_nFoundOopses; + char *buffer; + + buffer = (char*)calloc(getpagesize()+1, 1); + + syscall(__NR_syslog, 3, buffer, getpagesize()); + m_nFoundOopses = m_pSysLog.ExtractOops(buffer, strlen(buffer), 0); + free(buffer); + + if (m_nFoundOopses > 0) + Report(); +} + +void CAnalyzerKerneloops::ScanSysLogFile(const char *filename, int issyslog) +{ + char *buffer; + struct stat statb; + FILE *file; + int ret; + int m_nFoundOopses; + size_t buflen; + + memset(&statb, 0, sizeof(statb)); + + ret = stat(filename, &statb); + + if (statb.st_size < 1 || ret != 0) + return; + + /* + * 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 absurt 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. + */ + buflen = MAX(statb.st_size+1024, 32*1024*1024); + buffer = (char*)calloc(buflen, 1); + assert(buffer != NULL); + + file = fopen(filename, "rm"); + if (!file) { + free(buffer); + return; + } + fseek(file, -buflen, SEEK_END); + ret = fread(buffer, 1, buflen-1, file); + fclose(file); + + if (ret > 0) + m_nFoundOopses = m_pSysLog.ExtractOops(buffer, buflen-1, issyslog); + free(buffer); + + if (m_nFoundOopses > 0) { + Report(); + WriteSysLog(m_nFoundOopses); + } +} + +void CAnalyzerKerneloops::LoadSettings(const std::string& pPath) +{ + map_settings_t settings; + load_settings(pPath, settings); + + if (settings.find("SysLogFile")!= settings.end()) + { + m_sSysLogFile = settings["SysLogFile"]; + } } diff --git a/lib/Plugins/Kerneloops.h b/lib/Plugins/Kerneloops.h index 52cba4e5..7e0c0ac8 100644 --- a/lib/Plugins/Kerneloops.h +++ b/lib/Plugins/Kerneloops.h @@ -30,14 +30,26 @@ #include <string> +#include "KerneloopsSysLog.h" + class CAnalyzerKerneloops : public CAnalyzer { + private: + void WriteSysLog(int m_nCount); + void Report(); + std::string m_sSysLogFile; + CSysLog m_pSysLog; + public: + CAnalyzerKerneloops(); virtual ~CAnalyzerKerneloops() {} std::string GetLocalUUID(const std::string& pDebugDumpDir); std::string GetGlobalUUID(const std::string& pDebugDumpDir); - void CreateReport(const std::string& pDebugDumpDir) {} void Init(); + void CreateReport(const std::string& pDebugDumpDir) {} + void LoadSettings(const std::string& pPath); + void ScanDmesg(); + void ScanSysLogFile(const char *filename, int issyslog); }; PLUGIN_INFO(ANALYZER, diff --git a/lib/Plugins/KerneloopsSysLog.cpp b/lib/Plugins/KerneloopsSysLog.cpp index 67f16fc4..59e66870 100644 --- a/lib/Plugins/KerneloopsSysLog.cpp +++ b/lib/Plugins/KerneloopsSysLog.cpp @@ -24,22 +24,11 @@ * Arjan van de Ven <arjan@linux.intel.com> */ -#include <unistd.h> +#include "KerneloopsSysLog.h" + +#include <list> #include <stdlib.h> -#include <stdio.h> #include <string.h> -#include <assert.h> -#include <syslog.h> -#include <limits.h> -#include <asm/unistd.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include "DebugDump.h" -#include "KerneloopsDmesg.h" - - -#define MAX(A,B) ((A) > (B) ? (A) : (B)) /* * This limits the number of oopses we'll submit per session; @@ -48,47 +37,37 @@ */ #define MAX_OOPS 16 - -struct oops { - struct oops *next; - char *text; - char *version; -}; - -/* we queue up oopses, and then submit in a batch. - * This is useful to be able to cancel all submissions, in case - * we later find our marker indicating we submitted everything so far already - * previously. - */ -static struct oops *queued_oopses; -static int submitted; - static char **linepointer; static char *linelevel; static int linecount; -void queue_oops(char *oops, char *version) +CSysLog::CSysLog() : + m_nFoundOopses(0) +{} + +void CSysLog::QueueOops(char *data, char *version) { - struct oops *newoops; + COops m_NewOops; - if (submitted > MAX_OOPS) + if (m_nFoundOopses > MAX_OOPS) return; - newoops = (struct oops*)malloc(sizeof(struct oops)); - memset(newoops, 0, sizeof(struct oops)); - newoops->next = queued_oopses; - newoops->text = strdup(oops); - newoops->version = strdup(version); - queued_oopses = newoops; - submitted++; + m_NewOops.m_sData = strdup(data); + m_NewOops.m_sVersion = strdup(version); + + m_OopsQueue.push_back(m_NewOops); + m_nFoundOopses++; +} + +void CSysLog::ClearOopsList() +{ + m_OopsQueue.clear(); } -static void write_logfile(int count) +std::list<COops> CSysLog::GetOopsList() { - openlog("abrt", 0, LOG_KERN); - syslog(LOG_WARNING, "Kerneloops hook: Reported %i kernel oopses to Abrt", count); - closelog(); + return m_OopsQueue; } /* @@ -96,7 +75,7 @@ static void write_logfile(int count) * (null terminated). The linepointer array is assumed to be * allocated already. */ -static void fill_linepointers(char *buffer, int remove_syslog) +void CSysLog::FillLinePointers(char *buffer, int remove_syslog) { char *c; linecount = 0; @@ -180,7 +159,7 @@ static void fill_linepointers(char *buffer, int remove_syslog) /* * extract_version tries to find the kernel version in given data */ -static inline int extract_version(char *linepointer, char *version) +int CSysLog::ExtractVersion(char *linepointer, char *version) { int ret; @@ -211,25 +190,26 @@ static inline int extract_version(char *linepointer, char *version) /* * extract_oops tries to find oops signatures in a log */ -static void extract_oops(char *buffer, size_t buflen, int remove_syslog) +int CSysLog::ExtractOops(char *buffer, size_t buflen, int remove_syslog) { int i; char prevlevel = 0; int oopsstart = -1; int oopsend; int inbacktrace = 0; + int oopsesfound = 0; linepointer = (char**)calloc(buflen+1, sizeof(char*)); if (!linepointer) - return; + return 0; linelevel = (char*)calloc(buflen+1, sizeof(char)); if (!linelevel) { free(linepointer); linepointer = NULL; - return; + return 0; } - fill_linepointers(buffer, remove_syslog); + FillLinePointers(buffer, remove_syslog); oopsend = linecount; @@ -278,7 +258,7 @@ static void extract_oops(char *buffer, size_t buflen, int remove_syslog) oopsstart = i; if (strstr(c, "Oops:") && i >= 3) oopsstart = i-3; -#if 0 +#if DEBUG /* debug information */ if (oopsstart >= 0) { printf("Found start of oops at line %i\n", oopsstart); @@ -369,13 +349,15 @@ static void extract_oops(char *buffer, size_t buflen, int remove_syslog) is_version = 0; for (q = oopsstart; q <= oopsend; q++) { if (!is_version) - is_version = extract_version(linepointer[q], version); + is_version = ExtractVersion(linepointer[q], version); strcat(oops, linepointer[q]); strcat(oops, "\n"); } /* too short oopses are invalid */ - if (strlen(oops) > 100) - queue_oops(oops, version); + if (strlen(oops) > 100) { + QueueOops(oops, version); + oopsesfound++; + } oopsstart = -1; inbacktrace = 0; oopsend = linecount; @@ -417,13 +399,15 @@ static void extract_oops(char *buffer, size_t buflen, int remove_syslog) is_version = 0; for (q = oopsstart; q <= oopsend; q++) { if (!is_version) - is_version = extract_version(linepointer[q], version); + is_version = ExtractVersion(linepointer[q], version); strcat(oops, linepointer[q]); strcat(oops, "\n"); } /* too short oopses are invalid */ - if (strlen(oops) > 100) - queue_oops(oops, version); + if (strlen(oops) > 100) { + QueueOops(oops, version); + oopsesfound++; + } oopsstart = -1; inbacktrace = 0; oopsend = linecount; @@ -434,121 +418,6 @@ static void extract_oops(char *buffer, size_t buflen, int remove_syslog) free(linelevel); linepointer = NULL; linelevel = NULL; -} - -void scan_dmesg(void __unused *unused) -{ - char *buffer; - - buffer = (char*)calloc(getpagesize()+1, 1); - - syscall(__NR_syslog, 3, buffer, getpagesize()); - extract_oops(buffer, strlen(buffer), 0); - free(buffer); -} - -void scan_filename(char *filename, int issyslog) -{ - char *buffer; - struct stat statb; - FILE *file; - int ret; - size_t buflen; - - memset(&statb, 0, sizeof(statb)); - - ret = stat(filename, &statb); - - if (statb.st_size < 1 || ret != 0) - return; - - /* - * 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 absurt 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. - */ - buflen = MAX(statb.st_size+1024, 32*1024*1024); - buffer = (char*)calloc(buflen, 1); - assert(buffer != NULL); - - file = fopen(filename, "rm"); - if (!file) { - free(buffer); - return; - } - fseek(file, -buflen, SEEK_END); - ret = fread(buffer, 1, buflen-1, file); - fclose(file); - - if (ret > 0) - extract_oops(buffer, buflen-1, issyslog); - free(buffer); -} - -int scan_logs() -{ - int ret; - struct oops *oops; - struct oops *queue; - char path[PATH_MAX]; - int count; - ret = 0; - - time_t t = time(NULL); - if (((time_t) -1) == t) - { - fprintf(stderr, "Kerneloops: cannot get local time.\n"); - perror(""); - return -4; - } - - /* scan dmesg and messages for oopses */ - scan_dmesg(NULL); - scan_filename("/var/log/messages", 1); - - CDebugDump dd; - queue = queued_oopses; - queued_oopses = NULL; - barrier(); - oops = queue; - count = 0; - while (oops) { - struct oops *next; - - snprintf(path, sizeof(path), "%s/kerneloops-%d-%d", DEBUG_DUMPS_DIR, t, count); - try - { - dd.Create(path); - dd.SaveText(FILENAME_ANALYZER, "Kerneloops"); - dd.SaveText(FILENAME_UID, "0"); - dd.SaveText(FILENAME_EXECUTABLE, "kernel"); - dd.SaveText(FILENAME_KERNEL, oops->version); - dd.SaveText(FILENAME_PACKAGE, "not_applicable"); - dd.SaveText(FILENAME_TEXTDATA1, oops->text); - count++; - dd.Close(); - } - catch (std::string sError) - { - fprintf(stderr, "Kerneloops: %s\n", sError.c_str()); - ret = -2; - } - next = oops->next; - free(oops->text); - free(oops->version); - free(oops); - oops = next; - } - - if (ret == 0) - write_logfile(count); - - return ret; + return oopsesfound; } diff --git a/lib/Plugins/KerneloopsSysLog.h b/lib/Plugins/KerneloopsSysLog.h index 95e5b49c..e0bf16ff 100644 --- a/lib/Plugins/KerneloopsSysLog.h +++ b/lib/Plugins/KerneloopsSysLog.h @@ -24,13 +24,33 @@ * Arjan van de Ven <arjan@linux.intel.com> */ -#ifndef __INCLUDE_GUARD_KERNELOOPSLIB_H_ -#define __INCLUDE_GUARD_KERNELOOPSLIB_H_ +#ifndef __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_ +#define __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_ -/* borrowed from the kernel */ -#define barrier() __asm__ __volatile__("": : :"memory") -#define __unused __attribute__ ((__unused__)) +#include <string> +#include <list> -extern int scan_logs(); +class COops +{ + public: + std::string m_sData; + std::string m_sVersion; +}; + +class CSysLog +{ + private: + void QueueOops(char *data, char *version); + int ExtractVersion(char *linepointer, char *version); + void FillLinePointers(char *buffer, int remove_syslog); + std::list<COops> m_OopsQueue; + int m_nFoundOopses; + + public: + CSysLog(); + std::list<COops> GetOopsList(); + void ClearOopsList(); + int ExtractOops(char *buffer, size_t buflen, int remove_syslog); +}; #endif |