summaryrefslogtreecommitdiffstats
path: root/lib/Plugins/KerneloopsScanner.cpp
blob: 855213bcf0d1f4bbb6d6522830b9b8eb265c7416 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "KerneloopsScanner.h"
#include "DebugDump.h"
#include "ABRTException.h"
#include "CommLayerInner.h"
#include "PluginSettings.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/stat.h>
#include <asm/unistd.h>


#define MIN(A,B) ((A) < (B) ? (A) : (B))
#define FILENAME_KERNELOOPS  "kerneloops"

void CKerneloopsScanner::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();
    }
}

void CKerneloopsScanner::Run(const std::string& pActionDir,
                             const std::string& pArgs)
{
    ScanDmesg();
    if (!m_bSysLogFileScanned)
    {
        ScanSysLogFile(m_sSysLogFile.c_str(), 1);
        m_bSysLogFileScanned = true;
    }
}

void CKerneloopsScanner::SaveOopsToDebugDump()
{
    comm_layer_inner_status("Creating kernel oops crash reports...");

    CDebugDump m_pDebugDump;
    char m_sPath[PATH_MAX];
    std::list<COops> m_pOopsList;

    time_t t = time(NULL);
    if (((time_t) -1) == t)
    {
        throw CABRTException(EXCEP_PLUGIN, "CAnalyzerKerneloops::Report(): cannot get local time.");
    }

    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_KERNELOOPS, m_pOops.m_sData);
            m_pDebugDump.Close();
        }
        catch (CABRTException& e)
        {
            throw CABRTException(EXCEP_PLUGIN, "CAnalyzerKerneloops::Report(): " + e.what());
        }
        m_pOopsList.pop_back();
    }
}


void CKerneloopsScanner::ScanDmesg()
{
    comm_layer_inner_debug("Scanning dmesg...");

    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)
        SaveOopsToDebugDump();
}

void CKerneloopsScanner::ScanSysLogFile(const char *filename, int issyslog)
{
    comm_layer_inner_debug("Scanning syslog...");

    char *buffer;
    struct stat statb;
    FILE *file;
    int ret;
    int m_nFoundOopses;
    size_t buflen, nread;

    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 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.
     */
    buflen = MIN(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);
    nread = fread(buffer, 1, buflen, file);
    fclose(file);

    if (nread > 0)
        m_nFoundOopses = m_pSysLog.ExtractOops(buffer, nread, issyslog);
    free(buffer);

    if (m_nFoundOopses > 0) {
        SaveOopsToDebugDump();
        WriteSysLog(m_nFoundOopses);
    }
}

void CKerneloopsScanner::LoadSettings(const std::string& pPath)
{
    map_settings_t settings;
    plugin_load_settings(pPath, settings);

    if (settings.find("SysLogFile")!= settings.end())
    {
        m_sSysLogFile = settings["SysLogFile"];
    }
}