1 // +------------------------------------------------------------------+
2 // | ____ _ _ __ __ _ __ |
3 // | / ___| |__ ___ ___| | __ | \/ | |/ / |
4 // | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
5 // | | |___| | | | __/ (__| < | | | | . \ |
6 // | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
8 // | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
9 // +------------------------------------------------------------------+
11 // This file is part of Check_MK.
12 // The official homepage is at http://mathias-kettner.de/check_mk.
14 // check_mk is free software; you can redistribute it and/or modify it
15 // under the terms of the GNU General Public License as published by
16 // the Free Software Foundation in version 2. check_mk is distributed
17 // in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
18 // out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19 // PARTICULAR PURPOSE. See the GNU General Public License for more de-
20 // tails. You should have received a copy of the GNU General Public
21 // License along with GNU Make; see the file COPYING. If not, write
22 // to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 // Boston, MA 02110-1301 USA.
33 #include "Aggregator.h" // IWYU pragma: keep
34 #include "DowntimeOrComment.h" // IWYU pragma: keep
35 #include "EventConsoleConnection.h"
36 #include "InputBuffer.h"
37 #include "LogEntry.h" // IWYU pragma: keep
39 #include "MonitoringCore.h"
40 #include "OutputBuffer.h"
42 #include "StringUtils.h"
44 #include "mk_logwatch.h"
46 Store::Store(MonitoringCore
*mc
)
48 , _log_cache(mc
, mc
->maxCachedMessages())
52 , _table_contactgroups(mc
)
54 , _table_downtimes(mc
)
55 , _table_eventconsoleevents(mc
)
56 , _table_eventconsolehistory(mc
)
57 , _table_eventconsolereplication(mc
)
58 , _table_eventconsolerules(mc
)
59 , _table_eventconsolestatus(mc
)
60 , _table_hostgroups(mc
)
62 , _table_hostsbygroup(mc
)
63 , _table_log(mc
, &_log_cache
)
64 , _table_servicegroups(mc
)
66 , _table_servicesbygroup(mc
)
67 , _table_servicesbyhostgroup(mc
)
68 , _table_statehistory(mc
, &_log_cache
)
70 , _table_timeperiods(mc
)
72 addTable(_table_columns
);
73 addTable(_table_commands
);
74 addTable(_table_comments
);
75 addTable(_table_contactgroups
);
76 addTable(_table_contacts
);
77 addTable(_table_downtimes
);
78 addTable(_table_hostgroups
);
79 addTable(_table_hostsbygroup
);
80 addTable(_table_hosts
);
82 addTable(_table_servicegroups
);
83 addTable(_table_servicesbygroup
);
84 addTable(_table_servicesbyhostgroup
);
85 addTable(_table_services
);
86 addTable(_table_statehistory
);
87 addTable(_table_status
);
88 addTable(_table_timeperiods
);
89 addTable(_table_eventconsoleevents
);
90 addTable(_table_eventconsolehistory
);
91 addTable(_table_eventconsolestatus
);
92 addTable(_table_eventconsolereplication
);
93 addTable(_table_eventconsolerules
);
96 void Store::addTable(Table
&table
) {
97 _tables
.emplace(table
.name(), &table
);
98 _table_columns
.addTable(table
);
101 Table
&Store::findTable(OutputBuffer
&output
, const std::string
&name
) {
102 // NOTE: Even with an invalid table name we continue, so we can parse
103 // headers, especially ResponseHeader.
105 output
.setError(OutputBuffer::ResponseCode::invalid_request
,
106 "Invalid GET request, missing table name");
109 auto it
= _tables
.find(name
);
110 if (it
== _tables
.end()) {
111 output
.setError(OutputBuffer::ResponseCode::not_found
,
112 "Invalid GET request, no such table '" + name
+ "'");
118 void Store::registerDowntime(nebstruct_downtime_data
*data
) {
119 _downtimes
.registerDowntime(data
);
122 void Store::registerComment(nebstruct_comment_data
*data
) {
123 _comments
.registerComment(data
);
127 std::list
<std::string
> getLines(InputBuffer
&input
) {
128 std::list
<std::string
> lines
;
129 while (!input
.empty()) {
130 lines
.push_back(input
.nextLine());
131 if (lines
.back().empty()) {
139 void Store::logRequest(const std::string
&line
,
140 const std::list
<std::string
> &lines
) {
141 Informational
log(logger());
142 log
<< "request: " << line
;
143 if (logger()->isLoggable(LogLevel::debug
)) {
144 for (const auto &l
: lines
) {
148 size_t s
= lines
.size();
150 log
<< R
"(\n{)" << s
<< (s
== 1 ? " line follows" : " lines follow")
156 Store::ExternalCommand::ExternalCommand(const std::string
&str
) {
157 constexpr int timestamp_len
= 10;
158 constexpr int prefix_len
= timestamp_len
+ 3;
159 if (str
.size() <= prefix_len
|| str
[0] != '[' ||
160 str
[prefix_len
- 2] != ']' || str
[prefix_len
- 1] != ' ') {
161 throw std::invalid_argument("malformed timestamp in command '" + str
+
164 auto semi
= str
.find(';', prefix_len
);
165 _prefix
= str
.substr(0, prefix_len
);
166 _name
= str
.substr(prefix_len
, semi
- prefix_len
);
167 _arguments
= semi
== std::string::npos
? "" : str
.substr(semi
);
170 Store::ExternalCommand
Store::ExternalCommand::withName(
171 const std::string
&name
) const {
172 return ExternalCommand(_prefix
, name
, _arguments
);
175 std::string
Store::ExternalCommand::str() const {
176 return _prefix
+ _name
+ _arguments
;
179 std::vector
<std::string
> Store::ExternalCommand::args() const {
180 if (_arguments
.empty()) {
183 return mk::split(_arguments
.substr(1), ';');
186 bool Store::answerRequest(InputBuffer
&input
, OutputBuffer
&output
) {
187 // Precondition: output has been reset
188 InputBuffer::Result res
= input
.readRequest();
189 if (res
!= InputBuffer::Result::request_read
) {
190 if (res
!= InputBuffer::Result::eof
) {
191 std::ostringstream os
;
192 os
<< "client connection terminated: " << res
;
193 output
.setError(OutputBuffer::ResponseCode::incomplete_request
,
198 std::string line
= input
.nextLine();
199 if (mk::starts_with(line
, "GET ")) {
200 auto lines
= getLines(input
);
201 logRequest(line
, lines
);
202 return answerGetRequest(lines
, output
, mk::lstrip(line
.substr(4)));
204 if (mk::starts_with(line
, "GET")) {
205 // only to get error message
206 auto lines
= getLines(input
);
207 logRequest(line
, lines
);
208 return answerGetRequest(lines
, output
, "");
210 if (mk::starts_with(line
, "COMMAND ")) {
211 logRequest(line
, {});
213 answerCommandRequest(ExternalCommand(mk::lstrip(line
.substr(8))));
214 } catch (const std::invalid_argument
&err
) {
215 Warning(logger()) << err
.what();
219 if (mk::starts_with(line
, "LOGROTATE")) {
220 logRequest(line
, {});
221 Informational(logger()) << "Forcing logfile rotation";
222 rotate_log_file(time(nullptr));
223 schedule_new_event(EVENT_LOG_ROTATION
, 1, get_next_log_rotation_time(),
225 reinterpret_cast<void *>(get_next_log_rotation_time
),
226 1, nullptr, nullptr, 0);
229 logRequest(line
, {});
230 Warning(logger()) << "Invalid request '" << line
<< "'";
231 output
.setError(OutputBuffer::ResponseCode::invalid_request
,
232 "Invalid request method");
236 void Store::answerCommandRequest(const ExternalCommand
&command
) {
237 if (command
.name() == "MK_LOGWATCH_ACKNOWLEDGE") {
238 answerCommandMkLogwatchAcknowledge(command
);
241 if (mk::starts_with(command
.name(), "EC_")) {
242 answerCommandEventConsole(command
);
245 // Nagios doesn't have a LOG command, so we map it to the custom command
246 // _LOG, which we implement for ourselves.
247 answerCommandNagios(command
.name() == "LOG" ? command
.withName("_LOG")
251 void Store::answerCommandMkLogwatchAcknowledge(const ExternalCommand
&command
) {
252 // COMMAND [1462191638] MK_LOGWATCH_ACKNOWLEDGE;host123;\var\log\syslog
253 auto args
= command
.args();
254 if (args
.size() != 2) {
255 Warning(logger()) << "MK_LOGWATCH_ACKNOWLEDGE expects 2 arguments";
258 mk_logwatch_acknowledge(logger(), _mc
->mkLogwatchPath(), args
[0], args
[1]);
262 class ECTableConnection
: public EventConsoleConnection
{
264 ECTableConnection(Logger
*logger
, std::string path
, std::string command
)
265 : EventConsoleConnection(logger
, move(path
))
266 , _command(std::move(command
)) {}
269 void sendRequest(std::ostream
&os
) override
{ os
<< _command
; }
270 void receiveReply(std::istream
& /*is*/) override
{}
271 std::string _command
;
275 void Store::answerCommandEventConsole(const ExternalCommand
&command
) {
276 if (!_mc
->mkeventdEnabled()) {
277 Notice(logger()) << "event console disabled, ignoring command '"
278 << command
.str() << "'";
283 logger(), _mc
->mkeventdSocketPath(),
284 "COMMAND " + command
.name().substr(3) + command
.arguments())
286 } catch (const std::runtime_error
&err
) {
287 Alert(logger()) << err
.what();
291 void Store::answerCommandNagios(const ExternalCommand
&command
) {
292 std::lock_guard
<std::mutex
> lg(_command_mutex
);
293 auto command_str
= command
.str();
294 // The Nagios headers are (once again) not const-correct...
295 auto cmd
= const_cast<char *>(command_str
.c_str());
297 process_external_command1(cmd
);
299 submit_external_command(cmd
, nullptr);
303 bool Store::answerGetRequest(const std::list
<std::string
> &lines
,
304 OutputBuffer
&output
,
305 const std::string
&tablename
) {
306 return Query(lines
, findTable(output
, tablename
), _mc
->dataEncoding(),
307 _mc
->maxResponseSize(), output
, logger())
311 Logger
*Store::logger() const { return _mc
->loggerLivestatus(); }