Refactoring: Changed all check parameters starting with a 'p' to the new rulespec...
[check_mk.git] / livestatus / src / Store.cc
blobf168f6954ba7db3e4f4587e4014e4f08d5fc5914
1 // +------------------------------------------------------------------+
2 // | ____ _ _ __ __ _ __ |
3 // | / ___| |__ ___ ___| | __ | \/ | |/ / |
4 // | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
5 // | | |___| | | | __/ (__| < | | | | . \ |
6 // | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
7 // | |
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.
25 #include "Store.h"
26 #include <cstring>
27 #include <ctime>
28 #include <memory>
29 #include <sstream>
30 #include <stdexcept>
31 #include <utility>
32 #include <vector>
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
38 #include "Logger.h"
39 #include "MonitoringCore.h"
40 #include "OutputBuffer.h"
41 #include "Query.h"
42 #include "StringUtils.h"
43 #include "Table.h"
44 #include "mk_logwatch.h"
46 Store::Store(MonitoringCore *mc)
47 : _mc(mc)
48 , _log_cache(mc, mc->maxCachedMessages())
49 , _table_columns(mc)
50 , _table_commands(mc)
51 , _table_comments(mc)
52 , _table_contactgroups(mc)
53 , _table_contacts(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)
61 , _table_hosts(mc)
62 , _table_hostsbygroup(mc)
63 , _table_log(mc, &_log_cache)
64 , _table_servicegroups(mc)
65 , _table_services(mc)
66 , _table_servicesbygroup(mc)
67 , _table_servicesbyhostgroup(mc)
68 , _table_statehistory(mc, &_log_cache)
69 , _table_status(mc)
70 , _table_timeperiods(mc)
71 , _table_dummy(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);
81 addTable(_table_log);
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.
104 if (name.empty()) {
105 output.setError(OutputBuffer::ResponseCode::invalid_request,
106 "Invalid GET request, missing table name");
107 return _table_dummy;
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 + "'");
113 return _table_dummy;
115 return *it->second;
118 void Store::registerDowntime(nebstruct_downtime_data *data) {
119 _downtimes.registerDowntime(data);
122 void Store::registerComment(nebstruct_comment_data *data) {
123 _comments.registerComment(data);
126 namespace {
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()) {
132 break;
135 return lines;
137 } // namespace
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) {
145 log << R"(\n)" << l;
147 } else {
148 size_t s = lines.size();
149 if (s > 0) {
150 log << R"(\n{)" << s << (s == 1 ? " line follows" : " lines follow")
151 << "...}";
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 +
162 "'");
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()) {
181 return {};
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,
194 os.str());
196 return false;
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, {});
212 try {
213 answerCommandRequest(ExternalCommand(mk::lstrip(line.substr(8))));
214 } catch (const std::invalid_argument &err) {
215 Warning(logger()) << err.what();
217 return true;
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(),
224 0, 0,
225 reinterpret_cast<void *>(get_next_log_rotation_time),
226 1, nullptr, nullptr, 0);
227 return false;
229 logRequest(line, {});
230 Warning(logger()) << "Invalid request '" << line << "'";
231 output.setError(OutputBuffer::ResponseCode::invalid_request,
232 "Invalid request method");
233 return false;
236 void Store::answerCommandRequest(const ExternalCommand &command) {
237 if (command.name() == "MK_LOGWATCH_ACKNOWLEDGE") {
238 answerCommandMkLogwatchAcknowledge(command);
239 return;
241 if (mk::starts_with(command.name(), "EC_")) {
242 answerCommandEventConsole(command);
243 return;
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")
248 : command);
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";
256 return;
258 mk_logwatch_acknowledge(logger(), _mc->mkLogwatchPath(), args[0], args[1]);
261 namespace {
262 class ECTableConnection : public EventConsoleConnection {
263 public:
264 ECTableConnection(Logger *logger, std::string path, std::string command)
265 : EventConsoleConnection(logger, move(path))
266 , _command(std::move(command)) {}
268 private:
269 void sendRequest(std::ostream &os) override { os << _command; }
270 void receiveReply(std::istream & /*is*/) override {}
271 std::string _command;
273 } // namespace
275 void Store::answerCommandEventConsole(const ExternalCommand &command) {
276 if (!_mc->mkeventdEnabled()) {
277 Notice(logger()) << "event console disabled, ignoring command '"
278 << command.str() << "'";
279 return;
281 try {
282 ECTableConnection(
283 logger(), _mc->mkeventdSocketPath(),
284 "COMMAND " + command.name().substr(3) + command.arguments())
285 .run();
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());
296 #ifdef NAGIOS4
297 process_external_command1(cmd);
298 #else
299 submit_external_command(cmd, nullptr);
300 #endif
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())
308 .process();
311 Logger *Store::logger() const { return _mc->loggerLivestatus(); }