]> granicus.if.org Git - icinga2/blob - lib/livestatus/livestatusquery.cpp
Increase the timeout for "repl" sessions
[icinga2] / lib / livestatus / livestatusquery.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "livestatus/livestatusquery.hpp"
21 #include "livestatus/countaggregator.hpp"
22 #include "livestatus/sumaggregator.hpp"
23 #include "livestatus/minaggregator.hpp"
24 #include "livestatus/maxaggregator.hpp"
25 #include "livestatus/avgaggregator.hpp"
26 #include "livestatus/stdaggregator.hpp"
27 #include "livestatus/invsumaggregator.hpp"
28 #include "livestatus/invavgaggregator.hpp"
29 #include "livestatus/attributefilter.hpp"
30 #include "livestatus/negatefilter.hpp"
31 #include "livestatus/orfilter.hpp"
32 #include "livestatus/andfilter.hpp"
33 #include "icinga/externalcommandprocessor.hpp"
34 #include "config/configcompiler.hpp"
35 #include "base/debug.hpp"
36 #include "base/convert.hpp"
37 #include "base/objectlock.hpp"
38 #include "base/logger.hpp"
39 #include "base/exception.hpp"
40 #include "base/utility.hpp"
41 #include "base/json.hpp"
42 #include "base/serializer.hpp"
43 #include "base/timer.hpp"
44 #include "base/initialize.hpp"
45 #include <boost/algorithm/string/classification.hpp>
46 #include <boost/foreach.hpp>
47 #include <boost/algorithm/string/replace.hpp>
48 #include <boost/algorithm/string/split.hpp>
49 #include <boost/algorithm/string/join.hpp>
50
51 using namespace icinga;
52
53 static int l_ExternalCommands = 0;
54 static boost::mutex l_QueryMutex;
55 static std::map<String, LivestatusScriptFrame> l_LivestatusScriptFrames;
56 static Timer::Ptr l_FrameCleanupTimer;
57 static boost::mutex l_LivestatusScriptMutex;
58
59 static void ScriptFrameCleanupHandler(void)
60 {
61         boost::mutex::scoped_lock lock(l_LivestatusScriptMutex);
62
63         std::vector<String> cleanup_keys;
64
65         typedef std::pair<String, LivestatusScriptFrame> KVPair;
66
67         BOOST_FOREACH(const KVPair& kv, l_LivestatusScriptFrames) {
68                 if (kv.second.Seen < Utility::GetTime() - 1800)
69                         cleanup_keys.push_back(kv.first);
70         }
71
72         BOOST_FOREACH(const String& key, cleanup_keys)
73                 l_LivestatusScriptFrames.erase(key);
74 }
75
76 static void InitScriptFrameCleanup(void)
77 {
78         l_FrameCleanupTimer = new Timer();
79         l_FrameCleanupTimer->OnTimerExpired.connect(boost::bind(ScriptFrameCleanupHandler));
80         l_FrameCleanupTimer->SetInterval(30);
81         l_FrameCleanupTimer->Start();
82 }
83
84 INITIALIZE_ONCE(InitScriptFrameCleanup);
85
86 LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String& compat_log_path)
87         : m_KeepAlive(false), m_OutputFormat("csv"), m_ColumnHeaders(true),
88           m_LogTimeFrom(0), m_LogTimeUntil(static_cast<long>(Utility::GetTime()))
89 {
90         if (lines.size() == 0) {
91                 m_Verb = "ERROR";
92                 m_ErrorCode = LivestatusErrorQuery;
93                 m_ErrorMessage = "Empty Query. Aborting.";
94                 return;
95         }
96
97         String msg;
98         BOOST_FOREACH(const String& line, lines) {
99                 msg += line + "\n";
100         }
101         Log(LogDebug, "LivestatusQuery", msg);
102
103         m_CompatLogPath = compat_log_path;
104
105         /* default separators */
106         m_Separators.push_back("\n");
107         m_Separators.push_back(";");
108         m_Separators.push_back(",");
109         m_Separators.push_back("|");
110
111         String line = lines[0];
112
113         size_t sp_index = line.FindFirstOf(" ");
114
115         if (sp_index == String::NPos)
116                 BOOST_THROW_EXCEPTION(std::runtime_error("Livestatus header must contain a verb."));
117
118         String verb = line.SubStr(0, sp_index);
119         String target = line.SubStr(sp_index + 1);
120
121         m_Verb = verb;
122
123         if (m_Verb == "COMMAND") {
124                 m_KeepAlive = true;
125                 m_Command = target;
126         } else if (m_Verb == "SCRIPT") {
127                 m_Session = target;
128
129                 for (unsigned int i = 1; i < lines.size(); i++) {
130                         if (m_Command != "")
131                                 m_Command += "\n";
132                         m_Command += lines[i];
133                 }
134
135                 return;
136         } else if (m_Verb == "GET") {
137                 m_Table = target;
138         } else {
139                 m_Verb = "ERROR";
140                 m_ErrorCode = LivestatusErrorQuery;
141                 m_ErrorMessage = "Unknown livestatus verb: " + m_Verb;
142                 return;
143         }
144
145         std::deque<Filter::Ptr> filters, stats;
146         std::deque<Aggregator::Ptr> aggregators;
147
148         for (unsigned int i = 1; i < lines.size(); i++) {
149                 line = lines[i];
150
151                 size_t col_index = line.FindFirstOf(":");
152                 String header = line.SubStr(0, col_index);
153                 String params;
154
155                 //OutputFormat:json or OutputFormat: json
156                 if (line.GetLength() > col_index + 1)
157                         params = line.SubStr(col_index + 1);
158
159                 params.Trim();
160
161                 if (header == "ResponseHeader")
162                         m_ResponseHeader = params;
163                 else if (header == "OutputFormat")
164                         m_OutputFormat = params;
165                 else if (header == "KeepAlive")
166                         m_KeepAlive = (params == "on");
167                 else if (header == "Columns") {
168                         m_ColumnHeaders = false; // Might be explicitly re-enabled later on
169                         boost::algorithm::split(m_Columns, params, boost::is_any_of(" "));
170                 } else if (header == "Separators") {
171                         std::vector<String> separators;
172
173                         boost::algorithm::split(separators, params, boost::is_any_of(" "));
174                         /* ugly ascii long to char conversion, but works */
175                         if (separators.size() > 0)
176                                 m_Separators[0] = String(1, static_cast<char>(Convert::ToLong(separators[0])));
177                         if (separators.size() > 1)
178                                 m_Separators[1] = String(1, static_cast<char>(Convert::ToLong(separators[1])));
179                         if (separators.size() > 2)
180                                 m_Separators[2] = String(1, static_cast<char>(Convert::ToLong(separators[2])));
181                         if (separators.size() > 3)
182                                 m_Separators[3] = String(1, static_cast<char>(Convert::ToLong(separators[3])));
183                 } else if (header == "ColumnHeaders")
184                         m_ColumnHeaders = (params == "on");
185                 else if (header == "Filter") {
186                         Filter::Ptr filter = ParseFilter(params, m_LogTimeFrom, m_LogTimeUntil);
187
188                         if (!filter) {
189                                 m_Verb = "ERROR";
190                                 m_ErrorCode = LivestatusErrorQuery;
191                                 m_ErrorMessage = "Invalid filter specification: " + line;
192                                 return;
193                         }
194
195                         filters.push_back(filter);
196                 } else if (header == "Stats") {
197                         m_ColumnHeaders = false; // Might be explicitly re-enabled later on
198
199                         std::vector<String> tokens;
200                         boost::algorithm::split(tokens, params, boost::is_any_of(" "));
201
202                         if (tokens.size() < 2) {
203                                 m_Verb = "ERROR";
204                                 m_ErrorCode = LivestatusErrorQuery;
205                                 m_ErrorMessage = "Missing aggregator column name: " + line;
206                                 return;
207                         }
208
209                         String aggregate_arg = tokens[0];
210                         String aggregate_attr = tokens[1];
211
212                         Aggregator::Ptr aggregator;
213                         Filter::Ptr filter;
214
215                         if (aggregate_arg == "sum") {
216                                 aggregator = new SumAggregator(aggregate_attr);
217                         } else if (aggregate_arg == "min") {
218                                 aggregator = new MinAggregator(aggregate_attr);
219                         } else if (aggregate_arg == "max") {
220                                 aggregator = new MaxAggregator(aggregate_attr);
221                         } else if (aggregate_arg == "avg") {
222                                 aggregator = new AvgAggregator(aggregate_attr);
223                         } else if (aggregate_arg == "std") {
224                                 aggregator = new StdAggregator(aggregate_attr);
225                         } else if (aggregate_arg == "suminv") {
226                                 aggregator = new InvSumAggregator(aggregate_attr);
227                         } else if (aggregate_arg == "avginv") {
228                                 aggregator = new InvAvgAggregator(aggregate_attr);
229                         } else {
230                                 filter = ParseFilter(params, m_LogTimeFrom, m_LogTimeUntil);
231
232                                 if (!filter) {
233                                         m_Verb = "ERROR";
234                                         m_ErrorCode = LivestatusErrorQuery;
235                                         m_ErrorMessage = "Invalid filter specification: " + line;
236                                         return;
237                                 }
238
239                                 aggregator = new CountAggregator();
240                         }
241
242                         aggregator->SetFilter(filter);
243                         aggregators.push_back(aggregator);
244
245                         stats.push_back(filter);
246                 } else if (header == "Or" || header == "And" || header == "StatsOr" || header == "StatsAnd") {
247                         std::deque<Filter::Ptr>& deq = (header == "Or" || header == "And") ? filters : stats;
248
249                         unsigned int num = Convert::ToLong(params);
250                         CombinerFilter::Ptr filter;
251
252                         if (header == "Or" || header == "StatsOr") {
253                                 filter = new OrFilter();
254                                 Log(LogDebug, "LivestatusQuery")
255                                     << "Add OR filter for " << params << " column(s). " << deq.size() << " filters available.";
256                         } else {
257                                 filter = new AndFilter();
258                                 Log(LogDebug, "LivestatusQuery")
259                                     << "Add AND filter for " << params << " column(s). " << deq.size() << " filters available.";
260                         }
261
262                         if (num > deq.size()) {
263                                 m_Verb = "ERROR";
264                                 m_ErrorCode = 451;
265                                 m_ErrorMessage = "Or/StatsOr is referencing " + Convert::ToString(num) + " filters; stack only contains " + Convert::ToString(static_cast<long>(deq.size())) + " filters";
266                                 return;
267                         }
268
269                         while (num > 0 && num--) {
270                                 filter->AddSubFilter(deq.back());
271                                 Log(LogDebug, "LivestatusQuery")
272                                     << "Add " << num << " filter.";
273                                 deq.pop_back();
274                                 if (&deq == &stats)
275                                         aggregators.pop_back();
276                         }
277
278                         deq.push_back(filter);
279                         if (&deq == &stats) {
280                                 Aggregator::Ptr aggregator = new CountAggregator();
281                                 aggregator->SetFilter(filter);
282                                 aggregators.push_back(aggregator);
283                         }
284                 } else if (header == "Negate" || header == "StatsNegate") {
285                         std::deque<Filter::Ptr>& deq = (header == "Negate") ? filters : stats;
286
287                         if (deq.empty()) {
288                                 m_Verb = "ERROR";
289                                 m_ErrorCode = 451;
290                                 m_ErrorMessage = "Negate/StatsNegate used, however the filter stack is empty";
291                                 return;
292                         }
293
294                         Filter::Ptr filter = deq.back();
295                         deq.pop_back();
296
297                         if (!filter) {
298                                 m_Verb = "ERROR";
299                                 m_ErrorCode = 451;
300                                 m_ErrorMessage = "Negate/StatsNegate used, however last stats doesn't have a filter";
301                                 return;
302                         }
303
304                         deq.push_back(new NegateFilter(filter));
305
306                         if (deq == stats) {
307                                 Aggregator::Ptr aggregator = aggregators.back();
308                                 aggregator->SetFilter(filter);
309                         }
310                 }
311         }
312
313         /* Combine all top-level filters into a single filter. */
314         AndFilter::Ptr top_filter = new AndFilter();
315
316         BOOST_FOREACH(const Filter::Ptr& filter, filters) {
317                 top_filter->AddSubFilter(filter);
318         }
319
320         m_Filter = top_filter;
321         m_Aggregators.swap(aggregators);
322 }
323
324 int LivestatusQuery::GetExternalCommands(void)
325 {
326         boost::mutex::scoped_lock lock(l_QueryMutex);
327
328         return l_ExternalCommands;
329 }
330
331 Filter::Ptr LivestatusQuery::ParseFilter(const String& params, unsigned long& from, unsigned long& until)
332 {
333         /*
334          * time >= 1382696656
335          * type = SERVICE FLAPPING ALERT
336          */
337         std::vector<String> tokens;
338         size_t sp_index;
339         String temp_buffer = params;
340
341         /* extract attr and op */
342         for (int i = 0; i < 2; i++) {
343                 sp_index = temp_buffer.FindFirstOf(" ");
344
345                 /* check if this is the last argument */
346                 if (sp_index == String::NPos) {
347                         /* 'attr op' or 'attr op val' is valid */
348                         if (i < 1)
349                                 BOOST_THROW_EXCEPTION(std::runtime_error("Livestatus filter '" + params + "' does not contain all required fields."));
350
351                         break;
352                 }
353
354                 tokens.push_back(temp_buffer.SubStr(0, sp_index));
355                 temp_buffer = temp_buffer.SubStr(sp_index + 1);
356         }
357
358         /* add the rest as value */
359         tokens.push_back(temp_buffer);
360
361         if (tokens.size() == 2)
362                 tokens.push_back("");
363
364         if (tokens.size() < 3)
365                 return Filter::Ptr();
366
367         bool negate = false;
368         String attr = tokens[0];
369         String op = tokens[1];
370         String val = tokens[2];
371
372         if (op == "!=") {
373                 op = "=";
374                 negate = true;
375         } else if (op == "!~") {
376                 op = "~";
377                 negate = true;
378         } else if (op == "!=~") {
379                 op = "=~";
380                 negate = true;
381         } else if (op == "!~~") {
382                 op = "~~";
383                 negate = true;
384         }
385
386         Filter::Ptr filter = new AttributeFilter(attr, op, val);
387
388         if (negate)
389                 filter = new NegateFilter(filter);
390
391         /* pre-filter log time duration */
392         if (attr == "time") {
393                 if (op == "<" || op == "<=") {
394                         until = Convert::ToLong(val);
395                 } else if (op == ">" || op == ">=") {
396                         from = Convert::ToLong(val);
397                 }
398         }
399
400         Log(LogDebug, "LivestatusQuery")
401             << "Parsed filter with attr: '" << attr << "' op: '" << op << "' val: '" << val << "'.";
402
403         return filter;
404 }
405
406 void LivestatusQuery::PrintResultSet(std::ostream& fp, const Array::Ptr& rs) const
407 {
408         if (m_OutputFormat == "csv") {
409                 ObjectLock olock(rs);
410
411                 BOOST_FOREACH(const Array::Ptr& row, rs) {
412                         bool first = true;
413
414                         ObjectLock rlock(row);
415                         BOOST_FOREACH(const Value& value, row) {
416                                 if (first)
417                                         first = false;
418                                 else
419                                         fp << m_Separators[1];
420
421                                 if (value.IsObjectType<Array>())
422                                         PrintCsvArray(fp, value, 0);
423                                 else
424                                         fp << value;
425                         }
426
427                         fp << m_Separators[0];
428                 }
429         } else if (m_OutputFormat == "json") {
430                 fp << JsonEncode(rs);
431         } else if (m_OutputFormat == "python") {
432                 PrintPythonArray(fp, rs);
433         }
434 }
435
436 void LivestatusQuery::PrintCsvArray(std::ostream& fp, const Array::Ptr& array, int level) const
437 {
438         bool first = true;
439
440         ObjectLock olock(array);
441         BOOST_FOREACH(const Value& value, array) {
442                 if (first)
443                         first = false;
444                 else
445                         fp << ((level == 0) ? m_Separators[2] : m_Separators[3]);
446
447                 if (value.IsObjectType<Array>())
448                         PrintCsvArray(fp, value, level + 1);
449                 else
450                         fp << value;
451         }
452 }
453
454 void LivestatusQuery::PrintPythonArray(std::ostream& fp, const Array::Ptr& rs) const
455 {
456         fp << "[ ";
457
458         bool first = true;
459
460         BOOST_FOREACH(const Value& value, rs) {
461                 if (first)
462                         first = false;
463                 else
464                         fp << ", ";
465
466                 if (value.IsObjectType<Array>())
467                         PrintPythonArray(fp, value);
468                 else if (value.IsNumber())
469                         fp << value;
470                 else
471                         fp << QuoteStringPython(value);
472         }
473
474         fp << " ]";
475 }
476
477 String LivestatusQuery::QuoteStringPython(const String& str) {
478         String result = str;
479         boost::algorithm::replace_all(result, "\"", "\\\"");
480         return "r\"" + result + "\"";
481 }
482
483 void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
484 {
485         Log(LogInformation, "LivestatusQuery")
486             << "Table: " << m_Table;
487
488         Table::Ptr table = Table::GetByName(m_Table, m_CompatLogPath, m_LogTimeFrom, m_LogTimeUntil);
489
490         if (!table) {
491                 SendResponse(stream, LivestatusErrorNotFound, "Table '" + m_Table + "' does not exist.");
492
493                 return;
494         }
495
496         std::vector<Value> objects = table->FilterRows(m_Filter);
497         std::vector<String> columns;
498
499         if (m_Columns.size() > 0)
500                 columns = m_Columns;
501         else
502                 columns = table->GetColumnNames();
503
504         Array::Ptr rs = new Array();
505
506         if (m_Aggregators.empty()) {
507                 Array::Ptr header = new Array();
508
509                 BOOST_FOREACH(const Value& object, objects) {
510                         Array::Ptr row = new Array();
511
512                         BOOST_FOREACH(const String& columnName, columns) {
513                                 Column column = table->GetColumn(columnName);
514
515                                 if (m_ColumnHeaders)
516                                         header->Add(columnName);
517
518                                 row->Add(column.ExtractValue(object));
519                         }
520
521                         if (m_ColumnHeaders) {
522                                 rs->Add(header);
523                                 m_ColumnHeaders = false;
524                         }
525
526                         rs->Add(row);
527                 }
528         } else {
529                 std::vector<double> stats(m_Aggregators.size(), 0);
530                 int index = 0;
531
532                 /* add aggregated stats */
533                 BOOST_FOREACH(const Aggregator::Ptr aggregator, m_Aggregators) {
534                         BOOST_FOREACH(const Value& object, objects) {
535                                 aggregator->Apply(table, object);
536                         }
537
538                         stats[index] = aggregator->GetResult();
539                         index++;
540                 }
541
542                 /* add column headers both for raw and aggregated data */
543                 if (m_ColumnHeaders) {
544                         Array::Ptr header = new Array();
545
546                         BOOST_FOREACH(const String& columnName, m_Columns) {
547                                 header->Add(columnName);
548                         }
549
550                         for (size_t i = 1; i <= m_Aggregators.size(); i++) {
551                                 header->Add("stats_" + Convert::ToString(i));
552                         }
553
554                         rs->Add(header);
555                 }
556
557                 Array::Ptr row = new Array();
558
559                 /*
560                  * add selected columns next to stats
561                  * may not be accurate for grouping!
562                  */
563                 if (objects.size() > 0 && m_Columns.size() > 0) {
564                         BOOST_FOREACH(const String& columnName, m_Columns) {
565                                 Column column = table->GetColumn(columnName);
566
567                                 row->Add(column.ExtractValue(objects[0])); // first object wins
568                         }
569                 }
570
571                 for (size_t i = 0; i < m_Aggregators.size(); i++)
572                         row->Add(stats[i]);
573
574                 rs->Add(row);
575         }
576
577         std::ostringstream result;
578         PrintResultSet(result, rs);
579
580         SendResponse(stream, LivestatusErrorOK, result.str());
581 }
582
583 void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream)
584 {
585         {
586                 boost::mutex::scoped_lock lock(l_QueryMutex);
587
588                 l_ExternalCommands++;
589         }
590
591         Log(LogInformation, "LivestatusQuery")
592             << "Executing command: " << m_Command;
593         ExternalCommandProcessor::Execute(m_Command);
594         SendResponse(stream, LivestatusErrorOK, "");
595 }
596
597 void LivestatusQuery::ExecuteScriptHelper(const Stream::Ptr& stream)
598 {
599         Log(LogInformation, "LivestatusQuery")
600             << "Executing expression: " << m_Command;
601
602         m_ResponseHeader = "fixed16";
603
604         LivestatusScriptFrame& lsf = l_LivestatusScriptFrames[m_Session];
605         lsf.Seen = Utility::GetTime();
606
607         if (!lsf.Locals)
608                 lsf.Locals = new Dictionary();
609
610         String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
611         lsf.NextLine++;
612
613         lsf.Lines[fileName] = m_Command;
614
615         Expression *expr = ConfigCompiler::CompileText(fileName, m_Command);
616         Value result;
617         try {
618                 ScriptFrame frame;
619                 frame.Locals = lsf.Locals;
620                 result = expr->Evaluate(frame);
621         } catch (const ScriptError& ex) {
622                 delete expr;
623
624                 DebugInfo di = ex.GetDebugInfo();
625
626                 std::ostringstream msgbuf;
627
628                 msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
629                     << String(di.Path.GetLength() + 2, ' ')
630                     << String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
631                     << ex.what() << "\n";
632
633                 SendResponse(stream, LivestatusErrorQuery, msgbuf.str());
634                 return;
635         } catch (...) {
636                 delete expr;
637                 throw;
638         }
639         delete expr;
640         SendResponse(stream, LivestatusErrorOK, JsonEncode(Serialize(result, FAState | FAConfig), true));
641 }
642
643 void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream)
644 {
645         Log(LogDebug, "LivestatusQuery")
646             << "ERROR: Code: '" << m_ErrorCode << "' Message: '" << m_ErrorMessage << "'.";
647         SendResponse(stream, m_ErrorCode, m_ErrorMessage);
648 }
649
650 void LivestatusQuery::SendResponse(const Stream::Ptr& stream, int code, const String& data)
651 {
652         if (m_ResponseHeader == "fixed16")
653                 PrintFixed16(stream, code, data);
654
655         if (m_ResponseHeader == "fixed16" || code == LivestatusErrorOK) {
656                 try {
657                         stream->Write(data.CStr(), data.GetLength());
658                 } catch (const std::exception&) {
659                         Log(LogCritical, "LivestatusQuery", "Cannot write to TCP socket.");
660                 }
661         }
662 }
663
664 void LivestatusQuery::PrintFixed16(const Stream::Ptr& stream, int code, const String& data)
665 {
666         ASSERT(code >= 100 && code <= 999);
667
668         String sCode = Convert::ToString(code);
669         String sLength = Convert::ToString(static_cast<long>(data.GetLength()));
670
671         String header = sCode + String(16 - 3 - sLength.GetLength() - 1, ' ') + sLength + m_Separators[0];
672
673         try {
674                 stream->Write(header.CStr(), header.GetLength());
675         } catch (const std::exception&) {
676                 Log(LogCritical, "LivestatusQuery", "Cannot write to TCP socket.");
677         }
678 }
679
680 bool LivestatusQuery::Execute(const Stream::Ptr& stream)
681 {
682         try {
683                 Log(LogInformation, "LivestatusQuery")
684                     << "Executing livestatus query: " << m_Verb;
685
686                 if (m_Verb == "GET")
687                         ExecuteGetHelper(stream);
688                 else if (m_Verb == "COMMAND")
689                         ExecuteCommandHelper(stream);
690                 else if (m_Verb == "SCRIPT")
691                         ExecuteScriptHelper(stream);
692                 else if (m_Verb == "ERROR")
693                         ExecuteErrorHelper(stream);
694                 else
695                         BOOST_THROW_EXCEPTION(std::runtime_error("Invalid livestatus query verb."));
696         } catch (const std::exception& ex) {
697                 SendResponse(stream, LivestatusErrorQuery, DiagnosticInformation(ex));
698         }
699
700         if (!m_KeepAlive) {
701                 stream->Close();
702                 return false;
703         }
704
705         return true;
706 }