summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGavin Carr <gonzai@users.sourceforge.net>2007-03-21 00:52:56 (GMT)
committerGavin Carr <gonzai@users.sourceforge.net>2007-03-21 00:52:56 (GMT)
commitdc31f1cd3841d486e920e59ce42e888ca94e4289 (patch)
tree14bcc87e8349b99b01a649f27429690321fc21f9
parentc6cbf050974c8f6642fa1d7bde309710b66cbfa0 (diff)
downloadmonitoring-plugin-perl-dc31f1cd3841d486e920e59ce42e888ca94e4289.tar.gz
Finished initial --extra-opts support; added Getopt spec-to-help and multiline help support.
git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/Nagios-Plugin/trunk@1643 f882894a-f735-0410-b71e-b25c423dba1c
-rw-r--r--Changes8
-rw-r--r--MANIFEST44
-rw-r--r--lib/Nagios/Plugin/Config.pm170
-rw-r--r--lib/Nagios/Plugin/Functions.pm10
-rw-r--r--lib/Nagios/Plugin/Getopt.pm263
-rw-r--r--t/Nagios-Plugin-04.t1
-rw-r--r--t/Nagios-Plugin-Getopt-01.t8
-rw-r--r--t/Nagios-Plugin-Getopt-03.t17
-rw-r--r--t/Nagios-Plugin-Getopt-04.t95
-rw-r--r--t/npg03/README18
-rw-r--r--t/npg03/expected/00_noextra1
-rw-r--r--t/npg03/expected/05_disk11
-rw-r--r--t/npg03/expected/05_disk21
-rw-r--r--t/npg03/expected/05_disk31
-rw-r--r--t/npg03/expected/05_disk41
-rw-r--r--t/npg03/expected/05_disk51
-rw-r--r--t/npg03/expected/05_disk61
-rw-r--r--t/npg03/expected/05_singlechar11
-rw-r--r--t/npg03/expected/06_singlechar21
-rw-r--r--t/npg03/expected/07_singlechar31
-rw-r--r--t/npg03/input/00_basic2
-rw-r--r--t/npg03/input/00_noextra1
-rw-r--r--t/npg03/input/01_override12
-rw-r--r--t/npg03/input/02_override22
-rw-r--r--t/npg03/input/05_disk11
-rw-r--r--t/npg03/input/05_disk21
-rw-r--r--t/npg03/input/05_disk31
-rw-r--r--t/npg03/input/05_disk41
-rw-r--r--t/npg03/input/05_disk51
-rw-r--r--t/npg03/input/05_disk61
-rw-r--r--t/npg03/input/05_singlechar11
-rw-r--r--t/npg03/input/06_singlechar21
-rw-r--r--t/npg03/input/07_singlechar31
-rw-r--r--t/npg03/input/09_funnystuff2
-rw-r--r--t/npg03/input/13_nosection_explicit_dies2
-rw-r--r--t/npg03/input/14_badsection_dies2
-rw-r--r--t/npg03/plugins.ini (renamed from t/npg03/plugins.cfg)9
37 files changed, 553 insertions, 122 deletions
diff --git a/Changes b/Changes
index b207c21..11c6ab7 100644
--- a/Changes
+++ b/Changes
@@ -1,10 +1,10 @@
1Revision history for Perl module Nagios::Plugin. 1Revision history for Perl module Nagios::Plugin.
2 2
30.16 ?? 30.16 ??
4 - perldoc updates to Performance, Threshold, and Range (Gavin) 4 - added automatic spec-to-help-text support to N::P::Getopt (Gavin)
5 - remove default use of Threshold from N::P::Performance (Gavin) 5 - added initial --extra-opts support to N::P::Getopt (Gavin)
6 - remove remaining Class::Struct usages from Threshold + Range (Gavin) 6 - removed default use of Threshold from N::P::Performance (Gavin)
7 - remove remaining Class::Struct usages from Performance (Gavin) 7 - removed remaining Class::Struct usages from Performance, Threshold, and Range (Gavin)
8 - fixed warnings when no uom specified for add_perfdata (Ton) 8 - fixed warnings when no uom specified for add_perfdata (Ton)
9 - added max_state function in N::P::Functions (Ton) 9 - added max_state function in N::P::Functions (Ton)
10 10
diff --git a/MANIFEST b/MANIFEST
index 1d4f532..ce5186e 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,46 +1,56 @@
1Changes 1Changes
2Makefile.PL 2Makefile.PL
3MANIFEST 3MANIFEST
4META.yml
4README 5README
5t/check_stuff.pl 6lib/Nagios/Plugin.pm
6t/check_stuff.t 7lib/Nagios/Plugin/Performance.pm
8lib/Nagios/Plugin/Range.pm
9lib/Nagios/Plugin/Threshold.pm
10lib/Nagios/Plugin/Functions.pm
11lib/Nagios/Plugin/Getopt.pm
12lib/Nagios/Plugin/ExitResult.pm
7t/Nagios-Plugin-01.t 13t/Nagios-Plugin-01.t
8t/Nagios-Plugin-02.t 14t/Nagios-Plugin-02.t
9t/Nagios-Plugin-03.t 15t/Nagios-Plugin-03.t
10t/Nagios-Plugin-04.t 16t/Nagios-Plugin-04.t
11t/Nagios-Plugin-Functions-01.t 17t/Nagios-Plugin-Functions-01.t
12t/Nagios-Plugin-Functions-02.t 18t/Nagios-Plugin-Functions-02.t
19t/Nagios-Plugin-Functions-03.t
13t/Nagios-Plugin-Getopt-01.t 20t/Nagios-Plugin-Getopt-01.t
14t/Nagios-Plugin-Getopt-02.t 21t/Nagios-Plugin-Getopt-02.t
15t/Nagios-Plugin-Getopt-03.t 22t/Nagios-Plugin-Getopt-03.t
23t/Nagios-Plugin-Getopt-04.t
16t/Nagios-Plugin-Performance.t 24t/Nagios-Plugin-Performance.t
17t/Nagios-Plugin-Range.t 25t/Nagios-Plugin-Range.t
18t/Nagios-Plugin-Threshold.t 26t/Nagios-Plugin-Threshold.t
27t/check_stuff.pl
28t/check_stuff.t
19t/npg03/README 29t/npg03/README
20t/npg03/expected/00_basic 30t/npg03/expected/00_basic
31t/npg03/expected/00_noextra
21t/npg03/expected/01_override1 32t/npg03/expected/01_override1
22t/npg03/expected/02_override2 33t/npg03/expected/02_override2
23t/npg03/expected/05_singlechar1 34t/npg03/expected/05_disk1
24t/npg03/expected/06_singlechar2 35t/npg03/expected/05_disk2
25t/npg03/expected/07_singlechar3 36t/npg03/expected/05_disk3
37t/npg03/expected/05_disk4
38t/npg03/expected/05_disk5
39t/npg03/expected/05_disk6
26t/npg03/expected/09_funnystuff 40t/npg03/expected/09_funnystuff
27t/npg03/expected/12_nosection_implicit 41t/npg03/expected/12_nosection_implicit
28t/npg03/input/00_basic 42t/npg03/input/00_basic
43t/npg03/input/00_noextra
29t/npg03/input/01_override1 44t/npg03/input/01_override1
30t/npg03/input/02_override2 45t/npg03/input/02_override2
31t/npg03/input/05_singlechar1 46t/npg03/input/05_disk1
32t/npg03/input/06_singlechar2 47t/npg03/input/05_disk2
33t/npg03/input/07_singlechar3 48t/npg03/input/05_disk3
49t/npg03/input/05_disk4
50t/npg03/input/05_disk5
51t/npg03/input/05_disk6
34t/npg03/input/09_funnystuff 52t/npg03/input/09_funnystuff
35t/npg03/input/12_nosection_implicit 53t/npg03/input/12_nosection_implicit
36t/npg03/input/13_nosection_explicit_dies 54t/npg03/input/13_nosection_explicit_dies
37t/npg03/input/14_badsection_dies 55t/npg03/input/14_badsection_dies
38t/npg03/plugins.cfg 56t/npg03/plugins.ini
39lib/Nagios/Plugin.pm
40lib/Nagios/Plugin/Performance.pm
41lib/Nagios/Plugin/Range.pm
42lib/Nagios/Plugin/Threshold.pm
43lib/Nagios/Plugin/Functions.pm
44lib/Nagios/Plugin/Getopt.pm
45lib/Nagios/Plugin/ExitResult.pm
46META.yml Module meta-data (added by MakeMaker)
diff --git a/lib/Nagios/Plugin/Config.pm b/lib/Nagios/Plugin/Config.pm
new file mode 100644
index 0000000..92193c2
--- /dev/null
+++ b/lib/Nagios/Plugin/Config.pm
@@ -0,0 +1,170 @@
1package Nagios::Plugin::Config;
2
3use strict;
4use Carp;
5use File::Spec;
6use base qw(Config::Tiny);
7
8my $FILENAME1 = 'plugins.ini';
9my $FILENAME2 = 'nagios-plugins.ini';
10
11# Config paths ending in nagios (search for $FILENAME1)
12my @NAGIOS_CONFIG_PATH = qw(/etc/nagios /usr/local/nagios/etc /usr/local/etc/nagios /etc/opt/nagios);
13# Config paths not ending in nagios (search for $FILENAME2)
14my @CONFIG_PATH = qw(/etc /usr/local/etc /etc/opt);
15
16# Override Config::Tiny::read to default the filename, if not given
17sub read
18{
19 my $class = shift;
20
21 unless ($_[0]) {
22 SEARCH: {
23 if ($ENV{NAGIOS_CONFIG_PATH}) {
24 for (split /:/, $ENV{NAGIOS_CONFIG_PATH}) {
25 my $file = File::Spec->catfile($_, $FILENAME1);
26 unshift(@_, $file), last SEARCH if -f $file;
27 $file = File::Spec->catfile($_, $FILENAME2);
28 unshift(@_, $file), last SEARCH if -f $file;
29 }
30 }
31 for (@NAGIOS_CONFIG_PATH) {
32 my $file = File::Spec->catfile($_, $FILENAME1);
33 unshift(@_, $file), last SEARCH if -f $file;
34 }
35 for (@CONFIG_PATH) {
36 my $file = File::Spec->catfile($_, $FILENAME2);
37 unshift(@_, $file), last SEARCH if -f $file;
38 }
39 }
40
41 croak "Cannot find '$FILENAME1' or '$FILENAME2' in any standard location." unless $_[0];
42 }
43
44 $class->SUPER::read( @_ );
45}
46
47# Straight from Config::Tiny - only changes are repeated property key support
48# Would be nice if we could just override the per-line handling ...
49sub read_string
50{
51 my $class = ref $_[0] ? ref shift : shift;
52 my $self = bless {}, $class;
53 return undef unless defined $_[0];
54
55 # Parse the file
56 my $ns = '_';
57 my $counter = 0;
58 foreach ( split /(?:\015{1,2}\012|\015|\012)/, shift ) {
59 $counter++;
60
61 # Skip comments and empty lines
62 next if /^\s*(?:\#|\;|$)/;
63
64 # Handle section headers
65 if ( /^\s*\[\s*(.+?)\s*\]\s*$/ ) {
66 # Create the sub-hash if it doesn't exist.
67 # Without this sections without keys will not
68 # appear at all in the completed struct.
69 $self->{$ns = $1} ||= {};
70 next;
71 }
72
73 # Handle properties
74 if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) {
75 $self->{$ns}->{$1} = defined $self->{$ns}->{$1} ?
76 [ $self->{$ns}->{$1}, $2 ] :
77 $2;
78 next;
79 }
80
81 return $self->_error( "Syntax error at line $counter: '$_'" );
82 }
83
84 $self;
85}
86
87sub write { croak "Write access not permitted" }
88
891;
90
91=head1 NAME
92
93Nagios::Plugin::Config - read nagios plugin .ini style config files
94
95=head1 SYNOPSIS
96
97 # Read given nagios plugin config file
98 $Config = Nagios::Plugin::Config->read( '/etc/nagios/plugins.ini' );
99
100 # Search for and read default nagios plugin config file
101 $Config = Nagios::Plugin::Config->read();
102
103 # Access sections and properties (returns scalars or arrayrefs)
104 $rootproperty = $Config->{_}->{rootproperty};
105 $one = $Config->{section}->{one};
106 $Foo = $Config->{section}->{Foo};
107
108=head1 DESCRIPTION
109
110Nagios::Plugin::Config is a subclass of the excellent Config::Tiny,
111with the following changes:
112
113=over 4
114
115=item
116
117Repeated keys are allowed within sections, returning lists instead of scalars
118
119=item
120
121Write functionality has been removed i.e. access is read only
122
123=item
124
125Nagios::Plugin::Config searches for a default nagios plugins file if no explicit
126filename is given to C<read()>. The current standard locations checked are:
127
128=over 4
129
130=item /etc/nagios/plugins.ini
131
132=item /usr/local/nagios/etc/plugins.ini
133
134=item /usr/local/etc/nagios /etc/opt/nagios/plugins.ini
135
136=item /etc/nagios-plugins.ini
137
138=item /usr/local/etc/nagios-plugins.ini
139
140=item /etc/opt/nagios-plugins.ini
141
142=back
143
144To use a custom location, set a C<NAGIOS_CONFIG_PATH> environment variable
145to the set of directories that should be checked. The first C<plugins.ini> or
146C<nagios-plugins.ini> file found will be used.
147
148=back
149
150
151=head1 SEE ALSO
152
153L<Config::Tiny>, L<Nagios::Plugin>
154
155
156=head1 AUTHORS
157
158This code is maintained by the Nagios Plugin Development Team:
159L<http://nagiosplug.sourceforge.net>.
160
161
162=head1 COPYRIGHT and LICENCE
163
164Copyright (C) 2006-2007 by Nagios Plugin Development Team
165
166This library is free software; you can redistribute it and/or modify
167it under the same terms as Perl itself.
168
169=cut
170
diff --git a/lib/Nagios/Plugin/Functions.pm b/lib/Nagios/Plugin/Functions.pm
index 526dbed..751251f 100644
--- a/lib/Nagios/Plugin/Functions.pm
+++ b/lib/Nagios/Plugin/Functions.pm
@@ -46,6 +46,13 @@ our %STATUS_TEXT = reverse %ERRORS;
46my $_fake_exit = 0; 46my $_fake_exit = 0;
47sub _fake_exit { @_ ? $_fake_exit = shift : $_fake_exit }; 47sub _fake_exit { @_ ? $_fake_exit = shift : $_fake_exit };
48 48
49# Tweak default die handling: die is cool because it allows capturing both return codes and
50# output via eval, but the Nagios Plugin Guidelines like STDOUT over STDERR
51$SIG{__DIE__} = sub {
52 print STDOUT shift;
53 exit $!;
54};
55
49sub get_shortname { 56sub get_shortname {
50 my %arg = @_; 57 my %arg = @_;
51 58
@@ -390,7 +397,6 @@ This code is maintained by the Nagios Plugin Development Team: http://nagiosplug
390Copyright (C) 2006 by Nagios Plugin Development Team 397Copyright (C) 2006 by Nagios Plugin Development Team
391 398
392This library is free software; you can redistribute it and/or modify 399This library is free software; you can redistribute it and/or modify
393it under the same terms as Perl itself, either Perl version 5.8.4 or, 400it under the same terms as Perl itself.
394at your option, any later version of Perl 5 you may have available.
395 401
396=cut 402=cut
diff --git a/lib/Nagios/Plugin/Getopt.pm b/lib/Nagios/Plugin/Getopt.pm
index 806a6a0..743bf7e 100644
--- a/lib/Nagios/Plugin/Getopt.pm
+++ b/lib/Nagios/Plugin/Getopt.pm
@@ -10,15 +10,13 @@ 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;
14use base qw(Class::Accessor); 13use base qw(Class::Accessor);
15 14
16use Nagios::Plugin::Functions; 15use Nagios::Plugin::Functions;
17use vars qw($VERSION $DEFAULT_CONFIG_FILE); 16use Nagios::Plugin::Config;
17use vars qw($VERSION);
18$VERSION = $Nagios::Plugin::Functions::VERSION; 18$VERSION = $Nagios::Plugin::Functions::VERSION;
19 19
20$DEFAULT_CONFIG_FILE = '/etc/nagios/plugins.cfg';
21
22# Standard defaults 20# Standard defaults
23my %DEFAULT = ( 21my %DEFAULT = (
24 timeout => 15, 22 timeout => 15,
@@ -39,8 +37,8 @@ my @ARGS = ({
39 spec => 'version|V', 37 spec => 'version|V',
40 help => "-V, --version\n Print version information", 38 help => "-V, --version\n Print version information",
41 }, { 39 }, {
42 spec => 'default-opts:s@', 40 spec => 'extra-opts:s@',
43 help => "--default-opts=[<section>[@<config_file>]]\n Section and/or config_file from which to load default options (may repeat)", 41 help => "--extra-opts=[<section>[@<config_file>]]\n Section and/or config_file from which to load extra options (may repeat)",
44 }, { 42 }, {
45 spec => 'timeout|t=i', 43 spec => 'timeout|t=i',
46 help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)", 44 help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)",
@@ -77,6 +75,37 @@ sub _attr
77 $self->{_attr}->{$item} . "\n" . $extra; 75 $self->{_attr}->{$item} . "\n" . $extra;
78} 76}
79 77
78# Turn argument spec into help-style output
79sub _spec_to_help
80{
81 my ($self, $spec, $label) = @_;
82
83 my ($opts, $type) = split /=/, $spec, 2;
84 my (@short, @long);
85 for (split /\|/, $opts) {
86 if (length $_ == 1) {
87 push @short, "-$_";
88 } else {
89 push @long, "--$_";
90 }
91 }
92
93 my $help = join(', ', @short, @long);
94 if ($type) {
95 if ($label) {
96 $help .= '=' . $label;
97 }
98 else {
99 $help .= $type eq 'i' ? '=INTEGER' : '=STRING';
100 }
101 }
102 elsif ($label) {
103 carp "Label specified, but there's no type in spec '$spec'";
104 }
105 $help .= "\n ";
106 return $help;
107}
108
80# Options output for plugin -h 109# Options output for plugin -h
81sub _options 110sub _options
82{ 111{
@@ -94,10 +123,29 @@ sub _options
94 123
95 my @options = (); 124 my @options = ();
96 for my $arg (@args, @defer) { 125 for my $arg (@args, @defer) {
97 if ($arg->{help} =~ m/%s/) { 126 my $help_array = ref $arg->{help} && ref $arg->{help} eq 'ARRAY' ? $arg->{help} : [ $arg->{help} ];
98 push @options, sprintf($arg->{help}, $arg->{default} || ''); 127 my $label_array = $arg->{label} && ref $arg->{label} && ref $arg->{label} eq 'ARRAY' ? $arg->{label} : [ $arg->{label} ];
128 my $help_string = '';
129 for (my $i = 0; $i <= $#$help_array; $i++) {
130 my $help = $help_array->[$i];
131 # Add spec arguments to help if not already there
132 if ($help =~ m/^\s*-/) {
133 $help_string .= $help;
134 }
135 else {
136 $help_string .= $self->_spec_to_help($arg->{spec}, $label_array->[$i]) . $help;
137 $help_string .= "\n " if $i < $#$help_array;
138 }
139 }
140
141 # Add help_string to @options
142 if ($help_string =~ m/%s/) {
143 my $default = defined $arg->{default} ? $arg->{default} : '';
144 # We only handle '%s' formats here, so escape everything else
145 $help_string =~ s/%(?!s)/%%/g;
146 push @options, sprintf($help_string, $default, $default, $default, $default);
99 } else { 147 } else {
100 push @options, $arg->{help}; 148 push @options, $help_string;
101 } 149 }
102 } 150 }
103 151
@@ -195,15 +243,12 @@ sub _load_config_section
195 my $self = shift; 243 my $self = shift;
196 my ($section, $file, $flags) = @_; 244 my ($section, $file, $flags) = @_;
197 $section ||= $self->{_attr}->{plugin}; 245 $section ||= $self->{_attr}->{plugin};
198 $file ||= $DEFAULT_CONFIG_FILE;
199
200 $self->_die("Cannot find config file '$file'") if $flags->{fatal} && ! -f $file;
201 246
202 my $Config = Config::Tiny->read($file); 247 my $Config = Nagios::Plugin::Config->read($file);
203 $self->_die("Cannot read config file '$file'") unless defined $Config;
204 248
249 # TODO: is this check sane? Does --extra-opts=foo require a [foo] section?
205 $self->_die("Invalid section '$section' in config file '$file'") 250 $self->_die("Invalid section '$section' in config file '$file'")
206 if $flags->{fatal} && ! exists $Config->{$section}; 251 unless exists $Config->{$section};
207 252
208 return $Config->{$section}; 253 return $Config->{$section};
209} 254}
@@ -248,7 +293,7 @@ sub _cmdline
248 293
249 # Skip defaults and internals 294 # Skip defaults and internals
250 next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key}; 295 next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key};
251 next if grep { $key eq $_ } qw(help usage version default-opts); 296 next if grep { $key eq $_ } qw(help usage version extra-opts);
252 next unless defined $hash->{$key}; 297 next unless defined $hash->{$key};
253 298
254 # Render arg 299 # Render arg
@@ -275,44 +320,37 @@ sub _cmdline
275 return wantarray ? @args : join(' ', @args); 320 return wantarray ? @args : join(' ', @args);
276} 321}
277 322
278# Process and load default-opts sections 323# Process and load extra-opts sections
279sub _process_default_opts 324sub _process_extra_opts
280{ 325{
281 my $self = shift; 326 my $self = shift;
282 my ($args) = @_; 327 my ($args) = @_;
283 328
284 my $defopts_list = $args->{'default-opts'}; 329 my $extopts_list = $args->{'extra-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 330
293 my @sargs = (); 331 my @sargs = ();
294 for my $defopts (@$defopts_list) { 332 for my $extopts (@$extopts_list) {
295 $defopts ||= $self->{_attr}->{plugin}; 333 $extopts ||= $self->{_attr}->{plugin};
296 my $section = $defopts; 334 my $section = $extopts;
297 my $file = ''; 335 my $file = '';
298 336
299 # Parse section@file 337 # Parse section@file
300 if ($defopts =~ m/^(\w*)@(.*?)\s*$/) { 338 if ($extopts =~ m/^(\w*)@(.*?)\s*$/) {
301 $section = $1; 339 $section = $1;
302 $file = $2; 340 $file = $2;
303 } 341 }
304 342
305 # Load section args 343 # Load section args
306 my $shash = $self->_load_config_section($section, $file, { fatal => $defopts_explicit }); 344 my $shash = $self->_load_config_section($section, $file);
307 345
308 # Turn $shash into a series of commandline-like arguments 346 # Turn $shash into a series of commandline-like arguments
309 push @sargs, $self->_cmdline($shash); 347 push @sargs, $self->_cmdline($shash);
310 } 348 }
311 349
312 # Reset ARGV to default-opts + original 350 # Reset ARGV to extra-opts + original
313 @ARGV = ( @sargs, @{$self->{_attr}->{argv}} ); 351 @ARGV = ( @sargs, @{$self->{_attr}->{argv}} );
314 352
315 printf "[default-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV) 353 printf "[extra-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV)
316 if $args->{verbose} && $args->{verbose} >= 3; 354 if $args->{verbose} && $args->{verbose} >= 3;
317} 355}
318 356
@@ -332,17 +370,19 @@ sub arg
332 help => 1, 370 help => 1,
333 default => 0, 371 default => 0,
334 required => 0, 372 required => 0,
373 label => 0,
335 }); 374 });
336 } 375 }
337 376
338 # Positional args 377 # Positional args
339 else { 378 else {
340 my @args = validate_pos(@_, 1, 1, 0, 0); 379 my @args = validate_pos(@_, 1, 1, 0, 0, 0);
341 %args = ( 380 %args = (
342 spec => $args[0], 381 spec => $args[0],
343 help => $args[1], 382 help => $args[1],
344 default => $args[2], 383 default => $args[2],
345 required => $args[3], 384 required => $args[3],
385 label => $args[4],
346 ); 386 );
347 } 387 }
348 388
@@ -358,7 +398,7 @@ sub getopts
358 # Collate spec arguments for Getopt::Long 398 # Collate spec arguments for Getopt::Long
359 my @opt_array = $self->_process_specs_getopt_long; 399 my @opt_array = $self->_process_specs_getopt_long;
360 400
361 # Capture original @ARGV (for default-opts games) 401 # Capture original @ARGV (for extra-opts games)
362 $self->{_attr}->{argv} = [ @ARGV ]; 402 $self->{_attr}->{argv} = [ @ARGV ];
363 403
364 # Call GetOptions using @opt_array 404 # Call GetOptions using @opt_array
@@ -367,10 +407,10 @@ sub getopts
367 # Invalid options - give usage message and exit 407 # Invalid options - give usage message and exit
368 $self->_die($self->_usage) unless $ok; 408 $self->_die($self->_usage) unless $ok;
369 409
370 # Process default-opts 410 # Process extra-opts
371 $self->_process_default_opts($args1); 411 $self->_process_extra_opts($args1);
372 412
373 # Call GetOptions again, this time including default-opts 413 # Call GetOptions again, this time including extra-opts
374 $ok = GetOptions($self, @opt_array); 414 $ok = GetOptions($self, @opt_array);
375 # Invalid options - give usage message and exit 415 # Invalid options - give usage message and exit
376 $self->_die($self->_usage) unless $ok; 416 $self->_die($self->_usage) unless $ok;
@@ -410,7 +450,7 @@ sub _init
410 plugin => { default => $plugin }, 450 plugin => { default => $plugin },
411 blurb => 0, 451 blurb => 0,
412 extra => 0, 452 extra => 0,
413 'default-opts' => 0, 453 'extra-opts' => 0,
414 license => { default => $DEFAULT{license} }, 454 license => { default => $DEFAULT{license} },
415 timeout => { default => $DEFAULT{timeout} }, 455 timeout => { default => $DEFAULT{timeout} },
416 }); 456 });
@@ -452,17 +492,16 @@ processing for Nagios plugins
452 492
453 # Instantiate object (usage is mandatory) 493 # Instantiate object (usage is mandatory)
454 $ng = Nagios::Plugin::Getopt->new( 494 $ng = Nagios::Plugin::Getopt->new(
455 usage => "Usage: %s -H <host> -w <warning_threshold> 495 usage => "Usage: %s -H <host> -w <warning> -c <critical>",
456 -c <critical threshold>", 496 version => '0.1',
457 version => '0.01',
458 url => 'http://www.openfusion.com.au/labs/nagios/', 497 url => 'http://www.openfusion.com.au/labs/nagios/',
459 blurb => 'This plugin tests various stuff.', 498 blurb => 'This plugin tests various stuff.',
460 ); 499 );
461 500
462 # Add argument - named parameters (spec and help are mandatory) 501 # Add argument - named parameters (spec and help are mandatory)
463 $ng->arg( 502 $ng->arg(
464 spec => 'critical|c=s', 503 spec => 'critical|c=i',
465 help => qq(-c, --critical=INTEGER\n Exit with CRITICAL status if fewer than INTEGER foobars are free), 504 help => q(Exit with CRITICAL status if fewer than INTEGER foobars are free),
466 required => 1, 505 required => 1,
467 default => 10, 506 default => 10,
468 ); 507 );
@@ -470,8 +509,8 @@ processing for Nagios plugins
470 # Add argument - positional parameters - arg spec, help text, 509 # Add argument - positional parameters - arg spec, help text,
471 # default value, required? (first two mandatory) 510 # default value, required? (first two mandatory)
472 $ng->arg( 511 $ng->arg(
473 'warning|w=s', 512 'warning|w=i',
474 qq(-w, --warning=INTEGER\n Exit with WARNING status if fewer than INTEGER foobars are free), 513 q(Exit with WARNING status if fewer than INTEGER foobars are free),
475 5, 514 5,
476 1); 515 1);
477 516
@@ -545,9 +584,10 @@ License text, included in the longer --help output (see below for an
545example). By default, this is set to the standard nagios plugins 584example). By default, this is set to the standard nagios plugins
546GPL license text: 585GPL license text:
547 586
548 This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. 587 This nagios plugin is free software, and comes with ABSOLUTELY
549 It may be used, redistributed and/or modified under the terms of the GNU 588 NO WARRANTY. It may be used, redistributed and/or modified under
550 General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt). 589 the terms of the GNU General Public Licence (see
590 http://www.fsf.org/licensing/licenses/gpl.txt).
551 591
552Provide your own to replace this text in the help output. 592Provide your own to replace this text in the help output.
553 593
@@ -603,8 +643,8 @@ example:
603 -H, --hostname=ADDRESS 643 -H, --hostname=ADDRESS
604 Host name or IP address 644 Host name or IP address
605 -p, --ports=STRING 645 -p, --ports=STRING
606 Port numbers to check. Format: comma-separated, colons or hyphens for ranges, 646 Port numbers to check. Format: comma-separated, colons for ranges,
607 no spaces e.g. 8700:8705,8710-8715,8760 647 no spaces e.g. 8700:8705,8710:8715,8760
608 -t, --timeout=INTEGER 648 -t, --timeout=INTEGER
609 Seconds before plugin times out (default: 15) 649 Seconds before plugin times out (default: 15)
610 -v, --verbose 650 -v, --verbose
@@ -615,21 +655,25 @@ example:
615 655
616You can define arguments for your plugin using the arg() method, which 656You can define arguments for your plugin using the arg() method, which
617supports both named and positional arguments. In both cases 657supports both named and positional arguments. In both cases
618the 'spec' and 'help' arguments are required, while the 'default' 658the C<spec> and C<help> arguments are required, while the C<label>,
619and 'required' arguments are optional: 659C<default>, and C<required> arguments are optional:
620 660
621 # Define --hello argument (named parameters) 661 # Define --hello argument (named parameters)
622 $ng->arg( 662 $ng->arg(
623 spec => 'hello=s', 663 spec => 'hello|h=s',
624 help => "--hello\n Hello string", 664 help => "Hello string",
625 required => 1, 665 required => 1,
626 ); 666 );
627 667
628 # Define --hello argument (positional parameters) 668 # Define --hello argument (positional parameters)
629 # Parameter order is 'spec', 'help', 'default', 'required?' 669 # Parameter order is 'spec', 'help', 'default', 'required?', 'label'
630 $ng->arg('hello=s', "--hello\n Hello string", undef, 1); 670 $ng->arg('hello|h=s', "Hello parameter (default %s)", 5, 1);
671
672=over 4
631 673
632The 'spec' argument (the first argument in the positional variant) is a 674=item spec
675
676The C<spec> argument (the first argument in the positional variant) is a
633L<Getopt::Long> argument specification. See L<Getopt::Long> for the details, 677L<Getopt::Long> argument specification. See L<Getopt::Long> for the details,
634but basically it is a series of one or more argument names for this argument 678but basically it is a series of one or more argument names for this argument
635(separated by '|'), suffixed with an '=<type>' indicator if the argument 679(separated by '|'), suffixed with an '=<type>' indicator if the argument
@@ -651,24 +695,105 @@ and so on. The following are some examples:
651 695
652=back 696=back
653 697
654The 'help' argument is a string displayed in the --help option list output. 698=item help
655If the string contains a '%s' it will be formatted via L<sprintf> with the 699
656'default' as the argument i.e. 700The C<help> argument is a string displayed in the --help option list output,
701or it can be a list (an arrayref) of such strings, for multi-line help (see
702below).
703
704The help string is munged in two ways:
705
706=over 4
707
708=item
709
710First, if the help string does NOT begins with a '-' sign, it is prefixed
711by an expanded form of the C<spec> argument. For instance, the following
712hello argument:
713
714 $ng->arg(
715 spec => 'hello|h=s',
716 help => "Hello string",
717 );
718
719would be displayed in the help output as:
720
721 -h, --hello=STRING
722 Hello string
723
724where the '-h, --hello=STRING' part is derived from the spec definition
725(by convention with short args first, then long, then label/type, if any).
726
727=item
728
729Second, if the string contains a '%s' it will be formatted via
730C<sprintf> with the 'default' as the argument i.e.
657 731
658 sprintf($help, $default) 732 sprintf($help, $default)
659 733
660A gotcha is that standard percentage signs also need to be escaped 734=back
661(i.e. '%%') in this case. 735
736Multi-line help is useful in cases where an argument can be of different types
737and you want to make this explicit in your help output e.g.
738
739 $ng->arg(
740 spec => 'warning|w=s',
741 help => [
742 'Exit with WARNING status if less than BYTES bytes of disk are free',
743 'Exit with WARNING status if less than PERCENT of disk is free',
744 ],
745 label => [ 'BYTES', 'PERCENT%' ],
746 );
747
748would be displayed in the help output as:
749
750 -w, --warning=BYTES
751 Exit with WARNING status if less than BYTES bytes of disk are free
752 -w, --warning=PERCENT%
753 Exit with WARNING status if less than PERCENT of disk space is free
754
755Note that in this case we've also specified explicit labels in another
756arrayref corresponding to the C<help> one - if this had been omitted
757the types would have defaulted to 'STRING', instead of 'BYTES' and
758'PERCENT%'.
759
760
761=item label
762
763The C<label> argument is a scalar or an arrayref (see 'Multi-line help'
764description above) that overrides the standard type expansion when generating
765help text from the spec definition. By default, C<spec=i> arguments are
766labelled as C<=INTEGER> in the help text, and C<spec=s> arguments are labelled
767as C<=STRING>. By supplying your own C<label> argument you can override these
768standard 'INTEGER' and 'STRING' designations.
769
770For multi-line help, you can supply an ordered list (arrayref) of labels to
771match the list of help strings e.g.
772
773 label => [ 'BYTES', 'PERCENT%' ]
662 774
663The 'default' argument is the default value to be given to this parameter 775Any labels that are left as undef (or just omitted, if trailing) will just
776use the default 'INTEGER' or 'STRING' designations e.g.
777
778 label => [ undef, 'PERCENT%' ]
779
780
781=item default
782
783The C<default> argument is the default value to be given to this parameter
664if none is explicitly supplied. 784if none is explicitly supplied.
665 785
666The 'required' argument is a boolean used to indicate that this argument 786
787=item required
788
789The C<required> argument is a boolean used to indicate that this argument
667is mandatory (Nagios::Plugin::Getopt will exit with your usage message and 790is mandatory (Nagios::Plugin::Getopt will exit with your usage message and
668a 'Missing argument' indicator if any required arguments are not supplied). 791a 'Missing argument' indicator if any required arguments are not supplied).
669 792
793=back
794
670Note that --help lists your arguments in the order they are defined, so 795Note that --help lists your arguments in the order they are defined, so
671you might want to order your arg() calls accordingly. 796you should order your C<arg()> calls accordingly.
672 797
673 798
674=head2 GETOPTS 799=head2 GETOPTS
@@ -703,14 +828,14 @@ using their long variant names.
703 828
704=head2 BUILTIN PROCESSING 829=head2 BUILTIN PROCESSING
705 830
706The getopts() method also handles processing of the immediate builtin 831The C<getopts()> method also handles processing of the immediate builtin
707arguments, namely --usage, --version, --help, as well as checking all 832arguments, namely --usage, --version, --help, as well as checking all
708required arguments have been supplied, so you don't have to handle 833required arguments have been supplied, so you don't have to handle
709those yourself. This means that your plugin will exit from the getopts() 834those yourself. This means that your plugin will exit from the getopts()
710call in these cases - if you want to catch that you can run getopts() 835call in these cases - if you want to catch that you can run getopts()
711within an eval{}. 836within an eval{}.
712 837
713getopts() also sets up a default ALRM timeout handler so you can use an 838C<getopts()> also sets up a default ALRM timeout handler so you can use an
714 839
715 alarm $ng->timeout; 840 alarm $ng->timeout;
716 841
@@ -730,7 +855,7 @@ Gavin Carr <gavin@openfusion.com.au>
730 855
731=head1 COPYRIGHT AND LICENSE 856=head1 COPYRIGHT AND LICENSE
732 857
733Copyright (C) 2006 by the Nagios Plugin Development Team. 858Copyright (C) 2006-2007 by the Nagios Plugin Development Team.
734 859
735This module is free software. It may be used, redistributed 860This module is free software. It may be used, redistributed
736and/or modified under either the terms of the Perl Artistic 861and/or modified under either the terms of the Perl Artistic
diff --git a/t/Nagios-Plugin-04.t b/t/Nagios-Plugin-04.t
index 6f31b56..d88ad73 100644
--- a/t/Nagios-Plugin-04.t
+++ b/t/Nagios-Plugin-04.t
@@ -1,6 +1,5 @@
1 1
2# tests for toplevel access to Threshold and GetOpts stuff 2# tests for toplevel access to Threshold and GetOpts stuff
3# $Id$
4 3
5use strict; 4use strict;
6#use Test::More 'no_plan'; 5#use Test::More 'no_plan';
diff --git a/t/Nagios-Plugin-Getopt-01.t b/t/Nagios-Plugin-Getopt-01.t
index b708a5f..bad1367 100644
--- a/t/Nagios-Plugin-Getopt-01.t
+++ b/t/Nagios-Plugin-Getopt-01.t
@@ -27,8 +27,8 @@ sub setup
27 27
28 # Add argument - named version 28 # Add argument - named version
29 $ng->arg( 29 $ng->arg(
30 spec => 'critical|c=s', 30 spec => 'critical|c=i',
31 help => qq(-c, --critical=INTEGER\n Exit with CRITICAL status if less than INTEGER foobars are free), 31 help => qq(Exit with CRITICAL status if less than INTEGER foobars are free),
32 required => 1, 32 required => 1,
33 ); 33 );
34 34
@@ -104,6 +104,7 @@ like($@, qr/$PARAM{version}/, 'version info includes version');
104like($@, qr/$PARAM{url}/, 'version info includes url'); 104like($@, qr/$PARAM{url}/, 'version info includes url');
105unlike($@, qr/Usage:/, 'no usage message'); 105unlike($@, qr/Usage:/, 'no usage message');
106unlike($@, qr/Missing arg/, 'no missing arguments'); 106unlike($@, qr/Missing arg/, 'no missing arguments');
107
107@ARGV = ( '--version' ); 108@ARGV = ( '--version' );
108$ng = setup; 109$ng = setup;
109ok(! defined eval { $ng->getopts }, 'getopts died on version'); 110ok(! defined eval { $ng->getopts }, 'getopts died on version');
@@ -128,6 +129,7 @@ like($@, qr/--verbose/, 'help includes default options 2');
128like($@, qr/--warning/, 'help includes custom option 1'); 129like($@, qr/--warning/, 'help includes custom option 1');
129like($@, qr/--critical/, 'help includes custom option 2'); 130like($@, qr/--critical/, 'help includes custom option 2');
130unlike($@, qr/Missing arg/, 'no missing arguments'); 131unlike($@, qr/Missing arg/, 'no missing arguments');
132
131@ARGV = ( '--help' ); 133@ARGV = ( '--help' );
132$ng = setup; 134$ng = setup;
133ok(! defined eval { $ng->getopts }, 'getopts died on help'); 135ok(! defined eval { $ng->getopts }, 'getopts died on help');
@@ -140,6 +142,6 @@ like($@, qr/Usage:/, 'help includes usage message');
140like($@, qr/--version/, 'help includes default options 1'); 142like($@, qr/--version/, 'help includes default options 1');
141like($@, qr/--verbose/, 'help includes default options 2'); 143like($@, qr/--verbose/, 'help includes default options 2');
142like($@, qr/--warning/, 'help includes custom option 1'); 144like($@, qr/--warning/, 'help includes custom option 1');
143like($@, qr/--critical/, 'help includes custom option 2'); 145like($@, qr/-c, --critical=INTEGER/, 'help includes custom option 2, with expanded args');
144unlike($@, qr/Missing arg/, 'no missing arguments'); 146unlike($@, qr/Missing arg/, 'no missing arguments');
145 147
diff --git a/t/Nagios-Plugin-Getopt-03.t b/t/Nagios-Plugin-Getopt-03.t
index 9dc39da..557a2c6 100644
--- a/t/Nagios-Plugin-Getopt-03.t
+++ b/t/Nagios-Plugin-Getopt-03.t
@@ -1,4 +1,4 @@
1# Nagios::Plugin::Getopt --default-opts tests 1# Nagios::Plugin::Getopt --extra-opts tests
2 2
3use strict; 3use strict;
4use File::Spec; 4use File::Spec;
@@ -27,7 +27,8 @@ for my $efile (glob File::Spec->catfile($tdir, 'expected', '*')) {
27 } 27 }
28} 28}
29 29
30$Nagios::Plugin::Getopt::DEFAULT_CONFIG_FILE = File::Spec->catfile($tdir, 'plugins.cfg'); 30# Override NAGIOS_CONFIG_PATH to use our test plugins.ini file
31$ENV{NAGIOS_CONFIG_PATH} = "/random/bogus/path:$tdir";
31 32
32my %PARAM = ( 33my %PARAM = (
33 version => '0.01', 34 version => '0.01',
@@ -56,14 +57,18 @@ my $arg = [
56 { spec => 'S', help => '-S' }, 57 { spec => 'S', help => '-S' },
57 { spec => 'H=s', help => '-H' }, 58 { spec => 'H=s', help => '-H' },
58 { spec => 'p=s@', help => '-p' }, 59 { spec => 'p=s@', help => '-p' },
60 { spec => 'path=s@', help => '--path' },
59 { spec => 'username|u=s', help => '--username' }, 61 { spec => 'username|u=s', help => '--username' },
60 { spec => 'password=s', help => '--password' }, 62 { spec => 'password=s', help => '--password' },
61 { spec => 'critical=i', help => '--critical' }, 63 { spec => 'critical=s', help => '--critical' },
62 { spec => 'warning=i', help => '--warning' }, 64 { spec => 'warning=s', help => '--warning' },
63 { spec => 'expect=s', help => '--expect' }, 65 { spec => 'expect=s', help => '--expect' },
66 { spec => 'units=s', help => '--units' },
64]; 67];
65 68
66my %SKIP = map { $_ => 1 } qw(05_singlechar1 07_singlechar3); 69#my %SKIP = map { $_ => 1 } qw(05_singlechar1 07_singlechar3);
70#my %SKIP = map { $_ => 1 } qw(06_singlechar2);
71my %SKIP = ();
67 72
68# Process all test cases in $tdir/input 73# Process all test cases in $tdir/input
69my $glob = $ARGV[0] || '*'; 74my $glob = $ARGV[0] || '*';
@@ -82,7 +87,7 @@ for my $infile (glob File::Spec->catfile($tdir, 'input', $glob)) {
82 87
83 # Parse the options 88 # Parse the options
84 SKIP: { 89 SKIP: {
85 skip "Still discussing how overrides with multiple arguments should work ...", 1 if $SKIP{$infile}; 90 skip "Skipping ..." if $SKIP{$infile};
86 91
87 @ARGV = @args; 92 @ARGV = @args;
88 eval { $ng->getopts }; 93 eval { $ng->getopts };
diff --git a/t/Nagios-Plugin-Getopt-04.t b/t/Nagios-Plugin-Getopt-04.t
new file mode 100644
index 0000000..9092636
--- /dev/null
+++ b/t/Nagios-Plugin-Getopt-04.t
@@ -0,0 +1,95 @@
1# Nagios::Plugin::Getopt spec-to-help generation tests
2
3use strict;
4
5use Test::More tests => 11;
6BEGIN { use_ok('Nagios::Plugin::Getopt') };
7
8my %PARAM = (
9 version => '0.01',
10 usage => "Don't use this plugin!",
11);
12
13sub setup
14{
15 # Instantiate object
16 my $ng = Nagios::Plugin::Getopt->new(%PARAM);
17 ok($ng, 'constructor ok');
18
19 # Positional args, no short arguments, INTEGER
20 $ng->arg('warning=i' =>
21 qq(Exit with WARNING status if less than INTEGER foobars are free),
22 5);
23
24 # Named args, long + short arguments, INTEGER
25 $ng->arg(
26 spec => 'critical|c=i',
27 help => qq(Exit with CRITICAL status if less than INTEGER foobars are free),
28 required => 1,
29 );
30
31 # Named args, multiple short arguments, STRING, default expansion
32 $ng->arg(
33 spec => 'x|y|z=s',
34 help => qq(Foobar. Default: %s),
35 default => "XYZ",
36 );
37
38 # Named args, multiple mixed, no label
39 $ng->arg(
40 spec => 'long|longer|longest|l',
41 help => qq(Long format),
42 );
43
44 # Named args, long + short, explicit label
45 $ng->arg(
46 spec => 'hostname|H=s',
47 label => 'ADDRESS',
48 help => qq(Hostname),
49 );
50
51 # Positional args, long only, explicit label
52 $ng->arg('avatar=s', 'Avatar', undef, undef, 'AVATAR');
53
54 # Multiline help test, named args
55 $ng->arg(
56 spec => 'disk=s',
57 label => [ qw(BYTES PERCENT%), undef ],
58 help => [
59 qq(Disk limit in BYTES),
60 qq(Disk limit in PERCENT),
61 qq(Disk limit in FOOBARS (Default: %s)),
62 ],
63 default => 1024,
64 );
65
66 # Multiline help test, positional args
67 $ng->arg(
68 'limit=s',
69 [
70 qq(Limit in BYTES),
71 qq(Limit in PERCENT),
72 ],
73 undef,
74 undef,
75 [ undef, 'PERCENT%' ],
76 );
77
78 return $ng;
79}
80
81my $ng;
82
83@ARGV = ( '--help' );
84$ng = setup;
85ok(! defined eval { $ng->getopts }, 'getopts died on help');
86like($@, qr/\n --warning=INTEGER/, 'warning ok');
87like($@, qr/\n -c, --critical=INTEGER/, 'critical ok');
88like($@, qr/\n -x, -y, -z=STRING\n Foobar. Default: XYZ\n/, 'x|y|z ok');
89like($@, qr/\n -l, --long, --longer, --longest\n Long format\n/, 'long ok');
90like($@, qr/\n -H, --hostname=ADDRESS\n Hostname\n/, 'hostname ok');
91like($@, qr/\n --avatar=AVATAR\n Avatar\n/, 'avatar ok');
92like($@, qr/\n --disk=BYTES\n Disk limit in BYTES\n --disk=PERCENT%\n Disk limit in PERCENT\n --disk=STRING\n Disk limit in FOOBARS \(Default: 1024\)\n/, 'disk multiline ok');
93like($@, qr/\n --limit=STRING\n Limit in BYTES\n --limit=PERCENT%\n Limit in PERCENT\n/, 'limit multiline ok');
94#print $@;
95
diff --git a/t/npg03/README b/t/npg03/README
index a19f263..3dbdaf8 100644
--- a/t/npg03/README
+++ b/t/npg03/README
@@ -1,13 +1,15 @@
1Nagios-Plugin-Getopt-03.t automatically tests all cases defined in the 'input' directory 1Nagios-Plugin-Getopt-03.t automatically tests all cases defined in
2and expects the output to match the corresponding file in the 'expected' directory. To 2the 'input' directory and expects the output to match the
3define a new test case, just create a new file in the 'input' directory containing the 3corresponding file in the 'expected' directory. To define a new test
4input command line, and a corresponding file in the 'expected' directory containing 4case, just create a new file in the 'input' directory containing the
5what you think the expanded command line should be. Note that this expansion is normalised 5input command line, and a corresponding file in the 'expected'
6as follows: 6directory containing what you think the expanded command line should
7be. Note that this expansion is normalised as follows:
7 8
8- command line arguments are reported in alphabetical order 9- command line arguments are reported in alphabetical order
9- extraneous white space is removed 10- extraneous white space is removed
10 11
11Also, if you use a completely new argument than those defined in Nagios-Plugin-Getopt-03.t 12Also, if you use a completely new argument than those currently
12you will need to define it there as well. 13defined in Nagios-Plugin-Getopt-03.t you will need to define it
14there as well.
13 15
diff --git a/t/npg03/expected/00_noextra b/t/npg03/expected/00_noextra
new file mode 100644
index 0000000..d649587
--- /dev/null
+++ b/t/npg03/expected/00_noextra
@@ -0,0 +1 @@
check_mysql -H localhost -S
diff --git a/t/npg03/expected/05_disk1 b/t/npg03/expected/05_disk1
new file mode 100644
index 0000000..5570904
--- /dev/null
+++ b/t/npg03/expected/05_disk1
@@ -0,0 +1 @@
check_disk -p /tmp -p /home
diff --git a/t/npg03/expected/05_disk2 b/t/npg03/expected/05_disk2
new file mode 100644
index 0000000..692890b
--- /dev/null
+++ b/t/npg03/expected/05_disk2
@@ -0,0 +1 @@
check_disk -p /tmp -p /home -p /users
diff --git a/t/npg03/expected/05_disk3 b/t/npg03/expected/05_disk3
new file mode 100644
index 0000000..5252b4e
--- /dev/null
+++ b/t/npg03/expected/05_disk3
@@ -0,0 +1 @@
check_disk -p /tmp -p /var
diff --git a/t/npg03/expected/05_disk4 b/t/npg03/expected/05_disk4
new file mode 100644
index 0000000..34b382e
--- /dev/null
+++ b/t/npg03/expected/05_disk4
@@ -0,0 +1 @@
check_disk -p /tmp -p /var -p /home
diff --git a/t/npg03/expected/05_disk5 b/t/npg03/expected/05_disk5
new file mode 100644
index 0000000..ae61e62
--- /dev/null
+++ b/t/npg03/expected/05_disk5
@@ -0,0 +1 @@
check_disk -p /var -p /tmp -p /home
diff --git a/t/npg03/expected/05_disk6 b/t/npg03/expected/05_disk6
new file mode 100644
index 0000000..dc4870a
--- /dev/null
+++ b/t/npg03/expected/05_disk6
@@ -0,0 +1 @@
check_disk2 --critical=5% --path=/var --path=/home --path=/usr --units=GB --warning=10%
diff --git a/t/npg03/expected/05_singlechar1 b/t/npg03/expected/05_singlechar1
deleted file mode 100644
index 13a3f9b..0000000
--- a/t/npg03/expected/05_singlechar1
+++ /dev/null
@@ -1 +0,0 @@
1check_disk -p /home
diff --git a/t/npg03/expected/06_singlechar2 b/t/npg03/expected/06_singlechar2
deleted file mode 100644
index 8f9df5e..0000000
--- a/t/npg03/expected/06_singlechar2
+++ /dev/null
@@ -1 +0,0 @@
1check_disk -p /var
diff --git a/t/npg03/expected/07_singlechar3 b/t/npg03/expected/07_singlechar3
deleted file mode 100644
index f4e6ed7..0000000
--- a/t/npg03/expected/07_singlechar3
+++ /dev/null
@@ -1 +0,0 @@
1check_disk -p /home -p /users
diff --git a/t/npg03/input/00_basic b/t/npg03/input/00_basic
index 4c16788..f35f3c7 100644
--- a/t/npg03/input/00_basic
+++ b/t/npg03/input/00_basic
@@ -1 +1 @@
check_mysql -S --default-opts= --default-opts=more_options -H localhost check_mysql -S --extra-opts= --extra-opts=more_options -H localhost
diff --git a/t/npg03/input/00_noextra b/t/npg03/input/00_noextra
new file mode 100644
index 0000000..4d8a8fc
--- /dev/null
+++ b/t/npg03/input/00_noextra
@@ -0,0 +1 @@
check_mysql -S -H localhost
diff --git a/t/npg03/input/01_override1 b/t/npg03/input/01_override1
index 9e051e9..f3cd232 100644
--- a/t/npg03/input/01_override1
+++ b/t/npg03/input/01_override1
@@ -1 +1 @@
check_mysql --username=admin --default-opts=more_options --warning=5 check_mysql --username=admin --extra-opts=more_options --warning=5
diff --git a/t/npg03/input/02_override2 b/t/npg03/input/02_override2
index ceabe55..fa96ff7 100644
--- a/t/npg03/input/02_override2
+++ b/t/npg03/input/02_override2
@@ -1 +1 @@
check_mysql --default-opts= -u admin check_mysql --extra-opts= -u admin
diff --git a/t/npg03/input/05_disk1 b/t/npg03/input/05_disk1
new file mode 100644
index 0000000..5ccfe23
--- /dev/null
+++ b/t/npg03/input/05_disk1
@@ -0,0 +1 @@
check_disk --extra-opts= -p /home
diff --git a/t/npg03/input/05_disk2 b/t/npg03/input/05_disk2
new file mode 100644
index 0000000..53e36d9
--- /dev/null
+++ b/t/npg03/input/05_disk2
@@ -0,0 +1 @@
check_disk --extra-opts= -p /home -p /users
diff --git a/t/npg03/input/05_disk3 b/t/npg03/input/05_disk3
new file mode 100644
index 0000000..441accb
--- /dev/null
+++ b/t/npg03/input/05_disk3
@@ -0,0 +1 @@
check_disk --extra-opts=check_2_disks
diff --git a/t/npg03/input/05_disk4 b/t/npg03/input/05_disk4
new file mode 100644
index 0000000..da9d810
--- /dev/null
+++ b/t/npg03/input/05_disk4
@@ -0,0 +1 @@
check_disk -p /home --extra-opts=check_2_disks
diff --git a/t/npg03/input/05_disk5 b/t/npg03/input/05_disk5
new file mode 100644
index 0000000..9ba2d40
--- /dev/null
+++ b/t/npg03/input/05_disk5
@@ -0,0 +1 @@
check_disk -p /home --extra-opts=check_2_disks_reprise
diff --git a/t/npg03/input/05_disk6 b/t/npg03/input/05_disk6
new file mode 100644
index 0000000..c240d9c
--- /dev/null
+++ b/t/npg03/input/05_disk6
@@ -0,0 +1 @@
check_disk2 --warning=10% --critical=5% --extra-opts= --path=/usr
diff --git a/t/npg03/input/05_singlechar1 b/t/npg03/input/05_singlechar1
deleted file mode 100644
index 1edb8bf..0000000
--- a/t/npg03/input/05_singlechar1
+++ /dev/null
@@ -1 +0,0 @@
1check_disk --default-opts= -p /home
diff --git a/t/npg03/input/06_singlechar2 b/t/npg03/input/06_singlechar2
deleted file mode 100644
index 24965c7..0000000
--- a/t/npg03/input/06_singlechar2
+++ /dev/null
@@ -1 +0,0 @@
1check_disk --default-opts=check_2_disks
diff --git a/t/npg03/input/07_singlechar3 b/t/npg03/input/07_singlechar3
deleted file mode 100644
index 0abc70f..0000000
--- a/t/npg03/input/07_singlechar3
+++ /dev/null
@@ -1 +0,0 @@
1check_disk --default-opts= -p /home -p /users
diff --git a/t/npg03/input/09_funnystuff b/t/npg03/input/09_funnystuff
index c2d6160..ab279d9 100644
--- a/t/npg03/input/09_funnystuff
+++ b/t/npg03/input/09_funnystuff
@@ -1 +1 @@
check_disk --default-opts=funny_stuff check_disk --extra-opts=funny_stuff
diff --git a/t/npg03/input/13_nosection_explicit_dies b/t/npg03/input/13_nosection_explicit_dies
index 90aab51..f18660e 100644
--- a/t/npg03/input/13_nosection_explicit_dies
+++ b/t/npg03/input/13_nosection_explicit_dies
@@ -1 +1 @@
check_no_section --default-opts= -H localhost check_no_section --extra-opts= -H localhost
diff --git a/t/npg03/input/14_badsection_dies b/t/npg03/input/14_badsection_dies
index 70815a9..6b1c20a 100644
--- a/t/npg03/input/14_badsection_dies
+++ b/t/npg03/input/14_badsection_dies
@@ -1 +1 @@
check_no_section --default-opts=bad_section check_no_section --extra-opts=bad_section
diff --git a/t/npg03/plugins.cfg b/t/npg03/plugins.ini
index f893a21..2d3c551 100644
--- a/t/npg03/plugins.cfg
+++ b/t/npg03/plugins.ini
@@ -14,6 +14,15 @@ p=/tmp
14p=/tmp 14p=/tmp
15p=/var 15p=/var
16 16
17[check_2_disks_reprise]
18p=/var
19p=/tmp
20
21[check_disk2]
22path=/var
23path=/home
24units=GB
25
17[funny_stuff] 26[funny_stuff]
18username="Ton Voon" 27username="Ton Voon"
19p= 28p=