| 1 | // CheckEventLog.cpp : Defines the entry point for the DLL application. |
|---|
| 2 | // |
|---|
| 3 | |
|---|
| 4 | #include "stdafx.h" |
|---|
| 5 | #include "CheckEventLog.h" |
|---|
| 6 | #include <strEx.h> |
|---|
| 7 | #include <time.h> |
|---|
| 8 | |
|---|
| 9 | CheckEventLog gCheckEventLog; |
|---|
| 10 | |
|---|
| 11 | BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) |
|---|
| 12 | { |
|---|
| 13 | NSCModuleWrapper::wrapDllMain(hModule, ul_reason_for_call); |
|---|
| 14 | return TRUE; |
|---|
| 15 | } |
|---|
| 16 | |
|---|
| 17 | CheckEventLog::CheckEventLog() { |
|---|
| 18 | } |
|---|
| 19 | CheckEventLog::~CheckEventLog() { |
|---|
| 20 | } |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | bool CheckEventLog::loadModule() { |
|---|
| 24 | return true; |
|---|
| 25 | } |
|---|
| 26 | bool CheckEventLog::unloadModule() { |
|---|
| 27 | return true; |
|---|
| 28 | } |
|---|
| 29 | |
|---|
| 30 | bool CheckEventLog::hasCommandHandler() { |
|---|
| 31 | return true; |
|---|
| 32 | } |
|---|
| 33 | bool CheckEventLog::hasMessageHandler() { |
|---|
| 34 | return false; |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | |
|---|
| 38 | |
|---|
| 39 | class EventLogRecord { |
|---|
| 40 | EVENTLOGRECORD *pevlr_; |
|---|
| 41 | public: |
|---|
| 42 | EventLogRecord(EVENTLOGRECORD *pevlr) : pevlr_(pevlr) { |
|---|
| 43 | } |
|---|
| 44 | inline DWORD timeGenerated() const { |
|---|
| 45 | return pevlr_->TimeGenerated; |
|---|
| 46 | } |
|---|
| 47 | inline DWORD timeWritten() const { |
|---|
| 48 | return pevlr_->TimeWritten; |
|---|
| 49 | } |
|---|
| 50 | inline std::string eventSource() const { |
|---|
| 51 | return reinterpret_cast<LPSTR>(reinterpret_cast<LPBYTE>(pevlr_) + sizeof(EVENTLOGRECORD)); |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | inline DWORD eventType() const { |
|---|
| 55 | return pevlr_->EventType; |
|---|
| 56 | } |
|---|
| 57 | /* |
|---|
| 58 | std::string userSID() const { |
|---|
| 59 | if (pevlr_->UserSidOffset == 0) |
|---|
| 60 | return ""; |
|---|
| 61 | PSID p = reinterpret_cast<PSID>(reinterpret_cast<LPBYTE>(pevlr_) + + pevlr_->UserSidOffset); |
|---|
| 62 | LPSTR user = new CHAR[1025]; |
|---|
| 63 | LPSTR domain = new CHAR[1025]; |
|---|
| 64 | DWORD userLen = 1024; |
|---|
| 65 | DWORD domainLen = 1024; |
|---|
| 66 | SID_NAME_USE sidName; |
|---|
| 67 | LookupAccountSid(NULL, p, user, &userLen, domain, &domainLen, &sidName); |
|---|
| 68 | user[userLen] = 0; |
|---|
| 69 | domain[domainLen] = 0; |
|---|
| 70 | return std::string(domain) + "\\" + std::string(user); |
|---|
| 71 | } |
|---|
| 72 | */ |
|---|
| 73 | |
|---|
| 74 | std::string enumStrings() const { |
|---|
| 75 | std::string ret; |
|---|
| 76 | LPSTR p = reinterpret_cast<LPSTR>(reinterpret_cast<LPBYTE>(pevlr_) + pevlr_->StringOffset); |
|---|
| 77 | for (unsigned int i =0;i<pevlr_->NumStrings;i++) { |
|---|
| 78 | std::string s = p; |
|---|
| 79 | if (!s.empty()) |
|---|
| 80 | s += ", "; |
|---|
| 81 | ret += s; |
|---|
| 82 | p+= strlen(p)+1; |
|---|
| 83 | } |
|---|
| 84 | return ret; |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | static DWORD appendType(DWORD dwType, std::string sType) { |
|---|
| 88 | return dwType | translateType(sType); |
|---|
| 89 | } |
|---|
| 90 | static DWORD subtractType(DWORD dwType, std::string sType) { |
|---|
| 91 | return dwType & (!translateType(sType)); |
|---|
| 92 | } |
|---|
| 93 | static DWORD translateType(std::string sType) { |
|---|
| 94 | if (sType == "error") |
|---|
| 95 | return EVENTLOG_ERROR_TYPE; |
|---|
| 96 | if (sType == "warning") |
|---|
| 97 | return EVENTLOG_WARNING_TYPE; |
|---|
| 98 | if (sType == "info") |
|---|
| 99 | return EVENTLOG_INFORMATION_TYPE; |
|---|
| 100 | if (sType == "auditSuccess") |
|---|
| 101 | return EVENTLOG_AUDIT_SUCCESS; |
|---|
| 102 | if (sType == "auditFailure") |
|---|
| 103 | return EVENTLOG_AUDIT_FAILURE; |
|---|
| 104 | return strEx::stoi(sType); |
|---|
| 105 | } |
|---|
| 106 | static std::string translateType(DWORD dwType) { |
|---|
| 107 | if (dwType == EVENTLOG_ERROR_TYPE) |
|---|
| 108 | return "error"; |
|---|
| 109 | if (dwType == EVENTLOG_WARNING_TYPE) |
|---|
| 110 | return "warning"; |
|---|
| 111 | if (dwType == EVENTLOG_INFORMATION_TYPE) |
|---|
| 112 | return "info"; |
|---|
| 113 | if (dwType == EVENTLOG_AUDIT_SUCCESS) |
|---|
| 114 | return "auditSuccess"; |
|---|
| 115 | if (dwType == EVENTLOG_AUDIT_FAILURE) |
|---|
| 116 | return "auditFailure"; |
|---|
| 117 | return strEx::itos(dwType); |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | }; |
|---|
| 121 | |
|---|
| 122 | |
|---|
| 123 | struct searchQuery { |
|---|
| 124 | struct searchQueryItem { |
|---|
| 125 | |
|---|
| 126 | typedef enum { |
|---|
| 127 | eventType, |
|---|
| 128 | eventSource, |
|---|
| 129 | timeWritten, |
|---|
| 130 | timeGenerated, |
|---|
| 131 | message, |
|---|
| 132 | none |
|---|
| 133 | } queryType; |
|---|
| 134 | |
|---|
| 135 | typedef enum { out, in, undefined } filterType; |
|---|
| 136 | |
|---|
| 137 | filterType filter_; |
|---|
| 138 | queryType queryType_; |
|---|
| 139 | DWORD dwValue_; |
|---|
| 140 | boost::regex regexp_; |
|---|
| 141 | |
|---|
| 142 | |
|---|
| 143 | searchQueryItem() |
|---|
| 144 | : queryType_(none), dwValue_(0), filter_(out) |
|---|
| 145 | {} |
|---|
| 146 | searchQueryItem(filterType filter, queryType type, std::string str) |
|---|
| 147 | : queryType_(type), dwValue_(0), filter_(filter) |
|---|
| 148 | { |
|---|
| 149 | switch (queryType_ ) { |
|---|
| 150 | case eventType: |
|---|
| 151 | dwValue_ = EventLogRecord::translateType(str); |
|---|
| 152 | break; |
|---|
| 153 | |
|---|
| 154 | case timeGenerated: |
|---|
| 155 | dwValue_ = strEx::stoui_as_time(str)/1000; |
|---|
| 156 | break; |
|---|
| 157 | |
|---|
| 158 | case eventSource: |
|---|
| 159 | case message: |
|---|
| 160 | try { |
|---|
| 161 | regexp_ = str; |
|---|
| 162 | } catch (const boost::bad_expression e) { |
|---|
| 163 | throw (std::string)"Invalid syntax in regular expression:" + str; |
|---|
| 164 | } |
|---|
| 165 | break; |
|---|
| 166 | } |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | searchQueryItem& operator=(const searchQueryItem &other) { |
|---|
| 170 | queryType_ = other.queryType_; |
|---|
| 171 | dwValue_ = other.dwValue_; |
|---|
| 172 | filter_ = other.filter_; |
|---|
| 173 | try { |
|---|
| 174 | regexp_ = other.regexp_; |
|---|
| 175 | } catch (const boost::bad_expression e) { |
|---|
| 176 | throw (std::string)"Invalid syntax in regular expression:" + other.toString(); |
|---|
| 177 | } |
|---|
| 178 | return *this; |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | bool match(DWORD now, const EventLogRecord &record) const { |
|---|
| 182 | switch (queryType_) { |
|---|
| 183 | case eventType: |
|---|
| 184 | return record.eventType() & dwValue_; |
|---|
| 185 | |
|---|
| 186 | case eventSource: |
|---|
| 187 | if (regexp_.empty()) |
|---|
| 188 | return false; |
|---|
| 189 | return boost::regex_match(record.eventSource(), regexp_); |
|---|
| 190 | |
|---|
| 191 | case timeWritten: |
|---|
| 192 | return record.timeWritten() < (now-dwValue_); |
|---|
| 193 | |
|---|
| 194 | case timeGenerated: |
|---|
| 195 | return record.timeGenerated() < (now-dwValue_); |
|---|
| 196 | |
|---|
| 197 | case message: |
|---|
| 198 | if (regexp_.empty()) |
|---|
| 199 | return false; |
|---|
| 200 | return boost::regex_match(record.enumStrings(), regexp_); |
|---|
| 201 | |
|---|
| 202 | default: |
|---|
| 203 | return false; |
|---|
| 204 | } |
|---|
| 205 | } |
|---|
| 206 | std::string queryType2String(queryType query) const { |
|---|
| 207 | switch (queryType_) { |
|---|
| 208 | case eventType: |
|---|
| 209 | return "eventType"; |
|---|
| 210 | case eventSource: |
|---|
| 211 | return "eventSource"; |
|---|
| 212 | case timeWritten: |
|---|
| 213 | return "timeWritten"; |
|---|
| 214 | case timeGenerated: |
|---|
| 215 | return "timeGenerated"; |
|---|
| 216 | case message: |
|---|
| 217 | return "message"; |
|---|
| 218 | default: |
|---|
| 219 | return "unknown"; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | std::string toString() const { |
|---|
| 225 | std::stringstream ss; |
|---|
| 226 | ss << " Type: " << queryType2String(queryType_) << " = " << dwValue_ << ", '" << regexp_ << "'"; |
|---|
| 227 | return ss.str(); |
|---|
| 228 | } |
|---|
| 229 | }; |
|---|
| 230 | |
|---|
| 231 | |
|---|
| 232 | |
|---|
| 233 | unsigned int truncate; |
|---|
| 234 | unsigned warning_count; |
|---|
| 235 | unsigned critical_count; |
|---|
| 236 | bool descriptions; |
|---|
| 237 | std::list<searchQueryItem> queries; |
|---|
| 238 | |
|---|
| 239 | searchQuery() : truncate(0), descriptions(false), warning_count(0), critical_count(0) {} |
|---|
| 240 | |
|---|
| 241 | std::string toString() { |
|---|
| 242 | std::string ret; |
|---|
| 243 | for (std::list<searchQuery::searchQueryItem>::const_iterator cit = queries.begin(); cit != queries.end(); ++cit ) { |
|---|
| 244 | ret += (*cit).toString(); |
|---|
| 245 | } |
|---|
| 246 | return ret; |
|---|
| 247 | } |
|---|
| 248 | |
|---|
| 249 | }; |
|---|
| 250 | |
|---|
| 251 | // checkEventLog file=application truncate=1024 descriptions filter=[out|in] |
|---|
| 252 | // warning-count=3 critical-count=10 |
|---|
| 253 | // filter type = warning AND generated > 1d |
|---|
| 254 | // |
|---|
| 255 | // match (type, "warning") && match(generated, "1d") |
|---|
| 256 | // filer-eventType=warning |
|---|
| 257 | // filer-eventSource= |
|---|
| 258 | // filer-date=4d |
|---|
| 259 | // |
|---|
| 260 | // CheckEventLog |
|---|
| 261 | // request: CheckEventLog&<logfile>&<Query strings> |
|---|
| 262 | // Return: <return state>&<log entry 1> - <log entry 2>... |
|---|
| 263 | // <return state> 0 - No errors |
|---|
| 264 | // 1 - Unknown |
|---|
| 265 | // 2 - Errors |
|---|
| 266 | |
|---|
| 267 | // ./nrpe-2.0/src/check_nrpe -H 192.168.167 -p 5666 -c checkEventLog -a file=system file=application filter-eventType=warning filter-generated=1d descriptions filter-eventSource=Cdrom filter-eventSource=NSClient warning-count=3 critical-count=7 filter=in truncate=512 |
|---|
| 268 | // |
|---|
| 269 | // Examples: |
|---|
| 270 | // CheckEventLog&Application&1&<type>&<query>&huffa... |
|---|
| 271 | // CheckEventLog&Application&warn.require.eventType=warning&critical.require.eventType=error&truncate=1024&descriptions&all.exclude.eventSourceRegexp=^(Win|Msi|NSClient\+\+|Userenv|ASP\.NET|LoadPerf|Outlook|Application E|NSClient).* |
|---|
| 272 | #define BUFFER_SIZE 1024*64 |
|---|
| 273 | NSCAPI::nagiosReturn CheckEventLog::handleCommand(const strEx::blindstr command, const unsigned int argLen, char **char_args, std::string &message, std::string &perf) { |
|---|
| 274 | if (command != "CheckEventLog") |
|---|
| 275 | return NSCAPI::returnIgnored; |
|---|
| 276 | NSCAPI::nagiosReturn rCode = NSCAPI::returnOK; |
|---|
| 277 | std::list<std::string> args = arrayBuffer::arrayBuffer2list(argLen, char_args); |
|---|
| 278 | |
|---|
| 279 | std::string ret; |
|---|
| 280 | searchQuery query; |
|---|
| 281 | std::list<std::string> files; |
|---|
| 282 | searchQuery::searchQueryItem::filterType filter = searchQuery::searchQueryItem::out; |
|---|
| 283 | |
|---|
| 284 | for (std::list<std::string>::const_iterator it = args.begin(); it!=args.end(); ++it) { |
|---|
| 285 | try { |
|---|
| 286 | if ((*it) == "descriptions") { |
|---|
| 287 | query.descriptions = true; |
|---|
| 288 | } else { |
|---|
| 289 | std::pair<std::string,std::string> p = strEx::split((*it), "="); |
|---|
| 290 | if (p.first == "truncate") { |
|---|
| 291 | query.truncate = strEx::stoi(p.second); |
|---|
| 292 | } else if (p.first == "file") { |
|---|
| 293 | files.push_back(p.second); |
|---|
| 294 | } else if (p.first == "filter") { |
|---|
| 295 | if (p.second == "in") |
|---|
| 296 | filter = searchQuery::searchQueryItem::in; |
|---|
| 297 | else |
|---|
| 298 | filter = searchQuery::searchQueryItem::out; |
|---|
| 299 | } else if (p.first == "warning-count") { |
|---|
| 300 | query.warning_count = strEx::stoi(p.second); |
|---|
| 301 | } else if (p.first == "critical-count") { |
|---|
| 302 | query.critical_count = strEx::stoi(p.second); |
|---|
| 303 | |
|---|
| 304 | } else if (p.first == "filter-eventType") { |
|---|
| 305 | query.queries.push_back(searchQuery::searchQueryItem(filter, searchQuery::searchQueryItem::eventType, p.second)); |
|---|
| 306 | } else if (p.first == "filter-eventSource") { |
|---|
| 307 | query.queries.push_back(searchQuery::searchQueryItem(filter, searchQuery::searchQueryItem::eventSource, p.second)); |
|---|
| 308 | } else if (p.first == "filter-generated") { |
|---|
| 309 | query.queries.push_back(searchQuery::searchQueryItem(filter, searchQuery::searchQueryItem::timeGenerated, p.second)); |
|---|
| 310 | } else if (p.first == "filter-written") { |
|---|
| 311 | query.queries.push_back(searchQuery::searchQueryItem(filter, searchQuery::searchQueryItem::timeWritten, p.second)); |
|---|
| 312 | } else if (p.first == "filter-message") { |
|---|
| 313 | query.queries.push_back(searchQuery::searchQueryItem(filter, searchQuery::searchQueryItem::message, p.second)); |
|---|
| 314 | } |
|---|
| 315 | } |
|---|
| 316 | } catch (std::string s) { |
|---|
| 317 | if (message.empty()) |
|---|
| 318 | message += "UNKNOWN: "; |
|---|
| 319 | else |
|---|
| 320 | message += ", "; |
|---|
| 321 | message += s; |
|---|
| 322 | } |
|---|
| 323 | } |
|---|
| 324 | if (!message.empty()) { |
|---|
| 325 | return NSCAPI::returnUNKNOWN; |
|---|
| 326 | } |
|---|
| 327 | |
|---|
| 328 | unsigned int hit_count = 0; |
|---|
| 329 | |
|---|
| 330 | for (std::list<std::string>::const_iterator cit2 = files.begin(); cit2 != files.end(); ++cit2) { |
|---|
| 331 | HANDLE hLog = OpenEventLog(NULL, (*cit2).c_str()); |
|---|
| 332 | if (hLog == NULL) { |
|---|
| 333 | message = "Could not open the '" + (*cit2) + "' event log."; |
|---|
| 334 | return NSCAPI::returnUNKNOWN; |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | DWORD dwThisRecord, dwRead, dwNeeded; |
|---|
| 338 | EVENTLOGRECORD *pevlr; |
|---|
| 339 | BYTE bBuffer[BUFFER_SIZE]; |
|---|
| 340 | |
|---|
| 341 | pevlr = reinterpret_cast<EVENTLOGRECORD*>(&bBuffer); |
|---|
| 342 | |
|---|
| 343 | __time64_t ltime; |
|---|
| 344 | _time64(<ime); |
|---|
| 345 | DWORD currentTime = ltime; |
|---|
| 346 | |
|---|
| 347 | GetOldestEventLogRecord(hLog, &dwThisRecord); |
|---|
| 348 | |
|---|
| 349 | while (ReadEventLog(hLog, EVENTLOG_FORWARDS_READ|EVENTLOG_SEQUENTIAL_READ, |
|---|
| 350 | 0, pevlr, BUFFER_SIZE, &dwRead, &dwNeeded)) |
|---|
| 351 | { |
|---|
| 352 | while (dwRead > 0) |
|---|
| 353 | { |
|---|
| 354 | bool match = false; |
|---|
| 355 | bool undefined = true; |
|---|
| 356 | searchQuery::searchQueryItem::filterType tFilter = searchQuery::searchQueryItem::out; |
|---|
| 357 | EventLogRecord record(pevlr); |
|---|
| 358 | |
|---|
| 359 | for (std::list<searchQuery::searchQueryItem>::const_iterator cit3 = query.queries.begin(); cit3 != query.queries.end(); ++cit3 ) { |
|---|
| 360 | if ((*cit3).match(currentTime, record)) { |
|---|
| 361 | if ((*cit3).filter_ == searchQuery::searchQueryItem::in) |
|---|
| 362 | match = true; |
|---|
| 363 | else { |
|---|
| 364 | match = false; |
|---|
| 365 | } |
|---|
| 366 | } |
|---|
| 367 | } |
|---|
| 368 | |
|---|
| 369 | if (match) { |
|---|
| 370 | if (!ret.empty()) |
|---|
| 371 | ret += ", "; |
|---|
| 372 | ret += record.eventSource(); |
|---|
| 373 | if (query.descriptions) { |
|---|
| 374 | ret += "(" + EventLogRecord::translateType(record.eventType()) + ")"; |
|---|
| 375 | ret += "[" + record.enumStrings() + "]"; |
|---|
| 376 | } |
|---|
| 377 | hit_count++; |
|---|
| 378 | } |
|---|
| 379 | |
|---|
| 380 | dwRead -= pevlr->Length; |
|---|
| 381 | pevlr = (EVENTLOGRECORD *) ((LPBYTE) pevlr + pevlr->Length); |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | pevlr = (EVENTLOGRECORD *) &bBuffer; |
|---|
| 385 | } |
|---|
| 386 | |
|---|
| 387 | CloseEventLog(hLog); |
|---|
| 388 | } |
|---|
| 389 | |
|---|
| 390 | if ((query.critical_count > 0) && (hit_count > query.critical_count)) { |
|---|
| 391 | ret = "CRITICAL: " + strEx::itos(hit_count) + " > critical: " + ret; |
|---|
| 392 | rCode = NSCAPI::returnCRIT; |
|---|
| 393 | } else if ((query.warning_count > 0) && (hit_count > query.warning_count)) { |
|---|
| 394 | ret = "WARNING: " + strEx::itos(hit_count) + " > warning: " + ret; |
|---|
| 395 | rCode = NSCAPI::returnWARN; |
|---|
| 396 | } else { |
|---|
| 397 | ret = "OK: " + strEx::itos(hit_count) + ": " + ret; |
|---|
| 398 | } |
|---|
| 399 | if (query.truncate != 0) |
|---|
| 400 | ret = ret.substr(0, query.truncate); |
|---|
| 401 | if ((query.truncate > 0) && (ret.length() > query.truncate)) |
|---|
| 402 | ret = ret.substr(0, query.truncate); |
|---|
| 403 | message = ret; |
|---|
| 404 | return rCode; |
|---|
| 405 | } |
|---|
| 406 | |
|---|
| 407 | |
|---|
| 408 | NSC_WRAPPERS_MAIN_DEF(gCheckEventLog); |
|---|
| 409 | NSC_WRAPPERS_IGNORE_MSG_DEF(); |
|---|
| 410 | NSC_WRAPPERS_HANDLE_CMD_DEF(gCheckEventLog); |
|---|