File Coverage

blib/lib/Monitoring/Livestatus.pm
Criterion Covered Total %
statement 135 567 23.8
branch 50 324 15.4
condition 10 99 10.1
subroutine 18 41 43.9
pod 17 17 100.0
total 230 1048 21.9


line stmt bran cond sub pod time code
1             package Monitoring::Livestatus;
2              
3 4     4   2399 use 5.006;
  4         13  
4 4     4   16 use strict;
  4         6  
  4         92  
5 4     4   15 use warnings;
  4         7  
  4         98  
6 4     4   990 use Data::Dumper qw/Dumper/;
  4         10887  
  4         208  
7 4     4   21 use Carp qw/carp croak confess/;
  4         5  
  4         191  
8 4     4   20 use Digest::MD5 qw(md5_hex);
  4         12  
  4         185  
9 4     4   1546 use Cpanel::JSON::XS ();
  4         8916  
  4         93  
10 4     4   2005 use Storable qw/dclone/;
  4         10221  
  4         308  
11              
12 4     4   1555 use Monitoring::Livestatus::INET qw//;
  4         10  
  4         78  
13 4     4   1239 use Monitoring::Livestatus::UNIX qw//;
  4         9  
  4         22110  
14              
15             our $VERSION = '0.80';
16              
17              
18             # list of allowed options
19             my $allowed_options = {
20             'addpeer' => 1,
21             'backend' => 1,
22             'columns' => 1,
23             'deepcopy' => 1,
24             'header' => 1,
25             'limit' => 1,
26             'limit_start' => 1,
27             'limit_length' => 1,
28             'rename' => 1,
29             'slice' => 1,
30             'sum' => 1,
31             'callbacks' => 1,
32             'wrapped_json' => 1,
33             'sort' => 1,
34             'offset' => 1,
35             };
36              
37             =head1 NAME
38              
39             Monitoring::Livestatus - Perl API for check_mk livestatus to access runtime
40             data from Nagios and Icinga
41              
42             =head1 SYNOPSIS
43              
44             use Monitoring::Livestatus;
45             my $ml = Monitoring::Livestatus->new(
46             socket => '/var/lib/livestatus/livestatus.sock'
47             );
48             my $hosts = $ml->selectall_arrayref("GET hosts");
49              
50             =head1 DESCRIPTION
51              
52             This module connects via socket/tcp to the livestatus addon for Naemon, Nagios,
53             Icinga and Shinken. You first have to install and activate the livestatus addon
54             in your monitoring installation.
55              
56             =head1 CONSTRUCTOR
57              
58             =head2 new ( [ARGS] )
59              
60             Creates an C object. C takes at least the
61             socketpath. Arguments are in key-value pairs.
62              
63             =over 4
64              
65             =item socket
66              
67             path to the UNIX socket of check_mk livestatus
68              
69             =item server
70              
71             uses this server for a TCP connection
72              
73             =item peer
74              
75             alternative way to set socket or server, if value contains ':' server is used,
76             else socket
77              
78             =item name
79              
80             human readable name for this connection, defaults to the the socket/server
81             address
82              
83             =item verbose
84              
85             verbose mode
86              
87             =item line_separator
88              
89             ascii code of the line separator, defaults to 10, (newline)
90              
91             =item column_separator
92              
93             ascii code of the column separator, defaults to 0 (null byte)
94              
95             =item list_separator
96              
97             ascii code of the list separator, defaults to 44 (comma)
98              
99             =item host_service_separator
100              
101             ascii code of the host/service separator, defaults to 124 (pipe)
102              
103             =item keepalive
104              
105             enable keepalive. Default is off
106              
107             =item errors_are_fatal
108              
109             errors will die with an error message. Default: on
110              
111             =item warnings
112              
113             show warnings
114             currently only querys without Columns: Header will result in a warning
115              
116             =item timeout
117              
118             set a general timeout. Used for connect and querys, no default
119              
120             =item query_timeout
121              
122             set a query timeout. Used for retrieving querys, Default 60sec
123              
124             =item connect_timeout
125              
126             set a connect timeout. Used for initial connections, default 5sec
127              
128             =back
129              
130             If the constructor is only passed a single argument, it is assumed to
131             be a the C specification. Use either socker OR server.
132              
133             =cut
134              
135             sub new {
136 24     24 1 2994 my($class,@args) = @_;
137 24 100       62 unshift(@args, 'peer') if scalar @args == 1;
138 24         67 my(%options) = @args;
139              
140 24         220 my $self = {
141             'verbose' => 0, # enable verbose output
142             'socket' => undef, # use unix sockets
143             'server' => undef, # use tcp connections
144             'peer' => undef, # use for socket / server connections
145             'name' => undef, # human readable name
146             'line_separator' => 10, # defaults to newline
147             'column_separator' => 0, # defaults to null byte
148             'list_separator' => 44, # defaults to comma
149             'host_service_separator' => 124, # defaults to pipe
150             'keepalive' => 0, # enable keepalive?
151             'errors_are_fatal' => 1, # die on errors
152             'backend' => undef, # should be keept undef, used internally
153             'timeout' => undef, # timeout for tcp connections
154             'query_timeout' => 60, # query timeout for tcp connections
155             'connect_timeout' => 5, # connect timeout for tcp connections
156             'warnings' => 1, # show warnings, for example on querys without Column: Header
157             'logger' => undef, # logger object used for statistical informations and errors / warnings
158             'deepcopy' => undef, # copy result set to avoid errors with tied structures
159             'retries_on_connection_error' => 3, # retry x times to connect
160             'retry_interval' => 1, # retry after x seconds
161             };
162              
163 24         76 my %old_key = (
164             line_seperator => 'line_separator',
165             column_seperator => 'column_separator',
166             list_seperator => 'list_separator',
167             host_service_seperator => 'host_service_separator',
168             );
169              
170             # previous versions had spelling errors in the key name
171 24         53 for my $opt_key (keys %old_key) {
172 96 50       192 if(exists $options{$opt_key}) {
173 0         0 my $value = $options{$opt_key};
174 0         0 $options{ $old_key{$opt_key} } = $value;
175 0         0 delete $options{$opt_key};
176             }
177             }
178              
179 24         53 for my $opt_key (keys %options) {
180 93 50       128 if(exists $self->{$opt_key}) {
181 93         135 $self->{$opt_key} = $options{$opt_key};
182             }
183             else {
184 0         0 croak("unknown option: $opt_key");
185             }
186             }
187              
188 24 50 33     60 if($self->{'verbose'} && !defined $self->{'logger'}) {
189 0         0 croak('please specify a logger object when using verbose mode');
190             }
191              
192             # setting a general timeout?
193 24 100       51 if(defined $self->{'timeout'}) {
194 2         4 $self->{'query_timeout'} = $self->{'timeout'};
195 2         3 $self->{'connect_timeout'} = $self->{'timeout'};
196             }
197              
198 24         38 bless $self, $class;
199              
200             # set our peer(s) from the options
201 24         46 my $peer = $self->_get_peer();
202              
203 24 100       57 if(!defined $self->{'backend'}) {
204 10         17 $options{'name'} = $peer->{'name'};
205 10         14 $options{'peer'} = $peer->{'peer'};
206 10 100       23 if($peer->{'type'} eq 'UNIX') {
    50          
207 8         35 $self->{'CONNECTOR'} = Monitoring::Livestatus::UNIX->new(%options);
208             }
209             elsif($peer->{'type'} eq 'INET') {
210 2         17 $self->{'CONNECTOR'} = Monitoring::Livestatus::INET->new(%options);
211             }
212 10         18 $self->{'peer'} = $peer->{'peer'};
213             }
214              
215             # set names and peer for non multi backends
216 24 100 66     88 if(defined $self->{'CONNECTOR'}->{'name'} && !defined $self->{'name'}) {
217 10         15 $self->{'name'} = $self->{'CONNECTOR'}->{'name'};
218             }
219 24 50 66     56 if(defined $self->{'CONNECTOR'}->{'peer'} && !defined $self->{'peer'}) {
220 0         0 $self->{'peer'} = $self->{'CONNECTOR'}->{'peer'};
221             }
222              
223 24         126 return $self;
224             }
225              
226              
227             ########################################
228              
229             =head1 METHODS
230              
231             =head2 do
232              
233             do($statement)
234             do($statement, %opts)
235              
236             Send a single statement without fetching the result.
237             Always returns true.
238              
239             =cut
240              
241             sub do {
242 0     0 1 0 my($self, $statement, $opt) = @_;
243 0         0 $self->_send($statement, $opt);
244 0         0 return(1);
245             }
246              
247              
248             ########################################
249              
250             =head2 selectall_arrayref
251              
252             selectall_arrayref($statement)
253             selectall_arrayref($statement, %opts)
254             selectall_arrayref($statement, %opts, $limit )
255              
256             Sends a query and returns an array reference of arrays
257              
258             my $arr_refs = $ml->selectall_arrayref("GET hosts");
259              
260             to get an array of hash references do something like
261              
262             my $hash_refs = $ml->selectall_arrayref(
263             "GET hosts", { Slice => {} }
264             );
265              
266             to get an array of hash references from the first 2 returned rows only
267              
268             my $hash_refs = $ml->selectall_arrayref(
269             "GET hosts", { Slice => {} }, 2
270             );
271              
272             you may use limit to limit the result to this number of rows
273              
274             column aliases can be defined with a rename hash
275              
276             my $hash_refs = $ml->selectall_arrayref(
277             "GET hosts", {
278             Slice => {},
279             rename => {
280             'name' => 'host_name'
281             }
282             }
283             );
284              
285             =cut
286              
287             sub selectall_arrayref {
288 0     0 1 0 my($self, $statement, $opt, $limit, $result) = @_;
289 0 0       0 $limit = 0 unless defined $limit;
290              
291             # make opt hash keys lowercase
292 0 0       0 $opt = &_lowercase_and_verify_options($self, $opt) unless $result;
293              
294 0 0 0     0 $self->_log_statement($statement, $opt, $limit) if !$result && $self->{'verbose'};
295              
296 0 0       0 if(!defined $result) {
297 0         0 $result = &_send($self, $statement, $opt);
298 0 0       0 return $result if $ENV{'THRUK_SELECT'};
299              
300 0 0       0 if(!defined $result) {
301 0 0       0 return unless $self->{'errors_are_fatal'};
302 0         0 croak("got undef result for: $statement");
303             }
304             }
305              
306             # trim result set down to excepted row count
307 0 0 0     0 if(!$opt->{'offset'} && defined $limit && $limit >= 1) {
      0        
308 0 0       0 if(scalar @{$result->{'result'}} > $limit) {
  0         0  
309 0         0 @{$result->{'result'}} = @{$result->{'result'}}[0..$limit-1];
  0         0  
  0         0  
310             }
311             }
312              
313 0 0       0 if($opt->{'slice'}) {
314 0         0 my $callbacks = $opt->{'callbacks'};
315             # make an array of hashes, inplace to safe memory
316 0         0 my $keys = $result->{'keys'};
317             # renamed columns
318 0 0       0 if($opt->{'rename'}) {
319 0         0 $keys = dclone($result->{'keys'});
320 0         0 my $keysize = scalar @{$keys};
  0         0  
321 0         0 for(my $x=0; $x<$keysize;$x++) {
322 0         0 my $old = $keys->[$x];
323 0 0       0 if($opt->{'rename'}->{$old}) {
324 0         0 $keys->[$x] = $opt->{'rename'}->{$old};
325             }
326             }
327             }
328 0         0 $result = $result->{'result'};
329 0         0 my $rnum = scalar @{$result};
  0         0  
330 0         0 for(my $x=0;$x<$rnum;$x++) {
331             # sort array into hash slices
332 0         0 my %hash;
333 0         0 @hash{@{$keys}} = @{$result->[$x]};
  0         0  
  0         0  
334             # add callbacks
335 0 0       0 if($callbacks) {
336 0         0 for my $key (keys %{$callbacks}) {
  0         0  
337 0         0 $hash{$key} = $callbacks->{$key}->(\%hash);
338             }
339             }
340 0         0 $result->[$x] = \%hash;
341             }
342 0         0 return($result);
343             }
344              
345 0 0       0 if(exists $opt->{'callbacks'}) {
346 0         0 for my $res (@{$result->{'result'}}) {
  0         0  
347             # add callbacks
348 0 0       0 if(exists $opt->{'callbacks'}) {
349 0         0 for my $key (keys %{$opt->{'callbacks'}}) {
  0         0  
350 0         0 push @{$res}, $opt->{'callbacks'}->{$key}->($res);
  0         0  
351             }
352             }
353             }
354              
355 0         0 for my $key (keys %{$opt->{'callbacks'}}) {
  0         0  
356 0         0 push @{$result->{'keys'}}, $key;
  0         0  
357             }
358             }
359 0         0 return($result->{'result'});
360             }
361              
362              
363             ########################################
364              
365             =head2 selectall_hashref
366              
367             selectall_hashref($statement, $key_field)
368             selectall_hashref($statement, $key_field, %opts)
369              
370             Sends a query and returns a hashref with the given key
371              
372             my $hashrefs = $ml->selectall_hashref("GET hosts", "name");
373              
374             =cut
375              
376             sub selectall_hashref {
377 0     0 1 0 my($self, $statement, $key_field, $opt) = @_;
378              
379 0         0 $opt = &_lowercase_and_verify_options($self, $opt);
380              
381 0         0 $opt->{'slice'} = 1;
382              
383 0 0       0 croak('key is required for selectall_hashref') if !defined $key_field;
384              
385 0         0 my $result = $self->selectall_arrayref($statement, $opt);
386              
387 0         0 my %indexed;
388 0         0 for my $row (@{$result}) {
  0         0  
389 0 0       0 if($key_field eq '$peername') {
    0          
390 0         0 $indexed{$self->peer_name} = $row;
391             }
392             elsif(!defined $row->{$key_field}) {
393 0         0 my %possible_keys = keys %{$row};
  0         0  
394 0         0 croak("key $key_field not found in result set, possible keys are: ".join(', ', sort keys %possible_keys));
395             } else {
396 0         0 $indexed{$row->{$key_field}} = $row;
397             }
398             }
399 0         0 return(\%indexed);
400             }
401              
402              
403             ########################################
404              
405             =head2 selectcol_arrayref
406              
407             selectcol_arrayref($statement)
408             selectcol_arrayref($statement, %opt )
409              
410             Sends a query an returns an arrayref for the first columns
411              
412             my $array_ref = $ml->selectcol_arrayref("GET hosts\nColumns: name");
413              
414             $VAR1 = [
415             'localhost',
416             'gateway',
417             ];
418              
419             returns an empty array if nothing was found
420              
421             to get a different column use this
422              
423             my $array_ref = $ml->selectcol_arrayref(
424             "GET hosts\nColumns: name contacts",
425             { Columns => [2] }
426             );
427              
428             you can link 2 columns in a hash result set
429              
430             my %hash = @{
431             $ml->selectcol_arrayref(
432             "GET hosts\nColumns: name contacts",
433             { Columns => [1,2] }
434             )
435             };
436              
437             produces a hash with host the contact assosiation
438              
439             $VAR1 = {
440             'localhost' => 'user1',
441             'gateway' => 'user2'
442             };
443              
444             =cut
445              
446             sub selectcol_arrayref {
447 0     0 1 0 my($self, $statement, $opt) = @_;
448              
449             # make opt hash keys lowercase
450 0         0 $opt = &_lowercase_and_verify_options($self, $opt);
451              
452             # if now colums are set, use just the first one
453 0 0 0     0 if(!defined $opt->{'columns'} || ref $opt->{'columns'} ne 'ARRAY') {
454 0         0 @{$opt->{'columns'}} = qw{1};
  0         0  
455             }
456              
457 0         0 my $result = $self->selectall_arrayref($statement);
458              
459 0         0 my @column;
460 0         0 for my $row (@{$result}) {
  0         0  
461 0         0 for my $nr (@{$opt->{'columns'}}) {
  0         0  
462 0         0 push @column, $row->[$nr-1];
463             }
464             }
465 0         0 return(\@column);
466             }
467              
468              
469             ########################################
470              
471             =head2 selectrow_array
472              
473             selectrow_array($statement)
474             selectrow_array($statement, %opts)
475              
476             Sends a query and returns an array for the first row
477              
478             my @array = $ml->selectrow_array("GET hosts");
479              
480             returns undef if nothing was found
481              
482             =cut
483             sub selectrow_array {
484 0     0 1 0 my($self, $statement, $opt) = @_;
485              
486             # make opt hash keys lowercase
487 0         0 $opt = &_lowercase_and_verify_options($self, $opt);
488              
489 0         0 my @result = @{$self->selectall_arrayref($statement, $opt, 1)};
  0         0  
490 0 0       0 return @{$result[0]} if scalar @result > 0;
  0         0  
491 0         0 return;
492             }
493              
494              
495             ########################################
496              
497             =head2 selectrow_arrayref
498              
499             selectrow_arrayref($statement)
500             selectrow_arrayref($statement, %opts)
501              
502             Sends a query and returns an array reference for the first row
503              
504             my $arrayref = $ml->selectrow_arrayref("GET hosts");
505              
506             returns undef if nothing was found
507              
508             =cut
509             sub selectrow_arrayref {
510 0     0 1 0 my($self, $statement, $opt) = @_;
511              
512             # make opt hash keys lowercase
513 0         0 $opt = &_lowercase_and_verify_options($self, $opt);
514              
515 0         0 my $result = $self->selectall_arrayref($statement, $opt, 1);
516 0 0       0 return if !defined $result;
517 0 0       0 return $result->[0] if scalar @{$result} > 0;
  0         0  
518 0         0 return;
519             }
520              
521              
522             ########################################
523              
524             =head2 selectrow_hashref
525              
526             selectrow_hashref($statement)
527             selectrow_hashref($statement, %opt)
528              
529             Sends a query and returns a hash reference for the first row
530              
531             my $hashref = $ml->selectrow_hashref("GET hosts");
532              
533             returns undef if nothing was found
534              
535             =cut
536             sub selectrow_hashref {
537 0     0 1 0 my($self, $statement, $opt) = @_;
538              
539             # make opt hash keys lowercase
540 0         0 $opt = &_lowercase_and_verify_options($self, $opt);
541 0         0 $opt->{slice} = 1;
542              
543 0         0 my $result = $self->selectall_arrayref($statement, $opt, 1);
544 0 0       0 return if !defined $result;
545 0 0       0 return $result->[0] if scalar @{$result} > 0;
  0         0  
546 0         0 return;
547             }
548              
549              
550             ########################################
551              
552             =head2 selectscalar_value
553              
554             selectscalar_value($statement)
555             selectscalar_value($statement, %opt)
556              
557             Sends a query and returns a single scalar
558              
559             my $count = $ml->selectscalar_value("GET hosts\nStats: state = 0");
560              
561             returns undef if nothing was found
562              
563             =cut
564             sub selectscalar_value {
565 0     0 1 0 my($self, $statement, $opt) = @_;
566              
567             # make opt hash keys lowercase
568 0         0 $opt = &_lowercase_and_verify_options($self, $opt);
569              
570 0         0 my $row = $self->selectrow_arrayref($statement);
571 0 0       0 return if !defined $row;
572 0 0       0 return $row->[0] if scalar @{$row} > 0;
  0         0  
573 0         0 return;
574             }
575              
576             ########################################
577              
578             =head2 errors_are_fatal
579              
580             errors_are_fatal()
581             errors_are_fatal($value)
582              
583             Enable or disable fatal errors. When enabled the module will croak on any error.
584              
585             returns the current setting if called without new value
586              
587             =cut
588             sub errors_are_fatal {
589 0     0 1 0 my($self, $value) = @_;
590 0         0 my $old = $self->{'errors_are_fatal'};
591              
592 0         0 $self->{'errors_are_fatal'} = $value;
593 0 0       0 $self->{'CONNECTOR'}->{'errors_are_fatal'} = $value if defined $self->{'CONNECTOR'};
594              
595 0         0 return $old;
596             }
597              
598             ########################################
599              
600             =head2 warnings
601              
602             warnings()
603             warnings($value)
604              
605             Enable or disable warnings. When enabled the module will carp on warnings.
606              
607             returns the current setting if called without new value
608              
609             =cut
610             sub warnings {
611 0     0 1 0 my($self, $value) = @_;
612 0         0 my $old = $self->{'warnings'};
613              
614 0         0 $self->{'warnings'} = $value;
615 0 0       0 $self->{'CONNECTOR'}->{'warnings'} = $value if defined $self->{'CONNECTOR'};
616              
617 0         0 return $old;
618             }
619              
620              
621              
622             ########################################
623              
624             =head2 verbose
625              
626             verbose()
627             verbose($values)
628              
629             Enable or disable verbose output. When enabled the module will dump out debug output
630              
631             returns the current setting if called without new value
632              
633             =cut
634             sub verbose {
635 0     0 1 0 my($self, $value) = @_;
636 0         0 my $old = $self->{'verbose'};
637              
638 0         0 $self->{'verbose'} = $value;
639 0 0       0 $self->{'CONNECTOR'}->{'verbose'} = $value if defined $self->{'CONNECTOR'};
640              
641 0         0 return $old;
642             }
643              
644              
645             ########################################
646              
647             =head2 peer_addr
648              
649             $ml->peer_addr()
650              
651             returns the current peer address
652              
653             when using multiple backends, a list of all addresses is returned in list context
654              
655             =cut
656             sub peer_addr {
657 9     9 1 15 my($self) = @_;
658 9         40 return ''.$self->{'peer'};
659             }
660              
661              
662             ########################################
663              
664             =head2 peer_name
665              
666             $ml->peer_name()
667             $ml->peer_name($string)
668              
669             if new value is set, name is set to this value
670              
671             always returns the current peer name
672              
673             when using multiple backends, a list of all names is returned in list context
674              
675             =cut
676             sub peer_name {
677 9     9 1 3260 my($self, $value) = @_;
678              
679 9 50 33     26 if(defined $value and $value ne '') {
680 0         0 $self->{'name'} = $value;
681             }
682              
683 9         49 return ''.$self->{'name'};
684             }
685              
686              
687             ########################################
688              
689             =head2 peer_key
690              
691             $ml->peer_key()
692              
693             returns a uniq key for this peer
694              
695             when using multiple backends, a list of all keys is returned in list context
696              
697             =cut
698             sub peer_key {
699 0     0 1 0 my($self) = @_;
700              
701 0 0       0 if(!defined $self->{'key'}) { $self->{'key'} = md5_hex($self->peer_addr.' '.$self->peer_name); }
  0         0  
702              
703 0         0 return $self->{'key'};
704             }
705              
706             ########################################
707             # INTERNAL SUBS
708             ########################################
709             sub _send {
710 0     0   0 my($self, $statement, $opt) = @_;
711              
712 0 0       0 confess('duplicate data') if $opt->{'data'};
713              
714 0         0 delete $self->{'meta_data'};
715              
716 0         0 my $header = '';
717 0         0 my $keys;
718              
719 0         0 $Monitoring::Livestatus::ErrorCode = 0;
720 0         0 undef $Monitoring::Livestatus::ErrorMessage;
721              
722 0 0       0 return(490, $self->_get_error(490), undef) if !defined $statement;
723 0         0 chomp($statement);
724              
725 0         0 my($status,$msg,$body);
726 0 0 0     0 if($statement =~ m/^Separators:/mx) {
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
727 0         0 $status = 492;
728 0         0 $msg = $self->_get_error($status);
729             }
730              
731             elsif($statement =~ m/^KeepAlive:/mx) {
732 0         0 $status = 496;
733 0         0 $msg = $self->_get_error($status);
734             }
735              
736             elsif($statement =~ m/^ResponseHeader:/mx) {
737 0         0 $status = 495;
738 0         0 $msg = $self->_get_error($status);
739             }
740              
741             elsif($statement =~ m/^ColumnHeaders:/mx) {
742 0         0 $status = 494;
743 0         0 $msg = $self->_get_error($status);
744             }
745              
746             elsif($statement =~ m/^OuputFormat:/mx) {
747 0         0 $status = 493;
748 0         0 $msg = $self->_get_error($status);
749             }
750              
751             # should be cought in mlivestatus directly
752             elsif($statement =~ m/^Limit:\ (.*)$/mx and $1 !~ m/^\d+$/mx) {
753 0         0 $status = 403;
754 0         0 $msg = $self->_get_error($status);
755             }
756             elsif($statement =~ m/^GET\ (.*)$/mx and $1 =~ m/^\s*$/mx) {
757 0         0 $status = 403;
758 0         0 $msg = $self->_get_error($status);
759             }
760              
761             elsif($statement =~ m/^Columns:\ (.*)$/mx and ($1 =~ m/,/mx or $1 =~ /^\s*$/mx)) {
762 0         0 $status = 405;
763 0         0 $msg = $self->_get_error($status);
764             }
765             elsif($statement !~ m/^GET\ /mx and $statement !~ m/^COMMAND\ /mx) {
766 0         0 $status = 401;
767 0         0 $msg = $self->_get_error($status);
768             }
769              
770             else {
771              
772             # Add Limits header
773 0 0       0 if(defined $opt->{'limit_start'}) {
774 0         0 $statement .= "\nLimit: ".($opt->{'limit_start'} + $opt->{'limit_length'});
775             }
776              
777             # for querys with column header, no seperate columns will be returned
778 0 0 0     0 if($statement =~ m/^Columns:\ (.*)$/mx) {
    0          
779 0         0 ($statement,$keys) = $self->_extract_keys_from_columns_header($statement);
780             } elsif($statement =~ m/^Stats:\ (.*)$/mx or $statement =~ m/^StatsGroupBy:\ (.*)$/mx) {
781 0         0 ($statement,$keys) = extract_keys_from_stats_statement($statement);
782             }
783              
784             # Offset header (currently naemon only)
785 0 0       0 if(defined $opt->{'offset'}) {
786 0         0 $statement .= "\nOffset: ".$opt->{'offset'};
787             }
788              
789             # Sort header (currently naemon only)
790 0 0       0 if(defined $opt->{'sort'}) {
791 0         0 for my $sort (@{$opt->{'sort'}}) {
  0         0  
792 0         0 $statement .= "\nSort: ".$sort;
793             }
794             }
795              
796             # Commands need no additional header
797 0 0       0 if($statement !~ m/^COMMAND/mx) {
798 0 0       0 if($opt->{'wrapped_json'}) {
799 0         0 $header .= "OutputFormat: wrapped_json\n";
800             } else {
801 0         0 $header .= "OutputFormat: json\n";
802             }
803 0         0 $header .= "ResponseHeader: fixed16\n";
804 0 0       0 if($self->{'keepalive'}) {
805 0         0 $header .= "KeepAlive: on\n";
806             }
807             # remove empty lines from statement
808 0         0 $statement =~ s/\n+/\n/gmx;
809             }
810              
811             # add additional headers
812 0 0 0     0 if(defined $opt->{'header'} and ref $opt->{'header'} eq 'HASH') {
813 0         0 for my $key ( keys %{$opt->{'header'}}) {
  0         0  
814 0         0 $header .= $key.': '.$opt->{'header'}->{$key}."\n";
815             }
816             }
817              
818 0         0 chomp($statement);
819 0         0 my $send = "$statement\n$header";
820 0 0       0 $self->{'logger'}->debug('> '.Dumper($send)) if $self->{'verbose'};
821 0         0 ($status,$msg,$body) = &_send_socket($self, $send);
822 0 0       0 return([$status, $opt, $keys]) if $ENV{'THRUK_SELECT'};
823 0 0       0 if($self->{'verbose'}) {
824             #$self->{'logger'}->debug("got:");
825             #$self->{'logger'}->debug(Dumper(\@erg));
826 0         0 $self->{'logger'}->debug('status: '.Dumper($status));
827 0         0 $self->{'logger'}->debug('msg: '.Dumper($msg));
828 0         0 $self->{'logger'}->debug('< '.Dumper($body));
829             }
830             }
831              
832 0 0 0     0 if(!$status || $status >= 300) {
833 0 0       0 $body = '' if !defined $body;
834 0 0       0 $status = 300 if !defined $status;
835 0         0 chomp($body);
836 0         0 $Monitoring::Livestatus::ErrorCode = $status;
837 0 0 0     0 if(defined $body and $body ne '') {
838 0         0 $Monitoring::Livestatus::ErrorMessage = $body;
839             } else {
840 0         0 $Monitoring::Livestatus::ErrorMessage = $msg;
841             }
842 0 0       0 $self->{'logger'}->error($status.' - '.$Monitoring::Livestatus::ErrorMessage." in query:\n".$statement) if $self->{'verbose'};
843 0 0       0 if($self->{'errors_are_fatal'}) {
844 0         0 croak('ERROR '.$status.' - '.$Monitoring::Livestatus::ErrorMessage." in query:\n".$statement."\n");
845             }
846 0         0 return;
847             }
848              
849             # return a empty result set if nothing found
850 0 0       0 return({ keys => [], result => []}) if !defined $body;
851              
852 0         0 my $limit_start = 0;
853 0 0       0 if(defined $opt->{'limit_start'}) { $limit_start = $opt->{'limit_start'}; }
  0         0  
854             # body is already parsed
855 0         0 my $result;
856 0 0       0 if($status == 200) {
857 0         0 $result = $body;
858             } else {
859 0         0 my $json_decoder = Cpanel::JSON::XS->new->utf8->relaxed;
860             # fix json output
861 0         0 eval {
862 0         0 $result = $json_decoder->decode($body);
863             };
864             # fix low/high surrogate errors
865             # missing high surrogate character in surrogate pair
866             # surrogate pair expected
867 0 0       0 if($@) {
868             # replace u+D800 to u+DFFF (reserved utf-16 low/high surrogates)
869 0         0 $body =~ s/\\ud[89a-f]\w{2}/\\ufffd/gmxi;
870 0         0 eval {
871 0         0 $result = $json_decoder->decode($body);
872             };
873             }
874 0 0       0 if($@) {
875 0         0 my $message = 'ERROR '.$@." in text: '".$body."'\" for statement: '$statement'\n";
876 0 0       0 $self->{'logger'}->error($message) if $self->{'verbose'};
877 0 0       0 if($self->{'errors_are_fatal'}) {
878 0         0 croak($message);
879             }
880 0         0 return({ keys => [], result => []});
881             }
882             }
883 0 0       0 if(!defined $result) {
884 0         0 my $message = "ERROR undef result for text: '".$body."'\" for statement: '$statement'\n";
885 0 0       0 $self->{'logger'}->error($message) if $self->{'verbose'};
886 0 0       0 if($self->{'errors_are_fatal'}) {
887 0         0 croak($message);
888             }
889 0         0 return({ keys => [], result => []});
890             }
891              
892             # for querys with column header, no separate columns will be returned
893 0 0       0 if(!defined $keys) {
894 0 0       0 $self->{'logger'}->warn('got statement without Columns: header!') if $self->{'verbose'};
895 0 0       0 if($self->{'warnings'}) {
896 0         0 carp('got statement without Columns: header! -> '.$statement);
897             }
898 0         0 $keys = shift @{$result};
  0         0  
899             }
900              
901 0         0 return(&post_processing($self, $result, $opt, $keys));
902             }
903              
904             ########################################
905              
906             =head2 post_processing
907              
908             $ml->post_processing($result, $options, $keys)
909              
910             returns postprocessed result.
911              
912             Useful when using select based io.
913              
914             =cut
915             sub post_processing {
916 0     0 1 0 my($self, $result, $opt, $keys) = @_;
917              
918 0         0 my $orig_result;
919 0 0       0 if($opt->{'wrapped_json'}) {
920 0         0 $orig_result = $result;
921 0         0 $result = $result->{'data'};
922             }
923              
924             # add peer information?
925 0         0 my $with_peers = 0;
926 0 0 0     0 if(defined $opt->{'addpeer'} and $opt->{'addpeer'}) {
927 0         0 $with_peers = 1;
928             }
929              
930 0 0 0     0 if(defined $with_peers and $with_peers == 1) {
931 0         0 my $peer_name = $self->peer_name;
932 0         0 my $peer_addr = $self->peer_addr;
933 0         0 my $peer_key = $self->peer_key;
934              
935 0         0 unshift @{$keys}, 'peer_name';
  0         0  
936 0         0 unshift @{$keys}, 'peer_addr';
  0         0  
937 0         0 unshift @{$keys}, 'peer_key';
  0         0  
938              
939 0         0 for my $row (@{$result}) {
  0         0  
940 0         0 unshift @{$row}, $peer_name;
  0         0  
941 0         0 unshift @{$row}, $peer_addr;
  0         0  
942 0         0 unshift @{$row}, $peer_key;
  0         0  
943             }
944             }
945              
946             # set some metadata
947             $self->{'meta_data'} = {
948 0         0 'result_count' => scalar @{$result},
  0         0  
949             };
950 0 0       0 if($opt->{'wrapped_json'}) {
951 0         0 for my $key (keys %{$orig_result}) {
  0         0  
952 0 0       0 next if $key eq 'data';
953 0         0 $self->{'meta_data'}->{$key} = $orig_result->{$key};
954             }
955             }
956              
957 0         0 return({ keys => $keys, result => $result });
958             }
959              
960             ########################################
961             sub _open {
962 0     0   0 my($self) = @_;
963              
964             # return the current socket in keep alive mode
965 0 0 0     0 if($self->{'keepalive'} and defined $self->{'sock'} and $self->{'sock'}->connected) {
      0        
966 0 0       0 $self->{'logger'}->debug('reusing old connection') if $self->{'verbose'};
967 0         0 return($self->{'sock'});
968             }
969              
970 0         0 my $sock = $self->{'CONNECTOR'}->_open();
971              
972             # store socket for later retrieval
973 0 0       0 if($self->{'keepalive'}) {
974 0         0 $self->{'sock'} = $sock;
975             }
976              
977 0 0       0 $self->{'logger'}->debug('using new connection') if $self->{'verbose'};
978 0         0 return($sock);
979             }
980              
981             ########################################
982             sub _close {
983 0     0   0 my($self, $sock) = @_;
984 0         0 undef $self->{'sock'};
985 0         0 return($self->{'CONNECTOR'}->_close($sock));
986             }
987              
988              
989             ########################################
990              
991             =head1 QUERY OPTIONS
992              
993             In addition to the normal query syntax from the livestatus addon, it is
994             possible to set column aliases in various ways.
995              
996             =head2 AddPeer
997              
998             adds the peers name, addr and key to the result set:
999              
1000             my $hosts = $ml->selectall_hashref(
1001             "GET hosts\nColumns: name alias state",
1002             "name",
1003             { AddPeer => 1 }
1004             );
1005              
1006             =head2 Backend
1007              
1008             send the query only to some specific backends. Only
1009             useful when using multiple backends.
1010              
1011             my $hosts = $ml->selectall_arrayref(
1012             "GET hosts\nColumns: name alias state",
1013             { Backends => [ 'key1', 'key4' ] }
1014             );
1015              
1016             =head2 Columns
1017              
1018             only return the given column indexes
1019              
1020             my $array_ref = $ml->selectcol_arrayref(
1021             "GET hosts\nColumns: name contacts",
1022             { Columns => [2] }
1023             );
1024              
1025             see L for more examples
1026              
1027             =head2 Deepcopy
1028              
1029             deep copy/clone the result set.
1030              
1031             Only effective when using multiple backends and threads.
1032             This can be safely turned off if you don't change the
1033             result set.
1034             If you get an error like "Invalid value for shared scalar" error" this
1035             should be turned on.
1036              
1037             my $array_ref = $ml->selectcol_arrayref(
1038             "GET hosts\nColumns: name contacts",
1039             { Deepcopy => 1 }
1040             );
1041              
1042             =head2 Limit
1043              
1044             Just like the Limit: option from livestatus itself.
1045             In addition you can add a start,length limit.
1046              
1047             my $array_ref = $ml->selectcol_arrayref(
1048             "GET hosts\nColumns: name contacts",
1049             { Limit => "10,20" }
1050             );
1051              
1052             This example will return 20 rows starting at row 10. You will
1053             get row 10-30.
1054              
1055             Cannot be combined with a Limit inside the query
1056             because a Limit will be added automatically.
1057              
1058             Adding a limit this way will greatly increase performance and
1059             reduce memory usage.
1060              
1061             This option is multibackend safe contrary to the "Limit: " part of a statement.
1062             Sending a statement like "GET...Limit: 10" with 3 backends will result in 30 rows.
1063             Using this options, you will receive only the first 10 rows.
1064              
1065             =head2 Rename
1066              
1067             see L for detailed explainaton
1068              
1069             =head2 Slice
1070              
1071             see L for detailed explainaton
1072              
1073             =head2 Sum
1074              
1075             The Sum option only applies when using multiple backends.
1076             The values from all backends with be summed up to a total.
1077              
1078             my $stats = $ml->selectrow_hashref(
1079             "GET hosts\nStats: state = 0\nStats: state = 1",
1080             { Sum => 1 }
1081             );
1082              
1083             =cut
1084              
1085              
1086             ########################################
1087             # wrapper around _send_socket_do
1088             sub _send_socket {
1089 0     0   0 my($self, $statement) = @_;
1090              
1091 0         0 my $retries = 0;
1092 0         0 my($status, $msg, $recv, $sock);
1093              
1094             # try to avoid connection errors
1095 0         0 eval {
1096             local $SIG{PIPE} = sub {
1097 0     0   0 die('broken pipe');
1098 0         0 };
1099              
1100 0 0       0 if($self->{'retries_on_connection_error'} <= 0) {
1101 0         0 ($sock, $msg, $recv) = &_send_socket_do($self, $statement);
1102 0 0       0 return($sock, $msg, $recv) if $msg;
1103 0 0       0 return $sock if $ENV{'THRUK_SELECT'};
1104 0         0 ($status, $msg, $recv) = &_read_socket_do($self, $sock, $statement);
1105 0         0 return($status, $msg, $recv);
1106             }
1107              
1108 0   0     0 while((!defined $status || ($status == 491 || $status == 497 || $status == 500)) && $retries < $self->{'retries_on_connection_error'}) {
      0        
1109 0         0 $retries++;
1110 0         0 ($sock, $msg, $recv) = &_send_socket_do($self, $statement);
1111 0 0       0 return($status, $msg, $recv) if $msg;
1112 0 0       0 return $sock if $ENV{'THRUK_SELECT'};
1113 0         0 ($status, $msg, $recv) = &_read_socket_do($self, $sock, $statement);
1114 0 0       0 $self->{'logger'}->debug('query status '.$status) if $self->{'verbose'};
1115 0 0 0     0 if($status == 491 or $status == 497 or $status == 500) {
      0        
1116 0 0       0 $self->{'logger'}->debug('got status '.$status.' retrying in '.$self->{'retry_interval'}.' seconds') if $self->{'verbose'};
1117 0         0 $self->_close();
1118 0 0       0 sleep($self->{'retry_interval'}) if $retries < $self->{'retries_on_connection_error'};
1119             }
1120             }
1121             };
1122 0 0       0 if($@) {
1123 0 0       0 $self->{'logger'}->debug("try 1 failed: $@") if $self->{'verbose'};
1124 0 0 0     0 if(defined $@ and $@ =~ /broken\ pipe/mx) {
1125 0         0 ($sock, $msg, $recv) = &_send_socket_do($self, $statement);
1126 0 0       0 return($status, $msg, $recv) if $msg;
1127 0 0       0 return $sock if $ENV{'THRUK_SELECT'};
1128 0         0 return(&_read_socket_do($self, $sock, $statement));
1129             }
1130 0 0       0 croak($@) if $self->{'errors_are_fatal'};
1131             }
1132              
1133 0 0       0 $status = $sock unless $status;
1134 0 0       0 return $sock if $ENV{'THRUK_SELECT'};
1135 0 0 0     0 croak($msg) if($status >= 400 and $self->{'errors_are_fatal'});
1136              
1137 0         0 return($status, $msg, $recv);
1138             }
1139              
1140             ########################################
1141             sub _send_socket_do {
1142 0     0   0 my($self, $statement) = @_;
1143 0 0       0 my $sock = $self->_open() or return(491, $self->_get_error(491, $!), $!);
1144 0         0 utf8::decode($statement);
1145 0         0 utf8::encode($statement);
1146 0 0       0 print $sock $statement or return($self->_socket_error($statement, $sock, 'write to socket failed: '.$!));
1147 0         0 print $sock "\n";
1148 0         0 return $sock;
1149             }
1150              
1151             ########################################
1152             sub _read_socket_do {
1153 0     0   0 my($self, $sock, $statement) = @_;
1154 0         0 my($recv,$header);
1155              
1156             # COMMAND statements never return something
1157 0 0 0     0 if($statement && $statement =~ m/^COMMAND/mx) {
1158 0         0 return('201', $self->_get_error(201), undef);
1159             }
1160              
1161 0 0       0 $sock->read($header, 16) or return($self->_socket_error($statement, $sock, 'reading header from socket failed, check your livestatus logfile: '.$!));
1162 0 0       0 $self->{'logger'}->debug("header: $header") if $self->{'verbose'};
1163 0         0 my($status, $msg, $content_length) = &_parse_header($self, $header, $sock);
1164 0 0       0 return($status, $msg, undef) if !defined $content_length;
1165 0         0 our $json_decoder;
1166 0 0       0 if($json_decoder) {
1167 0         0 $json_decoder->incr_reset;
1168             } else {
1169 0         0 $json_decoder = Cpanel::JSON::XS->new->utf8->relaxed;
1170             }
1171 0 0       0 if($content_length > 0) {
1172 0 0       0 if($status == 200) {
1173 0         0 my $remaining = $content_length;
1174 0         0 my $length = 32768;
1175 0 0       0 if($remaining < $length) { $length = $remaining; }
  0         0  
1176 0   0     0 while($length > 0 && $sock->read(my $buf, $length)) {
1177             # replace u+D800 to u+DFFF (reserved utf-16 low/high surrogates)
1178 0         0 $buf =~ s/\\ud[89a-f]\w{2}/\\ufffd/gmxio;
1179 0         0 $json_decoder->incr_parse($buf);
1180 0         0 $remaining = $remaining -$length;
1181 0 0       0 if($remaining < $length) { $length = $remaining; }
  0         0  
1182             }
1183 0 0       0 $recv = $json_decoder->incr_parse or return($self->_socket_error($statement, $sock, 'reading body from socket failed: '.$json_decoder->incr_text.$json_decoder->incr_reset));
1184 0         0 $json_decoder->incr_reset;
1185             } else {
1186 0 0       0 $sock->read($recv, $content_length) or return($self->_socket_error($statement, $sock, 'reading body from socket failed'));
1187             }
1188             }
1189              
1190 0 0       0 $self->_close($sock) unless $self->{'keepalive'};
1191 0 0 0     0 if($status >= 400 && $recv) {
1192 0         0 $msg .= ' - '.$recv;
1193             }
1194 0         0 return($status, $msg, $recv);
1195             }
1196              
1197             ########################################
1198             sub _socket_error {
1199             #my($self, $statement, $sock, $body)...
1200 0     0   0 my($self, $statement, undef, $body) = @_;
1201              
1202 0         0 my $message = "\n";
1203 0         0 $message .= "peer ".Dumper($self->peer_name);
1204 0         0 $message .= "statement ".Dumper($statement);
1205 0         0 $message .= "message ".Dumper($body);
1206              
1207 0 0       0 $self->{'logger'}->error($message) if $self->{'verbose'};
1208              
1209 0 0       0 if($self->{'retries_on_connection_error'} <= 0) {
1210 0 0       0 if($self->{'errors_are_fatal'}) {
1211 0         0 croak($message);
1212             }
1213             else {
1214 0         0 carp($message);
1215             }
1216             }
1217 0         0 $self->_close();
1218 0         0 return(500, $self->_get_error(500), $message);
1219             }
1220              
1221             ########################################
1222             sub _parse_header {
1223 1     1   4 my($self, $header, $sock) = @_;
1224              
1225 1 50       4 if(!defined $header) {
1226 0         0 return(497, $self->_get_error(497), undef);
1227             }
1228              
1229 1         2 my $headerlength = length($header);
1230 1 50       7 if($headerlength != 16) {
1231 0         0 return(498, $self->_get_error(498)."\ngot: ".$header.<$sock>, undef);
1232             }
1233 1         4 chomp($header);
1234              
1235 1         4 my $status = substr($header,0,3);
1236 1         2 my $content_length = substr($header,5);
1237 1 50       7 if($content_length !~ m/^\s*(\d+)$/mx) {
1238 0         0 return(499, $self->_get_error(499)."\ngot: ".$header.<$sock>, undef);
1239             } else {
1240 1         4 $content_length = $1;
1241             }
1242              
1243 1         4 return($status, $self->_get_error($status), $content_length);
1244             }
1245              
1246             ########################################
1247              
1248             =head1 COLUMN ALIAS
1249              
1250             In addition to the normal query syntax from the livestatus addon, it is
1251             possible to set column aliases in various ways.
1252              
1253             A valid Columns: Header could look like this:
1254              
1255             my $hosts = $ml->selectall_arrayref(
1256             "GET hosts\nColumns: state as status"
1257             );
1258              
1259             Stats queries could be aliased too:
1260              
1261             my $stats = $ml->selectall_arrayref(
1262             "GET hosts\nStats: state = 0 as up"
1263             );
1264              
1265             This syntax is available for: Stats, StatsAnd, StatsOr and StatsGroupBy
1266              
1267              
1268             An alternative way to set column aliases is to define rename option key/value
1269             pairs:
1270              
1271             my $hosts = $ml->selectall_arrayref(
1272             "GET hosts\nColumns: name", {
1273             rename => { 'name' => 'hostname' }
1274             }
1275             );
1276              
1277             =cut
1278              
1279             ########################################
1280              
1281             =head2 extract_keys_from_stats_statement
1282              
1283             extract_keys_from_stats_statement($statement)
1284              
1285             Extract column keys from statement.
1286              
1287             =cut
1288             sub extract_keys_from_stats_statement {
1289 2     2 1 1297 my($statement) = @_;
1290              
1291 2         5 my(@header, $new_statement);
1292              
1293 2         12 for my $line (split/\n/mx, $statement) {
1294 44 100       85 if(substr($line, 0, 5) ne 'Stats') { # faster shortcut for non-stats lines
1295 2         5 $new_statement .= $line."\n";
1296 2         4 next;
1297             }
1298 42 100       144 if($line =~ m/^Stats:\ (.*)\s+as\s+(.*?)$/mxo) {
    100          
    100          
    100          
    100          
    50          
    0          
    0          
1299 5         11 push @header, $2;
1300 5         38 $line = 'Stats: '.$1;
1301             }
1302             elsif($line =~ m/^Stats:\ (.*)$/mxo) {
1303 27         57 push @header, $1;
1304             }
1305              
1306             elsif($line =~ m/^StatsAnd:\ (\d+)\s+as\s+(.*?)$/mxo) {
1307 4         10 for(my $x = 0; $x < $1; $x++) {
1308 9         34 pop @header;
1309             }
1310 4         5 $line = 'StatsAnd: '.$1;
1311 4         6 push @header, $2;
1312             }
1313             elsif($line =~ m/^StatsAnd:\ (\d+)$/mxo) {
1314 4         5 my @to_join;
1315 4         12 for(my $x = 0; $x < $1; $x++) {
1316 9         21 unshift @to_join, pop @header;
1317             }
1318 4         10 push @header, join(' && ', @to_join);
1319             }
1320              
1321             elsif($line =~ m/^StatsOr:\ (\d+)\s+as\s+(.*?)$/mxo) {
1322 1         5 for(my $x = 0; $x < $1; $x++) {
1323 2         5 pop @header;
1324             }
1325 1         2 $line = 'StatsOr: '.$1;
1326 1         2 push @header, $2;
1327             }
1328             elsif($line =~ m/^StatsOr:\ (\d+)$/mxo) {
1329 1         3 my @to_join;
1330 1         12 for(my $x = 0; $x < $1; $x++) {
1331 2         8 unshift @to_join, pop @header;
1332             }
1333 1         5 push @header, join(' || ', @to_join);
1334             }
1335              
1336             # StatsGroupBy header are always sent first
1337             elsif($line =~ m/^StatsGroupBy:\ (.*)\s+as\s+(.*?)$/mxo) {
1338 0         0 unshift @header, $2;
1339 0         0 $line = 'StatsGroupBy: '.$1;
1340             }
1341             elsif($line =~ m/^StatsGroupBy:\ (.*)$/mxo) {
1342 0         0 unshift @header, $1;
1343             }
1344 42         72 $new_statement .= $line."\n";
1345             }
1346              
1347 2         11 return($new_statement, \@header);
1348             }
1349              
1350             ########################################
1351             sub _extract_keys_from_columns_header {
1352 1     1   869 my($self, $statement) = @_;
1353              
1354 1         2 my(@header, $new_statement);
1355 1         5 for my $line (split/\n/mx, $statement) {
1356 2 100       9 if($line =~ m/^Columns:\s+(.*)$/mx) {
1357 1         7 for my $column (split/\s+/mx, $1) {
1358 8 100       12 if($column eq 'as') {
1359 2         4 pop @header;
1360             } else {
1361 6         9 push @header, $column;
1362             }
1363             }
1364 1         7 $line =~ s/\s+as\s+([^\s]+)/\ /gmx;
1365             }
1366 2         6 $new_statement .= $line."\n";
1367             }
1368              
1369 1         5 return($new_statement, \@header);
1370             }
1371              
1372             ########################################
1373              
1374             =head1 ERROR HANDLING
1375              
1376             Errorhandling can be done like this:
1377              
1378             use Monitoring::Livestatus;
1379             my $ml = Monitoring::Livestatus->new(
1380             socket => '/var/lib/livestatus/livestatus.sock'
1381             );
1382             $ml->errors_are_fatal(0);
1383             my $hosts = $ml->selectall_arrayref("GET hosts");
1384             if($Monitoring::Livestatus::ErrorCode) {
1385             croak($Monitoring::Livestatus::ErrorMessage);
1386             }
1387              
1388             =cut
1389             sub _get_error {
1390 1     1   3 my($self, $code, $append) = @_;
1391              
1392 1         18 my $codes = {
1393             '200' => 'OK. Reponse contains the queried data.',
1394             '201' => 'COMMANDs never return something',
1395             '400' => 'The request contains an invalid header.',
1396             '401' => 'The request contains an invalid header.',
1397             '402' => 'The request is completely invalid.',
1398             '403' => 'The request is incomplete.',
1399             '404' => 'The target of the GET has not been found (e.g. the table).',
1400             '405' => 'A non-existing column was being referred to',
1401             '452' => 'internal livestatus error',
1402             '490' => 'no query',
1403             '491' => 'failed to connect',
1404             '492' => 'Separators not allowed in statement. Please use the separator options in new()',
1405             '493' => 'OuputFormat not allowed in statement. Header will be set automatically',
1406             '494' => 'ColumnHeaders not allowed in statement. Header will be set automatically',
1407             '495' => 'ResponseHeader not allowed in statement. Header will be set automatically',
1408             '496' => 'Keepalive not allowed in statement. Please use the keepalive option in new()',
1409             '497' => 'got no header',
1410             '498' => 'header is not exactly 16byte long',
1411             '499' => 'not a valid header (no content-length)',
1412             '500' => 'socket error',
1413             };
1414              
1415 1 50       4 confess('non existant error code: '.$code) if !defined $codes->{$code};
1416 1         2 my $msg = $codes->{$code};
1417 1 50       3 $msg .= ' - '.$append if $append;
1418              
1419 1         6 return($msg);
1420             }
1421              
1422             ########################################
1423             sub _get_peer {
1424 24     24   39 my($self) = @_;
1425              
1426             # check if the supplied peer is a socket or a server address
1427 24 100       68 if(defined $self->{'peer'}) {
1428 21 100       50 if(ref $self->{'peer'} eq '') {
    50          
    0          
1429 17   66     53 my $name = $self->{'name'} || ''.$self->{'peer'};
1430 17 100       38 if(index($self->{'peer'}, ':') > 0) {
1431 5         20 return({ 'peer' => ''.$self->{'peer'}, type => 'INET', name => $name });
1432             } else {
1433 12         42 return({ 'peer' => ''.$self->{'peer'}, type => 'UNIX', name => $name });
1434             }
1435             }
1436             elsif(ref $self->{'peer'} eq 'ARRAY') {
1437 4         6 for my $peer (@{$self->{'peer'}}) {
  4         7  
1438 4 50       7 if(ref $peer eq 'HASH') {
1439 0 0       0 next if !defined $peer->{'peer'};
1440 0 0       0 $peer->{'name'} = ''.$peer->{'peer'} unless defined $peer->{'name'};
1441 0 0       0 if(!defined $peer->{'type'}) {
1442 0         0 $peer->{'type'} = 'UNIX';
1443 0 0       0 if(index($peer->{'peer'}, ':') >= 0) {
1444 0         0 $peer->{'type'} = 'INET';
1445             }
1446             }
1447 0         0 return $peer;
1448             } else {
1449 4         5 my $type = 'UNIX';
1450 4 50       11 if(index($peer, ':') >= 0) {
1451 0         0 $type = 'INET';
1452             }
1453 4         15 return({ 'peer' => ''.$peer, type => $type, name => ''.$peer });
1454             }
1455             }
1456             }
1457             elsif(ref $self->{'peer'} eq 'HASH') {
1458 0         0 for my $peer (keys %{$self->{'peer'}}) {
  0         0  
1459 0         0 my $name = $self->{'peer'}->{$peer};
1460 0         0 my $type = 'UNIX';
1461 0 0       0 if(index($peer, ':') >= 0) {
1462 0         0 $type = 'INET';
1463             }
1464 0         0 return({ 'peer' => ''.$peer, type => $type, name => ''.$name });
1465             }
1466             } else {
1467 0         0 confess('type '.(ref $self->{'peer'}).' is not supported for peer option');
1468             }
1469             }
1470 3 100       12 if(defined $self->{'socket'}) {
1471 2   33     14 my $name = $self->{'name'} || ''.$self->{'socket'};
1472 2         9 return({ 'peer' => ''.$self->{'socket'}, type => 'UNIX', name => $name });
1473             }
1474 1 50       4 if(defined $self->{'server'}) {
1475 1   33     7 my $name = $self->{'name'} || ''.$self->{'server'};
1476 1         5 return({ 'peer' => ''.$self->{'server'}, type => 'INET', name => $name });
1477             }
1478              
1479             # check if we got a peer
1480 0           croak('please specify a peer');
1481             }
1482              
1483              
1484             ########################################
1485             sub _lowercase_and_verify_options {
1486 0     0     my($self, $opts) = @_;
1487 0           my $return = {};
1488              
1489             # make keys lowercase
1490 0           %{$return} = map { lc($_) => $opts->{$_} } keys %{$opts};
  0            
  0            
  0            
1491              
1492 0 0         if($self->{'warnings'}) {
1493 0           for my $key (keys %{$return}) {
  0            
1494 0 0         if(!defined $allowed_options->{$key}) {
1495 0           carp("unknown option used: $key - please use only: ".join(', ', keys %{$allowed_options}));
  0            
1496             }
1497             }
1498             }
1499              
1500             # set limits
1501 0 0         if(defined $return->{'limit'}) {
1502 0 0         if(index($return->{'limit'}, ',') != -1) {
1503 0           my($limit_start,$limit_length) = split /,/mx, $return->{'limit'};
1504 0           $return->{'limit_start'} = $limit_start;
1505 0           $return->{'limit_length'} = $limit_length;
1506             }
1507             else {
1508 0           $return->{'limit_start'} = 0;
1509 0           $return->{'limit_length'} = $return->{'limit'};
1510             }
1511 0           delete $return->{'limit'};
1512             }
1513              
1514 0           return($return);
1515             }
1516              
1517             ########################################
1518             sub _log_statement {
1519 0     0     my($self, $statement, $opt, $limit) = @_;
1520 0           my $d = Data::Dumper->new([$opt]);
1521 0           $d->Indent(0);
1522 0           my $optstring = $d->Dump;
1523 0           $optstring =~ s/^\$VAR1\s+=\s+//mx;
1524 0           $optstring =~ s/;$//mx;
1525              
1526             # remove empty lines from statement
1527 0           $statement =~ s/\n+/\n/gmx;
1528              
1529 0           my $cleanstatement = $statement;
1530 0           $cleanstatement =~ s/\n/\\n/gmx;
1531 0           $self->{'logger'}->debug('selectall_arrayref("'.$cleanstatement.'", '.$optstring.', '.$limit.')');
1532 0           return 1;
1533             }
1534              
1535             ########################################
1536              
1537             1;
1538              
1539             =head1 SEE ALSO
1540              
1541             For more information about the query syntax and the livestatus plugin installation
1542             see the Livestatus page: http://mathias-kettner.de/checkmk_livestatus.html
1543              
1544             =head1 AUTHOR
1545              
1546             Sven Nierlein, 2009-present,
1547              
1548             =head1 COPYRIGHT AND LICENSE
1549              
1550             Copyright (C) by Sven Nierlein
1551              
1552             This library is free software; you can redistribute it and/or modify
1553             it under the same terms as Perl itself.
1554              
1555             =cut
1556              
1557             __END__