File Coverage

blib/lib/Nagios/NRPE/Daemon.pm
Criterion Covered Total %
statement 23 61 37.7
branch 0 6 0.0
condition 0 12 0.0
subroutine 8 13 61.5
pod 4 4 100.0
total 35 96 36.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             Nagios::NRPE::Daemon - A Nagios NRPE Daemon
6              
7             =head1 SYNOPSIS
8              
9             use Nagios::NRPE::Daemon;
10             use IPC::Cmd qw(can_run run run_forked);
11              
12             # create the commandlist we accept
13             my $commandlist =
14             };
15             my $callback = sub {
16             my ($self,$check,@options) = @_;
17             my $commandlist = $self->commandlist();
18             if ($commandlist->{$check}) {
19             my $args = $commandlist->{$check}->{args};
20             my $i = 0;
21             foreach (@options) {
22             $i++;
23             $args =~ "s/\$ARG$i\$/$_/";
24             }
25             my $buffer;
26             if (scalar run(command => $commandlist->{$check}->{bin} . " " . $args,
27             verbose => 0,
28             buffer => \$buffer,
29             timeout => 20)) {
30             return $buffer;
31             }
32             }
33             };
34              
35             my $daemon = Nagios::NRPE::Daemon->new(
36             listen => "127.0.0.1",
37             port => "5666",
38             pid_dir => '/var/run',
39             ssl => 0,
40             commandlist => {
41             "check_cpu" => { bin => "/usr/lib/nagios/plugin/check_cpu",
42             args => "-w 50 -c 80" }
43             },
44             callback => $callback
45             );
46              
47             =head1 DESCRIPTION
48              
49             A simple daemon implementation with the capabillity to add your own callbacks
50             and hooks in case you want to build your own NRPE Server.
51              
52             =cut
53              
54             package Nagios::NRPE::Daemon;
55              
56             our $VERSION = '1.0.2';
57              
58 1     1   9215 use 5.010_000;
  1         6  
59              
60 1     1   9 use strict;
  1         4  
  1         43  
61 1     1   9 use warnings;
  1         4  
  1         48  
62              
63 1     1   9 use Data::Dumper;
  1         2  
  1         86  
64 1     1   8 use Carp;
  1         3  
  1         71  
65 1     1   9 use IO::Socket;
  1         3  
  1         13  
66 1     1   767 use IO::Socket::INET6;
  1         3  
  1         10  
67 1         825 use Nagios::NRPE::Packet qw(NRPE_PACKET_VERSION_3
68             NRPE_PACKET_VERSION_2
69             NRPE_PACKET_RESPONSE
70             MAX_PACKETBUFFER_LENGTH
71             STATE_UNKNOWN
72             STATE_CRITICAL
73             STATE_WARNING
74 1     1   9572 STATE_OK);
  1         4  
