2013-03-10 03:09:01 +01:00
/******************************************************************************
* Icinga 2 *
2015-01-22 12:00:23 +01:00
* Copyright ( C ) 2012 - 2015 Icinga Development Team ( http : //www.icinga.org) *
2013-03-10 03:09:01 +01:00
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation ; either version 2 *
* of the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the Free Software Foundation *
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-05-28 14:25:12 +02:00
# include "livestatus/livestatusquery.hpp"
2014-05-25 16:23:35 +02:00
# include "livestatus/countaggregator.hpp"
# include "livestatus/sumaggregator.hpp"
# include "livestatus/minaggregator.hpp"
# include "livestatus/maxaggregator.hpp"
# include "livestatus/avgaggregator.hpp"
# include "livestatus/stdaggregator.hpp"
# include "livestatus/invsumaggregator.hpp"
# include "livestatus/invavgaggregator.hpp"
# include "livestatus/attributefilter.hpp"
# include "livestatus/negatefilter.hpp"
# include "livestatus/orfilter.hpp"
# include "livestatus/andfilter.hpp"
# include "icinga/externalcommandprocessor.hpp"
2014-12-16 19:28:46 +01:00
# include "config/configcompiler.hpp"
2014-05-25 16:23:35 +02:00
# include "base/debug.hpp"
# include "base/convert.hpp"
# include "base/objectlock.hpp"
2014-10-19 14:21:12 +02:00
# include "base/logger.hpp"
2014-05-25 16:23:35 +02:00
# include "base/exception.hpp"
# include "base/utility.hpp"
2014-10-26 19:59:49 +01:00
# include "base/json.hpp"
2014-12-16 19:28:46 +01:00
# include "base/serializer.hpp"
# include "base/timer.hpp"
# include "base/initialize.hpp"
2013-03-15 18:21:29 +01:00
# include <boost/algorithm/string/classification.hpp>
2013-03-16 21:18:53 +01:00
# include <boost/foreach.hpp>
2014-06-25 12:24:02 +02:00
# include <boost/algorithm/string/replace.hpp>
2013-03-18 11:02:18 +01:00
# include <boost/algorithm/string/split.hpp>
2014-12-16 19:28:46 +01:00
# include <boost/algorithm/string/join.hpp>
2013-03-10 03:09:01 +01:00
using namespace icinga ;
2013-07-19 15:42:00 +02:00
static int l_ExternalCommands = 0 ;
static boost : : mutex l_QueryMutex ;
2014-12-16 19:28:46 +01:00
static std : : map < String , LivestatusScriptFrame > l_LivestatusScriptFrames ;
static Timer : : Ptr l_FrameCleanupTimer ;
static boost : : mutex l_LivestatusScriptMutex ;
static void ScriptFrameCleanupHandler ( void )
{
boost : : mutex : : scoped_lock lock ( l_LivestatusScriptMutex ) ;
std : : vector < String > cleanup_keys ;
typedef std : : pair < String , LivestatusScriptFrame > KVPair ;
BOOST_FOREACH ( const KVPair & kv , l_LivestatusScriptFrames ) {
2014-12-16 19:51:33 +01:00
if ( kv . second . Seen < Utility : : GetTime ( ) - 1800 )
2014-12-16 19:28:46 +01:00
cleanup_keys . push_back ( kv . first ) ;
}
BOOST_FOREACH ( const String & key , cleanup_keys )
l_LivestatusScriptFrames . erase ( key ) ;
}
static void InitScriptFrameCleanup ( void )
{
l_FrameCleanupTimer = new Timer ( ) ;
l_FrameCleanupTimer - > OnTimerExpired . connect ( boost : : bind ( ScriptFrameCleanupHandler ) ) ;
l_FrameCleanupTimer - > SetInterval ( 30 ) ;
l_FrameCleanupTimer - > Start ( ) ;
}
INITIALIZE_ONCE ( InitScriptFrameCleanup ) ;
2013-07-19 15:42:00 +02:00
2014-05-28 14:25:12 +02:00
LivestatusQuery : : LivestatusQuery ( const std : : vector < String > & lines , const String & compat_log_path )
2015-03-04 12:03:35 +01:00
: m_KeepAlive ( false ) , m_OutputFormat ( " csv " ) , m_ColumnHeaders ( true ) , m_Limit ( - 1 ) , m_ErrorCode ( 0 ) ,
2013-10-29 13:44:43 +01:00
m_LogTimeFrom ( 0 ) , m_LogTimeUntil ( static_cast < long > ( Utility : : GetTime ( ) ) )
2013-03-10 05:10:51 +01:00
{
2013-07-12 18:25:18 +02:00
if ( lines . size ( ) = = 0 ) {
m_Verb = " ERROR " ;
m_ErrorCode = LivestatusErrorQuery ;
m_ErrorMessage = " Empty Query. Aborting. " ;
return ;
}
2013-11-05 12:31:30 +01:00
String msg ;
BOOST_FOREACH ( const String & line , lines ) {
msg + = line + " \n " ;
}
2014-05-28 14:25:12 +02:00
Log ( LogDebug , " LivestatusQuery " , msg ) ;
2013-11-05 12:31:30 +01:00
2013-11-07 17:29:45 +01:00
m_CompatLogPath = compat_log_path ;
2013-11-07 14:04:13 +01:00
2013-07-29 19:37:58 +02:00
/* default separators */
m_Separators . push_back ( " \n " ) ;
m_Separators . push_back ( " ; " ) ;
m_Separators . push_back ( " , " ) ;
m_Separators . push_back ( " | " ) ;
2013-03-10 05:10:51 +01:00
String line = lines [ 0 ] ;
2013-03-10 03:09:01 +01:00
size_t sp_index = line . FindFirstOf ( " " ) ;
if ( sp_index = = String : : NPos )
2013-03-16 21:18:53 +01:00
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Livestatus header must contain a verb. " ) ) ;
2013-03-10 03:09:01 +01:00
String verb = line . SubStr ( 0 , sp_index ) ;
String target = line . SubStr ( sp_index + 1 ) ;
2013-03-10 05:10:51 +01:00
m_Verb = verb ;
2013-03-10 03:09:01 +01:00
2013-03-10 05:10:51 +01:00
if ( m_Verb = = " COMMAND " ) {
2013-12-12 10:21:33 +01:00
m_KeepAlive = true ;
2013-03-10 05:10:51 +01:00
m_Command = target ;
2014-12-16 19:28:46 +01:00
} else if ( m_Verb = = " SCRIPT " ) {
m_Session = target ;
for ( unsigned int i = 1 ; i < lines . size ( ) ; i + + ) {
if ( m_Command ! = " " )
m_Command + = " \n " ;
m_Command + = lines [ i ] ;
}
return ;
2013-03-10 05:10:51 +01:00
} else if ( m_Verb = = " GET " ) {
m_Table = target ;
2013-03-10 03:09:01 +01:00
} else {
2013-03-10 15:11:32 +01:00
m_Verb = " ERROR " ;
2013-07-12 18:25:18 +02:00
m_ErrorCode = LivestatusErrorQuery ;
2013-03-10 15:11:32 +01:00
m_ErrorMessage = " Unknown livestatus verb: " + m_Verb ;
return ;
2013-03-10 03:09:01 +01:00
}
2013-03-16 21:18:53 +01:00
std : : deque < Filter : : Ptr > filters , stats ;
2013-07-11 15:52:01 +02:00
std : : deque < Aggregator : : Ptr > aggregators ;
2013-03-10 15:11:32 +01:00
for ( unsigned int i = 1 ; i < lines . size ( ) ; i + + ) {
2013-03-10 05:10:51 +01:00
line = lines [ i ] ;
2013-03-10 03:09:01 +01:00
size_t col_index = line . FindFirstOf ( " : " ) ;
String header = line . SubStr ( 0 , col_index ) ;
2013-07-12 10:54:57 +02:00
String params ;
2014-06-11 22:53:56 +02:00
//OutputFormat:json or OutputFormat: json
if ( line . GetLength ( ) > col_index + 1 )
2015-08-27 18:06:20 +02:00
params = line . SubStr ( col_index + 1 ) . Trim ( ) ;
2013-03-10 03:09:01 +01:00
if ( header = = " ResponseHeader " )
2013-03-10 05:10:51 +01:00
m_ResponseHeader = params ;
2013-03-10 09:23:13 +01:00
else if ( header = = " OutputFormat " )
m_OutputFormat = params ;
2013-12-12 10:21:33 +01:00
else if ( header = = " KeepAlive " )
m_KeepAlive = ( params = = " on " ) ;
2013-12-13 10:22:30 +01:00
else if ( header = = " Columns " ) {
m_ColumnHeaders = false ; // Might be explicitly re-enabled later on
2013-03-18 11:02:18 +01:00
boost : : algorithm : : split ( m_Columns , params , boost : : is_any_of ( " " ) ) ;
2013-12-13 10:22:30 +01:00
} else if ( header = = " Separators " ) {
std : : vector < String > separators ;
2013-07-29 19:37:58 +02:00
boost : : algorithm : : split ( separators , params , boost : : is_any_of ( " " ) ) ;
/* ugly ascii long to char conversion, but works */
if ( separators . size ( ) > 0 )
m_Separators [ 0 ] = String ( 1 , static_cast < char > ( Convert : : ToLong ( separators [ 0 ] ) ) ) ;
if ( separators . size ( ) > 1 )
m_Separators [ 1 ] = String ( 1 , static_cast < char > ( Convert : : ToLong ( separators [ 1 ] ) ) ) ;
if ( separators . size ( ) > 2 )
m_Separators [ 2 ] = String ( 1 , static_cast < char > ( Convert : : ToLong ( separators [ 2 ] ) ) ) ;
if ( separators . size ( ) > 3 )
m_Separators [ 3 ] = String ( 1 , static_cast < char > ( Convert : : ToLong ( separators [ 3 ] ) ) ) ;
2013-12-13 10:22:30 +01:00
} else if ( header = = " ColumnHeaders " )
2013-03-10 15:11:32 +01:00
m_ColumnHeaders = ( params = = " on " ) ;
2015-03-04 12:03:35 +01:00
else if ( header = = " Limit " )
m_Limit = Convert : : ToLong ( params ) ;
2013-07-11 17:52:06 +02:00
else if ( header = = " Filter " ) {
2013-10-29 13:44:43 +01:00
Filter : : Ptr filter = ParseFilter ( params , m_LogTimeFrom , m_LogTimeUntil ) ;
2013-07-11 17:52:06 +02:00
2013-07-11 15:52:01 +02:00
if ( ! filter ) {
2013-03-10 15:11:32 +01:00
m_Verb = " ERROR " ;
2013-07-12 18:25:18 +02:00
m_ErrorCode = LivestatusErrorQuery ;
2013-07-12 10:54:57 +02:00
m_ErrorMessage = " Invalid filter specification: " + line ;
2013-03-10 15:11:32 +01:00
return ;
}
2013-07-12 10:54:57 +02:00
filters . push_back ( filter ) ;
} else if ( header = = " Stats " ) {
2014-04-27 12:05:33 +02:00
m_ColumnHeaders = false ; // Might be explicitly re-enabled later on
2013-07-11 17:52:06 +02:00
std : : vector < String > tokens ;
boost : : algorithm : : split ( tokens , params , boost : : is_any_of ( " " ) ) ;
2013-07-12 10:54:57 +02:00
if ( tokens . size ( ) < 2 ) {
m_Verb = " ERROR " ;
2013-07-12 18:25:18 +02:00
m_ErrorCode = LivestatusErrorQuery ;
2013-07-12 10:54:57 +02:00
m_ErrorMessage = " Missing aggregator column name: " + line ;
return ;
}
2013-07-11 17:52:06 +02:00
String aggregate_arg = tokens [ 0 ] ;
String aggregate_attr = tokens [ 1 ] ;
2013-07-12 10:54:57 +02:00
Aggregator : : Ptr aggregator ;
Filter : : Ptr filter ;
2013-07-11 17:52:06 +02:00
if ( aggregate_arg = = " sum " ) {
2014-11-08 21:17:16 +01:00
aggregator = new SumAggregator ( aggregate_attr ) ;
2013-07-12 10:54:57 +02:00
} else if ( aggregate_arg = = " min " ) {
2014-11-08 21:17:16 +01:00
aggregator = new MinAggregator ( aggregate_attr ) ;
2013-07-12 10:54:57 +02:00
} else if ( aggregate_arg = = " max " ) {
2014-11-08 21:17:16 +01:00
aggregator = new MaxAggregator ( aggregate_attr ) ;
2013-07-12 10:54:57 +02:00
} else if ( aggregate_arg = = " avg " ) {
2014-11-08 21:17:16 +01:00
aggregator = new AvgAggregator ( aggregate_attr ) ;
2013-07-12 10:54:57 +02:00
} else if ( aggregate_arg = = " std " ) {
2014-11-08 21:17:16 +01:00
aggregator = new StdAggregator ( aggregate_attr ) ;
2013-07-12 10:54:57 +02:00
} else if ( aggregate_arg = = " suminv " ) {
2014-11-08 21:17:16 +01:00
aggregator = new InvSumAggregator ( aggregate_attr ) ;
2013-07-12 10:54:57 +02:00
} else if ( aggregate_arg = = " avginv " ) {
2014-11-08 21:17:16 +01:00
aggregator = new InvAvgAggregator ( aggregate_attr ) ;
2013-07-11 17:52:06 +02:00
} else {
2013-10-29 13:44:43 +01:00
filter = ParseFilter ( params , m_LogTimeFrom , m_LogTimeUntil ) ;
2013-07-11 17:52:06 +02:00
if ( ! filter ) {
m_Verb = " ERROR " ;
2013-07-12 18:25:18 +02:00
m_ErrorCode = LivestatusErrorQuery ;
2013-07-12 10:54:57 +02:00
m_ErrorMessage = " Invalid filter specification: " + line ;
2013-07-11 17:52:06 +02:00
return ;
}
2014-11-08 21:17:16 +01:00
aggregator = new CountAggregator ( ) ;
2013-07-11 15:52:01 +02:00
}
2013-07-11 17:52:06 +02:00
2013-07-12 10:54:57 +02:00
aggregator - > SetFilter ( filter ) ;
aggregators . push_back ( aggregator ) ;
stats . push_back ( filter ) ;
2014-04-27 13:29:18 +02:00
} else if ( header = = " Or " | | header = = " And " | | header = = " StatsOr " | | header = = " StatsAnd " ) {
2013-03-16 21:18:53 +01:00
std : : deque < Filter : : Ptr > & deq = ( header = = " Or " | | header = = " And " ) ? filters : stats ;
2013-03-10 17:54:46 +01:00
2013-10-03 18:58:48 +02:00
unsigned int num = Convert : : ToLong ( params ) ;
2013-03-10 15:11:32 +01:00
CombinerFilter : : Ptr filter ;
2013-11-06 18:45:52 +01:00
if ( header = = " Or " | | header = = " StatsOr " ) {
2014-11-08 21:17:16 +01:00
filter = new OrFilter ( ) ;
2014-10-19 17:52:17 +02:00
Log ( LogDebug , " LivestatusQuery " )
< < " Add OR filter for " < < params < < " column(s). " < < deq . size ( ) < < " filters available. " ;
2013-11-06 18:45:52 +01:00
} else {
2014-11-08 21:17:16 +01:00
filter = new AndFilter ( ) ;
2014-10-19 17:52:17 +02:00
Log ( LogDebug , " LivestatusQuery " )
< < " Add AND filter for " < < params < < " column(s). " < < deq . size ( ) < < " filters available. " ;
2013-11-06 18:45:52 +01:00
}
2013-03-10 15:11:32 +01:00
2013-03-10 17:54:46 +01:00
if ( num > deq . size ( ) ) {
m_Verb = " ERROR " ;
m_ErrorCode = 451 ;
2013-08-20 12:50:24 +02:00
m_ErrorMessage = " Or/StatsOr is referencing " + Convert : : ToString ( num ) + " filters; stack only contains " + Convert : : ToString ( static_cast < long > ( deq . size ( ) ) ) + " filters " ;
2013-03-10 17:54:46 +01:00
return ;
}
2013-11-06 18:45:52 +01:00
while ( num > 0 & & num - - ) {
2013-03-10 17:54:46 +01:00
filter - > AddSubFilter ( deq . back ( ) ) ;
2014-10-19 17:52:17 +02:00
Log ( LogDebug , " LivestatusQuery " )
< < " Add " < < num < < " filter. " ;
2013-03-10 17:54:46 +01:00
deq . pop_back ( ) ;
2014-04-27 13:29:18 +02:00
if ( & deq = = & stats )
aggregators . pop_back ( ) ;
2013-03-10 15:11:32 +01:00
}
2013-03-10 17:54:46 +01:00
deq . push_back ( filter ) ;
2014-04-27 13:29:18 +02:00
if ( & deq = = & stats ) {
2014-11-08 21:17:16 +01:00
Aggregator : : Ptr aggregator = new CountAggregator ( ) ;
2014-04-27 13:29:18 +02:00
aggregator - > SetFilter ( filter ) ;
aggregators . push_back ( aggregator ) ;
}
2013-03-10 17:54:46 +01:00
} else if ( header = = " Negate " | | header = = " StatsNegate " ) {
2013-03-16 21:18:53 +01:00
std : : deque < Filter : : Ptr > & deq = ( header = = " Negate " ) ? filters : stats ;
2013-03-10 15:27:55 +01:00
2013-03-10 17:54:46 +01:00
if ( deq . empty ( ) ) {
m_Verb = " ERROR " ;
m_ErrorCode = 451 ;
m_ErrorMessage = " Negate/StatsNegate used, however the filter stack is empty " ;
return ;
2013-03-10 15:27:55 +01:00
}
2013-03-10 17:54:46 +01:00
Filter : : Ptr filter = deq . back ( ) ;
2013-07-12 10:54:57 +02:00
deq . pop_back ( ) ;
if ( ! filter ) {
m_Verb = " ERROR " ;
m_ErrorCode = 451 ;
m_ErrorMessage = " Negate/StatsNegate used, however last stats doesn't have a filter " ;
return ;
}
2013-03-10 17:54:46 +01:00
2014-11-08 21:17:16 +01:00
deq . push_back ( new NegateFilter ( filter ) ) ;
2013-07-11 17:52:06 +02:00
2013-07-11 15:52:01 +02:00
if ( deq = = stats ) {
Aggregator : : Ptr aggregator = aggregators . back ( ) ;
aggregator - > SetFilter ( filter ) ;
}
2013-03-10 15:11:32 +01:00
}
2013-03-10 09:23:13 +01:00
}
2013-03-10 15:11:32 +01:00
2013-03-10 15:23:08 +01:00
/* Combine all top-level filters into a single filter. */
2014-11-08 21:17:16 +01:00
AndFilter : : Ptr top_filter = new AndFilter ( ) ;
2013-03-10 15:23:08 +01:00
BOOST_FOREACH ( const Filter : : Ptr & filter , filters ) {
top_filter - > AddSubFilter ( filter ) ;
}
m_Filter = top_filter ;
2013-07-11 15:52:01 +02:00
m_Aggregators . swap ( aggregators ) ;
}
2014-05-28 14:25:12 +02:00
int LivestatusQuery : : GetExternalCommands ( void )
2013-07-19 15:42:00 +02:00
{
boost : : mutex : : scoped_lock lock ( l_QueryMutex ) ;
return l_ExternalCommands ;
}
2014-05-28 14:25:12 +02:00
Filter : : Ptr LivestatusQuery : : ParseFilter ( const String & params , unsigned long & from , unsigned long & until )
2013-07-11 15:52:01 +02:00
{
2013-11-05 12:31:30 +01:00
/*
* time > = 1382696656
* type = SERVICE FLAPPING ALERT
*/
2013-07-11 15:52:01 +02:00
std : : vector < String > tokens ;
2013-11-05 12:31:30 +01:00
size_t sp_index ;
String temp_buffer = params ;
/* extract attr and op */
for ( int i = 0 ; i < 2 ; i + + ) {
sp_index = temp_buffer . FindFirstOf ( " " ) ;
2014-06-24 12:51:56 +02:00
/* check if this is the last argument */
if ( sp_index = = String : : NPos ) {
/* 'attr op' or 'attr op val' is valid */
if ( i < 1 )
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Livestatus filter ' " + params + " ' does not contain all required fields. " ) ) ;
break ;
}
2013-11-05 12:31:30 +01:00
tokens . push_back ( temp_buffer . SubStr ( 0 , sp_index ) ) ;
temp_buffer = temp_buffer . SubStr ( sp_index + 1 ) ;
}
2013-11-06 18:45:52 +01:00
2013-11-05 12:31:30 +01:00
/* add the rest as value */
tokens . push_back ( temp_buffer ) ;
2013-07-11 15:52:01 +02:00
if ( tokens . size ( ) = = 2 )
tokens . push_back ( " " ) ;
if ( tokens . size ( ) < 3 )
return Filter : : Ptr ( ) ;
bool negate = false ;
2013-11-05 12:31:30 +01:00
String attr = tokens [ 0 ] ;
String op = tokens [ 1 ] ;
String val = tokens [ 2 ] ;
2013-07-11 15:52:01 +02:00
if ( op = = " != " ) {
op = " = " ;
negate = true ;
} else if ( op = = " !~ " ) {
op = " ~ " ;
negate = true ;
} else if ( op = = " !=~ " ) {
op = " =~ " ;
negate = true ;
} else if ( op = = " !~~ " ) {
op = " ~~ " ;
negate = true ;
}
2014-11-08 21:17:16 +01:00
Filter : : Ptr filter = new AttributeFilter ( attr , op , val ) ;
2013-07-11 15:52:01 +02:00
if ( negate )
2014-11-08 21:17:16 +01:00
filter = new NegateFilter ( filter ) ;
2013-07-11 15:52:01 +02:00
2013-10-29 13:44:43 +01:00
/* pre-filter log time duration */
2013-11-05 12:31:30 +01:00
if ( attr = = " time " ) {
2013-10-29 13:44:43 +01:00
if ( op = = " < " | | op = = " <= " ) {
2013-11-05 12:31:30 +01:00
until = Convert : : ToLong ( val ) ;
2013-10-29 13:44:43 +01:00
} else if ( op = = " > " | | op = = " >= " ) {
2013-11-05 12:31:30 +01:00
from = Convert : : ToLong ( val ) ;
2013-10-29 13:44:43 +01:00
}
}
2013-11-06 18:45:52 +01:00
2014-10-19 17:52:17 +02:00
Log ( LogDebug , " LivestatusQuery " )
< < " Parsed filter with attr: ' " < < attr < < " ' op: ' " < < op < < " ' val: ' " < < val < < " '. " ;
2013-10-29 13:44:43 +01:00
2013-07-11 15:52:01 +02:00
return filter ;
2013-03-10 09:23:13 +01:00
}
2014-06-25 11:30:27 +02:00
void LivestatusQuery : : PrintResultSet ( std : : ostream & fp , const Array : : Ptr & rs ) const
2013-03-10 09:23:13 +01:00
{
if ( m_OutputFormat = = " csv " ) {
ObjectLock olock ( rs ) ;
BOOST_FOREACH ( const Array : : Ptr & row , rs ) {
bool first = true ;
ObjectLock rlock ( row ) ;
BOOST_FOREACH ( const Value & value , row ) {
if ( first )
first = false ;
else
2013-07-29 19:37:58 +02:00
fp < < m_Separators [ 1 ] ;
2013-03-10 09:23:13 +01:00
2013-07-15 10:50:41 +02:00
if ( value . IsObjectType < Array > ( ) )
PrintCsvArray ( fp , value , 0 ) ;
else
2013-07-23 08:57:22 +02:00
fp < < value ;
2013-03-10 09:23:13 +01:00
}
2013-07-29 19:37:58 +02:00
fp < < m_Separators [ 0 ] ;
2013-03-10 09:23:13 +01:00
}
} else if ( m_OutputFormat = = " json " ) {
2014-10-26 19:59:49 +01:00
fp < < JsonEncode ( rs ) ;
2014-06-25 11:30:27 +02:00
} else if ( m_OutputFormat = = " python " ) {
PrintPythonArray ( fp , rs ) ;
2013-03-10 03:09:01 +01:00
}
}
2014-06-25 11:30:27 +02:00
void LivestatusQuery : : PrintCsvArray ( std : : ostream & fp , const Array : : Ptr & array , int level ) const
2013-07-15 10:50:41 +02:00
{
bool first = true ;
ObjectLock olock ( array ) ;
BOOST_FOREACH ( const Value & value , array ) {
if ( first )
first = false ;
else
2013-07-29 19:37:58 +02:00
fp < < ( ( level = = 0 ) ? m_Separators [ 2 ] : m_Separators [ 3 ] ) ;
2013-07-15 10:50:41 +02:00
if ( value . IsObjectType < Array > ( ) )
PrintCsvArray ( fp , value , level + 1 ) ;
2014-12-17 08:54:28 +01:00
else if ( value . IsBoolean ( ) )
fp < < Convert : : ToLong ( value ) ;
2013-07-15 10:50:41 +02:00
else
2013-07-23 08:57:22 +02:00
fp < < value ;
2013-07-15 10:50:41 +02:00
}
}
2014-06-25 11:30:27 +02:00
void LivestatusQuery : : PrintPythonArray ( std : : ostream & fp , const Array : : Ptr & rs ) const
{
fp < < " [ " ;
bool first = true ;
BOOST_FOREACH ( const Value & value , rs ) {
if ( first )
first = false ;
else
fp < < " , " ;
if ( value . IsObjectType < Array > ( ) )
PrintPythonArray ( fp , value ) ;
else if ( value . IsNumber ( ) )
fp < < value ;
else
fp < < QuoteStringPython ( value ) ;
}
fp < < " ] " ;
}
String LivestatusQuery : : QuoteStringPython ( const String & str ) {
String result = str ;
boost : : algorithm : : replace_all ( result , " \" " , " \\ \" " ) ;
return " r \" " + result + " \" " ;
}
2014-05-28 14:25:12 +02:00
void LivestatusQuery : : ExecuteGetHelper ( const Stream : : Ptr & stream )
2013-03-10 03:09:01 +01:00
{
2015-02-25 14:11:59 +01:00
Log ( LogNotice , " LivestatusQuery " )
2014-10-19 17:52:17 +02:00
< < " Table: " < < m_Table ;
2013-03-10 09:23:13 +01:00
2013-11-07 14:04:13 +01:00
Table : : Ptr table = Table : : GetByName ( m_Table , m_CompatLogPath , m_LogTimeFrom , m_LogTimeUntil ) ;
2013-03-10 09:23:13 +01:00
if ( ! table ) {
2013-07-12 18:25:18 +02:00
SendResponse ( stream , LivestatusErrorNotFound , " Table ' " + m_Table + " ' does not exist. " ) ;
2013-03-10 09:23:13 +01:00
return ;
}
2015-03-04 12:03:35 +01:00
std : : vector < LivestatusRowValue > objects = table - > FilterRows ( m_Filter , m_Limit ) ;
2013-03-16 21:18:53 +01:00
std : : vector < String > columns ;
2013-11-05 12:31:30 +01:00
2013-03-10 09:23:13 +01:00
if ( m_Columns . size ( ) > 0 )
columns = m_Columns ;
else
columns = table - > GetColumnNames ( ) ;
2014-11-08 21:17:16 +01:00
Array : : Ptr rs = new Array ( ) ;
2013-03-10 09:23:13 +01:00
2013-07-11 15:52:01 +02:00
if ( m_Aggregators . empty ( ) ) {
2014-11-08 21:17:16 +01:00
Array : : Ptr header = new Array ( ) ;
2013-12-13 10:22:30 +01:00
2015-03-04 10:58:22 +01:00
typedef std : : pair < String , Column > ColumnPair ;
std : : vector < ColumnPair > column_objs ;
column_objs . reserve ( columns . size ( ) ) ;
BOOST_FOREACH ( const String & columnName , columns )
column_objs . push_back ( std : : make_pair ( columnName , table - > GetColumn ( columnName ) ) ) ;
rs - > Reserve ( 1 + objects . size ( ) ) ;
2015-02-13 15:50:20 +01:00
BOOST_FOREACH ( const LivestatusRowValue & object , objects ) {
2014-11-08 21:17:16 +01:00
Array : : Ptr row = new Array ( ) ;
2013-03-10 09:23:13 +01:00
2015-03-04 10:58:22 +01:00
row - > Reserve ( column_objs . size ( ) ) ;
2013-03-10 17:54:46 +01:00
2015-03-04 10:58:22 +01:00
BOOST_FOREACH ( const ColumnPair & cv , column_objs ) {
2013-12-13 10:22:30 +01:00
if ( m_ColumnHeaders )
2015-03-04 10:58:22 +01:00
header - > Add ( cv . first ) ;
2013-12-13 10:22:30 +01:00
2015-03-04 10:58:22 +01:00
row - > Add ( cv . second . ExtractValue ( object . Row , object . GroupByType , object . GroupByObject ) ) ;
2013-03-10 09:23:13 +01:00
}
2013-12-13 10:22:30 +01:00
if ( m_ColumnHeaders ) {
rs - > Add ( header ) ;
m_ColumnHeaders = false ;
}
2013-03-10 17:54:46 +01:00
rs - > Add ( row ) ;
}
} else {
2013-07-11 15:52:01 +02:00
std : : vector < double > stats ( m_Aggregators . size ( ) , 0 ) ;
int index = 0 ;
2013-11-07 17:53:55 +01:00
/* add aggregated stats */
2013-07-11 15:52:01 +02:00
BOOST_FOREACH ( const Aggregator : : Ptr aggregator , m_Aggregators ) {
2015-02-13 15:50:20 +01:00
BOOST_FOREACH ( const LivestatusRowValue & object , objects ) {
aggregator - > Apply ( table , object . Row ) ;
2013-03-10 17:54:46 +01:00
}
2013-11-05 12:31:30 +01:00
2013-07-11 15:52:01 +02:00
stats [ index ] = aggregator - > GetResult ( ) ;
index + + ;
2013-03-10 09:23:13 +01:00
}
2013-12-13 10:22:30 +01:00
/* add column headers both for raw and aggregated data */
if ( m_ColumnHeaders ) {
2014-11-08 21:17:16 +01:00
Array : : Ptr header = new Array ( ) ;
2013-12-13 10:22:30 +01:00
BOOST_FOREACH ( const String & columnName , m_Columns ) {
header - > Add ( columnName ) ;
}
2014-04-27 12:05:33 +02:00
for ( size_t i = 1 ; i < = m_Aggregators . size ( ) ; i + + ) {
2013-12-13 10:22:30 +01:00
header - > Add ( " stats_ " + Convert : : ToString ( i ) ) ;
}
rs - > Add ( header ) ;
}
2014-11-08 21:17:16 +01:00
Array : : Ptr row = new Array ( ) ;
2013-11-07 17:53:55 +01:00
2015-03-04 10:58:22 +01:00
row - > Reserve ( m_Columns . size ( ) + m_Aggregators . size ( ) ) ;
2013-11-07 17:53:55 +01:00
/*
2013-11-11 13:58:25 +01:00
* add selected columns next to stats
2013-11-07 17:53:55 +01:00
* may not be accurate for grouping !
*/
2013-11-11 13:58:25 +01:00
if ( objects . size ( ) > 0 & & m_Columns . size ( ) > 0 ) {
BOOST_FOREACH ( const String & columnName , m_Columns ) {
Column column = table - > GetColumn ( columnName ) ;
2013-11-07 17:53:55 +01:00
2015-02-13 15:50:20 +01:00
LivestatusRowValue object = objects [ 0 ] ; //first object wins
row - > Add ( column . ExtractValue ( object . Row , object . GroupByType , object . GroupByObject ) ) ;
2013-11-11 13:58:25 +01:00
}
2013-11-07 17:53:55 +01:00
}
2013-10-03 18:58:48 +02:00
for ( size_t i = 0 ; i < m_Aggregators . size ( ) ; i + + )
2013-03-10 17:54:46 +01:00
row - > Add ( stats [ i ] ) ;
2013-03-10 09:23:13 +01:00
rs - > Add ( row ) ;
}
2013-03-16 21:18:53 +01:00
std : : ostringstream result ;
2013-12-13 10:22:30 +01:00
PrintResultSet ( result , rs ) ;
2013-03-10 09:23:13 +01:00
2013-07-12 18:25:18 +02:00
SendResponse ( stream , LivestatusErrorOK , result . str ( ) ) ;
2013-03-10 03:09:01 +01:00
}
2014-05-28 14:25:12 +02:00
void LivestatusQuery : : ExecuteCommandHelper ( const Stream : : Ptr & stream )
2013-03-10 03:09:01 +01:00
{
2013-07-19 15:42:00 +02:00
{
boost : : mutex : : scoped_lock lock ( l_QueryMutex ) ;
l_ExternalCommands + + ;
}
2015-02-25 14:11:59 +01:00
Log ( LogNotice , " LivestatusQuery " )
2014-10-19 17:52:17 +02:00
< < " Executing command: " < < m_Command ;
2013-03-10 15:11:32 +01:00
ExternalCommandProcessor : : Execute ( m_Command ) ;
2013-07-12 18:25:18 +02:00
SendResponse ( stream , LivestatusErrorOK , " " ) ;
2013-03-10 03:09:01 +01:00
}
2014-12-16 19:28:46 +01:00
void LivestatusQuery : : ExecuteScriptHelper ( const Stream : : Ptr & stream )
{
Log ( LogInformation , " LivestatusQuery " )
< < " Executing expression: " < < m_Command ;
m_ResponseHeader = " fixed16 " ;
LivestatusScriptFrame & lsf = l_LivestatusScriptFrames [ m_Session ] ;
lsf . Seen = Utility : : GetTime ( ) ;
if ( ! lsf . Locals )
lsf . Locals = new Dictionary ( ) ;
String fileName = " < " + Convert : : ToString ( lsf . NextLine ) + " > " ;
lsf . NextLine + + ;
lsf . Lines [ fileName ] = m_Command ;
2015-03-18 14:36:46 +01:00
Expression * expr = NULL ;
2014-12-16 19:28:46 +01:00
Value result ;
try {
2015-08-26 06:57:24 +02:00
expr = ConfigCompiler : : CompileText ( fileName , m_Command ) ;
2014-12-16 19:28:46 +01:00
ScriptFrame frame ;
frame . Locals = lsf . Locals ;
2015-08-11 13:58:02 +02:00
frame . Self = lsf . Locals ;
2014-12-16 19:28:46 +01:00
result = expr - > Evaluate ( frame ) ;
} catch ( const ScriptError & ex ) {
delete expr ;
DebugInfo di = ex . GetDebugInfo ( ) ;
std : : ostringstream msgbuf ;
msgbuf < < di . Path < < " : " < < lsf . Lines [ di . Path ] < < " \n "
< < String ( di . Path . GetLength ( ) + 2 , ' ' )
< < String ( di . FirstColumn , ' ' ) < < String ( di . LastColumn - di . FirstColumn + 1 , ' ^ ' ) < < " \n "
< < ex . what ( ) < < " \n " ;
SendResponse ( stream , LivestatusErrorQuery , msgbuf . str ( ) ) ;
return ;
} catch ( . . . ) {
delete expr ;
throw ;
}
delete expr ;
2015-03-11 16:06:58 +01:00
SendResponse ( stream , LivestatusErrorOK , JsonEncode ( Serialize ( result , FAEphemeral | FAState | FAConfig ) , true ) ) ;
2014-12-16 19:28:46 +01:00
}
2014-05-28 14:25:12 +02:00
void LivestatusQuery : : ExecuteErrorHelper ( const Stream : : Ptr & stream )
2013-03-10 03:09:01 +01:00
{
2014-10-19 17:52:17 +02:00
Log ( LogDebug , " LivestatusQuery " )
< < " ERROR: Code: ' " < < m_ErrorCode < < " ' Message: ' " < < m_ErrorMessage < < " '. " ;
2013-03-10 17:54:46 +01:00
SendResponse ( stream , m_ErrorCode , m_ErrorMessage ) ;
2013-03-10 03:09:01 +01:00
}
2014-05-28 14:25:12 +02:00
void LivestatusQuery : : SendResponse ( const Stream : : Ptr & stream , int code , const String & data )
2013-03-10 03:09:01 +01:00
{
if ( m_ResponseHeader = = " fixed16 " )
PrintFixed16 ( stream , code , data ) ;
2013-03-10 05:10:51 +01:00
2013-11-06 18:54:01 +01:00
if ( m_ResponseHeader = = " fixed16 " | | code = = LivestatusErrorOK ) {
try {
stream - > Write ( data . CStr ( ) , data . GetLength ( ) ) ;
2014-06-05 16:17:53 +02:00
} catch ( const std : : exception & ) {
2015-10-15 14:58:21 +02:00
Log ( LogCritical , " LivestatusQuery " , " Cannot write query response to socket. " ) ;
2013-11-06 18:54:01 +01:00
}
}
2013-03-10 03:09:01 +01:00
}
2014-05-28 14:25:12 +02:00
void LivestatusQuery : : PrintFixed16 ( const Stream : : Ptr & stream , int code , const String & data )
2013-03-10 03:09:01 +01:00
{
ASSERT ( code > = 100 & & code < = 999 ) ;
String sCode = Convert : : ToString ( code ) ;
2013-08-20 12:50:24 +02:00
String sLength = Convert : : ToString ( static_cast < long > ( data . GetLength ( ) ) ) ;
2013-03-10 03:09:01 +01:00
2013-07-29 19:37:58 +02:00
String header = sCode + String ( 16 - 3 - sLength . GetLength ( ) - 1 , ' ' ) + sLength + m_Separators [ 0 ] ;
2013-11-06 18:45:52 +01:00
2013-11-06 18:54:01 +01:00
try {
stream - > Write ( header . CStr ( ) , header . GetLength ( ) ) ;
2014-06-05 16:17:53 +02:00
} catch ( const std : : exception & ) {
2014-10-19 17:52:17 +02:00
Log ( LogCritical , " LivestatusQuery " , " Cannot write to TCP socket. " ) ;
2013-11-06 18:54:01 +01:00
}
2013-03-10 03:09:01 +01:00
}
2014-05-28 14:25:12 +02:00
bool LivestatusQuery : : Execute ( const Stream : : Ptr & stream )
2013-03-10 03:09:01 +01:00
{
2013-03-10 15:11:32 +01:00
try {
2015-02-25 14:11:59 +01:00
Log ( LogNotice , " LivestatusQuery " )
2014-10-19 17:52:17 +02:00
< < " Executing livestatus query: " < < m_Verb ;
2013-07-10 16:11:40 +02:00
if ( m_Verb = = " GET " )
ExecuteGetHelper ( stream ) ;
else if ( m_Verb = = " COMMAND " )
ExecuteCommandHelper ( stream ) ;
2014-12-16 19:28:46 +01:00
else if ( m_Verb = = " SCRIPT " )
ExecuteScriptHelper ( stream ) ;
2013-07-10 16:11:40 +02:00
else if ( m_Verb = = " ERROR " )
ExecuteErrorHelper ( stream ) ;
else
BOOST_THROW_EXCEPTION ( std : : runtime_error ( " Invalid livestatus query verb. " ) ) ;
2013-03-10 15:11:32 +01:00
} catch ( const std : : exception & ex ) {
2013-11-20 21:55:14 +01:00
SendResponse ( stream , LivestatusErrorQuery , DiagnosticInformation ( ex ) ) ;
2013-03-10 15:11:32 +01:00
}
2013-03-10 05:10:51 +01:00
2013-07-04 09:45:44 +02:00
if ( ! m_KeepAlive ) {
2013-03-10 05:10:51 +01:00
stream - > Close ( ) ;
2013-07-04 09:45:44 +02:00
return false ;
}
return true ;
2013-03-10 03:09:01 +01:00
}