summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Nagios/Plugin/Getopt.pm162
1 files changed, 153 insertions, 9 deletions
diff --git a/lib/Nagios/Plugin/Getopt.pm b/lib/Nagios/Plugin/Getopt.pm
index bbf1fc9..806a6a0 100644
--- a/lib/Nagios/Plugin/Getopt.pm
+++ b/lib/Nagios/Plugin/Getopt.pm
@@ -10,12 +10,15 @@ use File::Basename;
10use Getopt::Long qw(:config no_ignore_case bundling); 10use Getopt::Long qw(:config no_ignore_case bundling);
11use Carp; 11use Carp;
12use Params::Validate qw(:all); 12use Params::Validate qw(:all);
13use Config::Tiny;
13use base qw(Class::Accessor); 14use base qw(Class::Accessor);
14 15
15use Nagios::Plugin::Functions; 16use Nagios::Plugin::Functions;
16use vars qw($VERSION); 17use vars qw($VERSION $DEFAULT_CONFIG_FILE);
17$VERSION = $Nagios::Plugin::Functions::VERSION; 18$VERSION = $Nagios::Plugin::Functions::VERSION;
18 19
20$DEFAULT_CONFIG_FILE = '/etc/nagios/plugins.cfg';
21
19# Standard defaults 22# Standard defaults
20my %DEFAULT = ( 23my %DEFAULT = (
21 timeout => 15, 24 timeout => 15,
@@ -36,6 +39,9 @@ my @ARGS = ({
36 spec => 'version|V', 39 spec => 'version|V',
37 help => "-V, --version\n Print version information", 40 help => "-V, --version\n Print version information",
38 }, { 41 }, {
42 spec => 'default-opts:s@',
43 help => "--default-opts=[<section>[@<config_file>]]\n Section and/or config_file from which to load default options (may repeat)",
44 }, {
39 spec => 'timeout|t=i', 45 spec => 'timeout|t=i',
40 help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)", 46 help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)",
41 default => $DEFAULT{timeout}, 47 default => $DEFAULT{timeout},
@@ -140,7 +146,7 @@ sub _process_specs_getopt_long
140 # Setup names and defaults 146 # Setup names and defaults
141 my $spec = $arg->{spec}; 147 my $spec = $arg->{spec};
142 # Use first arg as name (like Getopt::Long does) 148 # Use first arg as name (like Getopt::Long does)
143 $spec =~ s/=\w+$//; 149 $spec =~ s/[=:].*$//;
144 my $name = (split /\s*\|\s*/, $spec)[0]; 150 my $name = (split /\s*\|\s*/, $spec)[0];
145 $arg->{name} = $name; 151 $arg->{name} = $name;
146 if (defined $self->{$name}) { 152 if (defined $self->{$name}) {
@@ -182,6 +188,135 @@ sub _process_opts
182} 188}
183 189
184# ------------------------------------------------------------------------- 190# -------------------------------------------------------------------------
191# Default opts methods
192
193sub _load_config_section
194{
195 my $self = shift;
196 my ($section, $file, $flags) = @_;
197 $section ||= $self->{_attr}->{plugin};
198 $file ||= $DEFAULT_CONFIG_FILE;
199
200 $self->_die("Cannot find config file '$file'") if $flags->{fatal} && ! -f $file;
201
202 my $Config = Config::Tiny->read($file);
203 $self->_die("Cannot read config file '$file'") unless defined $Config;
204
205 $self->_die("Invalid section '$section' in config file '$file'")
206 if $flags->{fatal} && ! exists $Config->{$section};
207
208 return $Config->{$section};
209}
210
211# Helper method to setup a hash of spec definitions for _cmdline
212sub _setup_spec_index
213{
214 my $self = shift;
215 return if defined $self->{_spec};
216 $self->{_spec} = { map { $_->{name} => $_->{spec} } @{$self->{_args}} };
217}
218
219# Quote values that require it
220sub _cmdline_value
221{
222 my $self = shift;
223 local $_ = shift;
224 if (m/\s/ && (m/^[^"']/ || m/[^"']$/)) {
225 return qq("$_");
226 }
227 elsif ($_ eq '') {
228 return q("");
229 }
230 else {
231 return $_;
232 }
233}
234
235# Helper method to format key/values in $hash in a quasi-commandline format
236sub _cmdline
237{
238 my $self = shift;
239 my ($hash) = @_;
240 $hash ||= $self;
241
242 $self->_setup_spec_index;
243
244 my @args = ();
245 for my $key (sort keys %$hash) {
246 # Skip internal keys
247 next if $key =~ m/^_/;
248
249 # Skip defaults and internals
250 next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key};
251 next if grep { $key eq $_ } qw(help usage version default-opts);
252 next unless defined $hash->{$key};
253
254 # Render arg
255 my $spec = $self->{_spec}->{$key} || '';
256 if ($spec =~ m/[=:].+$/) {
257 # Arg takes value - may be a scalar or an arrayref
258 for my $value (ref $hash->{$key} eq 'ARRAY' ? @{$hash->{$key}} : ( $hash->{$key} )) {
259 $value = $self->_cmdline_value($value);
260 if (length($key) > 1) {
261 push @args, sprintf "--%s=%s", $key, $value;
262 }
263 else {
264 push @args, "-$key", $value;
265 }
266 }
267 }
268
269 else {
270 # Flag - render long or short based on option length
271 push @args, (length($key) > 1 ? '--' : '-') . $key;
272 }
273 }
274
275 return wantarray ? @args : join(' ', @args);
276}
277
278# Process and load default-opts sections
279sub _process_default_opts
280{
281 my $self = shift;
282 my ($args) = @_;
283
284 my $defopts_list = $args->{'default-opts'};
285 my $defopts_explicit = 1;
286
287 # If no default_opts defined, force one implicitly
288 if (! $defopts_list) {
289 $defopts_list = [ '' ];
290 $defopts_explicit = 0;
291 }
292
293 my @sargs = ();
294 for my $defopts (@$defopts_list) {
295 $defopts ||= $self->{_attr}->{plugin};
296 my $section = $defopts;
297 my $file = '';
298
299 # Parse section@file
300 if ($defopts =~ m/^(\w*)@(.*?)\s*$/) {
301 $section = $1;
302 $file = $2;
303 }
304
305 # Load section args
306 my $shash = $self->_load_config_section($section, $file, { fatal => $defopts_explicit });
307
308 # Turn $shash into a series of commandline-like arguments
309 push @sargs, $self->_cmdline($shash);
310 }
311
312 # Reset ARGV to default-opts + original
313 @ARGV = ( @sargs, @{$self->{_attr}->{argv}} );
314
315 printf "[default-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV)
316 if $args->{verbose} && $args->{verbose} >= 3;
317}
318
319# -------------------------------------------------------------------------
185# Public methods 320# Public methods
186 321
187# Define plugin argument 322# Define plugin argument
@@ -223,10 +358,21 @@ sub getopts
223 # Collate spec arguments for Getopt::Long 358 # Collate spec arguments for Getopt::Long
224 my @opt_array = $self->_process_specs_getopt_long; 359 my @opt_array = $self->_process_specs_getopt_long;
225 360
361 # Capture original @ARGV (for default-opts games)
362 $self->{_attr}->{argv} = [ @ARGV ];
363
226 # Call GetOptions using @opt_array 364 # Call GetOptions using @opt_array
227 my $ok = GetOptions($self, @opt_array); 365 my $args1 = {};
366 my $ok = GetOptions($args1, @opt_array);
367 # Invalid options - give usage message and exit
368 $self->_die($self->_usage) unless $ok;
228 369
229 # Invalid options - given usage message and exit 370 # Process default-opts
371 $self->_process_default_opts($args1);
372
373 # Call GetOptions again, this time including default-opts
374 $ok = GetOptions($self, @opt_array);
375 # Invalid options - give usage message and exit
230 $self->_die($self->_usage) unless $ok; 376 $self->_die($self->_usage) unless $ok;
231 377
232 # Process immediate options (possibly exiting) 378 # Process immediate options (possibly exiting)
@@ -264,6 +410,7 @@ sub _init
264 plugin => { default => $plugin }, 410 plugin => { default => $plugin },
265 blurb => 0, 411 blurb => 0,
266 extra => 0, 412 extra => 0,
413 'default-opts' => 0,
267 license => { default => $DEFAULT{license} }, 414 license => { default => $DEFAULT{license} },
268 timeout => { default => $DEFAULT{timeout} }, 415 timeout => { default => $DEFAULT{timeout} },
269 }); 416 });
@@ -295,11 +442,8 @@ __END__
295 442
296=head1 NAME 443=head1 NAME
297 444
298Nagios::Plugin::Getopt - OO perl module providing standardised argument processing for Nagios plugins 445Nagios::Plugin::Getopt - OO perl module providing standardised argument
299 446processing for Nagios plugins
300=head1 VERSION
301
302This documentation applies to version 0.01 of Nagios::Plugin::Getopt.
303 447
304 448
305=head1 SYNOPSIS 449=head1 SYNOPSIS