File Coverage

blib/lib/Monitoring/Livestatus.pm
Criterion Covered Total %
statement 135 569 23.7
branch 50 316 15.8
condition 10 108 9.2
subroutine 18 40 45.0
pod 17 17 100.0
total 230 1050 21.9


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