| 1 | /************************************************************************** |
|---|
| 2 | * Copyright (C) 2004-2007 by Michael Medin <michael@medin.name> * |
|---|
| 3 | * * |
|---|
| 4 | * This code is part of NSClient++ - http://trac.nakednuns.org/nscp * |
|---|
| 5 | * * |
|---|
| 6 | * This program is free software; you can redistribute it and/or modify * |
|---|
| 7 | * it under the terms of the GNU General Public License as published by * |
|---|
| 8 | * the Free Software Foundation; either version 2 of the License, or * |
|---|
| 9 | * (at your option) any later version. * |
|---|
| 10 | * * |
|---|
| 11 | * This program is distributed in the hope that it will be useful, * |
|---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
|---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
|---|
| 14 | * GNU General Public License for more details. * |
|---|
| 15 | * * |
|---|
| 16 | * You should have received a copy of the GNU General Public License * |
|---|
| 17 | * along with this program; if not, write to the * |
|---|
| 18 | * Free Software Foundation, Inc., * |
|---|
| 19 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
|---|
| 20 | ***************************************************************************/ |
|---|
| 21 | |
|---|
| 22 | #include "stdafx.h" |
|---|
| 23 | #include "CheckEventLog.h" |
|---|
| 24 | #include <filter_framework.hpp> |
|---|
| 25 | #include <boost/foreach.hpp> |
|---|
| 26 | |
|---|
| 27 | #include <time.h> |
|---|
| 28 | #include <utils.h> |
|---|
| 29 | #include <error.hpp> |
|---|
| 30 | #include <map> |
|---|
| 31 | #include <vector> |
|---|
| 32 | //#include <config.h> |
|---|
| 33 | |
|---|
| 34 | #include <boost/bind.hpp> |
|---|
| 35 | #include <boost/assign.hpp> |
|---|
| 36 | #include <boost/program_options.hpp> |
|---|
| 37 | |
|---|
| 38 | #include "filter.hpp" |
|---|
| 39 | #include "filters.hpp" |
|---|
| 40 | |
|---|
| 41 | #include <nscapi/nscapi_protobuf_functions.hpp> |
|---|
| 42 | #include <nscapi/nscapi_core_helper.hpp> |
|---|
| 43 | |
|---|
| 44 | #include <parsers/where/unary_fun.hpp> |
|---|
| 45 | #include <parsers/where/list_value.hpp> |
|---|
| 46 | #include <parsers/where/binary_op.hpp> |
|---|
| 47 | #include <parsers/where/unary_op.hpp> |
|---|
| 48 | #include <parsers/where/variable.hpp> |
|---|
| 49 | |
|---|
| 50 | #include <simple_timer.hpp> |
|---|
| 51 | #include <settings/client/settings_client.hpp> |
|---|
| 52 | namespace sh = nscapi::settings_helper; |
|---|
| 53 | |
|---|
| 54 | #include "simple_registry.hpp" |
|---|
| 55 | #include "eventlog_record.hpp" |
|---|
| 56 | |
|---|
| 57 | CheckEventLog::CheckEventLog() { |
|---|
| 58 | } |
|---|
| 59 | CheckEventLog::~CheckEventLog() { |
|---|
| 60 | } |
|---|
| 61 | struct parse_exception { |
|---|
| 62 | parse_exception(std::wstring) {} |
|---|
| 63 | }; |
|---|
| 64 | |
|---|
| 65 | |
|---|
| 66 | bool CheckEventLog::loadModule() { |
|---|
| 67 | return false; |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | |
|---|
| 71 | void real_time_thread::process_no_events(const filters::filter_config_object &object) { |
|---|
| 72 | std::wstring response; |
|---|
| 73 | if (!nscapi::core_helper::submit_simple_message(object.target, object.alias, NSCAPI::returnOK, object.ok_msg, object.perf_msg, response)) { |
|---|
| 74 | NSC_LOG_ERROR(_T("Failed to submit evenhtlog result: ") + response); |
|---|
| 75 | } |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | void real_time_thread::process_record(const filters::filter_config_object &object, const EventLogRecord &record) { |
|---|
| 79 | std::wstring response; |
|---|
| 80 | std::wstring message = record.render(true, object.syntax, object.date_format, object.dwLang); |
|---|
| 81 | if (!nscapi::core_helper::submit_simple_message(object.target, object.alias, object.severity, message, object.perf_msg, response)) { |
|---|
| 82 | NSC_LOG_ERROR(_T("Failed to submit evenhtlog result: ") + response); |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | if (cache_) { |
|---|
| 86 | boost::unique_lock<boost::timed_mutex> lock(cache_mutex_, boost::get_system_time() + boost::posix_time::seconds(5)); |
|---|
| 87 | if (!lock.owns_lock()) { |
|---|
| 88 | NSC_LOG_ERROR(_T("ERROR: Could not get CheckEventLogCache mutex.")); |
|---|
| 89 | return; |
|---|
| 90 | } |
|---|
| 91 | hit_cache_.push_back(message); |
|---|
| 92 | } |
|---|
| 93 | } |
|---|
| 94 | bool real_time_thread::check_cache(unsigned long &count, std::wstring &messages) { |
|---|
| 95 | if (!cache_) { |
|---|
| 96 | messages = _T("ERROR: Cache is not enabled!"); |
|---|
| 97 | NSC_LOG_ERROR(messages); |
|---|
| 98 | return false; |
|---|
| 99 | } |
|---|
| 100 | boost::unique_lock<boost::timed_mutex> lock(cache_mutex_, boost::get_system_time() + boost::posix_time::seconds(5)); |
|---|
| 101 | if (!lock.owns_lock()) { |
|---|
| 102 | messages = _T("ERROR: Could not get CheckEventLogCache mutex."); |
|---|
| 103 | NSC_LOG_ERROR(messages); |
|---|
| 104 | return false; |
|---|
| 105 | } |
|---|
| 106 | BOOST_FOREACH(const std::wstring &s, hit_cache_) { |
|---|
| 107 | if (!messages.empty()) |
|---|
| 108 | messages += _T(", "); |
|---|
| 109 | messages += s; |
|---|
| 110 | } |
|---|
| 111 | count = hit_cache_.size(); |
|---|
| 112 | hit_cache_.clear(); |
|---|
| 113 | return true; |
|---|
| 114 | } |
|---|
| 115 | void real_time_thread::debug_miss(const EventLogRecord &record) { |
|---|
| 116 | std::wstring message = record.render(true, _T("%type% %source%: %message%"), DATE_FORMAT, LANG_NEUTRAL); |
|---|
| 117 | NSC_DEBUG_MSG_STD(_T("No filter matched: ") + message); |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| 120 | void real_time_thread::thread_proc() { |
|---|
| 121 | |
|---|
| 122 | std::list<filters::filter_config_object> filters; |
|---|
| 123 | BOOST_FOREACH(filters::filter_config_object object, filters_.get_object_list()) { |
|---|
| 124 | eventlog_filter::filter_argument fargs = eventlog_filter::factories::create_argument(object.syntax, object.date_format); |
|---|
| 125 | fargs->filter = object.filter; |
|---|
| 126 | fargs->debug = object.debug; |
|---|
| 127 | fargs->alias = object.alias; |
|---|
| 128 | fargs->bShowDescriptions = true; |
|---|
| 129 | // eventlog_filter::filter_engine |
|---|
| 130 | object.engine = eventlog_filter::factories::create_engine(fargs); |
|---|
| 131 | |
|---|
| 132 | if (!object.engine) { |
|---|
| 133 | NSC_LOG_ERROR_STD(_T("Invalid filter: ") + object.filter); |
|---|
| 134 | continue; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | if (!object.engine->boot()) { |
|---|
| 138 | NSC_LOG_ERROR_STD(_T("Error booting filter: ") + object.filter); |
|---|
| 139 | continue; |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | std::wstring message; |
|---|
| 143 | if (!object.engine->validate(message)) { |
|---|
| 144 | NSC_LOG_ERROR_STD(_T("Error validating filter: ") + message); |
|---|
| 145 | continue; |
|---|
| 146 | } |
|---|
| 147 | filters.push_back(object); |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | typedef boost::shared_ptr<eventlog_wrapper> eventlog_type; |
|---|
| 153 | typedef std::vector<eventlog_type> eventlog_list; |
|---|
| 154 | eventlog_list list; |
|---|
| 155 | |
|---|
| 156 | BOOST_FOREACH(std::wstring l, lists_) { |
|---|
| 157 | eventlog_type el = eventlog_type(new eventlog_wrapper(l)); |
|---|
| 158 | if (!el->seek_end()) { |
|---|
| 159 | NSC_LOG_ERROR_STD(_T("Failed to find the end of eventlog: ") + l); |
|---|
| 160 | } else { |
|---|
| 161 | list.push_back(el); |
|---|
| 162 | } |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | // TODO: add support for scanning "missed messages" at startup |
|---|
| 166 | |
|---|
| 167 | HANDLE *handles = new HANDLE[1+list.size()]; |
|---|
| 168 | handles[0] = stop_event_; |
|---|
| 169 | for (int i=0;i<list.size();i++) { |
|---|
| 170 | list[i]->notify(handles[i+1]); |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | DWORD dwWaitTime = max_age_; |
|---|
| 174 | if (dwWaitTime > 0 && dwWaitTime < 5000) |
|---|
| 175 | dwWaitTime = 5000; |
|---|
| 176 | unsigned int errors = 0; |
|---|
| 177 | while (true) { |
|---|
| 178 | DWORD dwWaitReason = WaitForMultipleObjects(list.size()+1, handles, FALSE, dwWaitTime==0?INFINITE:dwWaitTime); |
|---|
| 179 | if (dwWaitReason == WAIT_TIMEOUT) { |
|---|
| 180 | BOOST_FOREACH(const filters::filter_config_object &object, filters) { |
|---|
| 181 | process_no_events(object); |
|---|
| 182 | } |
|---|
| 183 | } else if (dwWaitReason == WAIT_OBJECT_0) { |
|---|
| 184 | delete [] handles; |
|---|
| 185 | return; |
|---|
| 186 | } else if (dwWaitReason > WAIT_OBJECT_0 && dwWaitReason <= (WAIT_OBJECT_0 + list.size())) { |
|---|
| 187 | |
|---|
| 188 | eventlog_type el = list[dwWaitReason-WAIT_OBJECT_0-1]; |
|---|
| 189 | DWORD status = el->read_record(0, EVENTLOG_SEQUENTIAL_READ|EVENTLOG_FORWARDS_READ); |
|---|
| 190 | if (ERROR_SUCCESS != status && ERROR_HANDLE_EOF != status) { |
|---|
| 191 | delete [] handles; |
|---|
| 192 | return; |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | __time64_t ltime; |
|---|
| 196 | _time64(<ime); |
|---|
| 197 | |
|---|
| 198 | EVENTLOGRECORD *pevlr = el->read_record_with_buffer(); |
|---|
| 199 | while (pevlr != NULL) { |
|---|
| 200 | EventLogRecord elr(el->get_name(), pevlr, ltime); |
|---|
| 201 | boost::shared_ptr<eventlog_filter::filter_obj> arg = boost::shared_ptr<eventlog_filter::filter_obj>(new eventlog_filter::filter_obj(elr)); |
|---|
| 202 | bool matched = false; |
|---|
| 203 | |
|---|
| 204 | BOOST_FOREACH(const filters::filter_config_object &object, filters) { |
|---|
| 205 | if (object.engine->match(arg)) { |
|---|
| 206 | process_record(object, elr); |
|---|
| 207 | matched = true; |
|---|
| 208 | } |
|---|
| 209 | } |
|---|
| 210 | if (debug_ && !matched) |
|---|
| 211 | debug_miss(elr); |
|---|
| 212 | |
|---|
| 213 | pevlr = el->read_record_with_buffer(); |
|---|
| 214 | } |
|---|
| 215 | } else { |
|---|
| 216 | NSC_LOG_ERROR(_T("Error failed to wait for eventlog message: ") + error::lookup::last_error()); |
|---|
| 217 | if (errors++ > 10) { |
|---|
| 218 | NSC_LOG_ERROR(_T("To many errors giving up")); |
|---|
| 219 | delete [] handles; |
|---|
| 220 | return; |
|---|
| 221 | } |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | delete [] handles; |
|---|
| 225 | return; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | |
|---|
| 229 | bool real_time_thread::start() { |
|---|
| 230 | if (!enabled_) |
|---|
| 231 | return true; |
|---|
| 232 | |
|---|
| 233 | stop_event_ = CreateEvent(NULL, TRUE, FALSE, _T("EventLogShutdown")); |
|---|
| 234 | |
|---|
| 235 | thread_ = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&real_time_thread::thread_proc, this))); |
|---|
| 236 | return true; |
|---|
| 237 | } |
|---|
| 238 | bool real_time_thread::stop() { |
|---|
| 239 | SetEvent(stop_event_); |
|---|
| 240 | if (thread_) |
|---|
| 241 | thread_->join(); |
|---|
| 242 | return true; |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | void real_time_thread::add_realtime_filter(boost::shared_ptr<nscapi::settings_proxy> proxy, std::wstring key, std::wstring query) { |
|---|
| 246 | try { |
|---|
| 247 | filters_.add(proxy, filters_path_, key, query, key == _T("default")); |
|---|
| 248 | } catch (const std::exception &e) { |
|---|
| 249 | NSC_LOG_ERROR_STD(_T("Failed to add command: ") + key + _T(", ") + utf8::to_unicode(e.what())); |
|---|
| 250 | } catch (...) { |
|---|
| 251 | NSC_LOG_ERROR_STD(_T("Failed to add command: ") + key); |
|---|
| 252 | } |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | |
|---|
| 256 | bool CheckEventLog::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) { |
|---|
| 257 | try { |
|---|
| 258 | register_command(_T("CheckEventLog"), _T("Check for errors in the event logger!")); |
|---|
| 259 | register_command(_T("check_eventlog"), _T("Check for errors in the event logger!")); |
|---|
| 260 | register_command(_T("checkeventlogcache"), _T("Check for errors in the event logger!")); |
|---|
| 261 | register_command(_T("check_eventlog_cache"), _T("Check for errors in the event logger!")); |
|---|
| 262 | |
|---|
| 263 | sh::settings_registry settings(get_settings_proxy()); |
|---|
| 264 | settings.set_alias(alias, _T("eventlog")); |
|---|
| 265 | |
|---|
| 266 | thread_.filters_path_ = settings.alias().get_settings_path(_T("real-time/filters")); |
|---|
| 267 | |
|---|
| 268 | |
|---|
| 269 | settings.alias().add_path_to_settings() |
|---|
| 270 | (_T("EVENT LOG SECTION"), _T("Section for the EventLog Checker (CheckEventLog.dll).")) |
|---|
| 271 | |
|---|
| 272 | (_T("real-time"), _T("CONFIGURE REALTIME CHECKING"), _T("A set of options to configure the real time checks")) |
|---|
| 273 | |
|---|
| 274 | (_T("real-time/filters"), sh::fun_values_path(boost::bind(&real_time_thread::add_realtime_filter, &thread_, get_settings_proxy(), _1, _2)), |
|---|
| 275 | _T("REALTIME FILTERS"), _T("A set of filters to use in real-time mode")) |
|---|
| 276 | ; |
|---|
| 277 | |
|---|
| 278 | settings.alias().add_key_to_settings() |
|---|
| 279 | (_T("debug"), sh::bool_key(&debug_, false), |
|---|
| 280 | _T("DEBUG"), _T("Log more information when filtering (usefull to detect issues with filters) not usefull in production as it is a bit of a resource hog.")) |
|---|
| 281 | |
|---|
| 282 | (_T("lookup names"), sh::bool_key(&lookup_names_, true), |
|---|
| 283 | _T("LOOKUP NAMES"), _T("Lookup the names of eventlog files")) |
|---|
| 284 | |
|---|
| 285 | (_T("syntax"), sh::wstring_key(&syntax_), |
|---|
| 286 | _T("SYNTAX"), _T("Set this to use a specific syntax string for all commands (that don't specify one).")) |
|---|
| 287 | |
|---|
| 288 | (_T("buffer size"), sh::int_key(&buffer_length_, 128*1024), |
|---|
| 289 | _T("BUFFER_SIZE"), _T("The size of the buffer to use when getting messages this affects the speed and maximum size of messages you can recieve.")) |
|---|
| 290 | |
|---|
| 291 | ; |
|---|
| 292 | |
|---|
| 293 | settings.alias().add_key_to_settings(_T("real-time")) |
|---|
| 294 | |
|---|
| 295 | (_T("enabled"), sh::bool_fun_key<bool>(boost::bind(&real_time_thread::set_enabled, &thread_, _1), false), |
|---|
| 296 | _T("REAL TIME CHECKING"), _T("Spawns a backgrounnd thread which detects issues and reports them back instantly.")) |
|---|
| 297 | |
|---|
| 298 | (_T("startup age"), sh::string_fun_key<std::wstring>(boost::bind(&real_time_thread::set_start_age, &thread_, _1), _T("30m")), |
|---|
| 299 | _T("STARTUP AGE"), _T("The initial age to scan when starting NSClient++")) |
|---|
| 300 | |
|---|
| 301 | (_T("maximum age"), sh::string_fun_key<std::wstring>(boost::bind(&real_time_thread::set_max_age, &thread_, _1), _T("5m")), |
|---|
| 302 | _T("MAGIMUM AGE"), _T("How long before reporting \"ok\" (if this is set to off no ok will be reported only errors)")) |
|---|
| 303 | |
|---|
| 304 | (_T("log"), sh::string_fun_key<std::wstring>(boost::bind(&real_time_thread::set_eventlog, &thread_, _1), _T("application")), |
|---|
| 305 | _T("LOGS TO CHECK"), _T("Coma separated list of logs to check")) |
|---|
| 306 | |
|---|
| 307 | (_T("debug"), sh::bool_key(&thread_.debug_, false), |
|---|
| 308 | _T("DEBUG"), _T("Log missed records (usefull to detect issues with filters) not usefull in production as it is a bit of a resource hog.")) |
|---|
| 309 | |
|---|
| 310 | (_T("enable active"), sh::bool_key(&thread_.cache_, false), |
|---|
| 311 | _T("ENABLE ACTIVE MONITORING"), _T("This will store all matches so you can use real-time filters from active monitoring (use CheckEventlogCache).")) |
|---|
| 312 | ; |
|---|
| 313 | |
|---|
| 314 | settings.register_all(); |
|---|
| 315 | settings.notify(); |
|---|
| 316 | |
|---|
| 317 | if (mode == NSCAPI::normalStart) { |
|---|
| 318 | if (!thread_.start()) |
|---|
| 319 | NSC_LOG_ERROR_STD(_T("Failed to start collection thread")); |
|---|
| 320 | } |
|---|
| 321 | |
|---|
| 322 | } catch (nscapi::nscapi_exception &e) { |
|---|
| 323 | NSC_LOG_ERROR_STD(_T("Failed to register command: ") + utf8::cvt<std::wstring>(e.what())); |
|---|
| 324 | return false; |
|---|
| 325 | } catch (std::exception &e) { |
|---|
| 326 | NSC_LOG_ERROR_STD(_T("Exception: ") + utf8::cvt<std::wstring>(e.what())); |
|---|
| 327 | return false; |
|---|
| 328 | } catch (...) { |
|---|
| 329 | NSC_LOG_ERROR_STD(_T("Failed to register command.")); |
|---|
| 330 | return false; |
|---|
| 331 | } |
|---|
| 332 | return true; |
|---|
| 333 | } |
|---|
| 334 | bool CheckEventLog::unloadModule() { |
|---|
| 335 | if (!thread_.stop()) |
|---|
| 336 | NSC_LOG_ERROR_STD(_T("Failed to start collection thread")); |
|---|
| 337 | return true; |
|---|
| 338 | } |
|---|
| 339 | |
|---|
| 340 | bool CheckEventLog::hasCommandHandler() { |
|---|
| 341 | return true; |
|---|
| 342 | } |
|---|
| 343 | bool CheckEventLog::hasMessageHandler() { |
|---|
| 344 | return false; |
|---|
| 345 | } |
|---|
| 346 | |
|---|
| 347 | class uniq_eventlog_record { |
|---|
| 348 | DWORD ID; |
|---|
| 349 | WORD type; |
|---|
| 350 | WORD category; |
|---|
| 351 | public: |
|---|
| 352 | std::wstring message; |
|---|
| 353 | uniq_eventlog_record(EVENTLOGRECORD *pevlr) : ID(pevlr->EventID&0xffff), type(pevlr->EventType), category(pevlr->EventCategory) {} |
|---|
| 354 | bool operator< (const uniq_eventlog_record &other) const { |
|---|
| 355 | return (ID < other.ID) || ((ID==other.ID)&&(type < other.type)) || (ID==other.ID&&type==other.type)&&(category < other.category); |
|---|
| 356 | } |
|---|
| 357 | std::wstring to_string() const { |
|---|
| 358 | return _T("id=") + strEx::itos(ID) + _T("type=") + strEx::itos(type) + _T("category=") + strEx::itos(category); |
|---|
| 359 | } |
|---|
| 360 | }; |
|---|
| 361 | typedef std::map<uniq_eventlog_record,unsigned int> uniq_eventlog_map; |
|---|
| 362 | |
|---|
| 363 | struct event_log_buffer { |
|---|
| 364 | BYTE *bBuffer; |
|---|
| 365 | DWORD bufferSize_; |
|---|
| 366 | event_log_buffer(DWORD bufferSize) : bufferSize_(bufferSize) { |
|---|
| 367 | bBuffer = new BYTE[bufferSize+10]; |
|---|
| 368 | } |
|---|
| 369 | ~event_log_buffer() { |
|---|
| 370 | delete [] bBuffer; |
|---|
| 371 | } |
|---|
| 372 | EVENTLOGRECORD* getBufferUnsafe() { |
|---|
| 373 | return reinterpret_cast<EVENTLOGRECORD*>(bBuffer); |
|---|
| 374 | } |
|---|
| 375 | DWORD getBufferSize() { |
|---|
| 376 | return bufferSize_; |
|---|
| 377 | } |
|---|
| 378 | }; |
|---|
| 379 | typedef checkHolders::CheckContainer<checkHolders::MaxMinBoundsULongInteger> EventLogQuery1Container; |
|---|
| 380 | typedef checkHolders::CheckContainer<checkHolders::ExactBoundsULongInteger> EventLogQuery2Container; |
|---|
| 381 | |
|---|
| 382 | NSCAPI::nagiosReturn CheckEventLog::checkCache(std::list<std::wstring> &arguments, std::wstring &message, std::wstring &perf) { |
|---|
| 383 | |
|---|
| 384 | EventLogQuery1Container query1; |
|---|
| 385 | EventLogQuery2Container query2; |
|---|
| 386 | bool bPerfData = true; |
|---|
| 387 | unsigned int truncate = 0; |
|---|
| 388 | NSCAPI::nagiosReturn returnCode = NSCAPI::returnOK; |
|---|
| 389 | |
|---|
| 390 | try { |
|---|
| 391 | MAP_OPTIONS_BEGIN(arguments) |
|---|
| 392 | MAP_OPTIONS_NUMERIC_ALL(query1, _T("")) |
|---|
| 393 | MAP_OPTIONS_EXACT_NUMERIC_ALL(query2, _T("")) |
|---|
| 394 | MAP_OPTIONS_STR2INT(_T("truncate"), truncate) |
|---|
| 395 | MAP_OPTIONS_BOOL_FALSE(IGNORE_PERFDATA, bPerfData) |
|---|
| 396 | MAP_OPTIONS_MISSING(message, _T("Unknown argument: ")) |
|---|
| 397 | MAP_OPTIONS_END() |
|---|
| 398 | } catch (checkHolders::parse_exception e) { |
|---|
| 399 | message = e.getMessage(); |
|---|
| 400 | return NSCAPI::returnUNKNOWN; |
|---|
| 401 | } catch (...) { |
|---|
| 402 | message = _T("Invalid command line!"); |
|---|
| 403 | return NSCAPI::returnUNKNOWN; |
|---|
| 404 | } |
|---|
| 405 | |
|---|
| 406 | unsigned long count = 0; |
|---|
| 407 | if (!thread_.check_cache(count, message)) { |
|---|
| 408 | return NSCAPI::returnUNKNOWN; |
|---|
| 409 | } |
|---|
| 410 | |
|---|
| 411 | if (!bPerfData) { |
|---|
| 412 | query1.perfData = false; |
|---|
| 413 | query2.perfData = false; |
|---|
| 414 | } |
|---|
| 415 | if (query1.alias.empty()) |
|---|
| 416 | query1.alias = _T("eventlog"); |
|---|
| 417 | if (query2.alias.empty()) |
|---|
| 418 | query2.alias = _T("eventlog"); |
|---|
| 419 | if (query1.hasBounds()) |
|---|
| 420 | query1.runCheck(count, returnCode, message, perf); |
|---|
| 421 | else if (query2.hasBounds()) |
|---|
| 422 | query2.runCheck(count, returnCode, message, perf); |
|---|
| 423 | else { |
|---|
| 424 | message = _T("No bounds specified!"); |
|---|
| 425 | return NSCAPI::returnUNKNOWN; |
|---|
| 426 | } |
|---|
| 427 | if ((truncate > 0) && (message.length() > (truncate-4))) |
|---|
| 428 | message = message.substr(0, truncate-4) + _T("..."); |
|---|
| 429 | if (message.empty()) |
|---|
| 430 | message = _T("Eventlog check ok"); |
|---|
| 431 | return returnCode; |
|---|
| 432 | } |
|---|
| 433 | |
|---|
| 434 | NSCAPI::nagiosReturn CheckEventLog::handleCommand(const std::wstring &target, const std::wstring &command, std::list<std::wstring> &arguments, std::wstring &message, std::wstring &perf) { |
|---|
| 435 | if (command == _T("checkeventlogcache") || command == _T("check_eventlog_cache")) |
|---|
| 436 | return checkCache(arguments, message, perf); |
|---|
| 437 | if (command != _T("checkeventlog") && command != _T("check_eventlog")) |
|---|
| 438 | return NSCAPI::returnIgnored; |
|---|
| 439 | simple_timer time; |
|---|
| 440 | |
|---|
| 441 | NSCAPI::nagiosReturn returnCode = NSCAPI::returnOK; |
|---|
| 442 | |
|---|
| 443 | std::list<std::wstring> files; |
|---|
| 444 | EventLogQuery1Container query1; |
|---|
| 445 | EventLogQuery2Container query2; |
|---|
| 446 | |
|---|
| 447 | |
|---|
| 448 | eventlog_filter::filter_argument fargs = eventlog_filter::factories::create_argument(syntax_, DATE_FORMAT); |
|---|
| 449 | |
|---|
| 450 | bool bPerfData = true; |
|---|
| 451 | bool unique = false; |
|---|
| 452 | unsigned int truncate = 0; |
|---|
| 453 | event_log_buffer buffer(buffer_length_); |
|---|
| 454 | //bool bPush = true; |
|---|
| 455 | |
|---|
| 456 | try { |
|---|
| 457 | MAP_OPTIONS_BEGIN(arguments) |
|---|
| 458 | MAP_OPTIONS_NUMERIC_ALL(query1, _T("")) |
|---|
| 459 | MAP_OPTIONS_EXACT_NUMERIC_ALL(query2, _T("")) |
|---|
| 460 | MAP_OPTIONS_STR2INT(_T("truncate"), truncate) |
|---|
| 461 | MAP_OPTIONS_BOOL_TRUE(_T("unique"), unique) |
|---|
| 462 | MAP_OPTIONS_BOOL_TRUE(_T("descriptions"), fargs->bShowDescriptions) |
|---|
| 463 | MAP_OPTIONS_PUSH(_T("file"), files) |
|---|
| 464 | MAP_OPTIONS_BOOL_FALSE(IGNORE_PERFDATA, bPerfData) |
|---|
| 465 | MAP_OPTIONS_BOOL_EX(_T("filter"), fargs->bFilterIn, _T("in"), _T("out")) |
|---|
| 466 | MAP_OPTIONS_BOOL_EX(_T("filter"), fargs->bFilterAll, _T("all"), _T("any")) |
|---|
| 467 | MAP_OPTIONS_BOOL_EX(_T("debug"), fargs->debug, _T("true"), _T("false")) |
|---|
| 468 | MAP_OPTIONS_STR(_T("syntax"), fargs->syntax) |
|---|
| 469 | MAP_OPTIONS_STR(_T("filter"), fargs->filter) |
|---|
| 470 | MAP_OPTIONS_MISSING(message, _T("Unknown argument: ")) |
|---|
| 471 | MAP_OPTIONS_END() |
|---|
| 472 | } catch (filters::parse_exception e) { |
|---|
| 473 | message = e.getMessage(); |
|---|
| 474 | return NSCAPI::returnUNKNOWN; |
|---|
| 475 | } catch (filters::filter_exception e) { |
|---|
| 476 | message = e.getMessage(); |
|---|
| 477 | return NSCAPI::returnUNKNOWN; |
|---|
| 478 | } catch (checkHolders::parse_exception e) { |
|---|
| 479 | message = e.getMessage(); |
|---|
| 480 | return NSCAPI::returnUNKNOWN; |
|---|
| 481 | } catch (...) { |
|---|
| 482 | message = _T("Invalid command line!"); |
|---|
| 483 | return NSCAPI::returnUNKNOWN; |
|---|
| 484 | } |
|---|
| 485 | |
|---|
| 486 | unsigned long int hit_count = 0; |
|---|
| 487 | if (files.empty()) { |
|---|
| 488 | message = _T("No file specified try adding: file=Application"); |
|---|
| 489 | return NSCAPI::returnUNKNOWN; |
|---|
| 490 | } |
|---|
| 491 | bool buffer_error_reported = false; |
|---|
| 492 | |
|---|
| 493 | |
|---|
| 494 | eventlog_filter::filter_engine impl = eventlog_filter::factories::create_engine(fargs); |
|---|
| 495 | |
|---|
| 496 | if (!impl) { |
|---|
| 497 | message = _T("Failed to initialize filter subsystem."); |
|---|
| 498 | return NSCAPI::returnUNKNOWN; |
|---|
| 499 | } |
|---|
| 500 | |
|---|
| 501 | impl->boot(); |
|---|
| 502 | |
|---|
| 503 | __time64_t ltime; |
|---|
| 504 | _time64(<ime); |
|---|
| 505 | |
|---|
| 506 | if (!impl->validate(message)) { |
|---|
| 507 | return NSCAPI::returnUNKNOWN; |
|---|
| 508 | } |
|---|
| 509 | |
|---|
| 510 | |
|---|
| 511 | NSC_DEBUG_MSG_STD(_T("Boot time: ") + strEx::itos(time.stop())); |
|---|
| 512 | |
|---|
| 513 | for (std::list<std::wstring>::const_iterator cit2 = files.begin(); cit2 != files.end(); ++cit2) { |
|---|
| 514 | std::wstring name = *cit2; |
|---|
| 515 | if (lookup_names_) { |
|---|
| 516 | name = eventlog_wrapper::find_eventlog_name(*cit2); |
|---|
| 517 | if ((*cit2) != name) { |
|---|
| 518 | NSC_DEBUG_MSG_STD(_T("Opening alternative log: ") + name); |
|---|
| 519 | } |
|---|
| 520 | } |
|---|
| 521 | HANDLE hLog = OpenEventLog(NULL, name.c_str()); |
|---|
| 522 | if (hLog == NULL) { |
|---|
| 523 | message = _T("Could not open the '") + (*cit2) + _T("' event log: ") + error::lookup::last_error(); |
|---|
| 524 | return NSCAPI::returnUNKNOWN; |
|---|
| 525 | } |
|---|
| 526 | uniq_eventlog_map uniq_records; |
|---|
| 527 | |
|---|
| 528 | DWORD dwRead, dwNeeded; |
|---|
| 529 | while (true) { |
|---|
| 530 | BOOL bStatus = ReadEventLog(hLog, EVENTLOG_FORWARDS_READ|EVENTLOG_SEQUENTIAL_READ, |
|---|
| 531 | 0, buffer.getBufferUnsafe(), buffer.getBufferSize(), &dwRead, &dwNeeded); |
|---|
| 532 | if (bStatus == FALSE) { |
|---|
| 533 | DWORD err = GetLastError(); |
|---|
| 534 | if (err == ERROR_INSUFFICIENT_BUFFER) { |
|---|
| 535 | if (!buffer_error_reported) { |
|---|
| 536 | NSC_LOG_ERROR_STD(_T("EvenlogBuffer is too small change the value of buffer_length=") + strEx::itos(dwNeeded+1) + _T(": ") + error::lookup::last_error(err)); |
|---|
| 537 | buffer_error_reported = true; |
|---|
| 538 | } |
|---|
| 539 | } else if (err == ERROR_HANDLE_EOF) { |
|---|
| 540 | break; |
|---|
| 541 | } else { |
|---|
| 542 | NSC_LOG_ERROR_STD(_T("Failed to read from eventlog: ") + error::lookup::last_error(err)); |
|---|
| 543 | message = _T("Failed to read from eventlog: ") + error::lookup::last_error(err); |
|---|
| 544 | CloseEventLog(hLog); |
|---|
| 545 | return NSCAPI::returnUNKNOWN; |
|---|
| 546 | } |
|---|
| 547 | } |
|---|
| 548 | EVENTLOGRECORD *pevlr = buffer.getBufferUnsafe(); |
|---|
| 549 | while (dwRead > 0) { |
|---|
| 550 | EventLogRecord record((*cit2), pevlr, ltime); |
|---|
| 551 | boost::shared_ptr<eventlog_filter::filter_obj> arg = boost::shared_ptr<eventlog_filter::filter_obj>(new eventlog_filter::filter_obj(record)); |
|---|
| 552 | bool match = impl->match(arg); |
|---|
| 553 | if (match&&unique) { |
|---|
| 554 | match = false; |
|---|
| 555 | uniq_eventlog_record uniq_record = pevlr; |
|---|
| 556 | uniq_eventlog_map::iterator it = uniq_records.find(uniq_record); |
|---|
| 557 | if (it != uniq_records.end()) { |
|---|
| 558 | (*it).second ++; |
|---|
| 559 | } |
|---|
| 560 | else { |
|---|
| 561 | if (!fargs->syntax.empty()) { |
|---|
| 562 | uniq_record.message = record.render(fargs->bShowDescriptions, fargs->syntax); |
|---|
| 563 | } else if (!fargs->bShowDescriptions) { |
|---|
| 564 | uniq_record.message = record.get_source(); |
|---|
| 565 | } else { |
|---|
| 566 | uniq_record.message = record.get_source(); |
|---|
| 567 | uniq_record.message += _T("(") + EventLogRecord::translateType(record.eventType()) + _T(", ") + |
|---|
| 568 | strEx::itos(record.eventID()) + _T(", ") + EventLogRecord::translateSeverity(record.severity()) + _T(")"); |
|---|
| 569 | uniq_record.message += _T("[") + record.enumStrings() + _T("]"); |
|---|
| 570 | uniq_record.message += _T("{%count%}"); |
|---|
| 571 | } |
|---|
| 572 | uniq_records[uniq_record] = 1; |
|---|
| 573 | } |
|---|
| 574 | hit_count++; |
|---|
| 575 | } else if (match) { |
|---|
| 576 | if (!fargs->syntax.empty()) { |
|---|
| 577 | strEx::append_list(message, record.render(fargs->bShowDescriptions, fargs->syntax)); |
|---|
| 578 | } else if (!fargs->bShowDescriptions) { |
|---|
| 579 | strEx::append_list(message, record.get_source()); |
|---|
| 580 | } else { |
|---|
| 581 | strEx::append_list(message, record.get_source()); |
|---|
| 582 | message += _T("(") + EventLogRecord::translateType(record.eventType()) + _T(", ") + |
|---|
| 583 | strEx::itos(record.eventID()) + _T(", ") + EventLogRecord::translateSeverity(record.severity()) + _T(")"); |
|---|
| 584 | message += _T("[") + record.enumStrings() + _T("]"); |
|---|
| 585 | } |
|---|
| 586 | hit_count++; |
|---|
| 587 | } |
|---|
| 588 | dwRead -= pevlr->Length; |
|---|
| 589 | pevlr = reinterpret_cast<EVENTLOGRECORD*>((LPBYTE)pevlr + pevlr->Length); |
|---|
| 590 | } |
|---|
| 591 | } |
|---|
| 592 | CloseEventLog(hLog); |
|---|
| 593 | for (uniq_eventlog_map::const_iterator cit = uniq_records.begin(); cit != uniq_records.end(); ++cit) { |
|---|
| 594 | std::wstring msg = (*cit).first.message; |
|---|
| 595 | strEx::replace(msg, _T("%count%"), strEx::itos((*cit).second)); |
|---|
| 596 | strEx::append_list(message, msg); |
|---|
| 597 | } |
|---|
| 598 | } |
|---|
| 599 | NSC_DEBUG_MSG_STD(_T("Evaluation time: ") + strEx::itos(time.stop())); |
|---|
| 600 | |
|---|
| 601 | if (!bPerfData) { |
|---|
| 602 | query1.perfData = false; |
|---|
| 603 | query2.perfData = false; |
|---|
| 604 | } |
|---|
| 605 | if (query1.alias.empty()) |
|---|
| 606 | query1.alias = _T("eventlog"); |
|---|
| 607 | if (query2.alias.empty()) |
|---|
| 608 | query2.alias = _T("eventlog"); |
|---|
| 609 | if (query1.hasBounds()) |
|---|
| 610 | query1.runCheck(hit_count, returnCode, message, perf); |
|---|
| 611 | else if (query2.hasBounds()) |
|---|
| 612 | query2.runCheck(hit_count, returnCode, message, perf); |
|---|
| 613 | else { |
|---|
| 614 | message = _T("No bounds specified!"); |
|---|
| 615 | return NSCAPI::returnUNKNOWN; |
|---|
| 616 | } |
|---|
| 617 | if ((truncate > 0) && (message.length() > (truncate-4))) |
|---|
| 618 | message = message.substr(0, truncate-4) + _T("..."); |
|---|
| 619 | if (message.empty()) |
|---|
| 620 | message = _T("Eventlog check ok"); |
|---|
| 621 | return returnCode; |
|---|
| 622 | } |
|---|
| 623 | NSCAPI::nagiosReturn CheckEventLog::commandRAWLineExec(const wchar_t* char_command, const std::string &request, std::string &response) { |
|---|
| 624 | std::wstring command = char_command; |
|---|
| 625 | if (command == _T("insert-eventlog-message") || command == _T("insert-eventlog") || command == _T("insert-message") || command == _T("insert")) { |
|---|
| 626 | nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_exec_request(char_command, request); |
|---|
| 627 | std::wstring message; |
|---|
| 628 | std::vector<std::wstring> args(data.args.begin(), data.args.end()); |
|---|
| 629 | bool ok = insert_eventlog(args, message); |
|---|
| 630 | nscapi::functions::create_simple_exec_response(command, ok?NSCAPI::isSuccess:NSCAPI::hasFailed, message, response); |
|---|
| 631 | return ok?NSCAPI::isSuccess:NSCAPI::hasFailed; |
|---|
| 632 | } else if (command == _T("help")) { |
|---|
| 633 | std::vector<std::wstring> args; |
|---|
| 634 | args.push_back(_T("--help")); |
|---|
| 635 | std::wstring message; |
|---|
| 636 | insert_eventlog(args, message); |
|---|
| 637 | nscapi::functions::create_simple_exec_response(command, NSCAPI::isSuccess, message, response); |
|---|
| 638 | return NSCAPI::isSuccess; |
|---|
| 639 | } |
|---|
| 640 | return NSCAPI::returnIgnored; |
|---|
| 641 | } |
|---|
| 642 | |
|---|
| 643 | |
|---|
| 644 | |
|---|
| 645 | NSCAPI::nagiosReturn CheckEventLog::insert_eventlog(std::vector<std::wstring> arguments, std::wstring &message) { |
|---|
| 646 | try { |
|---|
| 647 | namespace po = boost::program_options; |
|---|
| 648 | |
|---|
| 649 | bool help = false; |
|---|
| 650 | std::wstring type, severity, source_name; |
|---|
| 651 | std::vector<std::wstring> strings; |
|---|
| 652 | WORD wEventID = 0, category = 0, customer = 0; |
|---|
| 653 | WORD facility = 0; |
|---|
| 654 | po::options_description desc("Allowed options"); |
|---|
| 655 | desc.add_options() |
|---|
| 656 | ("help,h", po::bool_switch(&help), "Show help screen") |
|---|
| 657 | ("source,s", po::wvalue<std::wstring>(&source_name)->default_value(_T("Application Error")), "source to use") |
|---|
| 658 | ("type,t", po::wvalue<std::wstring>(&type), "Event type") |
|---|
| 659 | ("level,l", po::wvalue<std::wstring>(&type), "Event level (type)") |
|---|
| 660 | ("facility,f", po::value<WORD>(&facility), "Facility/Qualifier") |
|---|
| 661 | ("qualifier,q", po::value<WORD>(&facility), "Facility/Qualifier") |
|---|
| 662 | ("severity", po::wvalue<std::wstring>(&severity), "Event severity") |
|---|
| 663 | ("category,c", po::value<WORD>(&category), "Event category") |
|---|
| 664 | ("customer", po::value<WORD>(&customer), "Customer bit 0,1") |
|---|
| 665 | ("arguments,a", po::wvalue<std::vector<std::wstring> >(&strings), "Message arguments (strings)") |
|---|
| 666 | ("eventlog-arguments", po::wvalue<std::vector<std::wstring> >(&strings), "Message arguments (strings)") |
|---|
| 667 | ("event-arguments", po::wvalue<std::vector<std::wstring> >(&strings), "Message arguments (strings)") |
|---|
| 668 | ("id,i", po::value<WORD>(&wEventID), "Event ID") |
|---|
| 669 | ; |
|---|
| 670 | |
|---|
| 671 | boost::program_options::variables_map vm; |
|---|
| 672 | |
|---|
| 673 | po::wparsed_options parsed = po::basic_command_line_parser<wchar_t>(arguments).options(desc).run(); |
|---|
| 674 | po::store(parsed, vm); |
|---|
| 675 | po::notify(vm); |
|---|
| 676 | |
|---|
| 677 | if (help) { |
|---|
| 678 | std::stringstream ss; |
|---|
| 679 | ss << "CheckEventLog Command line syntax:" << std::endl; |
|---|
| 680 | ss << desc; |
|---|
| 681 | message = utf8::cvt<std::wstring>(ss.str()); |
|---|
| 682 | return NSCAPI::isSuccess; |
|---|
| 683 | } else { |
|---|
| 684 | event_source source(source_name); |
|---|
| 685 | WORD dwType = EventLogRecord::translateType(type); |
|---|
| 686 | WORD wSeverity = EventLogRecord::translateSeverity(severity); |
|---|
| 687 | DWORD tID = (wEventID&0xffff) | ((facility&0xfff)<<16) | ((customer&0x1)<<29) | ((wSeverity&0x3)<<30); |
|---|
| 688 | |
|---|
| 689 | int size = 0; |
|---|
| 690 | BOOST_FOREACH(const std::wstring &s, strings) { |
|---|
| 691 | size += s.size()+1; |
|---|
| 692 | } |
|---|
| 693 | LPCWSTR *string_data = new LPCWSTR[strings.size()]; |
|---|
| 694 | int i=0; |
|---|
| 695 | BOOST_FOREACH(const std::wstring &s, strings) { |
|---|
| 696 | string_data[i++] = s.c_str(); |
|---|
| 697 | } |
|---|
| 698 | |
|---|
| 699 | if (!ReportEvent(source, dwType, category, tID, NULL, strings.size(), 0, string_data, NULL)) { |
|---|
| 700 | message = _T("Could not report the event"); |
|---|
| 701 | return NSCAPI::hasFailed; |
|---|
| 702 | } else { |
|---|
| 703 | message = _T("Message reported successfully"); |
|---|
| 704 | } |
|---|
| 705 | delete [] string_data; |
|---|
| 706 | } |
|---|
| 707 | } catch (const std::exception &e) { |
|---|
| 708 | NSC_LOG_ERROR_STD(_T("Failed to parse command line: ") + utf8::cvt<std::wstring>(e.what())); |
|---|
| 709 | } |
|---|
| 710 | return NSCAPI::returnIgnored; |
|---|
| 711 | } |
|---|
| 712 | |
|---|
| 713 | NSC_WRAP_DLL(); |
|---|
| 714 | NSC_WRAPPERS_MAIN_DEF(CheckEventLog); |
|---|
| 715 | NSC_WRAPPERS_IGNORE_MSG_DEF(); |
|---|
| 716 | NSC_WRAPPERS_HANDLE_CMD_DEF(); |
|---|
| 717 | NSC_WRAPPERS_CLI_DEF(); |
|---|