]> granicus.if.org Git - icinga2/blob - lib/livestatus/livestatusquery.cpp
Make sure that 'icinga2 console' serializes temporary attributes (rather than just...
[icinga2] / lib / livestatus / livestatusquery.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2015 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), m_Limit(-1), m_ErrorCode(0),
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 == "Limit")
186                         m_Limit = Convert::ToLong(params);
187                 else if (header == "Filter") {
188                         Filter::Ptr filter = ParseFilter(params, m_LogTimeFrom, m_LogTimeUntil);
189
190                         if (!filter) {
191                                 m_Verb = "ERROR";
192                                 m_ErrorCode = LivestatusErrorQuery;
193                                 m_ErrorMessage = "Invalid filter specification: " + line;
194                                 return;
195                         }
196
197                         filters.push_back(filter);
198                 } else if (header == "Stats") {
199                         m_ColumnHeaders = false; // Might be explicitly re-enabled later on
200
201                         std::vector<String> tokens;
202                         boost::algorithm::split(tokens, params, boost::is_any_of(" "));
203
204                         if (tokens.size() < 2) {
205                                 m_Verb = "ERROR";
206                                 m_ErrorCode = LivestatusErrorQuery;
207                                 m_ErrorMessage = "Missing aggregator column name: " + line;
208                                 return;
209                         }
210
211                         String aggregate_arg = tokens[0];
212                         String aggregate_attr = tokens[1];
213
214                         Aggregator::Ptr aggregator;
215                         Filter::Ptr filter;
216
217                         if (aggregate_arg == "sum") {
218                                 aggregator = new SumAggregator(aggregate_attr);
219                         } else if (aggregate_arg == "min") {
220                                 aggregator = new MinAggregator(aggregate_attr);
221                         } else if (aggregate_arg == "max") {
222                                 aggregator = new MaxAggregator(aggregate_attr);
223                         } else if (aggregate_arg == "avg") {
224                                 aggregator = new AvgAggregator(aggregate_attr);
225                         } else if (aggregate_arg == "std") {
226                                 aggregator = new StdAggregator(aggregate_attr);
227                         } else if (aggregate_arg == "suminv") {
228                                 aggregator = new InvSumAggregator(aggregate_attr);
229                         } else if (aggregate_arg == "avginv") {
230                                 aggregator = new InvAvgAggregator(aggregate_attr);
231                         } else {
232                                 filter = ParseFilter(params, m_LogTimeFrom, m_LogTimeUntil);
233
234                                 if (!filter) {
235                                         m_Verb = "ERROR";
236                                         m_ErrorCode = LivestatusErrorQuery;
237                                         m_ErrorMessage = "Invalid filter specification: " + line;
238                                         return;
239                                 }
240
241                                 aggregator = new CountAggregator();
242                         }
243
244                         aggregator->SetFilter(filter);
245                         aggregators.push_back(aggregator);
246
247                         stats.push_back(filter);
248                 } else if (header == "Or" || header == "And" || header == "StatsOr" || header == "StatsAnd") {
249                         std::deque<Filter::Ptr>& deq = (header == "Or" || header == "And") ? filters : stats;
250
251                         unsigned int num = Convert::ToLong(params);
252                         CombinerFilter::Ptr filter;
253
254                         if (header == "Or" || header == "StatsOr") {
255                                 filter = new OrFilter();
256                                 Log(LogDebug, "LivestatusQuery")
257                                     << "Add OR filter for " << params << " column(s). " << deq.size() << " filters available.";
258                         } else {
259                                 filter = new AndFilter();
260                                 Log(LogDebug, "LivestatusQuery")
261                                     << "Add AND filter for " << params << " column(s). " << deq.size() << " filters available.";
262                         }
263
264                         if (num > deq.size()) {
265                                 m_Verb = "ERROR";
266                                 m_ErrorCode = 451;
267                                 m_ErrorMessage = "Or/StatsOr is referencing " + Convert::ToString(num) + " filters; stack only contains " + Convert::ToString(static_cast<long>(deq.size())) + " filters";
268                                 return;
269                         }
270
271                         while (num > 0 && num--) {
272                                 filter->AddSubFilter(deq.back());
273                                 Log(LogDebug, "LivestatusQuery")
274                                     << "Add " << num << " filter.";
275                                 deq.pop_back();
276                                 if (&deq == &stats)
277                                         aggregators.pop_back();
278                         }
279
280                         deq.push_back(filter);
281                         if (&deq == &stats) {
282                                 Aggregator::Ptr aggregator = new CountAggregator();
283                                 aggregator->SetFilter(filter);
284                                 aggregators.push_back(aggregator);
285                         }
286                 } else if (header == "Negate" || header == "StatsNegate") {
287                         std::deque<Filter::Ptr>& deq = (header == "Negate") ? filters : stats;
288
289                         if (deq.empty()) {
290                                 m_Verb = "ERROR";
291                                 m_ErrorCode = 451;
292                                 m_ErrorMessage = "Negate/StatsNegate used, however the filter stack is empty";
293                                 return;
294                         }
295
296                         Filter::Ptr filter = deq.back();
297                         deq.pop_back();
298
299                         if (!filter) {
300                                 m_Verb = "ERROR";
301                                 m_ErrorCode = 451;
302                                 m_ErrorMessage = "Negate/StatsNegate used, however last stats doesn't have a filter";
303                                 return;
304                         }
305
306                         deq.push_back(new NegateFilter(filter));
307
308                         if (deq == stats) {
309                                 Aggregator::Ptr aggregator = aggregators.back();
310                                 aggregator->SetFilter(filter);
311                         }
312                 }
313         }
314
315         /* Combine all top-level filters into a single filter. */
316         AndFilter::Ptr top_filter = new AndFilter();
317
318         BOOST_FOREACH(const Filter::Ptr& filter, filters) {
319                 top_filter->AddSubFilter(filter);
320         }
321
322         m_Filter = top_filter;
323         m_Aggregators.swap(aggregators);
324 }
325
326 int LivestatusQuery::GetExternalCommands(void)
327 {
328         boost::mutex::scoped_lock lock(l_QueryMutex);
329
330         return l_ExternalCommands;
331 }
332
333 Filter::Ptr LivestatusQuery::ParseFilter(const String& params, unsigned long& from, unsigned long& until)
334 {
335         /*
336          * time >= 1382696656
337          * type = SERVICE FLAPPING ALERT
338          */
339         std::vector<String> tokens;
340         size_t sp_index;
341         String temp_buffer = params;
342
343         /* extract attr and op */
344         for (int i = 0; i < 2; i++) {
345                 sp_index = temp_buffer.FindFirstOf(" ");
346
347                 /* check if this is the last argument */
348                 if (sp_index == String::NPos) {
349                         /* 'attr op' or 'attr op val' is valid */
350                         if (i < 1)
351                                 BOOST_THROW_EXCEPTION(std::runtime_error("Livestatus filter '" + params + "' does not contain all required fields."));
352
353                         break;
354                 }
355
356                 tokens.push_back(temp_buffer.SubStr(0, sp_index));
357                 temp_buffer = temp_buffer.SubStr(sp_index + 1);
358         }
359
360         /* add the rest as value */
361         tokens.push_back(temp_buffer);
362
363         if (tokens.size() == 2)
364                 tokens.push_back("");
365
366         if (tokens.size() < 3)
367                 return Filter::Ptr();
368
369         bool negate = false;
370         String attr = tokens[0];
371         String op = tokens[1];
372         String val = tokens[2];
373
374         if (op == "!=") {
375                 op = "=";
376                 negate = true;
377         } else if (op == "!~") {
378                 op = "~";
379                 negate = true;
380         } else if (op == "!=~") {
381                 op = "=~";
382                 negate = true;
383         } else if (op == "!~~") {
384                 op = "~~";
385                 negate = true;
386         }
387
388         Filter::Ptr filter = new AttributeFilter(attr, op, val);
389
390         if (negate)
391                 filter = new NegateFilter(filter);
392
393         /* pre-filter log time duration */
394         if (attr == "time") {
395                 if (op == "<" || op == "<=") {
396                         until = Convert::ToLong(val);
397                 } else if (op == ">" || op == ">=") {
398                         from = Convert::ToLong(val);
399                 }
400         }
401
402         Log(LogDebug, "LivestatusQuery")
403             << "Parsed filter with attr: '" << attr << "' op: '" << op << "' val: '" << val << "'.";
404
405         return filter;
406 }
407
408 void LivestatusQuery::PrintResultSet(std::ostream& fp, const Array::Ptr& rs) const
409 {
410         if (m_OutputFormat == "csv") {
411                 ObjectLock olock(rs);
412
413                 BOOST_FOREACH(const Array::Ptr& row, rs) {
414                         bool first = true;
415
416                         ObjectLock rlock(row);
417                         BOOST_FOREACH(const Value& value, row) {
418                                 if (first)
419                                         first = false;
420                                 else
421                                         fp << m_Separators[1];
422
423                                 if (value.IsObjectType<Array>())
424                                         PrintCsvArray(fp, value, 0);
425                                 else
426                                         fp << value;
427                         }
428
429                         fp << m_Separators[0];
430                 }
431         } else if (m_OutputFormat == "json") {
432                 fp << JsonEncode(rs);
433         } else if (m_OutputFormat == "python") {
434                 PrintPythonArray(fp, rs);
435         }
436 }
437
438 void LivestatusQuery::PrintCsvArray(std::ostream& fp, const Array::Ptr& array, int level) const
439 {
440         bool first = true;
441
442         ObjectLock olock(array);
443         BOOST_FOREACH(const Value& value, array) {
444                 if (first)
445                         first = false;
446                 else
447                         fp << ((level == 0) ? m_Separators[2] : m_Separators[3]);
448
449                 if (value.IsObjectType<Array>())
450                         PrintCsvArray(fp, value, level + 1);
451                 else if (value.IsBoolean())
452                         fp << Convert::ToLong(value);
453                 else
454                         fp << value;
455         }
456 }
457
458 void LivestatusQuery::PrintPythonArray(std::ostream& fp, const Array::Ptr& rs) const
459 {
460         fp << "[ ";
461
462         bool first = true;
463
464         BOOST_FOREACH(const Value& value, rs) {
465                 if (first)
466                         first = false;
467                 else
468                         fp << ", ";
469
470                 if (value.IsObjectType<Array>())
471                         PrintPythonArray(fp, value);
472                 else if (value.IsNumber())
473                         fp << value;
474                 else
475                         fp << QuoteStringPython(value);
476         }
477
478         fp << " ]";
479 }
480
481 String LivestatusQuery::QuoteStringPython(const String& str) {
482         String result = str;
483         boost::algorithm::replace_all(result, "\"", "\\\"");
484         return "r\"" + result + "\"";
485 }
486
487 void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
488 {
489         Log(LogNotice, "LivestatusQuery")
490             << "Table: " << m_Table;
491
492         Table::Ptr table = Table::GetByName(m_Table, m_CompatLogPath, m_LogTimeFrom, m_LogTimeUntil);
493
494         if (!table) {
495                 SendResponse(stream, LivestatusErrorNotFound, "Table '" + m_Table + "' does not exist.");
496
497                 return;
498         }
499
500         std::vector<LivestatusRowValue> objects = table->FilterRows(m_Filter, m_Limit);
501         std::vector<String> columns;
502
503         if (m_Columns.size() > 0)
504                 columns = m_Columns;
505         else
506                 columns = table->GetColumnNames();
507
508         Array::Ptr rs = new Array();
509
510         if (m_Aggregators.empty()) {
511                 Array::Ptr header = new Array();
512
513                 typedef std::pair<String, Column> ColumnPair;
514
515                 std::vector<ColumnPair> column_objs;
516                 column_objs.reserve(columns.size());
517
518                 BOOST_FOREACH(const String& columnName, columns)
519                         column_objs.push_back(std::make_pair(columnName, table->GetColumn(columnName)));
520
521                 rs->Reserve(1 + objects.size());
522
523                 BOOST_FOREACH(const LivestatusRowValue& object, objects) {
524                         Array::Ptr row = new Array();
525
526                         row->Reserve(column_objs.size());
527
528                         BOOST_FOREACH(const ColumnPair& cv, column_objs) {
529                                 if (m_ColumnHeaders)
530                                         header->Add(cv.first);
531
532                                 row->Add(cv.second.ExtractValue(object.Row, object.GroupByType, object.GroupByObject));
533                         }
534
535                         if (m_ColumnHeaders) {
536                                 rs->Add(header);
537                                 m_ColumnHeaders = false;
538                         }
539
540                         rs->Add(row);
541                 }
542         } else {
543                 std::vector<double> stats(m_Aggregators.size(), 0);
544                 int index = 0;
545
546                 /* add aggregated stats */
547                 BOOST_FOREACH(const Aggregator::Ptr aggregator, m_Aggregators) {
548                         BOOST_FOREACH(const LivestatusRowValue& object, objects) {
549                                 aggregator->Apply(table, object.Row);
550                         }
551
552                         stats[index] = aggregator->GetResult();
553                         index++;
554                 }
555
556                 /* add column headers both for raw and aggregated data */
557                 if (m_ColumnHeaders) {
558                         Array::Ptr header = new Array();
559
560                         BOOST_FOREACH(const String& columnName, m_Columns) {
561                                 header->Add(columnName);
562                         }
563
564                         for (size_t i = 1; i <= m_Aggregators.size(); i++) {
565                                 header->Add("stats_" + Convert::ToString(i));
566                         }
567
568                         rs->Add(header);
569                 }
570
571                 Array::Ptr row = new Array();
572
573                 row->Reserve(m_Columns.size() + m_Aggregators.size());
574
575                 /*
576                  * add selected columns next to stats
577                  * may not be accurate for grouping!
578                  */
579                 if (objects.size() > 0 && m_Columns.size() > 0) {
580                         BOOST_FOREACH(const String& columnName, m_Columns) {
581                                 Column column = table->GetColumn(columnName);
582
583                                 LivestatusRowValue object = objects[0]; //first object wins
584
585                                 row->Add(column.ExtractValue(object.Row, object.GroupByType, object.GroupByObject));
586                         }
587                 }
588
589                 for (size_t i = 0; i < m_Aggregators.size(); i++)
590                         row->Add(stats[i]);
591
592                 rs->Add(row);
593         }
594
595         std::ostringstream result;
596         PrintResultSet(result, rs);
597
598         SendResponse(stream, LivestatusErrorOK, result.str());
599 }
600
601 void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream)
602 {
603         {
604                 boost::mutex::scoped_lock lock(l_QueryMutex);
605
606                 l_ExternalCommands++;
607         }
608
609         Log(LogNotice, "LivestatusQuery")
610             << "Executing command: " << m_Command;
611         ExternalCommandProcessor::Execute(m_Command);
612         SendResponse(stream, LivestatusErrorOK, "");
613 }
614
615 void LivestatusQuery::ExecuteScriptHelper(const Stream::Ptr& stream)
616 {
617         Log(LogInformation, "LivestatusQuery")
618             << "Executing expression: " << m_Command;
619
620         m_ResponseHeader = "fixed16";
621
622         LivestatusScriptFrame& lsf = l_LivestatusScriptFrames[m_Session];
623         lsf.Seen = Utility::GetTime();
624
625         if (!lsf.Locals)
626                 lsf.Locals = new Dictionary();
627
628         String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
629         lsf.NextLine++;
630
631         lsf.Lines[fileName] = m_Command;
632
633         Expression *expr = ConfigCompiler::CompileText(fileName, m_Command);
634         Value result;
635         try {
636                 ScriptFrame frame;
637                 frame.Locals = lsf.Locals;
638                 result = expr->Evaluate(frame);
639         } catch (const ScriptError& ex) {
640                 delete expr;
641
642                 DebugInfo di = ex.GetDebugInfo();
643
644                 std::ostringstream msgbuf;
645
646                 msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
647                     << String(di.Path.GetLength() + 2, ' ')
648                     << String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
649                     << ex.what() << "\n";
650
651                 SendResponse(stream, LivestatusErrorQuery, msgbuf.str());
652                 return;
653         } catch (...) {
654                 delete expr;
655                 throw;
656         }
657         delete expr;
658         SendResponse(stream, LivestatusErrorOK, JsonEncode(Serialize(result, FAEphemeral | FAState | FAConfig), true));
659 }
660
661 void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream)
662 {
663         Log(LogDebug, "LivestatusQuery")
664             << "ERROR: Code: '" << m_ErrorCode << "' Message: '" << m_ErrorMessage << "'.";
665         SendResponse(stream, m_ErrorCode, m_ErrorMessage);
666 }
667
668 void LivestatusQuery::SendResponse(const Stream::Ptr& stream, int code, const String& data)
669 {
670         if (m_ResponseHeader == "fixed16")
671                 PrintFixed16(stream, code, data);
672
673         if (m_ResponseHeader == "fixed16" || code == LivestatusErrorOK) {
674                 try {
675                         stream->Write(data.CStr(), data.GetLength());
676                 } catch (const std::exception&) {
677                         Log(LogCritical, "LivestatusQuery", "Cannot write to TCP socket.");
678                 }
679         }
680 }
681
682 void LivestatusQuery::PrintFixed16(const Stream::Ptr& stream, int code, const String& data)
683 {
684         ASSERT(code >= 100 && code <= 999);
685
686         String sCode = Convert::ToString(code);
687         String sLength = Convert::ToString(static_cast<long>(data.GetLength()));
688
689         String header = sCode + String(16 - 3 - sLength.GetLength() - 1, ' ') + sLength + m_Separators[0];
690
691         try {
692                 stream->Write(header.CStr(), header.GetLength());
693         } catch (const std::exception&) {
694                 Log(LogCritical, "LivestatusQuery", "Cannot write to TCP socket.");
695         }
696 }
697
698 bool LivestatusQuery::Execute(const Stream::Ptr& stream)
699 {
700         try {
701                 Log(LogNotice, "LivestatusQuery")
702                     << "Executing livestatus query: " << m_Verb;
703
704                 if (m_Verb == "GET")
705                         ExecuteGetHelper(stream);
706                 else if (m_Verb == "COMMAND")
707                         ExecuteCommandHelper(stream);
708                 else if (m_Verb == "SCRIPT")
709                         ExecuteScriptHelper(stream);
710                 else if (m_Verb == "ERROR")
711                         ExecuteErrorHelper(stream);
712                 else
713                         BOOST_THROW_EXCEPTION(std::runtime_error("Invalid livestatus query verb."));
714         } catch (const std::exception& ex) {
715                 SendResponse(stream, LivestatusErrorQuery, DiagnosticInformation(ex));
716         }
717
718         if (!m_KeepAlive) {
719                 stream->Close();
720                 return false;
721         }
722
723         return true;
724 }