75              
76             =pod
77              
78             =head1 SUBROUTINES
79              
80             =over
81              
82             =item new()
83              
84             Takes the following options as a hashref:
85              
86             * listen:
87              
88             Listen on this IP Address
89              
90             * port:
91              
92             Port to listen on
93              
94             * pid_dir
95              
96             The pidfile for this daemon
97              
98             * ssl
99              
100             Use ssl (1|0)
101              
102             * commandlist
103              
104             A hashref of the allowed commands on the daemon
105              
106             * callback
107              
108             A sub executed everytime a check should be run. Giving the daemon full control what should happen.
109              
110             my $callback = sub {
111             my ($self,$check,@options) = @_;
112             my $commandlist = $self->commandlist();
113             if ($commandlist->{$check}) {
114             my $args = $commandlist->{$check}->{args};
115             my $i = 0;
116             foreach (@options) {
117             $i++;
118             $args =~ "s/\$ARG$i\$/$_/";
119             }
120             my $buffer;
121             if (scalar run(command => $commandlist->{$check}->{bin} . " " . $args,
122             verbose => 0,
123             buffer => \$buffer,
124             timeout => 20)) {
125             return $buffer;
126             }
127             }
128             };
129              
130              
131             =back
132              
133             =cut
134              
135             sub new {
136 0     0 1   my ( $class, %hash ) = @_;
137 0           my $self = {};
138              
139 0   0       $self->{listen} = delete $hash{listen} || "0.0.0.0";
140 0   0       $self->{port} = delete $hash{port} || "5666";
141 0   0       $self->{pid_dir} = delete $hash{pid_dir} || "/var/run";
142 0   0       $self->{ssl} = delete $hash{ssl} || 0;
143 0   0       $self->{commandlist} = delete $hash{commandlist} || {};
144 0   0 0     $self->{callback} = delete $hash{callback} || sub { };
145              
146 0           bless $self, $class;
147             }
148              
149             =pod
150              
151             =over
152              
153             =item start()
154              
155             Starts the server and enters the Loop listening for packets
156              
157             =back
158              
159             =cut
160              
161             sub start {
162 0     0 1   my $self = shift;
163 0           my $packet = Nagios::NRPE::Packet->new();
164 0           my $callback = $self->{callback};
165 0           my ( $socket, $s );
166              
167 0           $socket = $self->create_socket();
168              
169 0           while (1) {
170 0           while ( ( $s = $socket->accept() ) ) {
171 0           my $request;
172 0           $s->recv( $request, 1036 );
173 0           my $unpacked_request = $packet->deassemble($request);
174 0           my $buffer = $unpacked_request->{buffer};
175 0           my $version = $unpacked_request->{packet_version};
176 0           my ( $command, @options ) = split /!/, $buffer;
177              
178 0           my $return = $self->{callback}( $self, $command, @options );
179 0           eval {
180 0           print $s $packet->assemble(
181             version => $version,
182             type => NRPE_PACKET_RESPONSE,
183             check => $return
184             );
185             };
186              
187 0           close($s);
188             }
189             }
190             }
191              
192             =pod
193              
194             =over
195              
196             =item commandlist()
197              
198             A hashref of elements that are valid commands.
199             An example for it is:
200              
201             "check_cpu" => { bin => "/usr/lib/nagios/plugin/check_cpu",
202             args => "-w 50 -c 80" }
203              
204             C can contain $ARG1$ elements like normal nrpe.cfg command elements.
205              
206             =back
207              
208             =cut
209              
210             sub commandlist {
211 0     0 1   my $self = shift;
212 0           return $self->{commandlist};
213             }
214              
215             =pod
216              
217             =over
218              
219             =item create_socket()
220              
221             A shorthand function returning either an encrypted or unencrypted socket
222             depending on wether ssl is set to 1 or 0.
223              
224             =back
225              
226             =cut
227              
228             sub create_socket {
229 0     0 1   my $self = shift;
230 0           my $socket;
231              
232 0 0         if ( $self->{ssl} ) {
233 0           eval {
234             # required for new IO::Socket::SSL versions
235 0           require IO::Socket::SSL;
236 0           IO::Socket::SSL->import();
237 0           IO::Socket::SSL::set_ctx_defaults( SSL_verify_mode => 0 );
238             };
239             $socket = IO::Socket::SSL->new(
240             Listen => 5,
241             LocalAddr => $self->{host},
242             LocalPort => $self->{port},
243 0 0         Proto => 'tcp',
244             Reuse => 1,
245             SSL_verify_mode => 0x01,
246             Type => SOCK_STREAM
247             ) or die( IO::Socket::SSL::errstr() );
248             }
249             else {
250             $socket = IO::Socket::INET6->new(
251             Listen => 5,
252             LocalAddr => $self->{host},
253             LocalPort => $self->{port},
254 0 0         Reuse => 1,
255             Proto => 'tcp',
256             Type => SOCK_STREAM
257             ) or die "ERROR: $@ \n";
258             }
259 0           return $socket;
260             }
261              
262             =pod
263              
264             =head1 COPYRIGHT AND LICENSE
265              
266             This software is copyright (c) 2017 by the authors (see AUTHORS file).
267              
268             This is free software; you can redistribute it and/or modify it under
269             the same terms as the Perl 5 programming language system itself.
270              
271             =cut
272              
273             1;