1package Monitoring::GLPlugin::Commandline::Getopt;
2use strict;
3use File::Basename;
4use Getopt::Long qw(:config no_ignore_case bundling);
5
6# Standard defaults
7my %DEFAULT = (
8  timeout => 15,
9  verbose => 0,
10  license =>
11"This monitoring plugin is free software, and comes with ABSOLUTELY NO WARRANTY.
12It may be used, redistributed and/or modified under the terms of the GNU
13General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).",
14);
15# Standard arguments
16my @ARGS = ({
17    spec => 'usage|?',
18    help => "-?, --usage\n   Print usage information",
19  }, {
20    spec => 'help|h',
21    help => "-h, --help\n   Print detailed help screen",
22  }, {
23    spec => 'version|V',
24    help => "-V, --version\n   Print version information",
25  }, {
26    #spec => 'extra-opts:s@',
27    #help => "--extra-opts=[<section>[@<config_file>]]\n   Section and/or config_file from which to load extra options (may repeat)",
28  }, {
29    spec => 'timeout|t=i',
30    help => sprintf("-t, --timeout=INTEGER\n   Seconds before plugin times out (default: %s)", $DEFAULT{timeout}),
31    default => $DEFAULT{timeout},
32  }, {
33    spec => 'verbose|v+',
34    help => "-v, --verbose\n   Show details for command-line debugging (can repeat up to 3 times)",
35    default => $DEFAULT{verbose},
36  },
37);
38# Standard arguments we traditionally display last in the help output
39my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose);
40
41sub _init {
42  my ($self, %params) = @_;
43  # Check params
44  my %attr = (
45    usage => 1,
46    version => 0,
47    url => 0,
48    plugin => { default => $Monitoring::GLPlugin::pluginname },
49    blurb => 0,
50    extra => 0,
51    'extra-opts' => 0,
52    license => { default => $DEFAULT{license} },
53    timeout => { default => $DEFAULT{timeout} },
54  );
55
56  # Add attr to private _attr hash (except timeout)
57  $self->{timeout} = delete $attr{timeout};
58  $self->{_attr} = { %attr };
59  foreach (keys %{$self->{_attr}}) {
60    if (exists $params{$_}) {
61      $self->{_attr}->{$_} = $params{$_};
62    } else {
63      $self->{_attr}->{$_} = $self->{_attr}->{$_}->{default}
64          if ref ($self->{_attr}->{$_}) eq 'HASH' &&
65              exists $self->{_attr}->{$_}->{default};
66    }
67  }
68  # Chomp _attr values
69  chomp foreach values %{$self->{_attr}};
70
71  # Setup initial args list
72  $self->{_args} = [ grep { exists $_->{spec} } @ARGS ];
73
74  $self
75}
76
77sub new {
78  my ($class, @params) = @_;
79  require Monitoring::GLPlugin::Commandline::Extraopts
80      if ! grep /BEGIN/, keys %Monitoring::GLPlugin::Commandline::Extraopts::;
81  my $self = bless {}, $class;
82  $self->_init(@params);
83}
84
85sub decode_rfc3986 {
86  my ($self, $password) = @_;
87  if ($password && $password =~ /^rfc3986:\/\/(.*)/) {
88    $password = $1;
89    $password =~ s/%([A-Za-z0-9]{2})/chr(hex($1))/seg;
90  }
91  return $password;
92}
93
94sub add_arg {
95  my ($self, %arg) = @_;
96  push (@{$self->{_args}}, \%arg);
97}
98
99sub mod_arg {
100  my ($self, $argname, %arg) = @_;
101  foreach my $old_arg (@{$self->{_args}}) {
102    next unless $old_arg->{spec} =~ /(\w+).*/ && $argname eq $1;
103    foreach my $key (keys %arg) {
104      $old_arg->{$key} = $arg{$key};
105    }
106  }
107}
108
109sub getopts {
110  my ($self) = @_;
111  my %commandline = ();
112  $self->{opts}->{all_my_opts} = {};
113  my @params = map { $_->{spec} } @{$self->{_args}};
114  if (! GetOptions(\%commandline, @params)) {
115    $self->print_help();
116    exit 3;
117  } else {
118    no strict 'refs';
119    no warnings 'redefine';
120    if (exists $commandline{'extra-opts'}) {
121      # read the extra file and overwrite other parameters
122      my $extras = Monitoring::GLPlugin::Commandline::Extraopts->new(
123          file => $commandline{'extra-opts'},
124          commandline => \%commandline
125      );
126      if (! $extras->is_valid()) {
127        printf "UNKNOWN - extra-opts are not valid: %s\n", $extras->errors();
128        exit 3;
129      } else {
130        $extras->overwrite();
131      }
132    }
133    do { $self->print_help(); exit 0; } if $commandline{help};
134    do { $self->print_version(); exit 0 } if $commandline{version};
135    do { $self->print_usage(); exit 3 } if $commandline{usage};
136    foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; } @{$self->{_args}}) {
137      my $field = $_;
138      *{"$field"} = sub {
139        return $self->{opts}->{$field};
140      };
141    }
142    *{"all_my_opts"} = sub {
143      return $self->{opts}->{all_my_opts};
144    };
145    foreach (@{$self->{_args}}) {
146      $_->{spec} =~ /^([\w\-]+)/;
147      my $spec = $1;
148      my $envname = uc $spec;
149      $envname =~ s/\-/_/g;
150      if (! exists $commandline{$spec}) {
151        # Kommandozeile hat oberste Prioritaet
152        # Also: --option ueberschreibt NAGIOS__HOSTOPTION
153        # Aaaaber: extra-opts haben immer noch Vorrang vor allem anderen.
154        # https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
155        # beschreibt das anders, Posix-Tools verhalten sich auch entsprechend.
156        # Irgendwann wird das hier daher umgeschrieben, so dass extra-opts
157        # die niedrigste Prioritaet erhalten.
158        if (exists $ENV{'NAGIOS__SERVICE'.$envname}) {
159          $commandline{$spec} = $ENV{'NAGIOS__SERVICE'.$envname};
160        } elsif (exists $ENV{'NAGIOS__HOST'.$envname}) {
161          $commandline{$spec} = $ENV{'NAGIOS__HOST'.$envname};
162        }
163      }
164      $self->{opts}->{$spec} = $_->{default};
165    }
166    foreach (map { $_->{spec} =~ /^([\w\-]+)/; $1; }
167        grep { exists $_->{required} && $_->{required} } @{$self->{_args}}) {
168      do { $self->print_usage(); exit 3 } if ! exists $commandline{$_};
169    }
170    foreach (grep { exists $_->{default} } @{$self->{_args}}) {
171      $_->{spec} =~ /^([\w\-]+)/;
172      my $spec = $1;
173      $self->{opts}->{$spec} = $_->{default};
174    }
175    foreach (keys %commandline) {
176      $self->{opts}->{$_} = $commandline{$_};
177      $self->{opts}->{all_my_opts}->{$_} = $commandline{$_};
178    }
179    foreach (grep { exists $_->{env} } @{$self->{_args}}) {
180      $_->{spec} =~ /^([\w\-]+)/;
181      my $spec = $1;
182      if (exists $ENV{'NAGIOS__HOST'.$_->{env}}) {
183        $self->{opts}->{$spec} = $ENV{'NAGIOS__HOST'.$_->{env}};
184      }
185      if (exists $ENV{'NAGIOS__SERVICE'.$_->{env}}) {
186        $self->{opts}->{$spec} = $ENV{'NAGIOS__SERVICE'.$_->{env}};
187      }
188    }
189    foreach (grep { exists $_->{aliasfor} } @{$self->{_args}}) {
190      my $field = $_->{aliasfor};
191      $_->{spec} =~ /^([\w\-]+)/;
192      my $aliasfield = $1;
193      next if $self->{opts}->{$field};
194      $self->{opts}->{$field} = $self->{opts}->{$aliasfield};
195      *{"$field"} = sub {
196        return $self->{opts}->{$field};
197      };
198    }
199    foreach (grep { exists $_->{decode} } @{$self->{_args}}) {
200      my $decoding = $_->{decode};
201      $_->{spec} =~ /^([\w\-]+)/;
202      my $spec = $1;
203      if (exists $self->{opts}->{$spec}) {
204        if ($decoding eq "rfc3986") {
205	  $self->{opts}->{$spec} =
206	      $self->decode_rfc3986($self->{opts}->{$spec});
207	}
208      }
209    }
210  }
211}
212
213sub create_opt {
214  my ($self, $key) = @_;
215  no strict 'refs';
216  *{"$key"} = sub {
217      return $self->{opts}->{$key};
218  };
219}
220
221sub override_opt {
222  my ($self, $key, $value) = @_;
223  $self->{opts}->{$key} = $value;
224}
225
226sub get {
227  my ($self, $opt) = @_;
228  return $self->{opts}->{$opt};
229}
230
231sub print_help {
232  my ($self) = @_;
233  $self->print_version();
234  printf "\n%s\n", $self->{_attr}->{license};
235  printf "\n%s\n\n", $self->{_attr}->{blurb};
236  $self->print_usage();
237  foreach (grep {
238      ! (exists $_->{hidden} && $_->{hidden})
239  } @{$self->{_args}}) {
240    printf " %s\n", $_->{help};
241  }
242}
243
244sub print_usage {
245  my ($self) = @_;
246  printf $self->{_attr}->{usage}, $self->{_attr}->{plugin};
247  print "\n";
248}
249
250sub print_version {
251  my ($self) = @_;
252  printf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version};
253  printf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url};
254  print "\n";
255}
256
257sub print_license {
258  my ($self) = @_;
259  printf "%s\n", $self->{_attr}->{license};
260  print "\n";
261}
262
2631;
264
265__END__
266