| 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 | std::string CheckEventLog::getModuleName() { |
|---|
| 31 | return "Event log Checker."; |
|---|
| 32 | } |
|---|
| 33 | NSCModuleWrapper::module_version CheckEventLog::getModuleVersion() { |
|---|
| 34 | NSCModuleWrapper::module_version version = {0, 0, 1 }; |
|---|
| 35 | return version; |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | bool CheckEventLog::hasCommandHandler() { |
|---|
| 39 | return true; |
|---|
| 40 | } |
|---|
| 41 | bool CheckEventLog::hasMessageHandler() { |
|---|
| 42 | return false; |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | |
|---|
| 46 | class EventLogRecord { |
|---|
| 47 | EVENTLOGRECORD *pevlr_; |
|---|
| 48 | public: |
|---|
| 49 | EventLogRecord(EVENTLOGRECORD *pevlr) : pevlr_(pevlr) { |
|---|
| 50 | } |
|---|
| 51 | inline DWORD timeGenerated() { |
|---|
| 52 | return pevlr_->TimeGenerated; |
|---|
| 53 | } |
|---|
| 54 | inline DWORD timeWritten() { |
|---|
| 55 | return pevlr_->TimeWritten; |
|---|
| 56 | } |
|---|
| 57 | inline std::string eventSource() { |
|---|
| 58 | return reinterpret_cast<LPSTR>(reinterpret_cast<LPBYTE>(pevlr_) + sizeof(EVENTLOGRECORD)); |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | inline DWORD eventType() { |
|---|
| 62 | return pevlr_->EventType; |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | std::string enumStrings() { |
|---|
| 66 | std::string ret; |
|---|
| 67 | LPSTR p = reinterpret_cast<LPSTR>(reinterpret_cast<LPBYTE>(pevlr_) + pevlr_->StringOffset); |
|---|
| 68 | for (unsigned int i =0;i<pevlr_->NumStrings;i++) { |
|---|
| 69 | std::string s = p; |
|---|
| 70 | if (!s.empty()) |
|---|
| 71 | s += ", "; |
|---|
| 72 | ret += s; |
|---|
| 73 | p+= strlen(p)+1; |
|---|
| 74 | } |
|---|
| 75 | return ret; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | static DWORD appendType(DWORD dwType, std::string sType) { |
|---|
| 79 | return dwType | translateType(sType); |
|---|
| 80 | } |
|---|
| 81 | static DWORD subtractType(DWORD dwType, std::string sType) { |
|---|
| 82 | return dwType & (!translateType(sType)); |
|---|
| 83 | } |
|---|
| 84 | static DWORD translateType(std::string sType) { |
|---|
| 85 | if (sType == "error") |
|---|
| 86 | return EVENTLOG_ERROR_TYPE; |
|---|
| 87 | if (sType == "warning") |
|---|
| 88 | return EVENTLOG_WARNING_TYPE; |
|---|
| 89 | if (sType == "info") |
|---|
| 90 | return EVENTLOG_INFORMATION_TYPE; |
|---|
| 91 | if (sType == "auditSuccess") |
|---|
| 92 | return EVENTLOG_AUDIT_SUCCESS; |
|---|
| 93 | if (sType == "auditFailure") |
|---|
| 94 | return EVENTLOG_AUDIT_FAILURE; |
|---|
| 95 | return 0; |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | }; |
|---|
| 99 | |
|---|
| 100 | |
|---|
| 101 | struct searchQuery { |
|---|
| 102 | struct searchQueryBundle { |
|---|
| 103 | struct searchQueryItem { |
|---|
| 104 | DWORD eventType_; |
|---|
| 105 | std::string eventSource_; |
|---|
| 106 | boost::regex eventSourceRegExp_; |
|---|
| 107 | bool notSetValue_; |
|---|
| 108 | DWORD writtenBeforeDelta_ ; |
|---|
| 109 | DWORD writtenAfterDelta_ ; |
|---|
| 110 | DWORD generatedBeforeDelta_; |
|---|
| 111 | DWORD generatedAfterDelta_; |
|---|
| 112 | boost::regex regexp_; |
|---|
| 113 | |
|---|
| 114 | searchQueryItem(bool notSetValue) |
|---|
| 115 | : eventType_(0), notSetValue_(notSetValue), |
|---|
| 116 | writtenBeforeDelta_(0), writtenAfterDelta_(0) , |
|---|
| 117 | generatedBeforeDelta_(0), generatedAfterDelta_(0) |
|---|
| 118 | {} |
|---|
| 119 | searchQueryItem& operator=(const searchQueryItem &other) { |
|---|
| 120 | eventType_ = other.eventType_; |
|---|
| 121 | eventSource_ = other.eventSource_; |
|---|
| 122 | notSetValue_ = other.notSetValue_; |
|---|
| 123 | writtenBeforeDelta_ = other.writtenBeforeDelta_; |
|---|
| 124 | writtenAfterDelta_ = other.writtenAfterDelta_; |
|---|
| 125 | generatedBeforeDelta_ = other.generatedBeforeDelta_; |
|---|
| 126 | generatedAfterDelta_ = other.generatedAfterDelta_; |
|---|
| 127 | try { |
|---|
| 128 | regexp_ = other.regexp_; |
|---|
| 129 | } catch (const boost::bad_expression e) { |
|---|
| 130 | throw (std::string)"Invalid syntax in regular expression:" + other.toString(); |
|---|
| 131 | } |
|---|
| 132 | try { |
|---|
| 133 | eventSourceRegExp_ = other.eventSourceRegExp_; |
|---|
| 134 | } catch (const boost::bad_expression e) { |
|---|
| 135 | throw (std::string)"Invalid syntax in event source regular expression:" + other.toString(); |
|---|
| 136 | } |
|---|
| 137 | return *this; |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | inline bool matchDateWritten(DWORD now, DWORD written) const { |
|---|
| 141 | if ((writtenAfterDelta_ == 0)&&(writtenBeforeDelta_ == 0)) |
|---|
| 142 | return notSetValue_; |
|---|
| 143 | bool ret = true; |
|---|
| 144 | if (writtenAfterDelta_ != 0) { |
|---|
| 145 | if (writtenAfterDelta_+written <= now) |
|---|
| 146 | ret = false; |
|---|
| 147 | } |
|---|
| 148 | if (writtenBeforeDelta_ != 0) { |
|---|
| 149 | if (writtenBeforeDelta_+written > now) |
|---|
| 150 | ret = false; |
|---|
| 151 | } |
|---|
| 152 | return ret; |
|---|
| 153 | } |
|---|
| 154 | inline bool matchDateGenerated(DWORD now, DWORD written) const { |
|---|
| 155 | if ((generatedAfterDelta_ == 0)&&(generatedBeforeDelta_ == 0)) |
|---|
| 156 | return notSetValue_; |
|---|
| 157 | bool ret = true; |
|---|
| 158 | if (generatedAfterDelta_ != 0) { |
|---|
| 159 | if (generatedAfterDelta_+written <= now) |
|---|
| 160 | ret = false; |
|---|
| 161 | } |
|---|
| 162 | if (generatedBeforeDelta_ != 0) { |
|---|
| 163 | if (generatedBeforeDelta_+written > now) |
|---|
| 164 | ret = false; |
|---|
| 165 | } |
|---|
| 166 | return ret; |
|---|
| 167 | } |
|---|
| 168 | inline bool matchType(DWORD eventType) const { |
|---|
| 169 | if (eventType_ == 0) |
|---|
| 170 | return notSetValue_; |
|---|
| 171 | return eventType_ & eventType; |
|---|
| 172 | } |
|---|
| 173 | inline bool matchSource(std::string eventSource) const { |
|---|
| 174 | if ((eventSource_.empty())&&eventSourceRegExp_.empty()) |
|---|
| 175 | return notSetValue_; |
|---|
| 176 | else if (eventSource_.empty()) |
|---|
| 177 | return boost::regex_match(eventSource, eventSourceRegExp_); |
|---|
| 178 | else if (eventSourceRegExp_.empty()) |
|---|
| 179 | return eventSource_ == eventSource; |
|---|
| 180 | return boost::regex_match(eventSource, eventSourceRegExp_) && (eventSource_ == eventSource); |
|---|
| 181 | } |
|---|
| 182 | inline bool matchRegexp(std::string msg) const { |
|---|
| 183 | if (regexp_.empty()) |
|---|
| 184 | return notSetValue_; |
|---|
| 185 | return boost::regex_match(msg, regexp_); |
|---|
| 186 | } |
|---|
| 187 | std::string toString() const { |
|---|
| 188 | std::stringstream ss; |
|---|
| 189 | ss << " Regexp: " << regexp_ << std::endl; |
|---|
| 190 | ss << " Event type: " << eventType_ << std::endl; |
|---|
| 191 | ss << " Event source: " << eventSource_ << std::endl; |
|---|
| 192 | ss << " Event source Regexp: " << eventSourceRegExp_ << std::endl; |
|---|
| 193 | ss << " Written delta: " << writtenAfterDelta_ << " > " << writtenBeforeDelta_ << std::endl; |
|---|
| 194 | ss << " Generated delta: " << generatedAfterDelta_ << " > " << generatedBeforeDelta_ << std::endl; |
|---|
| 195 | return ss.str(); |
|---|
| 196 | } |
|---|
| 197 | }; |
|---|
| 198 | struct searchQueryItem require; |
|---|
| 199 | struct searchQueryItem exclude; |
|---|
| 200 | searchQueryBundle() : require(true), exclude(false) {} |
|---|
| 201 | std::string toString() { |
|---|
| 202 | return " Required:\n" + require.toString() + "\n Exclude:\n" + exclude.toString(); |
|---|
| 203 | } |
|---|
| 204 | }; |
|---|
| 205 | |
|---|
| 206 | searchQueryBundle warn; |
|---|
| 207 | searchQueryBundle critical; |
|---|
| 208 | unsigned int truncate; |
|---|
| 209 | bool descriptions; |
|---|
| 210 | searchQuery() : truncate(0), descriptions(false) {} |
|---|
| 211 | |
|---|
| 212 | std::string toString() { |
|---|
| 213 | return "Warn:\n" + warn.toString() + "\nCritical:\n" + critical.toString(); |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | }; |
|---|
| 217 | |
|---|
| 218 | void addToQueryItem(searchQuery::searchQueryBundle::searchQueryItem &item, std::string arg) { |
|---|
| 219 | std::pair<std::string,std::string> p = strEx::split(arg, "="); |
|---|
| 220 | if (p.first == "eventType") |
|---|
| 221 | item.eventType_ = EventLogRecord::appendType(item.eventType_, p.second); |
|---|
| 222 | else if (p.first == "eventSource") |
|---|
| 223 | item.eventSource_ = p.second; |
|---|
| 224 | else if (p.first == "eventSourceRegexp") { |
|---|
| 225 | try { |
|---|
| 226 | std::string s = p.second; |
|---|
| 227 | item.eventSourceRegExp_ = s; |
|---|
| 228 | } catch (const boost::bad_expression e) { |
|---|
| 229 | item.eventSourceRegExp_ = ""; |
|---|
| 230 | throw (std::string)"Invalid syntax in regular expression:" + p.second; |
|---|
| 231 | } |
|---|
| 232 | } |
|---|
| 233 | else if (p.first == "generatedBeforeDelta") |
|---|
| 234 | item.generatedBeforeDelta_ = strEx::stoi(p.second); |
|---|
| 235 | else if (p.first == "generatedAfterDelta") |
|---|
| 236 | item.generatedAfterDelta_ = strEx::stoi(p.second); |
|---|
| 237 | else if (p.first == "writtenBeforeDelta") |
|---|
| 238 | item.writtenBeforeDelta_ = strEx::stoi(p.second); |
|---|
| 239 | else if (p.first == "writtenAfterDelta") |
|---|
| 240 | item.writtenAfterDelta_ = strEx::stoi(p.second); |
|---|
| 241 | else if (p.first == "regexp") { |
|---|
| 242 | try { |
|---|
| 243 | item.regexp_ = p.second; |
|---|
| 244 | } catch (const boost::bad_expression e) { |
|---|
| 245 | item.regexp_ = ""; |
|---|
| 246 | throw (std::string)"Invalid syntax in regular expression:" + p.second; |
|---|
| 247 | } |
|---|
| 248 | } else |
|---|
| 249 | throw (std::string)"Invalid argument: " + p.first; |
|---|
| 250 | |
|---|
| 251 | } |
|---|
| 252 | void addToQueryBundle(searchQuery::searchQueryBundle &bundle, std::string arg) { |
|---|
| 253 | std::pair<std::string,std::string> p = strEx::split(arg, "."); |
|---|
| 254 | if (p.first == "require") |
|---|
| 255 | addToQueryItem(bundle.require, p.second); |
|---|
| 256 | else if (p.first == "exclude") |
|---|
| 257 | addToQueryItem(bundle.exclude, p.second); |
|---|
| 258 | else |
|---|
| 259 | throw (std::string)"Invalid require/exclude: " + p.first; |
|---|
| 260 | } |
|---|
| 261 | void addToQuery(searchQuery &q, std::string arg) { |
|---|
| 262 | std::pair<std::string,std::string> p = strEx::split(arg, "."); |
|---|
| 263 | if (p.first == "warn") |
|---|
| 264 | addToQueryBundle(q.warn, p.second); |
|---|
| 265 | else if (p.first == "critical") |
|---|
| 266 | addToQueryBundle(q.critical, p.second); |
|---|
| 267 | else if (p.first == "all") { |
|---|
| 268 | addToQueryBundle(q.warn, p.second); |
|---|
| 269 | addToQueryBundle(q.critical, p.second); |
|---|
| 270 | } else { |
|---|
| 271 | std::pair<std::string,std::string> p = strEx::split(arg, "="); |
|---|
| 272 | if (p.first == "truncate") |
|---|
| 273 | q.truncate = strEx::stoi(p.second); |
|---|
| 274 | else if (p.first == "descriptions") |
|---|
| 275 | q.descriptions = true; |
|---|
| 276 | else |
|---|
| 277 | throw (std::string)"Invalid argument: " + arg; |
|---|
| 278 | } |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | void buildQury(searchQuery &query, std::list<std::string> args) { |
|---|
| 282 | for (std::list<std::string>::const_iterator it = args.begin(); it!=args.end(); it++) { |
|---|
| 283 | NSC_DEBUG_MSG_STD("Adding: " + *it); |
|---|
| 284 | addToQuery(query, *it); |
|---|
| 285 | } |
|---|
| 286 | } |
|---|
| 287 | // CheckEventLog |
|---|
| 288 | // request: CheckEventLog&<logfile>&<Query strings> |
|---|
| 289 | // Return: <return state>&<log entry 1> - <log entry 2>... |
|---|
| 290 | // <return state> 0 - No errors |
|---|
| 291 | // 1 - Unknown |
|---|
| 292 | // 2 - Errors |
|---|
| 293 | // Examples: |
|---|
| 294 | // CheckEventLog&Application&1&<type>&<query>&huffa... |
|---|
| 295 | // 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).* |
|---|
| 296 | #define BUFFER_SIZE 1024*64 |
|---|
| 297 | |
|---|
| 298 | NSCAPI::nagiosReturn CheckEventLog::handleCommand(const std::string command, const unsigned int argLen, char **char_args, std::string &message, std::string &perf) { |
|---|
| 299 | if (command != "CheckEventLog") |
|---|
| 300 | return NSCAPI::returnIgnored; |
|---|
| 301 | NSCAPI::nagiosReturn rCode = NSCAPI::returnOK; |
|---|
| 302 | std::list<std::string> args = NSCHelper::arrayBuffer2list(argLen, char_args); |
|---|
| 303 | if (args.size() < 2) { |
|---|
| 304 | message = "Missing argument"; |
|---|
| 305 | return NSCAPI::returnCRIT; |
|---|
| 306 | } |
|---|
| 307 | std::string ret; |
|---|
| 308 | bool critical = false; |
|---|
| 309 | searchQuery query; |
|---|
| 310 | std::string logFile = args.front(); args.pop_front(); |
|---|
| 311 | try { |
|---|
| 312 | buildQury(query, args); |
|---|
| 313 | } catch (std::string s) { |
|---|
| 314 | message = s; |
|---|
| 315 | return NSCAPI::returnCRIT; |
|---|
| 316 | } |
|---|
| 317 | NSC_DEBUG_MSG_STD("Base query: " + query.toString()); |
|---|
| 318 | |
|---|
| 319 | HANDLE hLog = OpenEventLog(NULL, logFile.c_str()); |
|---|
| 320 | if (hLog == NULL) { |
|---|
| 321 | message = "Could not open the Application event log."; |
|---|
| 322 | return NSCAPI::returnUNKNOWN; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | DWORD dwThisRecord, dwRead, dwNeeded; |
|---|
| 326 | EVENTLOGRECORD *pevlr; |
|---|
| 327 | BYTE bBuffer[BUFFER_SIZE]; |
|---|
| 328 | |
|---|
| 329 | pevlr = reinterpret_cast<EVENTLOGRECORD*>(&bBuffer); |
|---|
| 330 | |
|---|
| 331 | // get time now !!! |
|---|
| 332 | __time64_t ltime; |
|---|
| 333 | _time64(<ime); |
|---|
| 334 | DWORD currentTime = ltime; |
|---|
| 335 | |
|---|
| 336 | GetOldestEventLogRecord(hLog, &dwThisRecord); |
|---|
| 337 | |
|---|
| 338 | while (ReadEventLog(hLog, EVENTLOG_FORWARDS_READ|EVENTLOG_SEQUENTIAL_READ, |
|---|
| 339 | 0, pevlr, BUFFER_SIZE, &dwRead, &dwNeeded)) |
|---|
| 340 | { |
|---|
| 341 | while (dwRead > 0) |
|---|
| 342 | { |
|---|
| 343 | bool match = false; |
|---|
| 344 | bool c = false; |
|---|
| 345 | EventLogRecord record(pevlr); |
|---|
| 346 | |
|---|
| 347 | if ( query.critical.require.matchType(record.eventType()) && |
|---|
| 348 | query.critical.require.matchSource(record.eventSource()) && |
|---|
| 349 | query.critical.require.matchDateGenerated(currentTime, record.timeGenerated()) && |
|---|
| 350 | query.critical.require.matchDateWritten(currentTime, record.timeWritten()) && |
|---|
| 351 | query.critical.require.matchRegexp(record.enumStrings()) |
|---|
| 352 | ) { |
|---|
| 353 | match = true; |
|---|
| 354 | c = true; |
|---|
| 355 | } |
|---|
| 356 | if ( query.critical.exclude.matchType(record.eventType()) || |
|---|
| 357 | query.critical.exclude.matchSource(record.eventSource()) || |
|---|
| 358 | query.critical.exclude.matchDateGenerated(currentTime, record.timeGenerated()) || |
|---|
| 359 | query.critical.exclude.matchDateWritten(currentTime, record.timeWritten()) || |
|---|
| 360 | query.critical.exclude.matchRegexp(record.enumStrings()) |
|---|
| 361 | ) { |
|---|
| 362 | match = false; |
|---|
| 363 | c = false; |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | if ( query.warn.require.matchType(record.eventType()) && |
|---|
| 367 | query.warn.require.matchSource(record.eventSource()) && |
|---|
| 368 | query.warn.require.matchDateGenerated(currentTime, record.timeGenerated()) && |
|---|
| 369 | query.warn.require.matchDateWritten(currentTime, record.timeWritten()) && |
|---|
| 370 | query.warn.require.matchRegexp(record.enumStrings()) |
|---|
| 371 | ) |
|---|
| 372 | match = true; |
|---|
| 373 | if ( query.warn.exclude.matchType(record.eventType()) || |
|---|
| 374 | query.warn.exclude.matchSource(record.eventSource()) || |
|---|
| 375 | query.warn.exclude.matchDateGenerated(currentTime, record.timeGenerated()) || |
|---|
| 376 | query.warn.exclude.matchDateWritten(currentTime, record.timeWritten()) || |
|---|
| 377 | query.warn.exclude.matchRegexp(record.enumStrings()) |
|---|
| 378 | ) |
|---|
| 379 | match = false; |
|---|
| 380 | |
|---|
| 381 | if (match) { |
|---|
| 382 | if (c) |
|---|
| 383 | critical = true; |
|---|
| 384 | if (!ret.empty()) |
|---|
| 385 | ret += " - "; |
|---|
| 386 | ret += record.eventSource(); |
|---|
| 387 | if (query.descriptions) { |
|---|
| 388 | std::string s = record.enumStrings(); |
|---|
| 389 | if (!s.empty()) |
|---|
| 390 | ret += " [" + s + "]" ; |
|---|
| 391 | } |
|---|
| 392 | } |
|---|
| 393 | dwRead -= pevlr->Length; |
|---|
| 394 | pevlr = (EVENTLOGRECORD *) |
|---|
| 395 | ((LPBYTE) pevlr + pevlr->Length); |
|---|
| 396 | } |
|---|
| 397 | |
|---|
| 398 | pevlr = (EVENTLOGRECORD *) &bBuffer; |
|---|
| 399 | } |
|---|
| 400 | |
|---|
| 401 | CloseEventLog(hLog); |
|---|
| 402 | if (critical) |
|---|
| 403 | ret = "CRITICAL: " + ret; |
|---|
| 404 | else if (!ret.empty()) |
|---|
| 405 | ret = "WARNING: " + ret; |
|---|
| 406 | else |
|---|
| 407 | ret = "OK: No errors/warnings in event log."; |
|---|
| 408 | if (query.truncate != 0) |
|---|
| 409 | ret = ret.substr(0, query.truncate); |
|---|
| 410 | message = ret; |
|---|
| 411 | return rCode; |
|---|
| 412 | } |
|---|
| 413 | |
|---|
| 414 | |
|---|
| 415 | NSC_WRAPPERS_MAIN_DEF(gCheckEventLog); |
|---|
| 416 | NSC_WRAPPERS_IGNORE_MSG_DEF(); |
|---|
| 417 | NSC_WRAPPERS_HANDLE_CMD_DEF(gCheckEventLog); |
|---|