1package Monitoring::Plugin::Getopt;
2
3#
4# Monitoring::Plugin::Getopt - OO perl module providing standardised argument
5#   processing for nagios plugins
6#
7
8use 5.006;
9use strict;
10use warnings;
11
12use File::Basename;
13use Getopt::Long qw(:config no_ignore_case bundling);
14use Carp;
15use Params::Validate qw(:all);
16use base qw(Class::Accessor);
17
18use Monitoring::Plugin::Functions;
19use Monitoring::Plugin::Config;
20use vars qw($VERSION);
21$VERSION = $Monitoring::Plugin::Functions::VERSION;
22
23# Standard defaults
24my %DEFAULT = (
25  timeout => 15,
26  verbose => 0,
27  license =>
28"This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
29It may be used, redistributed and/or modified under the terms of the GNU
30General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).",
31);
32# Standard arguments
33my @ARGS = ({
34    spec => 'usage|?',
35    help => "-?, --usage\n   Print usage information",
36  }, {
37    spec => 'help|h',
38    help => "-h, --help\n   Print detailed help screen",
39  }, {
40    spec => 'version|V',
41    help => "-V, --version\n   Print version information",
42  }, {
43    spec => 'extra-opts:s@',
44    help => "--extra-opts=[section][\@file]\n   Read options from an ini file. See https://www.monitoring-plugins.org/doc/extra-opts.html\n   for usage and examples.",
45  }, {
46    spec => 'timeout|t=i',
47    help => "-t, --timeout=INTEGER\n   Seconds before plugin times out (default: %s)",
48    default => $DEFAULT{timeout},
49  }, {
50    spec => 'verbose|v+',
51    help => "-v, --verbose\n   Show details for command-line debugging (can repeat up to 3 times)",
52    default => $DEFAULT{verbose},
53  },
54);
55# Standard arguments we traditionally display last in the help output
56my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose);
57
58# -------------------------------------------------------------------------
59# Private methods
60
61sub _die
62{
63  my $self = shift;
64  my ($msg) = @_;
65  $msg .= "\n" unless substr($msg, -1) eq "\n";
66  Monitoring::Plugin::Functions::_plugin_exit(3, $msg);
67}
68
69# Return the given attribute, if set, including a final newline
70sub _attr
71{
72  my $self = shift;
73  my ($item, $extra) = @_;
74  $extra = '' unless defined $extra;
75  return '' unless $self->{_attr}->{$item};
76  $self->{_attr}->{$item} . "\n" . $extra;
77}
78
79# Turn argument spec into help-style output
80sub _spec_to_help
81{
82  my ($self, $spec, $label) = @_;
83
84  my ($opts, $type) = split /=|:|!/, $spec, 2;
85  my $optional = ($spec =~ m/:/);
86  my $boolean = ($spec =~ m/!/);
87  my (@short, @long);
88  for (split /\|/, $opts) {
89    if (length $_ == 1) {
90      push @short, "-$_";
91    } else {
92      push @long, $boolean ? "--[no-]$_" : "--$_";
93    }
94  }
95
96  my $help = join(', ', @short, @long);
97  if ($type) {
98    if (!$label) {
99      if ($type eq 'i' || $type eq '+' || $type =~ /\d+/) {
100        $label = 'INTEGER';
101      }
102      else {
103        $label = 'STRING';
104      }
105    }
106
107    if ($optional) {
108      $help .= '[=' . $label . ']';
109    }
110    else {
111      $help .= '=' . $label;
112    }
113  }
114  elsif ($label) {
115    carp "Label specified, but there's no type in spec '$spec'";
116  }
117  $help .= "\n   ";
118  return $help;
119}
120
121# Options output for plugin -h
122sub _options
123{
124  my $self = shift;
125
126  my @args = ();
127  my @defer = ();
128  for (@{$self->{_args}}) {
129    if (exists $DEFER_ARGS{$_->{name}}) {
130      push @defer, $_;
131    } else {
132      push @args, $_;
133    }
134  }
135
136  my @options = ();
137  for my $arg (@args, @defer) {
138    my $help_array = ref $arg->{help} && ref $arg->{help} eq 'ARRAY' ? $arg->{help} : [ $arg->{help} ];
139    my $label_array = $arg->{label} && ref $arg->{label} && ref $arg->{label} eq 'ARRAY' ? $arg->{label} : [ $arg->{label} ];
140    my $help_string = '';
141    for (my $i = 0; $i <= $#$help_array; $i++) {
142      my $help = $help_array->[$i];
143      # Add spec arguments to help if not already there
144      if ($help =~ m/^\s*-/) {
145        $help_string .= $help;
146      }
147      else {
148        $help_string .= $self->_spec_to_help($arg->{spec}, $label_array->[$i]) . $help;
149        $help_string .= "\n " if $i < $#$help_array;
150      }
151    }
152
153    # Add help_string to @options
154    if ($help_string =~ m/%s/) {
155      my $default = defined $arg->{default} ? $arg->{default} : '';
156      # We only handle '%s' formats here
157      my $replaced = $help_string;
158      $replaced =~ s|%s|$default|gmx;
159      push @options, $replaced;
160    } else {
161      push @options, $help_string;
162    }
163  }
164
165  return ' ' . join("\n ", @options);
166}
167
168# Output for plugin -? (or missing/invalid args)
169sub _usage {
170  my $self  = shift;
171  my $usage = $self->_attr('usage');
172  $usage    =~ s|%s|$self->{_attr}->{plugin}|gmx;
173  return($usage);
174}
175
176# Output for plugin -V
177sub _revision
178{
179  my $self = shift;
180  my $revision = sprintf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version};
181  $revision .= sprintf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url};
182  $revision .= "\n";
183  $revision;
184}
185
186# Output for plugin -h
187sub _help
188{
189  my $self = shift;
190  my $help = '';
191  $help .= $self->_revision . "\n";
192  $help .= $self->_attr('license', "\n");
193  $help .= $self->_attr('blurb', "\n");
194  $help .= $self->_usage   ? $self->_usage   . "\n" : '';
195  $help .= $self->_options ? $self->_options . "\n" : '';
196  $help .= $self->_attr('extra', "\n");
197  return $help;
198}
199
200# Return a Getopt::Long-compatible option array from the current set of specs
201sub _process_specs_getopt_long
202{
203  my $self = shift;
204
205  my @opts = ();
206  for my $arg (@{$self->{_args}}) {
207    push @opts, $arg->{spec};
208    # Setup names and defaults
209    my $spec = $arg->{spec};
210    # Use first arg as name (like Getopt::Long does)
211    $spec =~ s/[=:!].*$//;
212    my $name = (split /\s*\|\s*/, $spec)[0];
213    $arg->{name} = $name;
214    if (defined $self->{$name}) {
215      $arg->{default} = $self->{$name};
216    } else {
217      $self->{$name} = $arg->{default};
218    }
219  }
220
221  return @opts;
222}
223
224# Check for existence of required arguments
225sub _check_required_opts
226{
227  my $self = shift;
228
229  my @missing = ();
230  for my $arg (@{$self->{_args}}) {
231    if ($arg->{required} && ! defined $self->{$arg->{name}}) {
232      push @missing, $arg->{name};
233    }
234  }
235  if (@missing) {
236    $self->_die($self->_usage . "\n" .
237        join("\n", map { sprintf "Missing argument: %s", $_ } @missing) . "\n");
238  }
239}
240
241# Process and handle any immediate options
242sub _process_opts
243{
244  my $self = shift;
245
246  # Print message and exit for usage, version, help
247  $self->_die($self->_usage)    if $self->{usage};
248  $self->_die($self->_revision) if $self->{version};
249  $self->_die($self->_help)     if $self->{help};
250}
251
252# -------------------------------------------------------------------------
253# Default opts methods
254
255sub _load_config_section
256{
257  my $self = shift;
258  my ($section, $file, $flags) = @_;
259  $section ||= $self->{_attr}->{plugin};
260
261  my $Config;
262  eval { $Config = Monitoring::Plugin::Config->read($file); };
263  $self->_die($@) if ($@);
264  defined $Config
265      or $self->_die(Monitoring::Plugin::Config->errstr);
266
267  # TODO: is this check sane? Does --extra-opts=foo require a [foo] section?
268  ## Nevertheless, if we die as UNKNOWN here we should do the same on default
269  ## file *added eval/_die above*.
270  $file ||= $Config->mp_getfile();
271  $self->_die("Invalid section '$section' in config file '$file'")
272    unless exists $Config->{$section};
273
274  return $Config->{$section};
275}
276
277# Helper method to setup a hash of spec definitions for _cmdline
278sub _setup_spec_index
279{
280  my $self = shift;
281  return if defined $self->{_spec};
282  $self->{_spec} = { map { $_->{name} => $_->{spec} } @{$self->{_args}} };
283}
284
285# Quote values that require it
286sub _cmdline_value
287{
288  my $self = shift;
289  local $_ = shift;
290  if (m/\s/ && (m/^[^"']/ || m/[^"']$/)) {
291    return qq("$_");
292  }
293  elsif ($_ eq '') {
294    return q("");
295  }
296  else {
297    return $_;
298  }
299}
300
301# Helper method to format key/values in $hash in a quasi-commandline format
302sub _cmdline
303{
304  my $self = shift;
305  my ($hash) = @_;
306  $hash ||= $self;
307
308  $self->_setup_spec_index;
309
310  my @args = ();
311  for my $key (sort keys %$hash) {
312    # Skip internal keys
313    next if $key =~ m/^_/;
314
315    # Skip defaults and internals
316    next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key};
317    next if grep { $key eq $_ } qw(help usage version extra-opts);
318    next unless defined $hash->{$key};
319
320    # Render arg
321    my $spec = $self->{_spec}->{$key} || '';
322    if ($spec =~ m/[=:].+$/) {
323      # Arg takes value - may be a scalar or an arrayref
324      for my $value (ref $hash->{$key} eq 'ARRAY' ? @{$hash->{$key}} : ( $hash->{$key} )) {
325        $value = $self->_cmdline_value($value);
326        if (length($key) > 1) {
327          push @args, sprintf "--%s=%s", $key, $value;
328        }
329        else {
330          push @args, "-$key", $value;
331        }
332      }
333    }
334
335    else {
336      # Flag - render long or short based on option length
337      push @args, (length($key) > 1 ? '--' : '-') . $key;
338    }
339  }
340
341  return wantarray ? @args : join(' ', @args);
342}
343
344# Process and load extra-opts sections
345sub _process_extra_opts
346{
347  my $self = shift;
348  my ($args) = @_;
349
350  my $extopts_list = $args->{'extra-opts'};
351
352  my @sargs = ();
353  for my $extopts (@$extopts_list) {
354    $extopts ||= $self->{_attr}->{plugin};
355    my $section = $extopts;
356    my $file = '';
357
358    # Parse section@file
359    if ($extopts =~ m/^([^@]*)@(.*?)\s*$/) {
360      $section = $1;
361      $file = $2;
362    }
363
364    # Load section args
365    my $shash = $self->_load_config_section($section, $file);
366
367    # Turn $shash into a series of commandline-like arguments
368    push @sargs, $self->_cmdline($shash);
369  }
370
371  # Reset ARGV to extra-opts + original
372  @ARGV = ( @sargs, @{$self->{_attr}->{argv}} );
373
374  printf "[extra-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV)
375    if $args->{verbose} && $args->{verbose} >= 3;
376}
377
378# -------------------------------------------------------------------------
379# Public methods
380
381# Define plugin argument
382sub arg
383{
384  my $self = shift;
385  my %args;
386
387  # Param name to required boolean
388  my %params = (
389      spec     => 1,
390      help     => 1,
391      default  => 0,
392      required => 0,
393      label    => 0,
394  );
395
396  # Named args
397  if (exists $params{$_[0]} && @_ % 2 == 0) {
398    %args = validate( @_, \%params );
399  }
400
401  # Positional args
402  else {
403    my @order = qw(spec help default required label);
404    @args{@order} = validate_pos(@_, @params{@order});
405  }
406
407  # Add to private args arrayref
408  push @{$self->{_args}}, \%args;
409}
410
411# Process the @ARGV array using the current _args list (possibly exiting)
412sub getopts
413{
414  my $self = shift;
415
416  # Collate spec arguments for Getopt::Long
417  my @opt_array = $self->_process_specs_getopt_long;
418
419  # Capture original @ARGV (for extra-opts games)
420  $self->{_attr}->{argv} = [ @ARGV ];
421
422  # Call GetOptions using @opt_array
423  my $args1 = {};
424  my $ok = GetOptions($args1, @opt_array);
425  # Invalid options - give usage message and exit
426  $self->_die($self->_usage) unless $ok;
427
428  # Process extra-opts
429  $self->_process_extra_opts($args1);
430
431  # Call GetOptions again, this time including extra-opts
432  $ok = GetOptions($self, @opt_array);
433  # Invalid options - give usage message and exit
434  $self->_die($self->_usage) unless $ok;
435
436  # Process immediate options (possibly exiting)
437  $self->_process_opts;
438
439  # Required options (possibly exiting)
440  $self->_check_required_opts;
441
442  # Setup accessors for options
443  $self->mk_ro_accessors(grep ! /^_/, keys %$self);
444
445  # Setup default alarm handler for alarm($ng->timeout) in plugin
446  $SIG{ALRM} = sub {
447    my $plugin = uc $self->{_attr}->{plugin};
448    $plugin =~ s/^CHECK[-_]//i;
449    $self->_die(
450      sprintf("%s UNKNOWN - plugin timed out (timeout %ss)",
451        $plugin, $self->timeout));
452  };
453}
454
455# -------------------------------------------------------------------------
456# Constructor
457
458sub _init
459{
460  my $self = shift;
461
462  # Check params
463  my $plugin = basename($ENV{PLUGIN_NAME} || $ENV{NAGIOS_PLUGIN} || $0);
464  my %attr = validate( @_, {
465    usage => 1,
466    version => 0,
467    url => 0,
468    plugin => { default => $plugin },
469    blurb => 0,
470    extra => 0,
471    'extra-opts' => 0,
472    license => { default => $DEFAULT{license} },
473    timeout => { default => $DEFAULT{timeout} },
474  });
475
476  # Add attr to private _attr hash (except timeout)
477  $self->{timeout} = delete $attr{timeout};
478  $self->{_attr} = { %attr };
479  # Chomp _attr values
480  chomp foreach values %{$self->{_attr}};
481
482  # Setup initial args list
483  $self->{_args} = [ @ARGS ];
484
485  $self
486}
487
488sub new
489{
490  my $class = shift;
491  my $self = bless {}, $class;
492  $self->_init(@_);
493}
494
495# -------------------------------------------------------------------------
496
4971;
498
499__END__
500
501=head1 NAME
502
503Monitoring::Plugin::Getopt - OO perl module providing standardised argument
504processing for Nagios plugins
505
506
507=head1 SYNOPSIS
508
509  use Monitoring::Plugin::Getopt;
510
511  # Instantiate object (usage is mandatory)
512  $ng = Monitoring::Plugin::Getopt->new(
513    usage => "Usage: %s -H <host> -w <warning> -c <critical>",
514    version => '0.1',
515    url => 'http://www.openfusion.com.au/labs/nagios/',
516    blurb => 'This plugin tests various stuff.',
517  );
518
519  # Add argument - named parameters (spec and help are mandatory)
520  $ng->arg(
521    spec => 'critical|c=i',
522    help => q(Exit with CRITICAL status if fewer than INTEGER foobars are free),
523    required => 1,
524    default => 10,
525  );
526
527  # Add argument - positional parameters - arg spec, help text,
528  #   default value, required? (first two mandatory)
529  $ng->arg(
530    'warning|w=i',
531    q(Exit with WARNING status if fewer than INTEGER foobars are free),
532    5,
533    1);
534
535  # Parse arguments and process standard ones (e.g. usage, help, version)
536  $ng->getopts;
537
538  # Access arguments using named accessors or or via the generic get()
539  print $ng->opts->warning;
540  print $ng->opts->get('critical');
541
542
543
544=head1 DESCRIPTION
545
546Monitoring::Plugin::Getopt is an OO perl module providing standardised and
547simplified argument processing for Nagios plugins. It implements
548a number of standard arguments itself (--help, --version,
549--usage, --timeout, --verbose, and their short form counterparts),
550produces standardised nagios plugin help output, and allows
551additional arguments to be easily defined.
552
553
554=head2 CONSTRUCTOR
555
556  # Instantiate object (usage is mandatory)
557  $ng = Monitoring::Plugin::Getopt->new(
558    usage => 'Usage: %s --hello',
559    version => '0.01',
560  );
561
562The Monitoring::Plugin::Getopt constructor accepts the following named
563arguments:
564
565=over 4
566
567=item usage (required)
568
569Short usage message used with --usage/-? and with missing required
570arguments, and included in the longer --help output. Can include
571a '%s' sprintf placeholder which will be replaced with the plugin
572name e.g.
573
574  usage => qq(Usage: %s -H <hostname> -p <ports> [-v]),
575
576might be displayed as:
577
578  $ ./check_tcp_range --usage
579  Usage: check_tcp_range -H <hostname> -p <ports> [-v]
580
581=item version (required)
582
583Plugin version number, included in the --version/-V output, and in
584the longer --help output. e.g.
585
586  $ ./check_tcp_range --version
587  check_tcp_range 0.2 [http://www.openfusion.com.au/labs/nagios/]
588
589=item url
590
591URL for info about this plugin, included in the --version/-V output,
592and in the longer --help output (see preceding 'version' example).
593
594=item blurb
595
596Short plugin description, included in the longer --help output
597(see below for an example).
598
599=item license
600
601License text, included in the longer --help output (see below for an
602example). By default, this is set to the standard nagios plugins
603GPL license text:
604
605  This nagios plugin is free software, and comes with ABSOLUTELY
606  NO WARRANTY. It may be used, redistributed and/or modified under
607  the terms of the GNU General Public Licence (see
608  http://www.fsf.org/licensing/licenses/gpl.txt).
609
610Provide your own to replace this text in the help output.
611
612=item extra
613
614Extra text to be appended at the end of the longer --help output.
615
616=item plugin
617
618Plugin name. This defaults to the basename of your plugin, which is
619usually correct, but you can set it explicitly if not.
620
621=item timeout
622
623Timeout period in seconds, overriding the standard timeout default
624(15 seconds).
625
626=back
627
628The full --help output has the following form:
629
630  version string
631
632  license string
633
634  blurb
635
636  usage string
637
638  options list
639
640  extra text
641
642The 'blurb' and 'extra text' sections are omitted if not supplied. For
643example:
644
645  $ ./check_tcp_range -h
646  check_tcp_range 0.2 [http://www.openfusion.com.au/labs/nagios/]
647
648  This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
649  It may be used, redistributed and/or modified under the terms of the GNU
650  General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).
651
652  This plugin tests arbitrary ranges/sets of tcp ports for a host.
653
654  Usage: check_tcp_range -H <hostname> -p <ports> [-v]
655
656  Options:
657   -h, --help
658     Print detailed help screen
659   -V, --version
660     Print version information
661   -H, --hostname=ADDRESS
662     Host name or IP address
663   -p, --ports=STRING
664     Port numbers to check. Format: comma-separated, colons for ranges,
665     no spaces e.g. 8700:8705,8710:8715,8760
666   -t, --timeout=INTEGER
667     Seconds before plugin times out (default: 15)
668   -v, --verbose
669     Show details for command-line debugging (can repeat up to 3 times)
670
671
672=head2 ARGUMENTS
673
674You can define arguments for your plugin using the arg() method, which
675supports both named and positional arguments. In both cases
676the C<spec> and C<help> arguments are required, while the C<label>,
677C<default>, and C<required> arguments are optional:
678
679  # Define --hello argument (named parameters)
680  $ng->arg(
681    spec => 'hello|h=s',
682    help => "Hello string",
683    required => 1,
684  );
685
686  # Define --hello argument (positional parameters)
687  #   Parameter order is 'spec', 'help', 'default', 'required?', 'label'
688  $ng->arg('hello|h=s', "Hello parameter (default %s)", 5, 1);
689
690=over 4
691
692=item spec
693
694The C<spec> argument (the first argument in the positional variant) is a
695L<Getopt::Long> argument specification. See L<Getopt::Long> for the details,
696but basically it is a series of one or more argument names for this argument
697(separated by '|'), suffixed with an '=<type>' indicator if the argument
698takes a value. '=s' indicates a string argument; '=i' indicates an integer
699argument; appending an '@' indicates multiple such arguments are accepted;
700appending an '!' indicates negation using '--no'-prefix is possible; and so on.
701The following are some examples:
702
703=over 4
704
705=item hello=s
706
707=item hello|h=s
708
709=item ports|port|p=i
710
711=item exclude|X=s@
712
713=item perfdata!
714
715=item verbose|v+
716
717=back
718
719=item help
720
721The C<help> argument is a string displayed in the --help option list output,
722or it can be a list (an arrayref) of such strings, for multi-line help (see
723below).
724
725The help string is munged in two ways:
726
727=over 4
728
729=item
730
731First, if the help string does NOT begins with a '-' sign, it is prefixed
732by an expanded form of the C<spec> argument. For instance, the following
733hello argument:
734
735  $ng->arg(
736    spec => 'hello|h=s',
737    help => "Hello string",
738  );
739
740would be displayed in the help output as:
741
742  -h, --hello=STRING
743    Hello string
744
745where the '-h, --hello=STRING' part is derived from the spec definition
746(by convention with short args first, then long, then label/type, if any).
747
748=item
749
750Second, if the string contains a '%s' it will be formatted via
751C<sprintf> with the 'default' as the argument i.e.
752
753  sprintf($help, $default)
754
755=back
756
757Multi-line help is useful in cases where an argument can be of different types
758and you want to make this explicit in your help output e.g.
759
760  $ng->arg(
761    spec => 'warning|w=s',
762    help => [
763      'Exit with WARNING status if less than BYTES bytes of disk are free',
764      'Exit with WARNING status if less than PERCENT of disk is free',
765    ],
766    label => [ 'BYTES', 'PERCENT%' ],
767  );
768
769would be displayed in the help output as:
770
771 -w, --warning=BYTES
772    Exit with WARNING status if less than BYTES bytes of disk are free
773 -w, --warning=PERCENT%
774    Exit with WARNING status if less than PERCENT of disk space is free
775
776Note that in this case we've also specified explicit labels in another
777arrayref corresponding to the C<help> one - if this had been omitted
778the types would have defaulted to 'STRING', instead of 'BYTES' and
779'PERCENT%'.
780
781
782=item label
783
784The C<label> argument is a scalar or an arrayref (see 'Multi-line help'
785description above) that overrides the standard type expansion when generating
786help text from the spec definition. By default, C<spec=i> arguments are
787labelled as C<=INTEGER> in the help text, and C<spec=s> arguments are labelled
788as C<=STRING>. By supplying your own C<label> argument you can override these
789standard 'INTEGER' and 'STRING' designations.
790
791For multi-line help, you can supply an ordered list (arrayref) of labels to
792match the list of help strings e.g.
793
794  label => [ 'BYTES', 'PERCENT%' ]
795
796Any labels that are left as undef (or just omitted, if trailing) will just
797use the default 'INTEGER' or 'STRING' designations e.g.
798
799  label => [ undef, 'PERCENT%' ]
800
801
802=item default
803
804The C<default> argument is the default value to be given to this parameter
805if none is explicitly supplied.
806
807
808=item required
809
810The C<required> argument is a boolean used to indicate that this argument
811is mandatory (Monitoring::Plugin::Getopt will exit with your usage message and
812a 'Missing argument' indicator if any required arguments are not supplied).
813
814=back
815
816Note that --help lists your arguments in the order they are defined, so
817you should order your C<arg()> calls accordingly.
818
819
820=head2 GETOPTS
821
822The main parsing and processing functionality is provided by the getopts()
823method, which takes no arguments:
824
825  # Parse and process arguments
826  $ng->getopts;
827
828This parses the command line arguments passed to your plugin using
829Getopt::Long and the builtin and provided argument specifications.
830Flags and argument values are recorded within the object, and can
831be accessed either using the generic get() accessor, or using named
832accessors corresponding to your argument names. For example:
833
834  print $ng->get('hello');
835  print $ng->hello();
836
837  if ($ng->verbose) {
838    # ...
839  }
840
841  if ($ng->get('ports') =~ m/:/) {
842    # ...
843  }
844
845Note that where you have defined alternate argument names, the first is
846considered the citation form. All the builtin arguments are available
847using their long variant names.
848
849
850=head2 BUILTIN PROCESSING
851
852The C<getopts()> method also handles processing of the immediate builtin
853arguments, namely --usage, --version, --help, as well as checking all
854required arguments have been supplied, so you don't have to handle
855those yourself. This means that your plugin will exit from the getopts()
856call in these cases - if you want to catch that you can run getopts()
857within an eval{}.
858
859C<getopts()> also sets up a default ALRM timeout handler so you can use an
860
861  alarm $ng->timeout;
862
863around any blocking operations within your plugin (which you are free
864to override if you want to use a custom timeout message).
865
866
867=head1 SEE ALSO
868
869Monitoring::Plugin, Getopt::Long
870
871
872=head1 AUTHOR
873
874This code is maintained by the Monitoring Plugin Development Team: see
875https://monitoring-plugins.org
876
877Originally:
878  Gavin Carr <gavin@openfusion.com.au>
879
880=head1 COPYRIGHT AND LICENSE
881
882Copyright (C) 2014      by Monitoring Plugin Team
883Copyright (C) 2006-2014 by Nagios Plugin Development Team
884
885This library is free software; you can redistribute it and/or modify
886it under the same terms as Perl itself.
887
888=cut
889