File centreon_linux_local.pl of Package monitoring-plugins-centreon-linux-local

#!/usr/bin/perl

# This chunk of stuff was generated by App::FatPacker. To find the original
# file's code, look for the end of this BEGIN block or the string 'FATPACK'
BEGIN {
my %fatpacked;

$fatpacked{"centreon/plugins/alternative/FatPackerOptions.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_ALTERNATIVE_FATPACKEROPTIONS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::alternative::FatPackerOptions;
  
  use base qw(centreon::plugins::options);
  
  use strict;
  use warnings;
  use Pod::Usage;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      return $self;
  }
  
  sub display_help {
      my ($self, %options) = @_;
      
      my $stdout;
      foreach (@{$self->{pod_package}}) {
          
          {
              my $pp = $_->{package} . ".pm";
              $pp =~ s{::}{/}g;
              my $content_class = $INC{$pp}->{$pp};
              open my $str_fh, '<', \$content_class;
              
              local *STDOUT;
              open STDOUT, '>', \$stdout;
              pod2usage(-exitval => 'NOEXIT', -input => $str_fh,
                        -verbose => 99, 
                        -sections => $_->{sections});
              
              close $str_fh;
          }
          
          $self->{output}->add_option_msg(long_msg => $stdout) if (defined($stdout));
      }
  }
  
  1;
  
CENTREON_PLUGINS_ALTERNATIVE_FATPACKEROPTIONS

$fatpacked{"centreon/plugins/alternative/Getopt.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_ALTERNATIVE_GETOPT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::alternative::Getopt;
  
  use strict;
  use warnings;
  
  use Exporter;
  use vars qw(@ISA @EXPORT @EXPORT_OK);
  @ISA = qw(Exporter);
  
  BEGIN {
      @EXPORT    = qw(&GetOptions);
      @EXPORT_OK = qw();
  }
  
  use vars @EXPORT, @EXPORT_OK;
  
  our $warn_message = 0;
  
  sub get_assigned_value {
      my (%options) = @_;
      
      if (!defined($options{val}) || $options{val} eq '') {
          # Add defined also. Hardened code: already see: $ARGV[6] = undef for example
          if ($options{pos} + 1 < $options{num_args} && defined($ARGV[$options{pos} + 1]) && $ARGV[$options{pos} + 1] !~ /^--/) {
              my $val = $ARGV[$options{pos} + 1];
              splice @ARGV, $options{pos} + 1, 1;
              return ($options{num_args} - 1, $val);
          } else {
              return ($options{num_args}, '');
          }
      }
      
      return ($options{num_args}, $options{val});
  }
  
  sub GetOptions {
      my (%opts) = @_;
  
      my $search_str = ',' . join(',', keys %opts) . ',';
      my $num_args = scalar(@ARGV);
      for (my $i = 0; $i < $num_args;) {
          if (defined($ARGV[$i]) && $ARGV[$i] =~ /^--(.*?)(?:=|$)((?s).*)/) {
              my ($option, $value) = ($1, $2);
  
              # The special argument "--" forces an end of option-scanning.
              # All arguments placed after are stored in a list with the special option key '_double_dash_'.
              if ($option eq '' && $value eq '') {
                  my @values = splice @ARGV, $i + 1, $num_args - $i - 1;
                  push @{${$opts{'_double_dash_'}}}, @values;
                  splice @ARGV, $i, 1;
                  last;
              }
  
              # find type of option
              if ($search_str !~ /,((?:[^,]*?\|){0,}$option(?:\|.*?){0,}(:.*?){0,1}),/) {
  
                  # for old format plugins (with run function) that not allowed list-counters options
                  if($option =~ /list-counters/){
                      warn "list-counters option not available yet for this mode." if ($warn_message == 1);
                  }else{
                      warn "Unknown option: $option" if ($warn_message == 1);
                  }
                  $i++;
                  next;
              }
  
              my ($option_selected, $type_opt) = ($1, $2);
              if (!defined($type_opt)) {
                  ($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
                  if ($assigned ne '0') {
                      ${$opts{$option_selected}} = 1;
                  }
              } elsif ($type_opt =~ /:s$/) {
                  ($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
                  ${$opts{$option_selected}} = $assigned;
              } elsif ($type_opt =~ /:s\@$/) {
                  ${$opts{$option . $type_opt}} = [] if (!defined(${$opts{$option . $type_opt}}));
                  ($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
                  push @{${$opts{$option_selected}}}, $assigned;
              } elsif ($type_opt =~ /:s\%$/) {
                  ${$opts{$option . $type_opt}} = {} if (!defined(${$opts{$option . $type_opt}}));
                  ($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
                  if ($assigned =~ /^(.*?)=(.*)/) {
                      ${$opts{$option_selected}}->{$1} = $2;
                  }
              } 
              
              splice @ARGV, $i, 1;
              $num_args--;
          } else {
              warn "argument $ARGV[$i] alone" if ($warn_message == 1 && $i != 0 && defined($ARGV[$i]));
              $i++;
          }
      }
  }
  
  1;
  
CENTREON_PLUGINS_ALTERNATIVE_GETOPT

$fatpacked{"centreon/plugins/backend/http/curl.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_CURL';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::http::curl;
  
  use strict;
  use warnings;
  use URI;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'curl-opt:s@' => { name => 'curl_opt' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND CURL OPTIONS', once => 1);
      }
  
      $self->{output} = $options{output};
  
      $self->{curl_log} = $options{curl_logger};
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      centreon::plugins::misc::mymodule_load(
          output => $self->{output},
          module => 'Net::Curl::Easy',
          error_msg => "Cannot load module 'Net::Curl::Easy'."
      );
      centreon::plugins::misc::mymodule_load(
          output => $self->{output},
          module => 'centreon::plugins::backend::http::curlconstants',
          error_msg => "Cannot load module 'centreon::plugins::backend::http::curlconstants'."
      );
      $self->{constant_cb} = \&centreon::plugins::backend::http::curlconstants::get_constant_value;
  
      foreach (('unknown_status', 'warning_status', 'critical_status')) {
          if (defined($options{request}->{$_})) {
              $options{request}->{$_} =~ s/%\{http_code\}/\$values->{code}/g;
          }
      }
  
      if (!defined($options{request}->{curl_opt})) {
          $options{request}->{curl_opt} = [];
      }
  }
  
  my $http_code_explained = {
      100 => 'Continue',
      101 => 'Switching Protocols',
      200 => 'OK',
      201 => 'Created',
      202 => 'Accepted',
      203 => 'Non-Authoritative Information',
      204 => 'No Content',
      205 => 'Reset Content',
      206 => 'Partial Content',
      300 => 'Multiple Choices',
      301 => 'Moved Permanently',
      302 => 'Found',
      303 => 'See Other',
      304 => 'Not Modified',
      305 => 'Use Proxy',
      306 => '(Unused)',
      307 => 'Temporary Redirect',
      400 => 'Bad Request',
      401 => 'Unauthorized',
      402 => 'Payment Required',
      403 => 'Forbidden',
      404 => 'Not Found',
      405 => 'Method Not Allowed',
      406 => 'Not Acceptable',
      407 => 'Proxy Authentication Required',
      408 => 'Request Timeout',
      409 => 'Conflict',
      410 => 'Gone',
      411 => 'Length Required',
      412 => 'Precondition Failed',
      413 => 'Request Entity Too Large',
      414 => 'Request-URI Too Long',
      415 => 'Unsupported Media Type',
      416 => 'Requested Range Not Satisfiable',
      417 => 'Expectation Failed',
      450 => 'Timeout reached', # custom code
      500 => 'Internal Server Error',
      501 => 'Not Implemented',
      502 => 'Bad Gateway',
      503 => 'Service Unavailable',
      504 => 'Gateway Timeout',
      505 => 'HTTP Version Not Supported'
  };
  
  sub cb_debug {
      my ($easy, $type, $data, $uservar) = @_;
  
      my $msg = '';
      if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_TEXT')) {
          $msg = sprintf("== Info: %s", $data);
      }
      if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_HEADER_OUT')) {
          $msg = sprintf("=> Send header: %s", $data);
      }
      if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_DATA_OUT')) {
          $msg = sprintf("=> Send data: %s", $data);
      }
      if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_HEADER_IN')) {
          $msg = sprintf("=> Recv header: %s", $data);
      }
      if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_DATA_IN')) {
          $msg = sprintf("=> Recv data: %s", $data);
      }
      return 0 if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_SSL_DATA_OUT'));
      return 0 if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_SSL_DATA_IN'));
  
      $uservar->{output}->output_add(long_msg => $msg, debug => 1);
      return 0;
  }
  
  sub curl_setopt {
      my ($self, %options) = @_;
  
      eval {
          $self->{curl_easy}->setopt($options{option}, $options{parameter});
      };
      if ($@) {
          $self->{output}->add_option_msg(short_msg => "curl setopt error: '" . $@ . "'.");
          $self->{output}->option_exit();
      }
  }
  
  sub set_method {
      my ($self, %options) = @_;
  
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => undef);
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => undef);
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPGET'), parameter => 1);
  
      my $skip_log_post = 0;
      # POST inferred by CURLOPT_POSTFIELDS
      if ($options{content_type_forced} == 1) {
          if (defined($options{request}->{query_form_post})) {
              $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $options{request}->{query_form_post});
              $self->{curl_log}->log("--data-raw", $options{request}->{query_form_post});
              $skip_log_post = 1;
          }
      } elsif (defined($options{request}->{post_params})) {
          my $uri_post = URI->new();
          $uri_post->query_form($options{request}->{post_params});
          my $urlencodedheader = 'Content-Type: application/x-www-form-urlencoded';
          push @{$options{headers}}, $urlencodedheader;
  
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $uri_post->query);
          $self->{curl_log}->log("-H", $urlencodedheader);
  
          $self->{curl_log}->log("--data-raw", $uri_post->query);
          $skip_log_post = 1;
      }
  
      if ($options{request}->{method} eq 'GET') {
          # no curl_log call because GET is the default value
          return;
      }
  
      if ($options{request}->{method} eq 'POST') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POST'), parameter => 1);
          $self->{curl_log}->log('-X', $options{request}->{method}) unless $skip_log_post;
          return;
      }
  
      $self->{curl_log}->log('-X', $options{request}->{method});
      if ($options{request}->{method} eq 'PUT') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
      }
      if ($options{request}->{method} eq 'DELETE') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
      }
      if ($options{request}->{method} eq 'PATCH') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
      }
  }
  
  sub set_auth {
      my ($self, %options) = @_;
  
      if (defined($options{request}->{credentials})) {
          if (defined($options{request}->{basic})) {
              $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_BASIC'));
              $self->{curl_log}->log('--basic');
          } elsif (defined($options{request}->{ntlmv2})) {
              $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_NTLM'));
              $self->{curl_log}->log('--ntlm');
          } elsif (defined($options{request}->{digest})) {
              $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_DIGEST'));
              $self->{curl_log}->log('--digest');
          }else {
              $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_ANY'));
              $self->{curl_log}->log('--anyauth');
          }
          my $userpassword = $options{request}->{username}  . ':' . $options{request}->{password};
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_USERPWD'), parameter => $userpassword);
          $self->{curl_log}->log('--user', $userpassword);
      }
  
      if (defined($options{request}->{cert_file}) &&  $options{request}->{cert_file} ne '') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERT'), parameter => $options{request}->{cert_file});
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLKEY'), parameter => $options{request}->{key_file});
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_KEYPASSWD'), parameter => $options{request}->{cert_pwd});
  
          $self->{curl_log}->log('--cert', $options{request}->{cert_file});
          $self->{curl_log}->log('--key', $options{request}->{key_file});
          $self->{curl_log}->log('--pass', $options{request}->{cert_pwd}) if defined $options{request}->{cert_pwd} && $options{request}->{cert_pwd} ne '';
      }
  
      if (defined($options{request}->{cert_pkcs12})) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "P12");
          $self->{curl_log}->log('--cert-type', 'p12');
      } else {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "PEM");
          # no curl_log call because PEM is the default value
      }
  }
  
  sub set_form {
      my ($self, %options) = @_;
  
      if (!defined($self->{form_loaded})) {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Net::Curl::Form',
              error_msg => "Cannot load module 'Net::Curl::Form'."
          );
          $self->{form_loaded} = 1;
      }
  
      my $form = Net::Curl::Form->new();
      foreach (@{$options{form}}) {
          my %args = ();
          $args{ $self->{constant_cb}->(name => 'CURLFORM_COPYNAME()') } = $_->{copyname}
              if (defined($_->{copyname}));
          $args{ $self->{constant_cb}->(name => 'CURLFORM_COPYCONTENTS()') } = $_->{copycontents}
              if (defined($_->{copycontents}));
          $form->add(%args);
  
          $self->{curl_log}->log("--form-string", $_->{copyname}."=".$_->{copycontents})
              if defined($_->{copyname}) && defined($_->{copycontents});
      }
  
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPPOST()'), parameter => $form);
  }
  
  sub set_proxy {
      my ($self, %options) = @_;
  
      if (defined($options{request}->{proxyurl}) && $options{request}->{proxyurl} ne '') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_PROXY'), parameter => $options{request}->{proxyurl});
          $self->{curl_log}->log("--proxy", $options{request}->{proxyurl});
      }
  
      if (defined($options{request}->{proxypac}) && $options{request}->{proxypac} ne '') {
          $self->{output}->add_option_msg(short_msg => 'Unsupported proxypac option');
          $self->{output}->option_exit();
      }
  }
  
  sub set_extra_curl_opt {
      my ($self, %options) = @_;
  
      my $entries = {};
      foreach (@{$options{request}->{curl_opt}}) {
          my ($key, $value) = split /=>/;
          $key = centreon::plugins::misc::trim($key);
  
          if (!defined($entries->{$key})) {
              $entries->{$key} = { val => [], constants => [], force_array => 0 };
          }
  
          $value = centreon::plugins::misc::trim($value);
  
          # Here we want to convert a string containing curl options into a single value or into
          # an array of values depending on whether it begins with '[' and ends with ']'.
          # We also remove the quotes.
          # for example:
          #
          # $opt = ["CURLOPT_SSL_VERIFYPEER =>[opt1,'opt2','opt3']"];
          # is converted to a Perl array like this:
          # $VAR1 = [
          #  'opt1',
          #  'opt2',
          #  'opt3'
          # ];
          #
          # $opt = [ "CURLOPT_SSL_VERIFYPEER => 'opt1'" ];
          # is converted to:
          # $VAR1 = 'opt1';
          if ($value =~ /^\[(.*)\]$/) {
              $entries->{$key}->{force_array} = 1;
              $value = centreon::plugins::misc::trim($1);
              push @{$entries->{$key}->{constants}}, map { $_ = centreon::plugins::misc::trim($_); s/^'(.*)'$/$1/; $_  } split ',', $value;
          } else {
              push @{$entries->{$key}->{constants}}, $value =~ /^'(.*)'$/ ? $1 : $value;
          }
  
          if ($value  =~ /^CURLOPT|CURL/) {
              $value = $self->{constant_cb}->(name => $value);
          }
  
          push @{$entries->{$key}->{val}}, $value; 
      }
  
      foreach (keys %$entries) {
          my $key = $_;
  
          if ($self->{curl_log}->is_enabled()) {
              $self->{curl_log}->convert_curlopt_to_cups_parameter(
                  key => $key,
                  parameter => $entries->{$key}->{constants},
              );
          }
  
          if (/^CURLOPT|CURL/) {
              $key = $self->{constant_cb}->(name => $_);
          }
  
          my $parameter;
          if ($entries->{$_}->{force_array} == 1 || scalar(@{$entries->{$_}->{val}}) > 1) {
              $parameter = $entries->{$_}->{val};
          } else {
              $parameter = pop @{$entries->{$_}->{val}};
          }
          $self->curl_setopt(option => $key, parameter => $parameter);
  
      }
  }
  
  sub cb_get_header {
      my ($easy, $header, $uservar) = @_;
  
      $header =~ s/[\r\n]//g;
      if ($header =~ /^[\r\n]*$/) {
          $uservar->{nheaders}++;
      } else {
          $uservar->{response_headers}->[$uservar->{nheaders}] = {}
              if (!defined($uservar->{response_headers}->[$uservar->{nheaders}]));
          if ($header =~  /^(\S(?:.*?))\s*:\s*(.*)/) {
              my $header_name = lc($1);
              $uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name} = []
                  if (!defined($uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name}));
              push @{$uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name}}, $2;
          } else {
             $uservar->{response_headers}->[$uservar->{nheaders}]->{response_line} = $header; 
          }
      }
  
      return length($_[1]);
  }
  
  sub request {
      my ($self, %options) = @_;
  
      # Enable curl logger when debug mode is on
      $self->{curl_log}->init( enabled => $self->{output}->is_debug() );
  
      if (!defined($self->{curl_easy})) {
          $self->{curl_easy} = Net::Curl::Easy->new();
      }
  
      my $url;
      if (defined($options{request}->{full_url})) {
          $url = $options{request}->{full_url};
      } elsif (defined($options{request}->{port}) && $options{request}->{port} =~ /^[0-9]+$/) {
          $url = $options{request}->{proto}. "://" . $options{request}->{hostname} . ':' . $options{request}->{port} . $options{request}->{url_path};
      } else {
          $url = $options{request}->{proto}. "://" . $options{request}->{hostname} . $options{request}->{url_path};
      }
  
      my $uri = URI->new($url);
      if (defined($options{request}->{get_params})) {
          $uri->query_form($options{request}->{get_params});
      }
  
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_URL'), parameter => $uri);
  
      $self->{curl_log}->log($uri);
  
      if ($self->{output}->is_debug()) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGFUNCTION'), parameter => \&cb_debug);
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGDATA'), parameter => $self);
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_VERBOSE'), parameter => 1);
  
          $self->{curl_log}->log('--verbose');
      }
  
      if (defined($options{request}->{timeout}) && $options{request}->{timeout} =~ /\d/) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_TIMEOUT'), parameter => $options{request}->{timeout});
          $self->{curl_log}->log("--max-time", $options{request}->{timeout});
      }
  
      if (defined($options{request}->{cookies_file})) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEFILE'), parameter => $options{request}->{cookies_file});
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEJAR'), parameter => $options{request}->{cookies_file});
          $self->{curl_log}->log('--cookie', $options{request}->{cookies_file});
          $self->{curl_log}->log('--cookie-jar', $options{request}->{cookies_file});
      }
  
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 1);
      if (defined($options{request}->{no_follow})) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 0);
      } else {
          $self->{curl_log}->log('-L');
      }
  
      if (defined($options{request}->{http_peer_addr}) && $options{request}->{http_peer_addr} ne '') {
          $url =~ /^(?:http|https):\/\/(.*?)(\/|\:|$)/;
          my $resolve = $1 . ':' . $options{request}->{port_force} . ':' . $options{request}->{http_peer_addr};
          $self->{curl_easy}->setopt(
              $self->{constant_cb}->(name => 'CURLOPT_RESOLVE'),
              [$resolve]
          );
          $self->{curl_log}->log('--resolve', $resolve);
      }    
  
      my $headers = [];
      my $content_type_forced = 0;
      foreach my $key (keys %{$options{request}->{headers}}) {
          my $header = $key . ':' . (defined($options{request}->{headers}->{$key}) ? $options{request}->{headers}->{$key} : '');
          push @$headers, $header;
          if ($key =~ /content-type/i) {
              $content_type_forced = 1;
          }
          $self->{curl_log}->log("-H", $header);
      }
  
      $self->set_method(%options, content_type_forced => $content_type_forced, headers => $headers);
  
      if (defined($options{request}->{form})) {
          $self->set_form(form => $options{request}->{form});
      }
  
      if (scalar(@$headers) > 0) {
          $self->{curl_easy}->setopt($self->{constant_cb}->(name => 'CURLOPT_HTTPHEADER'), $headers);
      }
  
      if (defined($options{request}->{cacert_file}) && $options{request}->{cacert_file} ne '') {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CAINFO'), parameter => $options{request}->{cacert_file});
          $self->{curl_log}->log('--cacert', $options{request}->{cacert_file});
      }
      if (defined($options{request}->{insecure})) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYPEER'), parameter => 0);
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYHOST'), parameter => 0);
          $self->{curl_log}->log('--insecure');
      }
  
      $self->set_auth(%options);
      $self->set_proxy(%options);
      $self->set_extra_curl_opt(%options);
      $self->{response_body} = '';
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FILE'), parameter => \$self->{response_body});
      $self->{nheaders} = 0;
      $self->{response_headers} = [{}];
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HEADERDATA'), parameter => $self);
      $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HEADERFUNCTION'), parameter => \&cb_get_header);
  
      if (defined($options{request}->{certinfo}) && $options{request}->{certinfo} == 1) {
          $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CERTINFO'), parameter => 1);
          # no curl_log call because there is no equivalent in command line
      }
  
      $self->{response_code} = undef;
  
      if ($self->{curl_log}->is_enabled()) {
          $self->{output}->output_add(long_msg => 'curl request [curl backend]: ' . $self->{curl_log}->get_log());
      }
  
      eval {
          $self->{curl_easy}->perform();
      };
      if ($@) {
          my $err = $@;
          if (ref($@) eq "Net::Curl::Easy::Code") {
              my $num = $@;
              if ($num == $self->{constant_cb}->(name => 'CURLE_OPERATION_TIMEDOUT')) {
                  $self->{response_code} = 450;
              }
          }
  
          if (!defined($self->{response_code})) {
              $self->{output}->add_option_msg(short_msg => 'curl perform error : ' . $err);
              $self->{output}->option_exit();
          }
      }
  
      $self->{response_code} = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_RESPONSE_CODE'))
          if (!defined($self->{response_code}));
  
      # Check response
      my $status = 'ok';
      if (defined($options{request}->{critical_status}) && $options{request}->{critical_status} ne '' &&
          $self->{output}->test_eval(test => $options{request}->{critical_status}, values => { code => $self->{response_code} })) {
          $status = 'critical';
      } elsif (defined($options{request}->{warning_status}) && $options{request}->{warning_status} ne '' &&
          $self->{output}->test_eval(test => $options{request}->{warning_status}, values => { code => $self->{response_code} })) {
          $status = 'warning';
      } elsif (defined($options{request}->{unknown_status}) && $options{request}->{unknown_status} ne '' &&
          $self->{output}->test_eval(test => $options{request}->{unknown_status}, values => { code => $self->{response_code} })) {
          $status = 'unknown';
      }
  
      if (!$options{request}->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
          my $short_msg = $self->{response_code} . ' ' . 
              (defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'unknown');
  
          $self->{output}->output_add(
              severity => $status,
              short_msg => $short_msg
          );
          $self->{output}->display();
          $self->{output}->exit();
      }
  
      return $self->{response_body};
  }
  
  sub get_headers {
      my ($self, %options) = @_;
  
      my $headers = '';
      foreach (keys %{$self->{response_headers}->[$options{nheader}]}) {
          next if (/response_line/);
          foreach my $value (@{$self->{response_headers}->[$options{nheader}]->{$_}}) {
              $headers .= "$_: " . $value . "\n";
          }
      }
  
      return $headers;
  }
  
  sub get_first_header {
      my ($self, %options) = @_;
  
      if (!defined($options{name})) {
          return $self->get_headers(nheader => 0);
      }
  
      return undef
          if (!defined($self->{response_headers}->[0]->{ lc($options{name}) }));
      return wantarray ? @{$self->{response_headers}->[0]->{ lc($options{name}) }} : $self->{response_headers}->[0]->{ lc($options{name}) }->[0];
  }
  
  sub get_header {
      my ($self, %options) = @_;
  
      if (!defined($options{name})) {
          return $self->get_headers(nheader => -1);
      }
  
      return undef
          if (!defined($self->{response_headers}->[-1]->{ lc($options{name}) }));
      return wantarray ? @{$self->{response_headers}->[-1]->{ lc($options{name}) }} : $self->{response_headers}->[-1]->{ lc($options{name}) }->[0];
  }
  
  sub get_code {
      my ($self, %options) = @_;
  
      return $self->{response_code};
  }
  
  sub get_message {
      my ($self, %options) = @_;
  
      return defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'Unknown code';
  }
  
  sub get_certificate {
      my ($self, %options) = @_;
  
      my $certs = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_CERTINFO'));
      return ('pem', $certs->[0]->{Cert});
  }
  
  sub get_times {
      my ($self, %options) = @_;
  
      # TIME_T = 7.61.0
      my $resolve = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_NAMELOOKUP_TIME'));
      my $connect = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_CONNECT_TIME'));
      my $appconnect = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_APPCONNECT_TIME'));
      my $start = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_STARTTRANSFER_TIME'));
      my $total = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_TOTAL_TIME'));
      my $times = {
          resolve => $resolve * 1000,
          connect => ($connect - $resolve) * 1000,
          transfer => ($total - $start) * 1000
      };
      if ($appconnect > 0) {
          $times->{tls} = ($appconnect - $connect) * 1000;
          $times->{processing} = ($start - $appconnect) * 1000;
      } else {
          $times->{processing} = ($start - $connect) * 1000;
      }
  
      return $times;
  }
  
  1;
  
  
  =head1 NAME
  
  HTTP Curl backend layer.
  
  =head1 SYNOPSIS
  
  HTTP Curl backend layer.
  
  =head1 BACKEND CURL OPTIONS
  
  =over 8
  
  =item B<--curl-opt>
  
  Set CURL Options (--curl-opt="CURLOPT_SSL_VERIFYPEER => 0" --curl-opt="CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_1" ).
  
  =back
  
  =head1 DESCRIPTION
  
  B<http>.
  
  =cut
CENTREON_PLUGINS_BACKEND_HTTP_CURL

$fatpacked{"centreon/plugins/backend/http/curlconstants.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_CURLCONSTANTS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::http::curlconstants;
  
  use strict;
  use warnings;
  use Net::Curl::Easy qw(:constants);
  use Net::Curl::Form qw(:constants);
  
  sub get_constant_value {
      my (%options) = @_;
  
      return eval $options{name};
  }
  
  1;
CENTREON_PLUGINS_BACKEND_HTTP_CURLCONSTANTS

$fatpacked{"centreon/plugins/backend/http/lwp.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_LWP';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::http::lwp;
  
  use strict;
  use warnings;
  use centreon::plugins::backend::http::useragent;
  use URI;
  use IO::Socket::SSL;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'ssl:s'      => { name => 'ssl' },
              'ssl-opt:s@' => { name => 'ssl_opt' },
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND LWP OPTIONS', once => 1);
      }
  
      $self->{output} = $options{output};
      $self->{ua} = undef;
      $self->{debug_handlers} = 0;
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      foreach (('unknown_status', 'warning_status', 'critical_status')) {
          if (defined($options{request}->{$_})) {
              $options{request}->{$_} =~ s/%\{http_code\}/\$values->{code}/g;
          }
      }
  
      $self->{ssl_context} = '';
      if (!defined($options{request}->{ssl_opt})) {
          $options{request}->{ssl_opt} = [];
      }
      if (defined($options{request}->{ssl}) && $options{request}->{ssl} ne '') {
          push @{$options{request}->{ssl_opt}}, 'SSL_version => ' . $options{request}->{ssl};
      }
      if (defined($options{request}->{cert_file}) && !defined($options{request}->{cert_pkcs12})) {
          push @{$options{request}->{ssl_opt}}, 'SSL_use_cert => 1';
          push @{$options{request}->{ssl_opt}}, 'SSL_cert_file => "' . $options{request}->{cert_file} . '"';
          push @{$options{request}->{ssl_opt}}, 'SSL_key_file => "' . $options{request}->{key_file} . '"'
               if (defined($options{request}->{key_file}));
          push @{$options{request}->{ssl_opt}}, 'SSL_ca_file => "' . $options{request}->{cacert_file} . '"'
              if (defined($options{request}->{cacert_file}));
      }
      if ($options{request}->{insecure}) {
          push @{$options{request}->{ssl_opt}}, 'SSL_verify_mode => SSL_VERIFY_NONE';
      }
  
      my $append = '';
      foreach (@{$options{request}->{ssl_opt}}) {
          if ($_ ne '') {
              $self->{ssl_context} .= $append . $_;
              $append = ', ';
          }
      }
  }
  
  sub set_proxy {
      my ($self, %options) = @_;
  
      if (defined($options{request}->{proxypac}) && $options{request}->{proxypac} ne '') {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output}, module => 'HTTP::ProxyPAC',
              error_msg => "Cannot load module 'HTTP::ProxyPAC'."
          );
          my ($pac, $pac_uri);
          eval {
              if ($options{request}->{proxypac} =~ /^(http|https):\/\//) {
                  $pac_uri = URI->new($options{request}->{proxypac});
                  $pac = HTTP::ProxyPAC->new($pac_uri);
              } else {
                  $pac = HTTP::ProxyPAC->new($options{request}->{proxypac});
              }
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg => 'issue to load proxypac: ' . $@);
              $self->{output}->option_exit();
          }
          my $res = $pac->find_proxy($options{url});
          if (defined($res->direct) && $res->direct != 1) {
              my $proxy_uri = URI->new($res->proxy);
              $proxy_uri->userinfo($pac_uri->userinfo) if (defined($pac_uri->userinfo));
              $self->{ua}->proxy(['http', 'https'], $proxy_uri->as_string);
          }
      }
      if (defined($options{request}->{proxyurl}) && $options{request}->{proxyurl} ne '') {
          my $proxyurl = $options{request}->{proxyurl};
          if ($options{request}->{proto} eq "https" ||
              (defined($options{request}->{full_url}) && $options{request}->{full_url} =~ /^https/)) {
              $proxyurl = 'connect://' . $2 if ($proxyurl =~ /^(http|https):\/\/(.*)/);
          }
          $self->{ua}->proxy(['http', 'https'], $proxyurl);
      }
  }
  
  sub request {
      my ($self, %options) = @_;
  
      my %user_agent_params = (keep_alive => 1);
      if (defined($options{request}->{certinfo}) && $options{request}->{certinfo} == 1) {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output}, module => 'LWP::ConnCache',
              error_msg => "Cannot load module 'LWP::ConnCache'."
          );
          $self->{cache} = LWP::ConnCache->new();
          $self->{cache}->total_capacity(1);
          %user_agent_params = (conn_cache => $self->{cache});
      }
  
      my $request_options = $options{request};
      if (!defined($self->{ua})) {
          my $timeout;
          $timeout = $1 if (defined($request_options->{timeout}) && $request_options->{timeout} =~ /(\d+)/);
          $self->{ua} = centreon::plugins::backend::http::useragent->new(
              %user_agent_params,
              protocols_allowed => ['http', 'https'], 
              timeout => $timeout,
              credentials => $request_options->{credentials},
              username => $request_options->{username}, 
              password => $request_options->{password}
          );
          if (defined($request_options->{cookies_file})) {
              centreon::plugins::misc::mymodule_load(
                  output => $self->{output},
                  module => 'HTTP::Cookies',
                  error_msg => "Cannot load module 'HTTP::Cookies'."
              );
              $self->{ua}->cookie_jar(
                  HTTP::Cookies->new(
                      file => $request_options->{cookies_file},
                      autosave => 1
                  )
              );
          }
      }
  
      if ($self->{output}->is_debug() && $self->{debug_handlers} == 0) {
          $self->{debug_handlers} = 1;
          $self->{ua}->add_handler('request_send', sub {
              my ($response, $ua, $handler) = @_;
  
              $self->{output}->output_add(long_msg => '======> request send', debug => 1);
              $self->{output}->output_add(long_msg => $response->as_string, debug => 1);
              return ; 
          });
          $self->{ua}->add_handler("response_done", sub { 
              my ($response, $ua, $handler) = @_;
  
              $self->{output}->output_add(long_msg => '======> response done', debug => 1);
              $self->{output}->output_add(long_msg => $response->as_string, debug => 1);
              return ;
          });
      }
  
      if (defined($request_options->{no_follow})) {
          $self->{ua}->requests_redirectable(undef);
      } else {
          $self->{ua}->requests_redirectable([ 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH' ]);
      }
      if (defined($request_options->{http_peer_addr})) {
          push @LWP::Protocol::http::EXTRA_SOCK_OPTS, PeerAddr => $request_options->{http_peer_addr};
      }
  
      my ($req, $url);
      if (defined($request_options->{full_url})) {
          $url = $request_options->{full_url};
      } elsif (defined($request_options->{port}) && $request_options->{port} =~ /^[0-9]+$/) {
          $url = $request_options->{proto}. '://' . $request_options->{hostname} . ':' . $request_options->{port} . $request_options->{url_path};
      } else {
          $url = $request_options->{proto}. '://' . $request_options->{hostname} . $request_options->{url_path};
      }
  
      my $uri = URI->new($url);
      if (defined($request_options->{get_params})) {
          $uri->query_form($request_options->{get_params});
      }
  
      $req = HTTP::Request->new($request_options->{method}, $uri);
  
      my $content_type_forced = 0;
      foreach my $key (keys %{$request_options->{headers}}) {
          $req->header($key => $request_options->{headers}->{$key});
          if ($key =~ /content-type/i) {
              $content_type_forced = 1;
          }
      }
  
      if ($content_type_forced == 1) {
          $req->content($request_options->{query_form_post});
      } elsif (defined($options{request}->{post_params})) {
          my $uri_post = URI->new();
          $uri_post->query_form($request_options->{post_params});
          $req->content_type('application/x-www-form-urlencoded');
          $req->content($uri_post->query);
      }
  
      if (defined($request_options->{form})) {
          $self->{output}->add_option_msg(short_msg => 'unsupported form param');
          $self->{output}->option_exit();
      }
  
      if (defined($request_options->{credentials}) && defined($request_options->{ntlmv2})) {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Authen::NTLM',
              error_msg => "Cannot load module 'Authen::NTLM'."
          );
          Authen::NTLM::ntlmv2(1);
      }
  
      if (defined($request_options->{credentials}) && (defined($request_options->{basic}) || defined($request_options->{digest}))) {
          $req->authorization_basic($request_options->{username}, $request_options->{password});
      }
  
      $self->set_proxy(request => $request_options, url => $url);
  
      if (defined($request_options->{cert_pkcs12}) && $request_options->{cert_file} ne '' && $request_options->{cert_pwd} ne '') {
          eval 'use Net::SSL'; die $@ if $@;
          $ENV{HTTPS_PKCS12_FILE} = $request_options->{cert_file};
          $ENV{HTTPS_PKCS12_PASSWORD} = $request_options->{cert_pwd};
      }
  
      if (defined($self->{ssl_context}) && $self->{ssl_context} ne '') {
          my $context = new IO::Socket::SSL::SSL_Context(eval $self->{ssl_context});
          IO::Socket::SSL::set_default_context($context);
      }
  
      $self->{response} = $self->{ua}->request($req);
  
      $self->{response_code} = $self->{response}->code();
      $self->{response_message} = $self->{response}->message();
      $self->{headers} = $self->{response}->headers();
  
      if ($self->{response_code} == 500) {
          my $client_warning = $self->get_header(name => 'Client-Warning');
          if (defined($client_warning) && $client_warning eq 'Internal response' && $self->{response_message} =~ /connection timed out/mi) {
              $self->{response_code} = 450;
              $self->{response_message} = 'Timeout reached';
          }
      }
  
      # Check response
      my $status = 'ok';
      if (defined($request_options->{critical_status}) && $request_options->{critical_status} ne '' &&
          $self->{output}->test_eval(test => $request_options->{critical_status}, values => { code => $self->{response_code} })) {
          $status = 'critical';
      } elsif (defined($request_options->{warning_status}) && $request_options->{warning_status} ne '' &&
          $self->{output}->test_eval(test => $request_options->{warning_status}, values => { code => $self->{response_code} })) {
          $status = 'warning';
      } elsif (defined($request_options->{unknown_status}) && $request_options->{unknown_status} ne '' &&
          $self->{output}->test_eval(test => $request_options->{unknown_status}, values => { code => $self->{response_code} })) {
          $status = 'unknown';
      }
      if (!$request_options->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
          my $short_msg = $self->{response}->status_line;
          if ($short_msg =~ /^401/) {
              $short_msg .= ' (' . $1 . ' authentication expected)' if (defined($self->{response}->www_authenticate) &&
                  $self->{response}->www_authenticate =~ /(\S+)/);
          }
  
          $self->{output}->output_add(
              severity => $status,
              short_msg => $short_msg
          );
          $self->{output}->display();
          $self->{output}->exit();
      }
  
      return $self->{response}->content;
  }
  
  sub get_headers {
      my ($self, %options) = @_;
  
      my $headers = '';
      foreach ($options{response}->header_field_names()) {
          my $value = $options{response}->header($_);
          $headers .= "$_: " . (defined($value) ? $value : '') . "\n";
      }
  
      return $headers;
  }
  
  sub get_first_header {
      my ($self, %options) = @_;
  
      my @redirects = $self->{response}->redirects();
      if (!defined($options{name})) {
          return $self->get_headers(response => defined($redirects[0]) ? $redirects[0] : $self->{response});
      }
  
      return
          defined($redirects[0]) ? 
          $redirects[0]->headers()->header($options{name}) :
          $self->{headers}->header($options{name})
      ;
  }
  
  sub get_header {
      my ($self, %options) = @_;
  
      if (!defined($options{name})) {
          return $self->get_headers(response => $self->{response});
      }
      return $self->{headers}->header($options{name});
  }
  
  sub get_code {
      my ($self, %options) = @_;
  
      return $self->{response_code};
  }
  
  sub get_message {
      my ($self, %options) = @_;
  
      return $self->{response_message};
  }
  
  sub get_certificate {
      my ($self, %options) = @_;
  
      my ($con) = $self->{cache}->get_connections('https');
      return ('socket', $con);
  }
  
  sub get_times {
      my ($self, %options) = @_;
  
      return undef;
  }
  
  1;
  
  
  =head1 NAME
  
  HTTP LWP backend layer.
  
  =head1 SYNOPSIS
  
  HTTP LWP backend layer.
  
  =head1 BACKEND LWP OPTIONS
  
  =over 8
  
  =item B<--ssl-opt>
  
  Set SSL Options (--ssl-opt="SSL_version => TLSv1" --ssl-opt="SSL_verify_mode => SSL_VERIFY_NONE").
  
  =item B<--ssl>
  
  Set SSL version (--ssl=TLSv1).
  
  =back
  
  =head1 DESCRIPTION
  
  B<http>.
  
  =cut
CENTREON_PLUGINS_BACKEND_HTTP_LWP

$fatpacked{"centreon/plugins/backend/http/useragent.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_USERAGENT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::http::useragent;
  
  use strict;
  use warnings;
  use base 'LWP::UserAgent';
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      $self = LWP::UserAgent::new(@_);
      $self->agent("centreon::plugins::backend::http::useragent");
  
      $self->{credentials} = $options{credentials} if defined($options{credentials});
      $self->{username} = $options{username} if defined($options{username});
      $self->{password} = $options{password} if defined($options{password});
  
      return $self;
  }
  
  sub get_basic_credentials {
      my($self, $realm, $uri, $proxy) = @_;
      return if $proxy;
      return $self->{username}, $self->{password} if $self->{credentials} and wantarray;
      return $self->{username} . ':' . $self->{password} if $self->{credentials};
      return undef;
  }
  
  1;
CENTREON_PLUGINS_BACKEND_HTTP_USERAGENT

$fatpacked{"centreon/plugins/backend/ssh/libssh.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_SSH_LIBSSH';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::ssh::libssh;
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'libssh-strict-connect' => { name => 'libssh_strict_connect' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND LIBSSH OPTIONS', once => 1);
      }
  
      $self->{connected} = 0;
      $self->{output} = $options{output};
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      centreon::plugins::misc::mymodule_load(
          output => $self->{output},
          module => 'Libssh::Session',
          error_msg => "Cannot load module 'Libssh::Session'."
      );
      centreon::plugins::misc::mymodule_load(
          output => $self->{output},
          module => 'centreon::plugins::backend::ssh::libsshconstants',
          error_msg => "Cannot load module 'centreon::plugins::backend::ssh::libsshconstants'."
      );
      $self->{constant_cb} = \&centreon::plugins::backend::ssh::libsshconstants::get_constant_value;
  
      if (!defined($self->{ssh})) {
          $self->{ssh} = Libssh::Session->new();
      }
  
      $self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : 22;
      $self->{ssh}->options(port => $self->{ssh_port});
      $self->{ssh_username} = $options{option_results}->{ssh_username};
      $self->{ssh_password} = $options{option_results}->{ssh_password};
      
      $self->{ssh}->options(identity => $options{option_results}->{ssh_priv_key})
          if (defined($options{option_results}->{ssh_priv_key}) && $options{option_results}->{ssh_priv_key} ne '');
      $self->{ssh}->options(user => $options{option_results}->{ssh_username})
          if (defined($options{option_results}->{ssh_username}) && $options{option_results}->{ssh_username} ne '');
      $self->{ssh_strict_connect} = defined($options{option_results}->{libssh_strict_connect}) ? 0 : 1;
  }
  
  sub connect {
      my ($self, %options) = @_;
  
      return if ($self->{connected} == 1);
  
      $self->{ssh}->options(host => $options{hostname});
      if ($self->{ssh}->connect(SkipKeyProblem => $self->{ssh_strict_connect}) != $self->{constant_cb}->(name => 'SSH_OK')) {
          $self->{output}->add_option_msg(short_msg => 'connect issue: ' . $self->{ssh}->error());
          $self->{output}->option_exit();
      }
  
      if ($self->{ssh}->auth_publickey_auto() != $self->{constant_cb}->(name => 'SSH_AUTH_SUCCESS')) {
          if (defined($self->{ssh_username}) && $self->{ssh_username} ne '' &&
              defined($self->{ssh_password}) && $self->{ssh_password} ne '' &&
              $self->{ssh}->auth_password(password => $self->{ssh_password}) == $self->{constant_cb}->(name => 'SSH_AUTH_SUCCESS')) {
              $self->{connected} = 1;
              return ;
          }
  
          my $msg_error = $self->{ssh}->error(GetErrorSession => 1);
          $self->{output}->add_option_msg(short_msg => sprintf("auth issue: %s", defined($msg_error) && $msg_error ne '' ? $msg_error : 'pubkey issue'));
          $self->{output}->option_exit();
      }
  
      $self->{connected} = 1;
  }
  
  sub execute {
      my ($self, %options) = @_;
  
      if (defined($options{timeout}) && $options{timeout} =~ /(\d+)/) {
          $self->{ssh}->options(timeout => $options{timeout});
      }
  
      $self->connect(hostname => $options{hostname});
  
      my $cmd = '';
      $cmd = 'sudo ' if (defined($options{sudo}));
      $cmd .= $options{command_path} . '/' if (defined($options{command_path}));
      $cmd .= $options{command} . ' ' if (defined($options{command}));
      $cmd .= $options{command_options} if (defined($options{command_options}));
  
      my $ret;
      if (!defined($options{ssh_pipe}) || $options{ssh_pipe} == 0) {
          $ret = $self->{ssh}->execute_simple(
              cmd => $cmd,
              timeout => $options{timeout},
              timeout_nodata => $options{timeout}
          );
      } else {
          $ret = $self->{ssh}->execute_simple(
              input_data => $cmd,
              timeout => $options{timeout},
              timeout_nodata => $options{timeout}
          );
      }
  
      $self->{output}->output_add(long_msg => $ret->{stdout}, debug => 1) if (defined($ret->{stdout}));
      $self->{output}->output_add(long_msg => $ret->{stderr}, debug => 1) if (defined($ret->{stderr}));
  
      my ($content, $exit_code);
      if ($ret->{exit} == $self->{constant_cb}->(name => 'SSH_OK')) {
          $content = $ret->{stdout};
          $exit_code = $ret->{exit_code};
      } elsif ($ret->{exit} == $self->{constant_cb}->(name => 'SSH_AGAIN')) { # AGAIN means timeout
          $self->{output}->add_option_msg(short_msg => sprintf('command execution timeout'));
          $self->{output}->option_exit();
      } else {
          $self->{output}->add_option_msg(short_msg =>
              sprintf(
                  'command execution error: %s',
                  $self->{ssh}->error(GetErrorSession => 1)
              )
          );
          $self->{output}->option_exit();
      }
  
      if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1) {
          # Last failed login: Tue Feb 25 09:30:20 EST 2020 from 10.40.1.160 on ssh:notty
          # There was 1 failed login attempt since the last successful login.
          $content =~ s/^(?:Last failed login:|There was.*?failed login).*?\n//msg;
      }
  
      if ($exit_code != 0 && (!defined($options{no_quit}) || $options{no_quit} != 1)) {
          $self->{output}->add_option_msg(short_msg => sprintf('command execution error [exit code: %s]', $exit_code));
          $self->{output}->option_exit();
      }
  
      return ($content, $exit_code);
  }
  
  1;
  
  
  =head1 NAME
  
  libssh backend.
  
  =head1 SYNOPSIS
  
  libssh backend.
  
  =head1 BACKEND LIBSSH OPTIONS
  
  =over 8
  
  =item B<--libssh-strict-connect>
  
  Connection won't be OK even if there is a problem (server known changed or server found other) with the ssh server.
  
  =back
  
  =head1 DESCRIPTION
  
  B<libssh>.
  
  =cut
CENTREON_PLUGINS_BACKEND_SSH_LIBSSH

$fatpacked{"centreon/plugins/backend/ssh/libsshconstants.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_SSH_LIBSSHCONSTANTS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::ssh::libsshconstants;
  
  use strict;
  use warnings;
  use Libssh::Session qw(:all);
  
  sub get_constant_value {
      my (%options) = @_;
  
      return eval $options{name};
  }
  
  1;
CENTREON_PLUGINS_BACKEND_SSH_LIBSSHCONSTANTS

$fatpacked{"centreon/plugins/backend/ssh/plink.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_SSH_PLINK';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::ssh::plink;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'plink-command:s' => { name => 'plink_command' },
              'plink-path:s'    => { name => 'plink_path' },
              'plink-option:s@' => { name => 'plink_option' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND PLINK OPTIONS', once => 1);
      }
  
      $self->{output} = $options{output};
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      $self->{ssh_command} = defined($options{option_results}->{plink_command}) && $options{option_results}->{plink_command} ne '' ? 
          $options{option_results}->{plink_command} : 'plink';
      $self->{ssh_path} = $options{option_results}->{plink_path};
      $self->{ssh_option} = defined($options{option_results}->{plink_option}) ? $options{option_results}->{plink_option} : [];
      $self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : 22;
      $self->{ssh_priv_key} = $options{option_results}->{ssh_priv_key};
      $self->{ssh_username} = $options{option_results}->{ssh_username};
      $self->{ssh_password} = $options{option_results}->{ssh_password};
  
      centreon::plugins::misc::check_security_command(
          output => $self->{output},
          command => $options{option_results}->{plink_command},
          command_options => join('', @{$self->{ssh_option}}),
          command_path => $self->{ssh_path}
      );
  
      push @{$self->{ssh_option}}, '-batch';
      push @{$self->{ssh_option}}, '-l=' .  $self->{ssh_username} if (defined($self->{ssh_username}) && $self->{ssh_username} ne '');
      push @{$self->{ssh_option}}, '-pw=' . $self->{ssh_password} if (defined($self->{ssh_password}) && $self->{ssh_password} ne '');
      push @{$self->{ssh_option}}, '-P=' .  $self->{ssh_port} if (defined($self->{ssh_port}) && $self->{ssh_port} ne '');
      push @{$self->{ssh_option}}, '-i=' .  $self->{ssh_priv_key} if (defined($self->{ssh_priv_key}) && $self->{ssh_priv_key} ne '');
  }
  
  sub execute {
      my ($self, %options) = @_;
  
      push @{$self->{ssh_option}}, '-T' if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1);
      $options{command} .= $options{cmd_exit} if (defined($options{cmd_exit}) && $options{cmd_exit} ne '');
  
      my ($content, $exit_code) = centreon::plugins::misc::execute(
          output => $self->{output},
          sudo => $options{sudo},
          command => $options{command},
          command_path => $options{command_path},
          command_options => $options{command_options},
          ssh_pipe => $options{ssh_pipe},
          options => {
              remote => 1,
              ssh_address => $options{hostname},
              ssh_command => $self->{ssh_command},
              ssh_path => $self->{ssh_path},
              ssh_option => $self->{ssh_option},
              timeout => $options{timeout}
          },
          no_quit => $options{no_quit}
      );
  
      if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1) {
          # Using username "root".
          $content =~ s/^Using username ".*?"\.\n//msg;
      }
  
      # plink exit code is 0 even connection is abandoned
      if ($content =~ /server.*?key fingerprint.*connection abandoned/msi) {
          $self->{output}->add_option_msg(short_msg => 'please connect with plink command to your host and accept the server host key');
          $self->{output}->option_exit();
      }
  
      return ($content, $exit_code);
  }
  
  1;
  
  
  =head1 NAME
  
  plink backend.
  
  =head1 SYNOPSIS
  
  plink backend.
  
  =head1 BACKEND PLINK OPTIONS
  
  =over 8
  
  =item B<--plink-command>
  
  plink command (default: 'plink').
  
  =item B<--plink-path>
  
  plink command path (default: none)
  
  =item B<--plink-option>
  
  Specify plink options (example: --plink-option='-T').
  
  =back
  
  =head1 DESCRIPTION
  
  B<plink>.
  
  =cut
CENTREON_PLUGINS_BACKEND_SSH_PLINK

$fatpacked{"centreon/plugins/backend/ssh/sshcli.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_SSH_SSHCLI';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::backend::ssh::sshcli;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'sshcli-command:s' => { name => 'sshcli_command' },
              'sshcli-path:s'    => { name => 'sshcli_path' },
              'sshcli-option:s@' => { name => 'sshcli_option' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND SSHCLI OPTIONS', once => 1);
      }
  
      $self->{output} = $options{output};
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      $self->{ssh_command} = defined($options{option_results}->{sshcli_command}) && $options{option_results}->{sshcli_command} ne '' ?
                             $options{option_results}->{sshcli_command} : 'ssh';
      $self->{ssh_path} = $options{option_results}->{sshcli_path};
      $self->{ssh_option} = defined($options{option_results}->{sshcli_option}) ? $options{option_results}->{sshcli_option} : [];
      $self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : 22;
      $self->{ssh_priv_key} = $options{option_results}->{ssh_priv_key};
      $self->{ssh_username} = $options{option_results}->{ssh_username};
      if (defined($options{option_results}->{ssh_password}) && $options{option_results}->{ssh_password} ne '') {
          $self->{output}->add_option_msg(short_msg => 'sshcli backend cannot use ssh password. please use backend plink or libssh');
          $self->{output}->option_exit();
      }
  
      centreon::plugins::misc::check_security_command(
          output          => $self->{output},
          command         => $options{option_results}->{sshcli_command},
          command_options => join('', @{$self->{ssh_option}}),
          command_path    => $self->{ssh_path}
      );
  
      push @{$self->{ssh_option}}, '-o=BatchMode=yes';
      push @{$self->{ssh_option}}, '-l=' . $self->{ssh_username} if (defined($self->{ssh_username}) && $self->{ssh_username} ne '');
      push @{$self->{ssh_option}}, '-p=' . $self->{ssh_port} if (defined($self->{ssh_port}) && $self->{ssh_port} ne '');
      push @{$self->{ssh_option}}, '-i=' . $self->{ssh_priv_key} if (defined($self->{ssh_priv_key}) && $self->{ssh_priv_key} ne '');
  }
  
  sub execute {
      my ($self, %options) = @_;
  
      push @{$self->{ssh_option}}, '-T' if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1);
      $options{command} .= $options{cmd_exit} if (defined($options{cmd_exit}) && $options{cmd_exit} ne '');
  
      my ($content, $exit_code) = centreon::plugins::misc::execute(
          output          => $self->{output},
          sudo            => $options{sudo},
          command         => $options{command},
          command_path    => $options{command_path},
          command_options => $options{command_options},
          ssh_pipe        => $options{ssh_pipe},
          options         => {
              remote         => 1,
              ssh_address    => $options{hostname},
              ssh_command    => $self->{ssh_command},
              ssh_path       => $self->{ssh_path},
              ssh_option     => $self->{ssh_option},
              timeout        => $options{timeout},
              ssh_option_eol => $options{default_sshcli_option_eol}
  
          },
          no_quit         => $options{no_quit}
      );
  
      if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1) {
          # Last failed login: Tue Feb 25 09:30:20 EST 2020 from 10.40.1.160 on ssh:notty
          # There was 1 failed login attempt since the last successful login.
          $content =~ s/^(?:Last failed login:|There was.*?failed login).*?\n//msg;
      }
  
      return ($content, $exit_code);
  }
  
  1;
  
  
  =head1 NAME
  
  SSH CLI backend.
  
  =head1 SYNOPSIS
  
  SSH CLI backend.
  
  =head1 BACKEND SSH CLI OPTIONS
  
  =over 8
  
  =item B<--sshcli-command>
  
  ssh command (default: C<ssh>).
  
  =item B<--sshcli-path>
  
  ssh command path (default: C<none>)
  
  =item B<--sshcli-option>
  
  Specify SSH CLI options (example: C<--sshcli-option='-o=StrictHostKeyChecking=no'>).
  
  =back
  
  =head1 DESCRIPTION
  
  B<SSH CLI>.
  
  =cut
CENTREON_PLUGINS_BACKEND_SSH_SSHCLI

$fatpacked{"centreon/plugins/curllogger.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_CURLLOGGER';
  #
  # Copyright 2025 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::curllogger;
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      $self->{log_as_curl} = [];
      $self->{is_log_as_curl} = 0;
  
      # As this is only used for debugging purposes, we disable it if the ShellQuote
      # module is missing
      eval "use String::ShellQuote";
      if ($@) {
          $self->{is_log_as_curl} = -1;
      }
  
      $self;
  }
  
  sub init {
      my ($self, %options) = @_;
  
      $self->{log_as_curl} = [];
      return if $self->{is_log_as_curl} == -1;
  
      $self->{is_log_as_curl} = $options{enabled} || 0;
  }
  
  sub is_enabled {
      my ($self) = @_;
  
      return $self->{is_log_as_curl} == 1;
  }
  
  sub log {
      my ($self, @params) = @_;
  
      return unless $self->{is_log_as_curl} == 1 && @params;
  
      push @{$self->{log_as_curl}}, shell_quote(@params);
  }
  
  sub get_log {
      my ($self) = @_;
  
      return "curl ".join ' ', @{$self->{log_as_curl}};
  }
  
  # Conversion of some parameters manually passed to the set_extra_curl_opt function
  # into their command-line equivalents. Only the parameters used in the plugin code
  # are handled. If new parameters are added, this hash must be updated.
  # The hash contains curl parameters CURLOPT_*. Its keys map to either a hash when
  # multiple values are handled, or directly to an array when only one response is
  # supported. The placeholder <value> is replaced by the provided value.
  # Eg: "CURLOPT_POSTREDIR => CURL_REDIR_POST_ALL" will produce: --post301 --post302 --post303
  #     "CURLOPT_SSL_VERIFYPEER => 0" will produce: --insecure
  #     "CURLOPT_AWS_SIGV4 => 'osc'" will produce: --aws-sigv4 osc
  our %curlopt_to_parameter = (
     'CURLOPT_POSTREDIR' => { 'CURL_REDIR_POST_ALL' => [ '--post301', '--post302', '--post303', ],
                              'CURL_REDIR_POST_301' => [ '--post301' ],
                              'CURL_REDIR_POST_302' => [ '--post302' ],
                              'CURL_REDIR_POST_303' => [ '--post303' ],
                            },
     'CURLOPT_SSLVERSION' => { 'CURL_SSLVERSION_TLSv1_0' => [ '--tlsv1.0' ],
                               'CURL_SSLVERSION_TLSv1_1' => [ '--tlsv1.1' ],
                               'CURL_SSLVERSION_TLSv1_2' => [ '--tlsv1.2' ],
                               'CURL_SSLVERSION_TLSv1_3' => [ '--tlsv1.3' ],
                             },
     'CURLOPT_SSL_VERIFYPEER' => { '0' => [ '--insecure' ] },
     'CURLOPT_SSL_VERIFYHOST' => { '0' => [ '--insecure' ] },
  
     'CURLOPT_AWS_SIGV4' => [ '--aws-sigv4', '<value>' ],
  );
  
  sub convert_curlopt_to_cups_parameter {
      my ($self, %options) = @_;
  
      my $key = $options{key};
  
      return unless exists $curlopt_to_parameter{$key};
  
      # we want an array of parameters
      my $parameters = ref $options{parameter} eq 'ARRAY' ? $options{parameter} : [ $options{parameter} ] ;
  
      my @cups_parameters ;
      if (ref $curlopt_to_parameter{$key} eq 'ARRAY') {
          @cups_parameters = map { s/<value>/$parameters->[0]/; $_ } @{$curlopt_to_parameter{$key}};
      } else {
          foreach my $parameter (@$parameters) {
              if (exists $curlopt_to_parameter{$key}->{$parameter}) {
                  push @cups_parameters, @{$curlopt_to_parameter{$key}->{$parameter}};
              } elsif ($parameter =~ /^-/) {
                  push @cups_parameters, $parameter;
              }
          }
      }
      $self->log($_) for @cups_parameters;
  }
  
  1;
  
CENTREON_PLUGINS_CURLLOGGER

$fatpacked{"centreon/plugins/http.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_HTTP';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::http;
  
  use strict;
  use warnings;
  
  use centreon::plugins::curllogger;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'http-peer-addr:s'  => { name => 'http_peer_addr' },
              'proxyurl:s'        => { name => 'proxyurl' },
              'proxypac:s'        => { name => 'proxypac' },
              'insecure'          => { name => 'insecure' },
              'http-backend:s'    => { name => 'http_backend' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'HTTP GLOBAL OPTIONS');
      }
  
      my $curllogger = centreon::plugins::curllogger->new();
  
      centreon::plugins::misc::mymodule_load(
          output => $options{output},
          module => 'centreon::plugins::backend::http::lwp',
          error_msg => "Cannot load module 'centreon::plugins::backend::http::lwp'."
      );
      $self->{backend_lwp} = centreon::plugins::backend::http::lwp->new(%options);
  
      centreon::plugins::misc::mymodule_load(
          output => $options{output},
          module => 'centreon::plugins::backend::http::curl',
          error_msg => "Cannot load module 'centreon::plugins::backend::http::curl'."
      );
      $self->{backend_curl} = centreon::plugins::backend::http::curl->new(%options, curl_logger => $curllogger);
  
      $self->{default_backend} = defined($options{default_backend}) && $options{default_backend} ne '' ?
          $options{default_backend} : 'lwp';
      $self->{output} = $options{output};
      $self->{options} = {
          proto => 'http',
          url_path => '/',
          timeout => 5,
          method => 'GET',
          unknown_status => '%{http_code} < 200 or %{http_code} >= 300',
          warning_status => undef,
          critical_status => undef,
      };
  
      $self->{add_headers} = {};
      return $self;
  }
  
  sub set_options {
      my ($self, %options) = @_;
  
      $self->{options} = { %{$self->{options}} };
      foreach (keys %options) {
          $self->{options}->{$_} = $options{$_} if (defined($options{$_}));
      }
  }
  
  sub add_header {
      my ($self, %options) = @_;
  
      $self->{add_headers}->{$options{key}} = $options{value};
  }
  
  sub remove_header {
      my ($self, %options) = @_;
  
      delete $self->{add_headers}->{$options{key}} if (defined($self->{add_headers}->{$options{key}}));
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      $options{request}->{http_backend} = $self->{default_backend}
          if (!defined($options{request}->{http_backend}) || $options{request}->{http_backend} eq '');
      $self->{http_backend} = $options{request}->{http_backend};
      if ($self->{http_backend} !~ /^\s*lwp|curl\s*$/i) {
          $self->{output}->add_option_msg(short_msg => "Unsupported http backend specified '" . $self->{http_backend} . "'.");
          $self->{output}->option_exit();
      }
  
      if (defined($options{request}->{$self->{http_backend} . '_backend_options'})) {
          foreach (keys %{$options{request}->{$self->{http_backend} . '_backend_options'}}) {
              $options{request}->{$_} = $options{request}->{$self->{http_backend} . '_backend_options'}->{$_};
          }
      }
  
      if (($options{request}->{proto} ne 'http') && ($options{request}->{proto} ne 'https')) {
          $self->{output}->add_option_msg(short_msg => "Unsupported protocol specified: '(" . $options{request}->{proto} . ")'. . Use either https or http.");
          $self->{output}->option_exit();
      }
      if (!defined($options{request}->{hostname})) {
          $self->{output}->add_option_msg(short_msg => "Please set the hostname option");
          $self->{output}->option_exit();
      }
      if ((defined($options{request}->{credentials})) && (!defined($options{request}->{username}) || !defined($options{request}->{password}))) {
          $self->{output}->add_option_msg(short_msg => "You need to set --username= and --password= options when --credentials is used");
          $self->{output}->option_exit();
      }
      if ((defined($options{request}->{cert_pkcs12})) && (!defined($options{request}->{cert_file}) && !defined($options{request}->{cert_pwd}))) {
          $self->{output}->add_option_msg(short_msg => "You need to set --cert-file= and --cert-pwd= options when --pkcs12 is used");
          $self->{output}->option_exit();
      }
  
      $options{request}->{port_force} = $self->get_port();
  
      $options{request}->{headers} = {};
      if (defined($options{request}->{header})) {
          foreach (@{$options{request}->{header}}) {
              if (/^(:.+?|.+?):(.*)/) {
                  $options{request}->{headers}->{$1} = $2;
              }
          }
      }
      foreach (keys %{$self->{add_headers}}) {
          $options{request}->{headers}->{$_} = $self->{add_headers}->{$_};
      }
  
      foreach my $method (('get', 'post')) {
          if (defined($options{request}->{$method . '_param'})) {
              $options{request}->{$method . '_params'} = {};
              foreach (@{$options{request}->{$method . '_param'}}) {
                  if (/^([^=]+)={0,1}(.*)$/s) {
                      my $key = $1;
                      my $value = defined($2) ? $2 : 1;
                      if (defined($options{request}->{$method . '_params'}->{$key})) {
                          if (ref($options{request}->{$method . '_params'}->{$key}) ne 'ARRAY') {
                              $options{request}->{$method . '_params'}->{$key} = [ $options{request}->{$method . '_params'}->{$key} ];
                          }
                          push @{$options{request}->{$method . '_params'}->{$key}}, $value;
                      } else {
                          $options{request}->{$method . '_params'}->{$key} = $value;
                      }
                  }
              }
          }
      }
  
      $self->{'backend_' . $self->{http_backend}}->check_options(%options);
  }
  
  sub get_port {
      my ($self, %options) = @_;
  
      my $port = '';
      if (defined($self->{options}->{port}) && $self->{options}->{port} ne '') {
          $port = $self->{options}->{port};
      } else {
          $port = 80 if ($self->{options}->{proto} eq 'http');
          $port = 443 if ($self->{options}->{proto} eq 'https');
      }
  
      return $port;
  }
  
  sub get_port_request {
      my ($self, %options) = @_;
  
      my $port = '';
      if (defined($self->{options}->{port}) && $self->{options}->{port} ne '') {
          $port = $self->{options}->{port};
      }
      return $port;
  }
  
  sub request {
      my ($self, %options) = @_;
  
      my $request_options = { %{$self->{options}} };
      foreach (keys %options) {
          $request_options->{$_} = $options{$_} if (defined($options{$_}));
      }
      $self->check_options(request => $request_options);
  
      return $self->{'backend_' . $self->{http_backend}}->request(request => $request_options);
  }
  
  sub get_first_header {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{http_backend}}->get_first_header(%options);
  }
  
  sub get_header {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{http_backend}}->get_header(%options);
  }
  
  sub get_code {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{http_backend}}->get_code();
  }
  
  sub get_message {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{http_backend}}->get_message();
  }
  
  sub get_certificate {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{http_backend}}->get_certificate();
  }
  
  sub get_times {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{http_backend}}->get_times();
  }
  
  1;
  
  
  =head1 NAME
  
  HTTP abstraction layer.
  
  =head1 SYNOPSIS
  
  HTTP abstraction layer for lwp and curl backends
  
  =head1 HTTP GLOBAL OPTIONS
  
  =over 8
  
  =item B<--http-peer-addr>
  
  Set the address you want to connect to. Useful if hostname is only a vhost, to avoid IP resolution.
  
  =item B<--proxyurl>
  
  Proxy URL. Example: http://my.proxy:3128
  
  =item B<--proxypac>
  
  Proxy PAC file (can be a URL or a local file).
  
  =item B<--insecure>
  
  Accept insecure SSL connections.
  
  =item B<--http-backend>
  
  Perl library to use for HTTP transactions.
  Possible values are: lwp (default) and curl.
  
  =back
  
  =head1 DESCRIPTION
  
  B<http>.
  
  =cut
CENTREON_PLUGINS_HTTP

$fatpacked{"centreon/plugins/misc.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_MISC';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::misc;
  
  use strict;
  use warnings;
  use utf8;
  use JSON::XS;
  
  sub execute {
      my (%options) = @_;
      
      if ($^O eq 'MSWin32') {
          return windows_execute(%options, timeout => $options{options}->{timeout});
      } else {
          return unix_execute(%options);
      }
  }
  
  sub windows_execute {
      my (%options) = @_;
      my $result;
      my ($stdout, $pid, $ended) = ('');
      my ($exit_code, $cmd);
  
      $cmd = $options{command_path} . '/' if (defined($options{command_path}));
      $cmd .= $options{command} . ' ' if (defined($options{command}));
      $cmd .= $options{command_options} if (defined($options{command_options}));
  
      centreon::plugins::misc::mymodule_load(
          output => $options{output}, module => 'Win32::Job',
          error_msg => "Cannot load module 'Win32::Job'."
      );
      centreon::plugins::misc::mymodule_load(
          output => $options{output}, module => 'Time::HiRes',
          error_msg => "Cannot load module 'Time::HiRes'."
      );
  
      $| = 1;
      pipe FROM_CHILD, TO_PARENT or do {
          $options{output}->add_option_msg(short_msg => "Internal error: can't create pipe from child to parent: $!");
          $options{output}->option_exit();
      };
      my $job = Win32::Job->new;
      my $stderr = 'NUL';
      $stderr = \*TO_PARENT if ($options{output}->is_debug());
      if (!($pid = $job->spawn(undef, $cmd,
                         { stdin => 'NUL',
                           stdout => \*TO_PARENT,
                           stderr => $stderr }))) {
          $options{output}->add_option_msg(short_msg => "Internal error: execution issue: $^E");
          $options{output}->option_exit();
      }
      close TO_PARENT;
  
      my $ein = '';
      vec($ein, fileno(FROM_CHILD), 1) = 1;
      $job->watch(
          sub {            
              my ($buffer);
              my $time = $options{timeout};
              my $last_time = Time::HiRes::time();
              $ended = 0;
              while (select($ein, undef, undef, $options{timeout})) {
                  if (sysread(FROM_CHILD, $buffer, 16384)) {
                      $buffer =~ s/\r//g;
                      $stdout .= $buffer;
                  } else {
                      $ended = 1;
                      last;
                  }
                  $options{timeout} -= Time::HiRes::time() - $last_time;
                  last if ($options{timeout} <= 0);         
                  $last_time = Time::HiRes::time();
              }
              return 1 if ($ended == 0);
              return 0;
          },
          0.1
      );
  
      $result = $job->status;
      close FROM_CHILD;    
  
      if ($ended == 0) {
          $options{output}->add_option_msg(short_msg => 'Command too long to execute (timeout)...');
          $options{output}->option_exit();
      }
      chomp $stdout;
      
      if (defined($options{no_quit}) && $options{no_quit} == 1) {
          return ($stdout, $result->{$pid}->{exitcode});
      }
  
      if ($result->{$pid}->{exitcode} != 0) {
          $stdout =~ s/\n/ - /g;
          $options{output}->add_option_msg(short_msg => "Command error: $stdout");
          $options{output}->option_exit();
      }
  
      return ($stdout, $result->{$pid}->{exitcode});
  }
  
  sub unix_execute {
      my (%options) = @_;
      my $cmd = '';
      my $args = [];
      my ($lerror, $stdout, $exit_code);
  
      my $redirect_stderr = 1;
      $redirect_stderr = $options{redirect_stderr} if (defined($options{redirect_stderr}));
      my $wait_exit = 1;
      $wait_exit = $options{wait_exit} if (defined($options{wait_exit}));
  
      # Build command line
      # Can choose which command is done remotely (can filter and use local file)
      if (defined($options{options}->{remote}) && 
          ($options{options}->{remote} eq '' || !defined($options{label}) || $options{label} =~ /$options{options}->{remote}/)) {
          my $sub_cmd;
  
          $cmd = $options{options}->{ssh_path} . '/' if (defined($options{options}->{ssh_path}));
          $cmd .= $options{options}->{ssh_command} if (defined($options{options}->{ssh_command}));
  
          foreach (@{$options{options}->{ssh_option}}) {
              if (/^(.*?)(?:=(.*))?$/) {
                  push @$args, $1 if (defined($1));
                  push @$args, $2 if (defined($2));
              }
          }
  
          if (defined($options{options}->{ssh_address}) && $options{options}->{ssh_address} ne '') {
              push @$args, $options{options}->{ssh_address};
          } else {
              push @$args, $options{options}->{hostname};
          }
  
          if (defined($options{options}->{ssh_option_eol})) {
              foreach (@{$options{options}->{ssh_option_eol}}) {
                  if (/^(.*?)(?:=(.*))?$/) {
                      push @$args, $1 if (defined($1));
                      push @$args, $2 if (defined($2));
                  }
              }
          }
  
          $sub_cmd = 'sudo ' if (defined($options{sudo}));
          $sub_cmd .= $options{command_path} . '/' if (defined($options{command_path}));
          $sub_cmd .= $options{command} . ' ' if (defined($options{command}));
          $sub_cmd .= $options{command_options} if (defined($options{command_options}));
          # On some equipment. Cannot get a pseudo terminal
          if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1) {
              $cmd = "echo '" . $sub_cmd . "' | " . $cmd . ' ' . join(' ', @$args);
              ($lerror, $stdout, $exit_code) = backtick(
                  command => $cmd,
                  timeout => $options{options}->{timeout},
                  wait_exit => $wait_exit,
                  redirect_stderr => $redirect_stderr
              );
          } else {
              ($lerror, $stdout, $exit_code) = backtick(
                  command => $cmd,
                  arguments => [@$args, $sub_cmd],
                  timeout => $options{options}->{timeout},
                  wait_exit => $wait_exit,
                  redirect_stderr => $redirect_stderr
              );
          }
      } else {
          $cmd = 'sudo ' if (defined($options{sudo}));
          $cmd .= $options{command_path} . '/' if (defined($options{command_path}));
          $cmd .= $options{command} if (defined($options{command}));
          $cmd .= ' ' . $options{command_options} if (defined($options{command_options}));
  
          if (defined($options{no_shell_interpretation}) and $options{no_shell_interpretation} ne '') {
              my @args = split(' ',$cmd);
              ($lerror, $stdout, $exit_code) = backtick(
                  command         => $args[0],
                  arguments       => [@args[1.. $#args]],
                  timeout         => $options{options}->{timeout},
                  wait_exit       => $wait_exit,
                  redirect_stderr => $redirect_stderr
              );
          }
          else {
              ($lerror, $stdout, $exit_code) = backtick(
                  command         => $cmd,
                  timeout         => $options{options}->{timeout},
                  wait_exit       => $wait_exit,
                  redirect_stderr => $redirect_stderr
              );
          }
      }
  
      if (defined($options{options}->{show_output}) && 
          ($options{options}->{show_output} eq '' || (defined($options{label}) && $options{label} eq $options{options}->{show_output}))) {
          print $stdout;
          exit $exit_code;
      }
  
      $stdout =~ s/\r//g;
      if ($lerror <= -1000) {
          $options{output}->add_option_msg(short_msg => $stdout);
          $options{output}->option_exit();
      }
  
      if (defined($options{no_quit}) && $options{no_quit} == 1) {
          return ($stdout, $exit_code);
      }
  
      if ($exit_code != 0 && (!defined($options{no_errors}) || !defined($options{no_errors}->{$exit_code}))) {
          $stdout =~ s/\n/ - /g;
          $options{output}->add_option_msg(short_msg => "Command error: $stdout");
          $options{output}->option_exit();
      }
  
      return $stdout;
  }
  
  sub mymodule_load {
      my (%options) = @_;
      my $file;
      ($file = ($options{module} =~ /\.pm$/ ? $options{module} : $options{module} . '.pm')) =~ s{::}{/}g;
  
      eval {
          local $SIG{__DIE__} = 'IGNORE';
          require $file;
          $file =~ s{/}{::}g;
          $file =~ s/\.pm$//;
      };
      if ($@) {
          return 1 if (defined($options{no_quit}) && $options{no_quit} == 1);
          $options{output}->add_option_msg(long_msg => $@);
          $options{output}->add_option_msg(short_msg => $options{error_msg});
          $options{output}->option_exit();
      }
      return wantarray ? (0, $file) : 0;
  }
  
  sub backtick {
      my %arg = (
          command => undef,
          arguments => [],
          timeout => 30,
          wait_exit => 0,
          redirect_stderr => 0,
          @_,
      );
      my @output;
      my $pid;
      my $return_code;
  
      my $sig_do;
      if ($arg{wait_exit} == 0) {
          $sig_do = 'IGNORE';
          $return_code = undef;
      } else {
          $sig_do = 'DEFAULT';
      }
      local $SIG{CHLD} = $sig_do;
      $SIG{TTOU} = 'IGNORE';
      $| = 1;
  
      if (!defined($pid = open( KID, "-|" ))) {
          return (-1001, "Cant fork: $!", -1);
      }
  
      if ($pid) {
          
          eval {
             local $SIG{ALRM} = sub { die "Timeout by signal ALARM\n"; };
             alarm( $arg{timeout} );
             while (<KID>) {
                 chomp;
                 push @output, $_;
             }
  
             alarm(0);
          };
  
          if ($@) {
              if ($pid != -1) {
                  kill -9, $pid;
              }
  
              alarm(0);
              return (-1000, 'Command too long to execute (timeout)...', -1);
          } else {
              if ($arg{wait_exit} == 1) {
                  # We're waiting the exit code                
                  waitpid($pid, 0);
                  $return_code = ($? >> 8);
              }
              close KID;
          }
      } else {
          # child
          # set the child process to be a group leader, so that
          # kill -9 will kill it and all its descendents
          # We have ignore SIGTTOU to let write background processes
          setpgrp( 0, 0 );
  
          if ($arg{redirect_stderr} == 1) {
              open STDERR, '>&STDOUT';
          }
          if (scalar(@{$arg{arguments}}) <= 0) {
              exec($arg{command});
          } else {
              exec($arg{command}, @{$arg{arguments}});
          }
          # Exec is in error. No such command maybe.
          exit(127);
      }
  
      return (0, join("\n", @output), $return_code);
  }
  
  sub is_empty {
      my $value = shift;
      if (!defined($value) or $value eq '') {
          return 1;
      }
      return 0;
  }
  
  sub trim {
      my ($value) = $_[0];
      
      # Sometimes there is a null character
      $value =~ s/\x00$//;
      $value =~ s/^[ \t\n]+//;
      $value =~ s/[ \t\n]+$//;
      return $value;
  }
  
  sub powershell_encoded {
      require Encode;
      require MIME::Base64;
      my $bytes = Encode::encode('utf16LE', $_[0]);
      return MIME::Base64::encode_base64($bytes, '');
  }
  
  sub powershell_escape {
      my ($value) = $_[0];
      $value =~ s/`/``/g;
      $value =~ s/#/`#/g;
      $value =~ s/'/`'/g;
      $value =~ s/"/`"/g;
      return $value;
  }
  
  sub minimal_version {
      my ($version_src, $version_dst) = @_;
          
      # No Version. We skip   
      if (!defined($version_src) || !defined($version_dst) || 
          $version_src !~ /^[0-9]+(?:\.[0-9\.]+)*$/ || $version_dst !~ /^[0-9x]+(?:\.[0-9x]+)*$/) {
          return 1;
      }
    
      my @version_src = split /\./, $version_src;
      my @versions = split /\./, $version_dst;
      for (my $i = 0; $i < scalar(@versions); $i++) {
          return 1 if ($versions[$i] eq 'x');
          return 1 if (!defined($version_src[$i]));
          $version_src[$i] =~ /^([0-9]*)/;
          next if ($versions[$i] == int($1));
          return 0 if ($versions[$i] > int($1));
          return 1 if ($versions[$i] < int($1));
      }
      
      return 1;
  }
  
  sub change_seconds {
      my %options = @_;
      my ($str, $str_append) = ('', '');
      my $periods = [
          { unit => 'y', value => 31556926 },
          { unit => 'M', value => 2629743 },
          { unit => 'w', value => 604800 },
          { unit => 'd', value => 86400 },
          { unit => 'h', value => 3600 },
          { unit => 'm', value => 60 },
          { unit => 's', value => 1 },
      ];
      my %values = ('y' => 1, 'M' => 2, 'w' => 3, 'd' => 4, 'h' => 5, 'm' => 6, 's' => 7);
      my $sign = '';
      if ($options{value} < 0) {
          $sign = '-';
          $options{value} = abs($options{value});
      }
      
      foreach (@$periods) {
          next if (defined($options{start}) && $values{$_->{unit}} < $values{$options{start}});
          my $count = int($options{value} / $_->{value});
  
          next if ($count == 0);
          $str .= $str_append . $count . $_->{unit};
          $options{value} = $options{value} % $_->{value};
          $str_append = ' ';
      }
  
      if ($str eq '') {
          $str = $options{value};
          $str .= $options{start} if (defined($options{start}));
      }
      return $sign . $str;
  }
  
  sub scale_bytesbit {
      my (%options) = @_;
      
      my $base = 1024;
      if (defined($options{dst_unit}) && defined($options{src_unit})) {
          $options{value} *= 8 if ($options{dst_unit} =~ /b/ && $options{src_unit} =~ /B/);
          $options{value} /= 8 if ($options{dst_unit} =~ /B/ && $options{src_unit} =~ /b/);
          if ($options{dst_unit} =~ /b/) {
              $base = 1000;
          }
      }
          
      my %expo = ('' => 0, k => 1, m => 2, g => 3, t => 4, p => 5, e => 6);
      my ($src_expo, $dst_expo) = (0, 0);
      $src_expo = $expo{lc($options{src_quantity})} if (defined($options{src_quantity}) && $options{src_quantity} =~ /[kmgtpe]/i);
      if ($options{dst_unit} eq 'auto') {
          my @auto = ('', 'k', 'm', 'g', 't', 'p', 'e');
          my $i = defined($options{src_quantity}) ? $expo{$options{src_quantity}} : 0;
          for (; $i < scalar(@auto); $i++) {
              last if ($options{value} < $base);
              $options{value} = $options{value} / $base;
          }
  
          return ($options{value}, $auto[$i], $options{src_unit});
      } elsif (defined($options{dst_quantity}) && ($options{dst_quantity} eq '' || $options{dst_quantity} =~ /[kmgtpe]/i )) {
          my $dst_expo = $expo{lc($options{dst_quantity})};
          if ($dst_expo - $src_expo > 0) {
              $options{value} = $options{value} / ($base ** ($dst_expo - $src_expo));
          } elsif ($dst_expo - $src_expo < 0) {
              $options{value} = $options{value} * ($base ** (($dst_expo - $src_expo) * -1));
          }
      }
      
      return $options{value};
  }
  
  sub convert_bytes {
      my (%options) = @_;
  
      my %expo = (k => 1, m => 2, g => 3, t => 4, p => 5);
      my ($value, $unit) = ($options{value}, $options{unit});
      if (defined($options{pattern})) {
          return undef if ($value !~ /$options{pattern}/);
          $value = $1;
          $unit = $2;
      }
      
      my $base = defined($options{network}) ? 1000 : 1024;    
      if ($unit =~ /([kmgtp])i?b/i) {
          $value = $value * ($base ** $expo{lc($1)});
      }
  
      return $value;
  }
  
  sub convert_fahrenheit {
      my (%options) = @_;
  
      return ($options{value} - 32) / 1.8;
  }
  
  sub expand_exponential {
      my (%options) = @_;
  
      return $options{value} unless ($options{value} =~ /^(.*)e([-+]?)(.*)$/);
      my ($num, $sign, $exp) = ($1, $2, $3);
      my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
      return sprintf("%${sig}f", $options{value});
  }
  
  sub alert_triggered {
      my (%options) = @_;
  
      my ($rv_warn, $warning) = parse_threshold(threshold => $options{warning});
      my ($rv_crit, $critical) = parse_threshold(threshold => $options{critical});
  
      foreach ([$rv_warn, $warning], [$rv_crit, $critical]) {
          next if ($_->[0] == 0);
  
          if ($_->[1]->{arobase} == 0 && ($options{value} < $_->[1]->{start} || $options{value} > $_->[1]->{end})) {
              return 1;
          } elsif ($_->[1]->{arobase}  == 1 && ($options{value} >= $_->[1]->{start} && $options{value} <= $_->[1]->{end})) {
              return 1;
          }
      }
  
      return 0;
  }
  
  sub parse_threshold {
      my (%options) = @_;
  
      my $perf = trim($options{threshold});
      my $perf_result = { arobase => 0, infinite_neg => 0, infinite_pos => 0, start => '', end => '' };
  
      my $global_status = 1;    
      if ($perf =~ /^(\@?)((?:~|(?:\+|-)?\d+(?:[\.,]\d+)?(?:[KMGTPE][bB])?|):)?((?:\+|-)?\d+(?:[\.,]\d+)?(?:[KMGTPE][bB])?)?$/) {
          $perf_result->{start} = $2 if (defined($2));
          $perf_result->{end} = $3 if (defined($3));
          $perf_result->{arobase} = 1 if (defined($1) && $1 eq '@');
          $perf_result->{start} =~ s/[\+:]//g;
          $perf_result->{end} =~ s/\+//;
          if ($perf_result->{start} =~ s/([KMGTPE])([bB])//) {
              $perf_result->{start} = scale_bytesbit(
                  value => $perf_result->{start},
                  src_unit => $2, dst_unit => $2,
                  src_quantity => $1, dst_quantity => '',
              );
          }
          if ($perf_result->{end} =~ s/([KMGTPE])([bB])//) {
              $perf_result->{end} = scale_bytesbit(
                  value => $perf_result->{end},
                  src_unit => $2, dst_unit => $2,
                  src_quantity => $1, dst_quantity => '',
              );
          }
          if ($perf_result->{end} eq '') {
              $perf_result->{end} = 1e500;
              $perf_result->{infinite_pos} = 1;
          }
          $perf_result->{start} = 0 if ($perf_result->{start} eq '');      
          $perf_result->{start} =~ s/,/\./;
          $perf_result->{end} =~ s/,/\./;
          
          if ($perf_result->{start} eq '~') {
              $perf_result->{start} = -1e500;
              $perf_result->{infinite_neg} = 1;
          }
      } else {
          $global_status = 0;
      }
  
      return ($global_status, $perf_result);
  }
  
  sub get_threshold_litteral {
      my (%options) = @_;
  
      my $perf_output = ($options{arobase} == 1 ? '@' : '') . 
          (($options{infinite_neg} == 0) ? $options{start} : '~') . 
          ':' . 
          (($options{infinite_pos} == 0) ? $options{end} : '');
      return $perf_output;
  }
  
  sub set_timezone {
      my (%options) = @_;
  
      return {} if (!defined($options{name}) || $options{name} eq '');
  
      centreon::plugins::misc::mymodule_load(
          output => $options{output}, module => 'DateTime::TimeZone',
          error_msg => "Cannot load module 'DateTime::TimeZone'."
      );
      if (DateTime::TimeZone->is_valid_name($options{name})) {
          return { time_zone => DateTime::TimeZone->new(name => $options{name}) };
      }
  
      # try to manage syntax (:Pacific/Noumea for example)
      if ($options{name} =~ /^:(.*)$/ && DateTime::TimeZone->is_valid_name($1)) {
          return { time_zone => DateTime::TimeZone->new(name => $1) };
      }
  
      return {};
  }
  
  sub uniq {
      my %seen;
  
      return grep { !$seen{$_}++ } @_;
  }
  
  sub eval_ssl_options {
      my (%options) = @_;
  
      my $ssl_context = {};
      return $ssl_context if (!defined($options{ssl_opt}));
      
      my ($rv) = centreon::plugins::misc::mymodule_load(
          output => $options{output}, module => 'Safe',
          no_quit => 1
      );
      centreon::plugins::misc::mymodule_load(
          output => $options{output}, module => 'IO::Socket::SSL',
          no_quit => 1
      );
  
      my $safe;
      if ($rv == 0) {
          $safe = Safe->new();
          $safe->permit_only(':base_core', 'rv2gv', 'padany');
          $safe->share('$values');
          $safe->share('$assign_var');
          $safe->share_from('IO::Socket::SSL', [
              'SSL_VERIFY_NONE', 'SSL_VERIFY_PEER', 'SSL_VERIFY_FAIL_IF_NO_PEER_CERT', 'SSL_VERIFY_CLIENT_ONCE',
              'SSL_RECEIVED_SHUTDOWN', 'SSL_SENT_SHUTDOWN',
              'SSL_OCSP_NO_STAPLE', 'SSL_OCSP_MUST_STAPLE', 'SSL_OCSP_FAIL_HARD', 'SSL_OCSP_FULL_CHAIN', 'SSL_OCSP_TRY_STAPLE'
          ]);
      }
      
      foreach (@{$options{ssl_opt}}) {
          if (/(SSL_[A-Za-z_]+)\s+=>\s*(\S+)/) {
              my ($label, $eval) = ($1, $2);
  
              our $assign_var;
              if (defined($safe)) {
                  $safe->reval("\$assign_var = $eval", 1);
                  if ($@) {
                      die 'Unsafe code evaluation: ' . $@;
                  }
              } else {
                  eval "\$assign_var = $eval";
              }
  
              $ssl_context->{$label} = $assign_var;
          }
      }
  
      return $ssl_context;
  }
  
  sub slurp_file {
      my (%options) = @_;
  
      my $content = do {
          local $/ = undef;
          if (!open my $fh, '<', $options{file}) {
              $options{output}->add_option_msg(short_msg => "Could not open file $options{file}: $!");
              $options{output}->option_exit();
          }
          <$fh>;
      };
  
      return $content;
  }
  
  sub sanitize_command_param {
      my (%options) = @_;
  
      return if (!defined($options{value}));
  
      $options{value} =~ s/[`;!&|]//g;
      return $options{value};
  }
  
  my $security_file = '/etc/centreon-plugins/security.json';
  my $whitelist_file = '/etc/centreon-plugins/whitelist.json';
  if ($^O eq 'MSWin32') {
      $security_file = 'C:/Program Files/centreon-plugins/security.json';
      $whitelist_file = 'C:/Program Files/centreon-plugins/whitelist.json';
  }
  
  sub check_security_command {
      my (%options) = @_;
  
      return 0 if (!(
          (defined($options{command}) && $options{command} ne '') ||
          (defined($options{command_options}) && $options{command_options} ne '') ||
          (defined($options{command_path}) && $options{command_path} ne ''))
      );
  
      return 0 if (! -r "$security_file" || -z "$security_file");
  
      my $content = slurp_file(output => $options{output}, file => $security_file);
  
      my $security;
      eval {
          $security = JSON::XS->new->utf8->decode($content);
      };
      if ($@) {
          $options{output}->add_option_msg(short_msg => 'Cannot decode security file content');
          $options{output}->option_exit();
      }
  
      if (defined($security->{block_command_overload}) && $security->{block_command_overload} == 1) {
          $options{output}->add_option_msg(short_msg => 'Cannot overload command (security)');
          $options{output}->option_exit();
      }
  
      return 0;
  }
  
  sub check_security_whitelist {
      my (%options) = @_;
  
      my $command = $options{command};
      $command = $options{command_path} . '/' . $options{command} if (defined($options{command_path}) && $options{command_path} ne '');
      $command .= ' ' . $options{command_options} if (defined($options{command_options}) && $options{command_options} ne '');
  
      return 0 if (! -r "$security_file" || -z "$security_file");
  
      my $content = slurp_file(output => $options{output}, file => $security_file);
  
      my $security;
      eval {
          $security = JSON::XS->new->utf8->decode($content);
      };
      if ($@) {
          $options{output}->add_option_msg(short_msg => 'Cannot decode security file content');
          $options{output}->option_exit();
      }
  
      return 0 if (!defined($security->{whitelist_enabled}) || $security->{whitelist_enabled} !~ /^(?:1|true)$/i);
  
      if (! -r "$whitelist_file") {
          $options{output}->add_option_msg(short_msg => 'Cannot read whitelist security file content');
          $options{output}->option_exit();
      }
  
      if (-z "$whitelist_file") {
          $options{output}->add_option_msg(short_msg => 'Cannot execute command (security)');
          $options{output}->option_exit();
      }
  
      $content = slurp_file(output => $options{output}, file => $whitelist_file);
  
      my $whitelist;
      eval {
          $whitelist = JSON::XS->new->utf8->decode($content);
      };
      if ($@) {
          $options{output}->add_option_msg(short_msg => 'Cannot decode whitelist security file content');
          $options{output}->option_exit();
      }
  
      my $matched = 0;
      foreach (@$whitelist) {
          if ($command =~ /$_/) {
              $matched = 1;
              last;
          }
      }
  
      if ($matched == 0) {
          $options{output}->add_option_msg(short_msg => 'Cannot execute command (security)');
          $options{output}->option_exit();
      }
  
      return 0;
  }
  
  sub json_decode {
      my ($content, %options) = @_;
  
      $content =~ s/\r//mg;
      my $object;
  
      my $decoder = JSON::XS->new->utf8;
      # this option
      if ($options{booleans_as_strings}) {
          # boolean_values() is not available on old versions of JSON::XS (Alma 8 still provides v3.04)
          if (JSON::XS->can('boolean_values')) {
              $decoder = $decoder->boolean_values("false", "true");
          } else {
              # if boolean_values is not available, perform a dirty substitution of booleans
              $content =~ s/"(\w+)"\s*:\s*(true|false)(\s*,?)/"$1": "$2"$3/gm;
          }
      }
  
      eval {
          $object = $decoder->decode($content);
      };
      if ($@) {
          print STDERR "Cannot decode JSON string: $@" . "\n";
          return undef;
      }
      return $object;
  }
  
  sub json_encode {
      my ($object) = @_;
  
      $object =~ s/\r//mg;
      my $encoded;
      eval {
          $encoded = encode_json($object);
      };
      if ($@) {
          print STDERR 'Cannot encode object to JSON. Error message: ' . $@;
          return undef;
      }
  
      return $encoded;
  }
  
  sub is_local_ip($) {
      my ($ip) = @_;
  
      return 0 unless $ip;
  
      return 1 if $ip =~ /^127\./;
      return 1 if $ip =~ /^10\./;
      return 1 if $ip =~ /^192\.168\./;
      return 1 if $ip =~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./;
      return 1 if $ip =~ /^169\.254\./;
      return 1 if $ip eq '0.0.0.0';
  
      return 0;
  }
  
  # This function is used with "sort", it sorts an array of IP addresses.
  # $_[0] and $_[1] correspond to Perl's special variables $a and $b used by sort.
  # I can't use $a and $b directly here, otherwise Perl generates a warning: "uninitialized value".
  sub sort_ips($$) {
      my @a = split /\./, $_[0];
      my @b = split /\./, $_[1];
      return $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3]
  }
  
  # function to assess if a string has to be excluded given an include regexp and an exclude regexp
  sub is_excluded {
      my ($string, $include_regexp, $exclude_regexp) = @_;
      return 1 unless defined($string);
      return 1 if (defined($exclude_regexp) && $exclude_regexp ne '' && $string =~ /$exclude_regexp/);
      return 0 if (!defined($include_regexp) || $include_regexp eq '' || $string =~ /$include_regexp/);
  
      return 1;
  }
  
  1;
  
  
  =head1 NAME
  
  centreon::plugins::misc - A collection of miscellaneous utility functions for Centreon plugins.
  
  =head1 SYNOPSIS
  
      use centreon::plugins::misc;
  
      my $result = centreon::plugins::misc::execute(
          command => 'ls',
          command_options => '-l'
      );
  
  =head1 DESCRIPTION
  
  The `centreon::plugins::misc` module provides a variety of utility functions that can be used in Centreon plugins. These functions include command execution, string manipulation, file handling, and more.
  
  =head1 METHODS
  
  =head2 execute
  
      my $result = centreon::plugins::misc::execute(%options);
  
  Executes a command and returns the result.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<command> - The command to execute.
  
  =item * C<command_options> - Options for the command.
  
  =item * C<timeout> - Timeout for the command execution.
  
  =back
  
  =back
  
  =head2 windows_execute
  
      my ($stdout, $exit_code) = centreon::plugins::misc::windows_execute(%options);
  
  Executes a command on Windows and returns the output and exit code.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<command> - The command to execute.
  
  =item * C<command_options> - Options for the command.
  
  =item * C<timeout> - Timeout for the command execution.
  
  =back
  
  =back
  
  =head2 unix_execute
  
      my $stdout = centreon::plugins::misc::unix_execute(%options);
  
  Executes a command on Unix and returns the output.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<command> - The command to execute.
  
  =item * C<command_options> - Options for the command.
  
  =item * C<timeout> - Timeout for the command execution.
  
  =item * C<wait_exit> - bool.
  
  =item * C<redirect_stderr> - bool.
  
  =item * C<sudo> - bool prepend sudo to the command executed.
  
  =item * C<no_shell_interpretation> - bool don't use sh interpolation on command executed
  
  
  =back
  
  =back
  
  =head2 mymodule_load
  
      my $result = centreon::plugins::misc::mymodule_load(%options);
  
  Loads a Perl module dynamically.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<module> - The module to load.
  
  =item * C<error_msg> - Error message to display if the module cannot be loaded.
  
  =back
  
  =back
  
  =head2 backtick
  
      my ($status, $output, $exit_code) = centreon::plugins::misc::backtick(%options);
  
  Executes a command using backticks and returns the status, output, and exit code.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<command> - The command to execute.
  
  =item * C<arguments> - Arguments for the command.
  
  =item * C<timeout> - Timeout for the command execution.
  
  =back
  
  =back
  
  =head2 is_empty
  
      my $is_empty = centreon::plugins::misc::is_empty($value);
  
  Checks if a value is empty.
  
  =over 4
  
  =item * C<$value> - The value to check.
  
  =back
  
  =head2 trim
  
      my $trimmed_value = centreon::plugins::misc::trim($value);
  
  Trims whitespace from a string.
  
  =over 4
  
  =item * C<$value> - The string to trim.
  
  =back
  
  =head2 powershell_encoded
  
      my $encoded = centreon::plugins::misc::powershell_encoded($value);
  
  Encodes a string for use in PowerShell.
  
  =over 4
  
  =item * C<$value> - The string to encode.
  
  =back
  
  =head2 powershell_escape
  
      my $escaped = centreon::plugins::misc::powershell_escape($value);
  
  Escapes special characters in a string for use in PowerShell.
  
  =over 4
  
  =item * C<$value> - The string to escape.
  
  =back
  
  =head2 minimal_version
  
      my $is_minimal = centreon::plugins::misc::minimal_version($version_src, $version_dst);
  
  Checks if a version is at least a specified version.
  
  =over 4
  
  =item * C<$version_src> - The source version.
  
  =item * C<$version_dst> - The destination version.
  
  =back
  
  =head2 change_seconds
  
      my $formatted_time = centreon::plugins::misc::change_seconds(%options);
  
  Converts seconds into a human-readable format.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The number of seconds.
  
  =item * C<start> - The starting unit.
  
  =back
  
  =back
  
  =head2 scale_bytesbit
  
      my $scaled_value = centreon::plugins::misc::scale_bytesbit(%options);
  
  Scales a value between bytes and bits.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The value to scale.
  
  =item * C<src_unit> - The source unit.
  
  =item * C<dst_unit> - The destination unit.
  
  =back
  
  =back
  
  =head2 convert_bytes
  
      my $bytes = centreon::plugins::misc::convert_bytes(%options);
  
  Converts a value to bytes.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The value to convert.
  
  =item * C<unit> - The unit of the value.
  
  =back
  
  =back
  
  =head2 convert_fahrenheit
  
      my $celsius = centreon::plugins::misc::convert_fahrenheit(%options);
  
  Converts a temperature from Fahrenheit to Celsius.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The temperature in Fahrenheit.
  
  =back
  
  =back
  
  =head2 expand_exponential
  
      my $expanded = centreon::plugins::misc::expand_exponential(%options);
  
  Expands an exponential value to its full form.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The exponential value.
  
  =back
  
  =back
  
  =head2 alert_triggered
  
      my $is_triggered = centreon::plugins::misc::alert_triggered(%options);
  
  Checks if an alert is triggered based on thresholds.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The value to check.
  
  =item * C<warning> - The warning threshold.
  
  =item * C<critical> - The critical threshold.
  
  =back
  
  =back
  
  =head2 parse_threshold
  
      my ($status, $threshold) = centreon::plugins::misc::parse_threshold(%options);
  
  Parses a threshold string.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<threshold> - The threshold string.
  
  =back
  
  =back
  
  =head2 get_threshold_litteral
  
      my $threshold_str = centreon::plugins::misc::get_threshold_litteral(%options);
  
  Returns the literal representation of a threshold.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<arobase> - Indicates if the threshold is inclusive.
  
  =item * C<start> - The start of the threshold.
  
  =item * C<end> - The end of the threshold.
  
  =back
  
  =back
  
  =head2 set_timezone
  
      my $timezone = centreon::plugins::misc::set_timezone(%options);
  
  Sets the timezone.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<name> - The name of the timezone.
  
  =back
  
  =back
  
  =head2 uniq
  
      my @unique = centreon::plugins::misc::uniq(@values);
  
  Returns a list of unique values.
  
  =over 4
  
  =item * C<@values> - The list of values.
  
  =back
  
  =head2 eval_ssl_options
  
      my $ssl_context = centreon::plugins::misc::eval_ssl_options(%options);
  
  Evaluates SSL options.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<ssl_opt> - The SSL options.
  
  =back
  
  =back
  
  =head2 slurp_file
  
      my $content = centreon::plugins::misc::slurp_file(%options);
  
  Reads the content of a file.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<file> - The file to read.
  
  =back
  
  =back
  
  =head2 sanitize_command_param
  
      my $sanitized = centreon::plugins::misc::sanitize_command_param(%options);
  
  Sanitizes a command parameter.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<value> - The value to sanitize.
  
  =back
  
  =back
  
  =head2 check_security_command
  
      my $status = centreon::plugins::misc::check_security_command(%options);
  
  Checks the security of a command.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<command> - The command to check.
  
  =item * C<command_options> - Options for the command.
  
  =back
  
  =back
  
  =head2 check_security_whitelist
  
      my $status = centreon::plugins::misc::check_security_whitelist(%options);
  
  Checks if a command is in the security whitelist.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<command> - The command to check.
  
  =item * C<command_options> - Options for the command.
  
  =back
  
  =back
  
  =head2 json_decode
  
      my $decoded = centreon::plugins::misc::json_decode($content, %options);
  
  Decodes a JSON string.
  
  =over 4
  
  =item * C<$content> - The JSON string to decode and transform into an object.
  
  =item * C<%options> - Options passed to the function.
  
  =over 4
  
  =item * C<booleans_as_strings> - Defines whether booleans must be converted to C<true>/C<false> strings instead of
  JSON:::PP::Boolean values. C<1> => strings, C<0> => booleans.
  
  =back
  
  =back
  
  =head2 json_encode
  
      my $encoded = centreon::plugins::misc::json_encode($object);
  
  Encodes an object to a JSON string.
  
  =over 4
  
  =item * C<$object> - The object to encode.
  
  =back
  
  =head2 is_local_ip
  
      my $is_local = centreon::plugins::misc::is_local_ip($ip);
  
  Returns 1 if an IPv4 IP is within a local address range.
  
  =over 4
  
  =item * C<$ip> - IP to test.
  
  =back
  
  =head2 sort_ips
  
      my @array = ( '192.168.0.3', '127.0.0.1' );
      @array = sort centreon::plugins::misc::sort_ips @array;
  
  Returns a sorted array.
  
  =over 4
  
  =item * C<@array> - An array containing IPs to be sorted.
  
  =back
  
  =head2 is_excluded
  
      my $excluded = is_excluded($string, $include_regexp, $exclude_regexp);
  
  Determines whether a string should be excluded based on include and exclude regular expressions.
  
  =over 4
  
  =item * C<$string> - The string to evaluate. If undefined, the function returns 1 (excluded).
  
  =item * C<$include_regexp> - A regular expression to include the string.
  
  =item * C<$exclude_regexp> - A regular expression to exclude the string. If defined and matches the string, the function returns 1 (excluded).
  
  =back
  
  Returns 1 if the string is excluded, 0 if it is included.
  The string is excluded if $exclude_regexp is defined and matches the string, or if $include_regexp is defined and does
  not match the string. The string will also be excluded if it is undefined.
  
  =head1 AUTHOR
  
  Centreon
  
  =head1 LICENSE
  
  Licensed under the Apache License, Version 2.0.
  
  =cut
CENTREON_PLUGINS_MISC

$fatpacked{"centreon/plugins/mode.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_MODE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::mode;
  
  use strict;
  use warnings;
  use centreon::plugins::perfdata;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
      $self->{perfdata} = centreon::plugins::perfdata->new(output => $options{output});
      
      %{$self->{option_results}} = ();
      @{$self->{option_extras}} = @{$options{options}->{extra_arguments}};
      $self->{output} = $options{output};
      $self->{output}->use_new_perfdata(value => 1)
          if (defined($options{force_new_perfdata}) && $options{force_new_perfdata} == 1);
      $self->{mode} = $options{mode};
      $self->{version} = '1.0';
  
      return $self;
  }
  
  sub init {
      my ($self, %options) = @_;
      # options{default} = { mode_xxx => { option_name => option_value }, }
      %{$self->{option_results}} = %{$options{option_results}};
      # Manage default value
      return if (!defined($options{default}));
      foreach (keys %{$options{default}}) {
          if ($_ eq $self->{mode}) {
              foreach my $value (keys %{$options{default}->{$_}}) {
                  if (!defined($self->{option_results}->{$value})) {
                      $self->{option_results}->{$value} = $options{default}->{$_}->{$value};
                  }
              }
          }
      }
  }
  
  sub version {
      my ($self, %options) = @_;
      
      $self->{output}->add_option_msg(short_msg => "Mode Version: " . $self->{version});
  }
  
  sub disco_format {
      my ($self, %options) = @_;
  
  }
  
  sub disco_show {
      my ($self, %options) = @_;
  
  }
  
  1;
  
  
CENTREON_PLUGINS_MODE

$fatpacked{"centreon/plugins/multi.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_MULTI';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::multi;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $options{options}->add_options(arguments => { 
          'modes-exec:s'   => { name => 'modes_exec' },
          'option-mode:s@' => { name => 'option_mode' }
      });
      $self->{options} = $options{options};
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (!defined($self->{option_results}->{modes_exec})) {
          $self->{output}->add_option_msg(short_msg => "Need to specify --modes-exec option.");
          $self->{output}->option_exit(); 
      }
      $self->{options_mode_extra} = {};
      if (defined($self->{option_results}->{option_mode})) {
          foreach (@{$self->{option_results}->{option_mode}}) {
              next if (! /^(.+?),(.*)$/);
              $self->{options_mode_extra}->{$1} = [] if (!defined($self->{options_mode_extra}->{$1}));
              push @{$self->{options_mode_extra}->{$1}}, $2;
          }
      }
  
      $self->{modes} = $options{modes};
  }
  
  sub run {
      my ($self, %options) = @_;
  
      $self->{output}->parameter(attr => 'nodisplay', value => 1);
      $self->{output}->parameter(attr => 'noexit_die', value => 1);
      $self->{output}->use_new_perfdata(value => 1);
  
      my @modes = split /,/, $self->{option_results}->{modes_exec};
      foreach (@modes) {
          next if (!defined($self->{modes}->{$_}));
          eval {
              centreon::plugins::misc::mymodule_load(
                  output => $self->{output},
                  module => $self->{modes}->{$_}, 
                  error_msg => "Cannot load module --mode $_"
              );
              @ARGV = (@{$self->{options_mode_extra}->{$_}}) if (defined($self->{options_mode_extra}->{$_}));
              $self->{output}->mode(name => $_);
  
              my $mode = $self->{modes}->{$_}->new(options => $self->{options}, output => $self->{output}, mode => $_);
              $self->{options}->parse_options();
              my $option_results = $self->{options}->get_options();
              $mode->check_options(option_results => $option_results, %options);
              $mode->run(%options);
          };
          if ($@) {
              $self->{output}->output_add(long_msg => 'eval result mode ' . $_ . ': ' . $@, debug => 1);
          }
      }
  
      $self->{output}->mode(name => 'multi');
      $self->{output}->parameter(attr => 'nodisplay', value => 0);
      $self->{output}->parameter(attr => 'noexit_die', value => 0);
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check multiple modes at once.
  
  =over 8
  
  =item B<--modes-exec>
  
  Modes to use, separated by commas.
  Example for linux: --modes-exec=cpu,memory,storage,interfaces
  
  =item B<--option-mode>
  
  Define options for the modes selected in --modes-exec.
  The option can be used several times.
  E.g.: to define two options for the interfaces mode and one for the storage mode:
  --option-mode='interfaces,--statefile-dir=/tmp' --option-mode='interfaces,--add-traffic' --option-mode='storage,--statefile-dir=/tmp'
  
  =back
  
  =cut
CENTREON_PLUGINS_MULTI

$fatpacked{"centreon/plugins/options.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_OPTIONS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::options;
  
  use Pod::Usage;
  use strict;
  use warnings;
  
  my $alternative = 1;
  
  sub new {
      my ($class) = @_;
      my $self  = {};
      bless $self, $class;
  
      $self->{pod_where_loaded} = 0;
      $self->{sanity} = 0;
      $self->{options_stored} = {};
      $self->{options} = {};
      @{$self->{pod_package}} = ();
      $self->{pod_packages_once} = {};
      $self->{extra_arguments} = [];
  
      if ($alternative == 0) {
          require Getopt::Long;
          Getopt::Long->import();
          Getopt::Long::Configure("pass_through");
          Getopt::Long::Configure('bundling');
          Getopt::Long::Configure('no_auto_abbrev');
      } else {
          require centreon::plugins::alternative::Getopt;
          $centreon::plugins::alternative::Getopt::warn_message = 0;
          centreon::plugins::alternative::Getopt->import();
      }
  
      return $self;
  }
  
  sub set_sanity {
      my ($self, %options) = @_;
  
      if ($alternative == 0) {
          Getopt::Long::Configure('no_pass_through');
      } else {
          $centreon::plugins::alternative::Getopt::warn_message = 1;
      }
  
      $self->{sanity} = 1;
  }
  
  sub set_output {
      my ($self, %options) = @_;
  
      $self->{output} = $options{output};
  }
  
  sub display_help {
      my ($self, %options) = @_;
  
      my $stdout;
      foreach (@{$self->{pod_package}}) {
          my $where = $self->pod_where(package => $_->{package});
  
          {
              local *STDOUT;
              open STDOUT, '>', \$stdout;
              pod2usage(
                  -exitval => 'NOEXIT', -input => $where,
                  -verbose => 99, 
                  -sections => $_->{sections}
              ) if (defined($where));
          }
  
          $self->{output}->add_option_msg(long_msg => $stdout) if (defined($stdout));
      }
  }
  
  sub add_help {
      my ($self, %options) = @_;
      # $options{package} = string package
      # $options{sections} = string sections
      # $options{help_first} = put at the beginning
      # $options{once} = put help only one time for a package
  
      if (defined($options{once}) && defined($self->{pod_packages_once}->{$options{package}})) {
          return ;
      }
  
      if (defined($options{help_first})) {
          unshift @{$self->{pod_package}}, {package => $options{package}, sections => $options{sections}};
      } else {
          push @{$self->{pod_package}}, { package => $options{package}, sections => $options{sections} };
      }
  
      $self->{pod_packages_once}->{$options{package}} = 1;
  }
  
  sub add_options {
      my ($self, %options) = @_;
      # $options{arguments} = ref to hash table with string and name to store (example: { 'mode:s' => { name => 'mode', default => 'defaultvalue' )
  
      foreach (keys %{$options{arguments}}) {
          if (defined($options{arguments}->{$_}->{redirect})) {
              $self->{options}->{$_} = \$self->{options_stored}->{$options{arguments}->{$_}->{redirect}};
              next;
          }
  
          if (defined($options{arguments}->{$_}->{default})) {
              $self->{options_stored}->{$options{arguments}->{$_}->{name}} = $options{arguments}->{$_}->{default};
          } else {
              $self->{options_stored}->{$options{arguments}->{$_}->{name}} = undef;
          }
          
          $self->{options}->{$_} = \$self->{options_stored}->{$options{arguments}->{$_}->{name}};
      }
  }
  
  sub parse_options {
      my $self = shift;
      #%{$self->{options_stored}} = ();
  
      my $save_warn_handler;
      if ($self->{sanity} == 1) {
          $save_warn_handler = $SIG{__WARN__};
          $SIG{__WARN__} = sub {
              $self->{output}->add_option_msg(short_msg => $_[0]);
              $self->{output}->option_exit(nolabel => 1);
          };
      }
  
      # Store all arguments placed after the special argument "--" in the 'extra_arguments' list
      $self->{options}->{'_double_dash_'} = \$self->{extra_arguments};
  
      GetOptions(
         %{$self->{options}}
      );
      %{$self->{options}} = ();
  
      $SIG{__WARN__} = $save_warn_handler if ($self->{sanity} == 1);
  }
  
  sub pod_where {
      my ($self, %options) = @_;
  
      if ($self->{pod_where_loaded} == 0) {
          $self->{pod_where_loaded} = 1;
          my ($code) = centreon::plugins::misc::mymodule_load(
              module => 'Pod::Find',
              no_quit => 1
          );
          if ($code) {
              $code = centreon::plugins::misc::mymodule_load(
                  module => 'Pod::Simple::Search',
                  no_quit => 1
              );
              die "Cannot load module 'Pod::Simple::Search'" if ($code);
              $self->{pod_where_loaded} = 2;
              $self->{pod_simple_search} = Pod::Simple::Search->new();
              $self->{pod_simple_search}->inc(1);
          }
      }
  
      if ($self->{pod_where_loaded} == 1) {
          return Pod::Find::pod_where({-inc => 1}, $options{package});
      }
      
      return $self->{pod_simple_search}->find($options{package});
  }
  
  sub get_option {
      my ($self, %options) = @_;
  
      return $self->{options_stored}->{$options{argument}};
  }
  
  sub get_options {
      my $self = shift;
  
      return $self->{options_stored};
  }
  
  sub clean {
      my $self = shift;
  
      $self->{options_stored} = {};
  }
  
  1;
  
CENTREON_PLUGINS_OPTIONS

$fatpacked{"centreon/plugins/output.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_OUTPUT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::output;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{options})) {
          print "Class Output: Need to specify 'options' argument to load.\n";
          exit 3;
      }
  
      $options{options}->add_options(arguments => {
          'explode-perfdata-max:s@' => { name => 'explode_perfdata_max' },
          'range-perfdata:s'        => { name => 'range_perfdata' },
          'filter-perfdata:s'       => { name => 'filter_perfdata' },
          'filter-perfdata-adv:s'   => { name => 'filter_perfdata_adv' },
          'change-perfdata:s@'      => { name => 'change_perfdata' },
          'extend-perfdata:s@'      => { name => 'extend_perfdata' },
          'extend-perfdata-group:s@'=> { name => 'extend_perfdata_group' },
          'change-exit:s@'          => { name => 'change_exit' },
          'change-short-output:s@'  => { name => 'change_short_output' },
          'change-long-output:s@'   => { name => 'change_long_output' },
          'change-output-adv:s@'    => { name => 'change_output_adv' },
          'use-new-perfdata'        => { name => 'use_new_perfdata' },
          'filter-uom:s'            => { name => 'filter_uom' },
          'verbose'                 => { name => 'verbose' },
          'debug'                   => { name => 'debug' },
          'debug-stream'            => { name => 'debug_stream' },
          'opt-exit:s'              => { name => 'opt_exit', default => 'unknown' },
          'output-xml'              => { name => 'output_xml' },
          'output-json'             => { name => 'output_json' },
          'output-ignore-perfdata'  => { name => 'output_ignore_perfdata' },
          'output-ignore-label'     => { name => 'output_ignore_label' },
          'output-openmetrics'      => { name => 'output_openmetrics' },
          'output-file:s'           => { name => 'output_file' },
          'disco-format'            => { name => 'disco_format' },
          'disco-show'              => { name => 'disco_show' },
          'float-precision:s'       => { name => 'float_precision', default => 8 },
          'source-encoding:s'       => { name => 'source_encoding' , default => 'UTF-8' }
      });
  
      $self->{option_results} = {};
      $self->{option_msg} = [];
  
      $self->{nodisplay} = 0;
      $self->{noexit_die} = 0;
  
      $self->{is_output_xml} = 0;
      $self->{is_output_json} = 0;
      $self->{errors} = {OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3, PENDING => 4};
      $self->{errors_num} = {0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN', 4 => 'PENDING'};
      $self->{myerrors} = {0 => "OK", 1 => "WARNING", 3 => "UNKNOWN", 7 => "CRITICAL"};
      $self->{myerrors_mask} = {CRITICAL => 7, WARNING => 1, UNKNOWN => 3, OK => 0};
      $self->{global_short_concat_outputs} = {OK => undef, WARNING => undef, CRITICAL => undef, UNKNOWN => undef, UNQUALIFIED_YET => undef};
      $self->{global_short_outputs} = {OK => [], WARNING => [], CRITICAL => [], UNKNOWN => [], UNQUALIFIED_YET => []};
      $self->{global_long_output} = [];
      $self->{perfdatas} = [];
      $self->{explode_perfdatas} = {};
      $self->{change_perfdata} = {};
      $self->{explode_perfdata_total} = 0;
      $self->{range_perfdata} = 0;
      $self->{global_status} = 0;
      $self->{encode_import} = 0;
      $self->{perlqq} = 0;
      $self->{safe_test} = 0;
  
      $self->{disco_elements} = [];
      $self->{disco_entries} = [];
  
      $self->{plugin} = '';
      $self->{mode} = '';
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      # $options{option_results} = ref to options result
  
      %{$self->{option_results}} = %{$options{option_results}};
      $self->{option_results}->{opt_exit} = lc($self->{option_results}->{opt_exit});
      if (!$self->is_litteral_status(status => $self->{option_results}->{opt_exit})) {
          $self->add_option_msg(short_msg => "Unknown value '" . $self->{option_results}->{opt_exit}  . "' for --opt-exit.");
          $self->option_exit(exit_litteral => 'unknown');
      }
      # Go in XML Mode
      if ($self->is_disco_show() || $self->is_disco_format()) {
          # By Default XML
          if (!defined($self->{option_results}->{output_json})) {
              $self->{option_results}->{output_xml} = 1;
          }
      }
  
      if (defined($self->{option_results}->{range_perfdata})) {
          $self->{range_perfdata} = $self->{option_results}->{range_perfdata};
          $self->{range_perfdata} = 1 if ($self->{range_perfdata} eq '');
          if ($self->{range_perfdata} !~ /^[012]$/) {
              $self->add_option_msg(short_msg => "Wrong range-perfdata option '" . $self->{range_perfdata} . "'");
              $self->option_exit();
          }
      }
  
      if (defined($self->{option_results}->{change_output_adv})) {
          foreach (@{$self->{option_results}->{change_output_adv}}) {
              my ($expr, $short_output, $exit_code) = split /,/;
              next if (!defined($expr) || $expr eq '');
  
              $expr =~ s/%\{(.*?)\}/\$values->{$1}/g;
              $expr =~ s/%\((.*?)\)/\$values->{$1}/g;
  
              if ( defined($exit_code) && $exit_code ne '' && defined( $self->{errors}->{uc($exit_code)} ) ) {
                  $exit_code = uc($exit_code);
              } else {
                  $exit_code = undef;
              }
  
              $self->{change_output_adv} = [] if (!defined($self->{change_output_adv}));
              push @{$self->{change_output_adv}}, {
                  expr => $expr,
                  short_output => $short_output,
                  exit_code => $exit_code
              };
          }
      }
  
      if (defined($self->{option_results}->{change_exit})) {
          $self->{change_exit} = {};
          foreach (@{$self->{option_results}->{change_exit}}) {
              my ($src, $dst) = split(/=/);
              next if (!defined($src) || !defined($self->{errors}->{ uc($src) }));
              next if (!defined($dst) || !defined($self->{errors}->{ uc($dst) }));
              $self->{change_exit}->{uc($src)} = uc($dst);
          }
      }
  
      if (defined($self->{option_results}->{explode_perfdata_max})) {
          if (${$self->{option_results}->{explode_perfdata_max}}[0] eq '') {
              $self->{explode_perfdata_total} = 2;
          } else {
              $self->{explode_perfdata_total} = 1;
              foreach (@{$self->{option_results}->{explode_perfdata_max}}) {
                  my ($perf_match, $perf_result) = split /,/;
                  if (!defined($perf_result)) {
                      $self->add_option_msg(short_msg => "Wrong explode-perfdata-max option '" . $_ . "' (syntax: match,value)");
                      $self->option_exit();
                  }
                  $self->{explode_perfdatas}->{$perf_match} = $perf_result;
              }
          }
      }
  
      if (defined($self->{option_results}->{filter_perfdata_adv}) && $self->{option_results}->{filter_perfdata_adv} ne '') {
          $self->{option_results}->{filter_perfdata_adv} =~ s/%\{(.*?)\}/\$values->{$1}/g;
          $self->{option_results}->{filter_perfdata_adv} =~ s/%\((.*?)\)/\$values->{$1}/g;
          $self->{option_results}->{filter_perfdata_adv} =~ s/alert_triggered\(\)/alert_triggered\(%\$values\)/g;
      }
  
      $self->load_perfdata_extend_args();
      $self->{option_results}->{use_new_perfdata} = 1 if (defined($self->{option_results}->{output_openmetrics}));
  
      $self->{source_encoding} = (!defined($self->{option_results}->{source_encoding}) || $self->{option_results}->{source_encoding} eq '') ?
          'UTF-8' : $self->{option_results}->{source_encoding};
  }
  
  sub add_option_msg {
      my ($self, %options) = @_;
      # $options{short_msg} = string msg
      # $options{long_msg} = string msg
      $options{severity} = 'UNQUALIFIED_YET';
  
      $self->output_add(%options);
  }
  
  sub set_ignore_label {
      my ($self, %options) = @_;
  
      $self->{option_results}->{output_ignore_label} = 1;
  }
  
  sub set_status {
      my ($self, %options) = @_;
      # $options{exit_litteral} = string litteral exit
  
      # Nothing to do for 'UNQUALIFIED_YET'
      if (!$self->{myerrors_mask}->{uc($options{exit_litteral})}) {
          return ;
      }
      $self->{global_status} |= $self->{myerrors_mask}->{uc($options{exit_litteral})};
  }
  
  sub output_add {
      my ($self, %params) = @_;
      my %args = (
          severity => 'OK',
          separator => ' - ',
          debug => 0,
          short_msg => undef,
          long_msg => undef,
      );
      my $options = { %args, %params };
  
      if (defined($options->{short_msg})) {
          chomp $options->{short_msg};
          if (defined($self->{global_short_concat_outputs}->{uc($options->{severity})})) {
              $self->{global_short_concat_outputs}->{uc($options->{severity})} .= $options->{separator} . $options->{short_msg};
          } else {
              $self->{global_short_concat_outputs}->{uc($options->{severity})} = $options->{short_msg};
          }
  
          push @{$self->{global_short_outputs}->{uc($options->{severity})}}, $options->{short_msg};
          $self->set_status(exit_litteral => $options->{severity});
      }
  
      if (defined($options->{long_msg})) {
          chomp $options->{long_msg};
  
          push @{$self->{global_long_output}}, $options->{long_msg} if ($options->{debug} == 0 || defined($self->{option_results}->{debug}));
          print $options->{long_msg} . "\n" if (defined($self->{option_results}->{debug_stream}));
      }
  }
  
  sub perfdata_add {
      my ($self, %options) = @_;
  
      my $perfdata = {
          label => '', value => '', unit => '', warning => '', critical => '', min => '', max => '', mode => $self->{mode}
      };
      foreach (keys %options) {
          next if (!defined($options{$_}));
          $perfdata->{$_} = $options{$_};
      }
  
      if ((defined($self->{option_results}->{use_new_perfdata}) || defined($options{force_new_perfdata})) &&
          defined($options{nlabel})) {
          $perfdata->{label} = $options{nlabel};
      }
      if (defined($options{instances})) {
          $options{instances} = [$options{instances}] if (!ref($options{instances}));
          my ($external_instance_separator, $internal_instance_separator) = ('#', '~');
          if (defined($self->{option_results}->{use_new_perfdata}) || defined($options{force_new_perfdata})) {
              $perfdata->{label} = join('~', @{$options{instances}}) . '#' . $perfdata->{label};
          } else {
              $perfdata->{label} .= '_' . join('_', @{$options{instances}});
          }
      }
  
      $perfdata->{label} =~ s/'/''/g;
      push @{$self->{perfdatas}}, $perfdata;
  }
  
  sub filter_perfdata {
      my ($self, %options) = @_;
  
      return 1 if (
          defined($self->{option_results}->{filter_perfdata}) &&
          $options{perf}->{label} !~ /$self->{option_results}->{filter_perfdata}/
      );
  
      return 1 if (
          defined($self->{option_results}->{filter_perfdata_adv}) &&
          $self->{option_results}->{filter_perfdata_adv} ne '' &&
          !$self->test_eval(test => $self->{option_results}->{filter_perfdata_adv}, values => $options{perf})
      );
  
      return 0;
  }
  
  sub range_perfdata {
      my ($self, %options) = @_;
  
      return if ($self->{range_perfdata} == 0);
      if ($self->{range_perfdata} == 1) {
          for (my $i = 0; $i < scalar(@{$options{ranges}}); $i++) {
              ${${$options{ranges}}[$i]} =~ s/^(@?)-?[0\.]+:/$1/;
          }
      } else {
          for (my $i = 0; $i < scalar(@{$options{ranges}}); $i++) {
              ${${$options{ranges}}[$i]} = '';
          }
      }
  }
  
  sub output_json {
      my ($self, %options) = @_;
      my $force_ignore_perfdata = (defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) ? 1 : 0;
      my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
      my $json_content = {
          plugin => {
              name => $self->{plugin},
              mode => $self->{mode},
              exit => $options{exit_litteral},
              outputs => [],
              perfdatas => []
          }
      };
  
      foreach my $code_litteral (keys %{$self->{global_short_outputs}}) {
          foreach (@{$self->{global_short_outputs}->{$code_litteral}}) {
              my ($child_output, $child_type, $child_msg, $child_exit);
              my $lcode_litteral = ($code_litteral eq 'UNQUALIFIED_YET' ? uc($options{exit_litteral}) : $code_litteral);
  
              push @{$json_content->{plugin}->{outputs}}, {
                  type => 1,
                  msg => ($options{nolabel} == 0 ? ($lcode_litteral . ': ') : '') . $_,
                  exit => $lcode_litteral
              };
          }
      }
  
      if (defined($self->{option_results}->{verbose}) || $force_long_output == 1) {
          foreach (@{$self->{global_long_output}}) {
              push @{$json_content->{plugin}->{outputs}}, {
                  type => 2,
                  msg => $_
              };
          }
      }
  
      if ($options{force_ignore_perfdata} == 0) {
          $self->change_perfdata();
          foreach my $perf (@{$self->{perfdatas}}) {
              next if ($self->filter_perfdata(perf => $perf));
              $self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
  
              my %values = ();
              foreach my $key (keys %$perf) {
                  $perf->{$key} = '' if (defined($self->{option_results}->{filter_uom}) && $key eq 'unit' &&
                      $perf->{$key} !~ /$self->{option_results}->{filter_uom}/);
                  $values{$key} = $perf->{$key};
              }
  
              push @{$json_content->{plugin}->{perfdatas}}, {
                  %values
              };
          }
      }
  
      print $self->{json_output}->encode($json_content);
  }
  
  sub output_xml {
      my ($self, %options) = @_;
      my $force_ignore_perfdata = (defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) ? 1 : 0;
      my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
      my ($child_plugin_name, $child_plugin_mode, $child_plugin_exit, $child_plugin_output, $child_plugin_perfdata);
  
      my $root = $self->{xml_output}->createElement('plugin');
      $self->{xml_output}->setDocumentElement($root);
  
      $child_plugin_name = $self->{xml_output}->createElement('name');
      $child_plugin_name->appendText($self->{plugin});
  
      $child_plugin_mode = $self->{xml_output}->createElement('mode');
      $child_plugin_mode->appendText($self->{mode});
  
      $child_plugin_exit = $self->{xml_output}->createElement('exit');
      $child_plugin_exit->appendText($options{exit_litteral});
  
      $child_plugin_output = $self->{xml_output}->createElement('outputs');
      $child_plugin_perfdata = $self->{xml_output}->createElement('perfdatas');
  
      $root->addChild($child_plugin_name);
      $root->addChild($child_plugin_mode);
      $root->addChild($child_plugin_exit);
      $root->addChild($child_plugin_output);
      $root->addChild($child_plugin_perfdata);
  
      foreach my $code_litteral (keys %{$self->{global_short_outputs}}) {
          foreach (@{$self->{global_short_outputs}->{$code_litteral}}) {
              my ($child_output, $child_type, $child_msg, $child_exit);
              my $lcode_litteral = ($code_litteral eq 'UNQUALIFIED_YET' ? uc($options{exit_litteral}) : $code_litteral);
  
              $child_output = $self->{xml_output}->createElement('output');
              $child_plugin_output->addChild($child_output);
  
              $child_type = $self->{xml_output}->createElement('type');
              $child_type->appendText(1); # short
  
              $child_msg = $self->{xml_output}->createElement('msg');
              $child_msg->appendText(($options{nolabel} == 0 ? ($lcode_litteral . ': ') : '') . $_);
              $child_exit = $self->{xml_output}->createElement('exit');
              $child_exit->appendText($lcode_litteral);
  
              $child_output->addChild($child_type);
              $child_output->addChild($child_exit);
              $child_output->addChild($child_msg);
          }
      }
  
      if (defined($self->{option_results}->{verbose}) || $force_long_output == 1) {
          foreach (@{$self->{global_long_output}}) {
              my ($child_output, $child_type, $child_msg);
  
              $child_output = $self->{xml_output}->createElement('output');
              $child_plugin_output->addChild($child_output);
  
              $child_type = $self->{xml_output}->createElement('type');
              $child_type->appendText(2); # long
  
              $child_msg = $self->{xml_output}->createElement('msg');
              $child_msg->appendText($_);
  
              $child_output->addChild($child_type);
              $child_output->addChild($child_msg);
          }
      }
  
      if ($options{force_ignore_perfdata} == 0) {
          $self->change_perfdata();
          foreach my $perf (@{$self->{perfdatas}}) {
              next if ($self->filter_perfdata(perf => $perf));
              $self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
  
              my ($child_perfdata);
              $child_perfdata = $self->{xml_output}->createElement('perfdata');
              $child_plugin_perfdata->addChild($child_perfdata);
              foreach my $key (keys %$perf) {
                  $perf->{$key} = '' if (defined($self->{option_results}->{filter_uom}) && $key eq 'unit' &&
                      $perf->{$key} !~ /$self->{option_results}->{filter_uom}/);
                  my $child = $self->{xml_output}->createElement($key);
                  $child->appendText($perf->{$key});
                  $child_perfdata->addChild($child);
              }
          }
      }
  
      print $self->{xml_output}->toString(1);
  }
  
  sub output_openmetrics {
      my ($self, %options) = @_;
  
      centreon::plugins::misc::mymodule_load(
          output => $self->{output}, module => 'Time::HiRes',
          error_msg => "Cannot load module 'Time::HiRes'."
      );
  
      my $time_ms = int(Time::HiRes::time() * 1000);
      $self->change_perfdata();
  
      foreach my $perf (@{$self->{perfdatas}}) {
          next if ($self->filter_perfdata(perf => $perf));
  
          $perf->{unit} = '' if (
              defined($self->{option_results}->{filter_uom}) &&
              $perf->{unit} !~ /$self->{option_results}->{filter_uom}/
          );
          $self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
          my $label = $perf->{label};
          my $instance;
          if ($label =~ /^(.*?)#(.*)$/) {
              ($perf->{instance}, $label) = ($1, $2);
          }
          my ($bucket, $append) = ('{plugin="' . $self->{plugin} . '",mode="' . $perf->{mode} . '"', '');
          foreach ('unit', 'warning', 'critical', 'min', 'max', 'instance') {
              if (defined($perf->{$_}) && $perf->{$_} ne '') {
                  $bucket .= ',' . $_ . '="' . $perf->{$_} . '"';
              }
          }
          $bucket .= '}';
  
          print $label . $bucket . ' ' . $perf->{value} . ' ' . $time_ms . "\n";
      }
  }
  
  sub output_txt_short_display {
      my ($self, %options) = @_;
  
      if (defined($self->{global_short_concat_outputs}->{CRITICAL})) {
          print (($options{nolabel} == 0 ? 'CRITICAL: ' : '') . $self->{global_short_concat_outputs}->{CRITICAL} . " ");
      }
      if (defined($self->{global_short_concat_outputs}->{WARNING})) {
          print (($options{nolabel} == 0 ? 'WARNING: ' : '') . $self->{global_short_concat_outputs}->{WARNING} . " ");
      }
      if (defined($self->{global_short_concat_outputs}->{UNKNOWN})) {
          print (($options{nolabel} == 0 ? 'UNKNOWN: ' : '') . $self->{global_short_concat_outputs}->{UNKNOWN} . " ");
      }
      if (uc($options{exit_litteral}) eq 'OK') {
          print (($options{nolabel} == 0 ? 'OK: ' : '') . (defined($self->{global_short_concat_outputs}->{OK}) ? $self->{global_short_concat_outputs}->{OK} : '') . " ");
      }
  }
  
  sub output_txt_short {
      my ($self, %options) = @_;
  
      if (!defined($self->{option_results}->{change_short_output}) && 
          !defined($self->{change_output_adv})) {
          $self->output_txt_short_display(%options);
          return ;
      }
  
      my $stdout = '';
      {
          local *STDOUT;
          open STDOUT, '>', \$stdout;
          $self->output_txt_short_display(%options);
      }
  
      foreach (@{$self->{option_results}->{change_short_output}}) {
           my ($pattern, $replace, $modifier) = split /~/;
           next if (!defined($pattern));
           $replace = '' if (!defined($replace));
           $modifier = '' if (!defined($modifier));
           eval "\$stdout =~ s{$pattern}{$replace}$modifier";
      }
  
      my $exit = defined($options{exit_litteral}) ? uc($options{exit_litteral}) : uc($self->{myerrors}->{ $self->{global_status} });
      foreach (@{$self->{change_output_adv}}) {
          if ($self->test_eval(test => $_->{expr}, values => { short_output => $stdout, exit_code => $self->{errors}->{$exit} })) {
              if (defined($_->{short_output}) && $_->{short_output} ne '') {
                  $stdout = $_->{short_output};
              }
              if (defined($_->{exit_code}) && $_->{exit_code} ne '') {
                  $self->{coa_save_exit_code} = $_->{exit_code};
              }
          }
      }
  
      print $stdout;
  }
  
  sub output_txt {
      my ($self, %options) = @_;
      my $force_ignore_perfdata = (defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) ? 1 : 0;
      my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
  
      return if ($self->{nodisplay} == 1);
  
      if (defined($self->{global_short_concat_outputs}->{UNQUALIFIED_YET})) {
          $self->output_add(severity => uc($options{exit_litteral}), short_msg => $self->{global_short_concat_outputs}->{UNQUALIFIED_YET});
      }
  
      $self->output_txt_short(%options);
  
      if ($force_ignore_perfdata == 0) {
          my $pipe = 0;
          $self->change_perfdata();
          foreach my $perf (@{$self->{perfdatas}}) {
              next if ($self->filter_perfdata(perf => $perf));
              $perf->{unit} = '' if (defined($self->{option_results}->{filter_uom}) &&
                  $perf->{unit} !~ /$self->{option_results}->{filter_uom}/);
              $self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
              if ($pipe == 0) {
                  print '|';
                  $pipe = 1;
              }
              print " '" . $perf->{label} . "'=" . $perf->{value} . $perf->{unit} . ';' . $perf->{warning} . ';' . $perf->{critical} . ';' . $perf->{min} . ';' . $perf->{max};
          }
      }
  
      print "\n";
  
      if (defined($self->{option_results}->{verbose}) || $force_long_output == 1) {
          if (scalar(@{$self->{global_long_output}})) {
              print join("\n", @{$self->{global_long_output}});
              print "\n";
          }
      }
  }
  
  sub change_long_output {
      my ($self, %options) = @_;
  
      return if (!(defined($self->{option_results}->{verbose}) || $options{force_long_output} == 1));
      return if (!defined($self->{option_results}->{change_long_output}));
  
      my $long_output = join("\n", @{$self->{global_long_output}});
  
      foreach (@{$self->{option_results}->{change_long_output}}) {
          my ($pattern, $replace, $modifier) = split /~/;
          next if (!defined($pattern));
          $replace = '' if (!defined($replace));
          $modifier = '' if (!defined($modifier));
          eval "\$long_output =~ s{$pattern}{$replace}$modifier";
      }
  
      $self->{global_long_output} = [split(/\n/, $long_output)];
  }
  
  sub display {
      my ($self, %options) = @_;
      my $nolabel = (defined($options{nolabel}) || defined($self->{option_results}->{output_ignore_label})) ? 1 : 0;
      my $force_ignore_perfdata = ((defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) || $self->{option_results}->{output_ignore_perfdata}) ? 1 : 0;
      my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
      $force_long_output = 1 if (defined($self->{option_results}->{debug}));
  
      if (defined($self->{option_results}->{output_openmetrics})) {
          $self->perfdata_add(nlabel => 'plugin.mode.status', value => $self->{errors}->{$self->{myerrors}->{$self->{global_status}}});
      }
  
      if (defined($self->{option_results}->{change_long_output})) {
          $self->change_long_output(force_long_output => $force_long_output);
      }
  
      return if ($self->{nodisplay} == 1);
  
      if (defined($self->{option_results}->{output_file})) {
          if (!open (STDOUT, '>', $self->{option_results}->{output_file})) {
              $self->output_add(
                  severity => 'UNKNOWN',
                  short_msg => "cannot open file  '" . $self->{option_results}->{output_file} . "': $!"
              );
          }
      }
      if (defined($self->{option_results}->{output_xml})) {
          $self->create_xml_document();
          if ($self->{is_output_xml}) {
              $self->output_xml(
                  exit_litteral => $self->get_litteral_status(),
                  nolabel => $nolabel,
                  force_ignore_perfdata => $force_ignore_perfdata, force_long_output => $force_long_output
              );
              return ;
          }
      } elsif (defined($self->{option_results}->{output_json})) {
          $self->create_json_document();
          if ($self->{is_output_json}) {
              $self->output_json(
                  exit_litteral => $self->get_litteral_status(),
                  nolabel => $nolabel,
                  force_ignore_perfdata => $force_ignore_perfdata, force_long_output => $force_long_output
              );
              return ;
          }
      } elsif (defined($self->{option_results}->{output_openmetrics})) {
          $self->output_openmetrics();
          return ;
      }
  
      $self->output_txt(
          exit_litteral => $self->get_litteral_status(),
          nolabel => $nolabel,
          force_ignore_perfdata => $force_ignore_perfdata, force_long_output => $force_long_output
      );
  }
  
  sub die_exit {
      my ($self, %options) = @_;
      # $options{exit_litteral} = string litteral exit
      # $options{nolabel} = interger label display
      my $exit_litteral = defined($options{exit_litteral}) ? $options{exit_litteral} : $self->{option_results}->{opt_exit};
      my $nolabel = (defined($options{nolabel}) || defined($self->{option_results}->{output_ignore_label})) ? 1 : 0;
      # ignore long output in the following case
      $self->{option_results}->{verbose} = undef;
  
      if (defined($self->{option_results}->{output_xml})) {
          $self->create_xml_document();
          if ($self->{is_output_xml}) {
              $self->output_xml(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1);
              $self->exit(exit_litteral => $exit_litteral);
          }
      } elsif (defined($self->{option_results}->{output_json})) {
          $self->create_json_document();
          if ($self->{is_output_json}) {
              $self->output_json(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1);
              $self->exit(exit_litteral => $exit_litteral);
          }
      }
  
      $self->output_txt(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1);
      $self->exit(exit_litteral => $exit_litteral);
  }
  
  sub option_exit {
      my ($self, %options) = @_;
      # $options{exit_litteral} = string litteral exit
      # $options{nolabel} = interger label display
      my $exit_litteral = defined($options{exit_litteral}) ? $options{exit_litteral} : $self->{option_results}->{opt_exit};
      my $nolabel = (defined($options{nolabel}) || defined($self->{option_results}->{output_ignore_label})) ? 1 : 0;
  
      if (defined($self->{option_results}->{output_xml})) {
          $self->create_xml_document();
          if ($self->{is_output_xml}) {
              $self->output_xml(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1, force_long_output => 1);
              $self->exit(exit_litteral => $exit_litteral);
          }
      } elsif (defined($self->{option_results}->{output_json})) {
          $self->create_json_document();
          if ($self->{is_output_json}) {
              $self->output_json(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1, force_long_output => 1);
              $self->exit(exit_litteral => $exit_litteral);
          }
      } elsif (defined($self->{option_results}->{output_openmetrics})) {
          $self->set_status(exit_litteral => $exit_litteral);
          $self->output_openmetrics();
          $self->exit(exit_litteral => $exit_litteral);
      }
  
      $self->output_txt(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1, force_long_output => 1);
      $self->exit(exit_litteral => $exit_litteral);
  }
  
  sub exit {
      my ($self, %options) = @_;
  
      if ($self->{noexit_die} == 1) {
          die 'exit';
      }
  
      my $exit;
      if (defined($options{exit_litteral})) {
          $exit = uc($options{exit_litteral});
      } else {
          $exit = $self->{myerrors}->{ $self->{global_status} };
      }
   
      if (defined($self->{coa_save_exit_code})) {
          $exit = $self->{coa_save_exit_code};
      }
      if (defined($self->{change_exit}) && defined($self->{change_exit}->{$exit})) {
          $exit = $self->{change_exit}->{$exit};
      }
      exit $self->{errors}->{$exit};
  }
  
  sub get_option {
      my ($self, %options) = @_;
  
      return $self->{option_results}->{$options{option}};
  }
  
  sub get_most_critical {
      my ($self, %options) = @_;
      my $current_status = 0; # For 'OK'
  
      foreach (@{$options{status}}) {
          if ($self->{myerrors_mask}->{uc($_)} > $current_status) {
              $current_status = $self->{myerrors_mask}->{uc($_)};
          }
      }
      return $self->{myerrors}->{$current_status};
  }
  
  sub get_litteral_status {
      my ($self, %options) = @_;
  
      if (defined($options{status})) {
          if (defined($self->{errors_num}->{$options{status}})) {
              return $self->{errors_num}->{$options{status}};
          }
          return $options{status};
      } else {
          return $self->{myerrors}->{$self->{global_status}};
      }
  }
  
  sub is_status {
      my ($self, %options) = @_;
      # $options{value} = string status
      # $options{litteral} = value is litteral
      # $options{compare} = string status
  
      if (defined($options{litteral})) {
          my $value = defined($options{value}) ? $options{value} : $self->get_litteral_status();
  
          if (uc($value) eq uc($options{compare})) {
              return 1;
          }
          return 0;
      }
  
      my $value = defined($options{value}) ? $options{value} : $self->{global_status};
      my $dec_val = $self->{myerrors_mask}->{$value};
      my $lresult = $value & $dec_val;
      # Need to manage 0
      if ($lresult > 0 || ($dec_val == 0 && $value == 0)) {
          return 1;
      }
      return 0;
  }
  
  sub is_litteral_status {
      my ($self, %options) = @_;
      # $options{status} = string status
  
      if (defined($self->{errors}->{uc($options{status})})) {
          return 1;
      }
  
      return 0;
  }
  
  sub create_json_document {
      my ($self) = @_;
  
      if (centreon::plugins::misc::mymodule_load(
          no_quit => 1, module => 'JSON',
          error_msg => "Cannot load module 'JSON'.")
          ) {
          print "Cannot load module 'JSON'\n";
          $self->exit(exit_litteral => 'unknown');
      }
      $self->{is_output_json} = 1;
      $self->{json_output} = JSON->new->utf8();
  }
  
  sub create_xml_document {
      my ($self) = @_;
  
      if (centreon::plugins::misc::mymodule_load(
          no_quit => 1, module => 'XML::LibXML',
          error_msg => "Cannot load module 'XML::LibXML'.")
          ) {
          print "Cannot load module 'XML::LibXML'\n";
          $self->exit(exit_litteral => 'unknown');
      }
      $self->{is_output_xml} = 1;
      $self->{xml_output} = XML::LibXML::Document->new('1.0', 'utf-8');
  }
  
  sub plugin {
      my ($self, %options) = @_;
      # $options{name} = string name
  
      if (defined($options{name})) {
          $self->{plugin} = $options{name};
      }
      return $self->{plugin};
  }
  
  sub mode {
      my ($self, %options) = @_;
  
      if (defined($options{name})) {
          $self->{mode} = $options{name};
      }
      return $self->{mode};
  }
  
  sub add_disco_format {
      my ($self, %options) = @_;
  
      push @{$self->{disco_elements}}, @{$options{elements}};
  }
  
  sub display_disco_format {
      my ($self, %options) = @_;
  
      if (defined($self->{option_results}->{output_xml})) {
          $self->create_xml_document();
  
          my $root = $self->{xml_output}->createElement('data');
          $self->{xml_output}->setDocumentElement($root);
  
          foreach (@{$self->{disco_elements}}) {
              my $child = $self->{xml_output}->createElement("element");
              $child->appendText($_);
              $root->addChild($child);
          }
  
          print $self->{xml_output}->toString(1);
      } elsif (defined($self->{option_results}->{output_json})) {
          $self->create_json_document();
          my $json_content = {data => [] };
          foreach (@{$self->{disco_elements}}) {
              push @{$json_content->{data}}, $_;
          }
  
          print $self->{json_output}->encode($json_content);
      }
  }
  
  sub display_disco_show {
      my ($self, %options) = @_;
  
      if (defined($self->{option_results}->{output_xml})) {
          $self->create_xml_document();
  
          my $root = $self->{xml_output}->createElement('data');
          $self->{xml_output}->setDocumentElement($root);
  
          foreach (@{$self->{disco_entries}}) {
              my $child = $self->{xml_output}->createElement('label');
              foreach my $key (keys %$_) {
                  # Encode all non printable chars as hexadecimal entities to produce valid XML
                  # I.e. "test ^H" becomes "test &#x8;"
                  my $val = $_->{$key};
                  $val=~s{([[:cntrl:]])}{"&#x".sprintf("%X",ord($1)).";"}ge;
                  $child->setAttribute($key, $val);
              }
              $root->addChild($child);
          }
  
          print $self->{xml_output}->toString(1);
      } elsif (defined($self->{option_results}->{output_json})) {
          $self->create_json_document();
          my $json_content = {data => [] };
          foreach (@{$self->{disco_entries}}) {
              my %values = ();
              foreach my $key (keys %$_) {
                  $values{$key} = $_->{$key};
              }
              push @{$json_content->{data}}, {%values};
          }
  
          print $self->{json_output}->encode($json_content);
      }
  }
  
  sub decode {
      my ($self, $value) = @_;
  
      if ($self->{encode_import} == 0) {
          # Some Perl version dont have the following module (like Perl 5.6.x)
          my $rv = centreon::plugins::misc::mymodule_load(
              no_quit => 1,
              module => 'Encode',
              error_msg => "Cannot load module 'Encode'."
          );
          return $value if ($rv);
  
          $self->{encode_import} = 1;
          eval '$self->{perlqq} = Encode::PERLQQ';
      }
  
      return centreon::plugins::misc::trim(Encode::decode($self->{source_encoding}, $value, $self->{perlqq}));
  }
  
  sub parameter {
      my ($self, %options) = @_;
  
      if (defined($options{attr})) {
          $self->{$options{attr}} = $options{value};
      }
      return $self->{$options{attr}};
  }
  
  sub add_disco_entry {
      my ($self, %options) = @_;
  
      push @{$self->{disco_entries}}, {%options};
  }
  
  sub is_disco_format {
      my ($self) = @_;
  
      if (defined($self->{option_results}->{disco_format})) {
          return 1;
      }
      return 0;
  }
  
  sub is_disco_show {
      my ($self) = @_;
  
      if (defined($self->{option_results}->{disco_show})) {
          return 1;
      }
      return 0;
  }
  
  sub is_verbose {
      my ($self) = @_;
  
      if (defined($self->{option_results}->{verbose})) {
          return 1;
      }
      return 0;
  }
  
  sub is_debug {
      my ($self) = @_;
  
      if (defined($self->{option_results}->{debug}) || defined($self->{option_results}->{debug_stream})) {
          return 1;
      }
      return 0;
  }
  
  sub load_eval {
      my ($self) = @_;
  
      my ($code) = centreon::plugins::misc::mymodule_load(
          output => $self->{output}, module => 'Safe',
          no_quit => 1
      );
      if ($code == 0) {
          $self->{safe} = Safe->new();
          $self->{safe}->share('$values');
          $self->{safe}->share('$assign_var');
          $self->{safe}->share_from('centreon::plugins::misc', ['alert_triggered']);
      }
  
      $self->{safe_test} = 1;
  }
  
  sub test_eval {
      my ($self, %options) = @_;
  
      $self->load_eval() if ($self->{safe_test} == 0);
  
      my $result;
      if (defined($self->{safe})) {
          our $values = $options{values};
          $result = $self->{safe}->reval($options{test}, 1);
          if ($@) {
              die 'Unsafe code evaluation: ' . $@;
          }
      } elsif (defined($options{values})) {
          my $values = $options{values};
          {
              local $SIG{__WARN__} = sub {}; # ignore
  
              $result = eval "$options{test}";
              if ($@) {
                  die 'Code evaluation error: ' . $@;
              }
          }
      }
  
      return $result;
  }
  
  sub open_eval {
      my ($self, %options) = @_;
  
      $self->load_eval() if ($self->{safe_test} == 0);
      our $values = $options{values};
      $self->{safe}->reval("$options{eval}", 1);
  
      return $values;
  }
  
  sub assign_eval {
      my ($self, %options) = @_;
  
      $self->load_eval() if ($self->{safe_test} == 0);
  
      our $assign_var;
      if (defined($self->{safe})) {
          our $values = $options{values};
          $self->{safe}->reval("\$assign_var = $options{eval}", 1);
          if ($@) {
              die 'Unsafe code evaluation: ' . $@;
          }
      } else {
          my $values = $options{values};
          eval "\$assign_var = $options{eval}";
      }
  
      return $assign_var;
  }
  
  sub use_new_perfdata {
      my ($self, %options) = @_;
  
      $self->{option_results}->{use_new_perfdata} = $options{value}
          if (defined($options{value}));
      if (defined($self->{option_results}->{use_new_perfdata})) {
          return 1;
      }
      return 0;
  }
  
  sub get_instance_perfdata_separator {
      my ($self) = @_;
  
      if (defined($self->{option_results}->{use_new_perfdata})) {
          return '~';
      }
      return '_';
  }
  
  sub parse_pfdata_scale {
      my ($self, %options) = @_;
  
      # --extend-perfdata=traffic_in,,scale(Mbps),mbps
      my $args = { unit => 'auto' };
      if ($options{args} =~ /^([KMGTPEkmgtpe])?(B|b|bps|Bps|b\/s|auto)$/) {
          $args->{quantity} = defined($1) ? $1 : '';
          $args->{unit} = $2;
      } elsif ($options{args} ne '') {
          return 1;
      }
  
      return (0, $args);
  }
  
  sub parse_pfdata_math {
      my ($self, %options) = @_;
  
      # --extend-perfdata=perfx,,math(current + 10 - 100, 1)
      my $args = { math => undef, apply_threshold => 0 };
      my ($math, $apply_threshold) = split /\|/, $options{args};
      if ($math =~ /^((?:[\s\.\-\+\*\/0-9\(\)]|current)+)$/) {
          $args->{math} = $1;
      } elsif ($options{args} ne '') {
          return 1;
      }
  
      if (defined($apply_threshold) && $apply_threshold =~ /^\s*(0|1)\s*$/ ) {
          $args->{apply_threshold} = $1;
      }
  
      return (0, $args);
  }
  
  sub parse_pfdata_eval {
      my ($self, %options) = @_;
  
      # --extend-perfdata=perfx,,eval(%(label) =~ s/a/A/g)
      my $args = { expr => $options{args} };
      $args->{expr} =~ s/%\{(.*?)\}/\$values->{$1}/g;
      $args->{expr} =~ s/%\((.*?)\)/\$values->{$1}/g;
      return (0, $args);
  }
  
  sub parse_group_pfdata {
      my ($self, %options) = @_;
  
      $options{args} =~ s/^\s+//;
      $options{args} =~ s/\s+$//;
      my $args = { pattern_pf => $options{args} };
      return $args;
  }
  
  sub parse_pfdata_min {
      my ($self, %options) = @_;
  
      my $args = $self->parse_group_pfdata(%options);
      return (0, $args);
  }
  
  sub parse_pfdata_max {
      my ($self, %options) = @_;
  
      my $args = $self->parse_group_pfdata(%options);
      return (0, $args);
  }
  
  sub parse_pfdata_average {
      my ($self, %options) = @_;
  
      my $args = $self->parse_group_pfdata(%options);
      return (0, $args);
  }
  
  sub parse_pfdata_sum {
      my ($self, %options) = @_;
  
      my $args = $self->parse_group_pfdata(%options);
      return (0, $args);
  }
  
  sub apply_pfdata_scale {
      my ($self, %options) = @_;
  
      return if (${$options{perf}}->{unit} !~ /^([KMGTPEkmgtpe])?(B|b|bps|Bps|b\/s)$/);
  
      my ($src_quantity, $src_unit) = ($1, $2);
      my ($value, $dst_quantity, $dst_unit) = centreon::plugins::misc::scale_bytesbit(
          value => ${$options{perf}}->{value},
          src_quantity => $src_quantity, src_unit => $src_unit, dst_quantity => $options{args}->{quantity}, dst_unit => $options{args}->{unit}
      );
      ${$options{perf}}->{value} = sprintf('%.2f', $value);
      if (defined($dst_unit)) {
         ${$options{perf}}->{unit} = $dst_quantity . $dst_unit;
      } else {
          ${$options{perf}}->{unit} = $options{args}->{quantity} . $options{args}->{unit};
      }
  
      if (defined(${$options{perf}}->{max}) && ${$options{perf}}->{max} ne '') {
          ($value) = centreon::plugins::misc::scale_bytesbit(value => ${$options{perf}}->{max},
              src_quantity => $src_quantity, src_unit => $src_unit,
              dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity},
              dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit});
          ${$options{perf}}->{max} = sprintf('%.2f', $value);
      }
  
      foreach my $threshold ('warning', 'critical') {
          next if (${$options{perf}}->{$threshold} eq '');
          my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
          next if ($status == 0);
  
          if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
              ($result->{start}) = centreon::plugins::misc::scale_bytesbit(value => $result->{start},
                  src_quantity => $src_quantity, src_unit => $src_unit,
                  dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity},
                  dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit});
          }
          if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
              ($result->{end}) = centreon::plugins::misc::scale_bytesbit(value => $result->{end},
                  src_quantity => $src_quantity, src_unit => $src_unit,
                  dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity},
                  dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit});
          }
  
          ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result);
      }
  }
  
  sub apply_pfdata_invert {
      my ($self, %options) = @_;
  
      return if (!defined(${$options{perf}}->{max}) || ${$options{perf}}->{max} eq '');
  
      ${$options{perf}}->{value} = ${$options{perf}}->{max} - ${$options{perf}}->{value};
      foreach my $threshold ('warning', 'critical') {
          next if (${$options{perf}}->{$threshold} eq '');
          my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
          next if ($status == 0);
  
          my $tmp = { arobase => $result->{arobase}, infinite_pos => 0, infinite_neg => 0, start => $result->{start}, end => $result->{end} };
          $tmp->{infinite_neg} = 1 if ($result->{infinite_pos} == 1);
          $tmp->{infinite_pos} = 1 if ($result->{infinite_neg} == 1);
  
          if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
              $tmp->{end} = ${$options{perf}}->{max} - $result->{start};
          }
          if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
              $tmp->{start} = ${$options{perf}}->{max} - $result->{end};
          }
  
          ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$tmp);
      }
  }
  
  sub apply_pfdata_percent {
      my ($self, %options) = @_;
  
      return if (!defined(${$options{perf}}->{max}) || ${$options{perf}}->{max} eq '');
  
      ${$options{perf}}->{value} = sprintf('%.2f', ${$options{perf}}->{value} * 100 / ${$options{perf}}->{max});
      ${$options{perf}}->{unit} = '%';
      foreach my $threshold ('warning', 'critical') {
          next if (${$options{perf}}->{$threshold} eq '');
          my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
          next if ($status == 0);
  
          if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
              $result->{start} = sprintf('%.2f', $result->{start} * 100 / ${$options{perf}}->{max});
          }
          if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
              $result->{end} = sprintf('%.2f', $result->{end} * 100 / ${$options{perf}}->{max});
          }
  
          ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result);
      }
  
      ${$options{perf}}->{max} = 100;
  }
  
  sub apply_pfdata_eval {
      my ($self, %options) = @_;
  
      ${$options{perf}} = $self->open_eval(eval => $options{args}->{expr}, values => ${$options{perf}});
  }
  
  sub apply_pfdata_math {
      my ($self, %options) = @_;
  
      my $math = $options{args}->{math};
      $math =~ s/current/\$value/g;
  
      my $value = ${$options{perf}}->{value};
      eval "\${\$options{perf}}->{value} = $math";
  
      return if ($options{args}->{apply_threshold} == 0);
  
      foreach my $threshold ('warning', 'critical') {
          next if (${$options{perf}}->{$threshold} eq '');
          my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
          next if ($status == 0);
  
          if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
              $value = $result->{start};
              eval "\$result->{start} = $math";
          }
          if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
              $value = $result->{end};
              eval "\$result->{end} = $math";
          }
  
          ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result);
      }
  
      ${$options{perf}}->{max} = 100;
  }
  
  sub apply_pfdata_min {
      my ($self, %options) = @_;
  
      my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
      my $min;
      for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
          next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
          next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
          $min = $self->{perfdatas}->[$i]->{value}
              if (!defined($min) || $min > $self->{perfdatas}->[$i]->{value});
      }
  
      ${$options{perf}}->{value} = $min
          if (defined($min));
  }
  
  sub apply_pfdata_max {
      my ($self, %options) = @_;
  
      my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
      my $max;
      for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
          next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
          next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
          $max = $self->{perfdatas}->[$i]->{value}
              if (!defined($max) || $max < $self->{perfdatas}->[$i]->{value});
      }
  
      ${$options{perf}}->{value} = $max
          if (defined($max));
  }
  
  sub apply_pfdata_sum {
      my ($self, %options) = @_;
  
      my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
      my ($sum, $num) = (0, 0);
      for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
          next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
          next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
          $sum += $self->{perfdatas}->[$i]->{value};
          $num++;
      }
  
      ${$options{perf}}->{value} = $sum
          if ($num > 0);
  }
  
  sub apply_pfdata_average {
      my ($self, %options) = @_;
  
      my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
      my ($sum, $num) = (0, 0);
      for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
          next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
          next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
          $sum += $self->{perfdatas}->[$i]->{value};
          $num++;
      }
  
      ${$options{perf}}->{value} = sprintf("%.2f", ($sum / $num))
          if ($num > 0);
  }
  
  sub apply_perfdata_thresholds {
      my ($self, %options) = @_;
  
      foreach (('warning', 'critical')) {
          next if (!defined($options{$_}));
  
          my @thresholds = split(':', $options{$_}, -1);
          for (my $i = 0; $i < scalar(@thresholds); $i++) {
              if ($thresholds[$i] =~ /(\d+(?:\.\d+)?)\s*%/) {
                  if (!defined($options{max}) || $options{max} eq '') {
                      $thresholds[$i] = '';
                      next;
                  }
                  $thresholds[$i] = $1 * $options{max} / 100;
              } elsif ($thresholds[$i] =~ /(\d+(?:\.\d+)?)/) {
                  $thresholds[$i] = $1;
              } else {
                  $thresholds[$i] = '';
              }
          }
  
          ${$options{perf}}->{$_} = join(':', @thresholds);
      }
  }
  
  sub load_perfdata_extend_args {
      my ($self, %options) = @_;
  
      foreach (
          [$self->{option_results}->{change_perfdata}, 1],
          [$self->{option_results}->{extend_perfdata}, 2],
          [$self->{option_results}->{extend_perfdata_group}, 3],
      ) {
          next if (!defined($_->[0]));
          foreach my $arg (@{$_->[0]}) {
              $self->parse_perfdata_extend_args(arg => $arg, type => $_->[1]);
          }
      }
  }
  
  sub parse_perfdata_extend_args {
      my ($self, %options) = @_;
  
      # --extend-perfdata=searchlabel,newlabel,method[,[newuom],[min],[max],[warning],[critical]]
      my ($pfdata_match, $pfdata_substitute, $method, $uom_sub, $min_sub, $max_sub, $warn_sub, $crit_sub) =
          split /,/, $options{arg};
      return if ((!defined($pfdata_match) || $pfdata_match eq '') && $options{type} != 3);
  
      $self->{pfdata_extends} = [] if (!defined($self->{pfdata_extends}));
      my $pfdata_extends = {
          pfdata_match => defined($pfdata_match) && $pfdata_match ne '' ? $pfdata_match : undef,
          pfdata_substitute => defined($pfdata_substitute) && $pfdata_substitute ne '' ? $pfdata_substitute : undef,
          uom_sub => defined($uom_sub) && $uom_sub ne '' ? $uom_sub : undef,
          min_sub => defined($min_sub) && $min_sub ne '' ? $min_sub : undef,
          max_sub => defined($max_sub) && $max_sub ne '' ? $max_sub : undef,
          warn_sub => defined($warn_sub) && $warn_sub ne '' ? $warn_sub : undef,
          crit_sub => defined($crit_sub) && $crit_sub ne '' ? $crit_sub : undef,
          type => $options{type}
      };
  
      if (defined($method) && $method ne '') {
          if ($method !~ /^\s*(invert|percent|scale|math|min|max|average|sum|eval)\s*\(\s*(.*?)\s*\)\s*$/) {
              $self->output_add(long_msg => "method in argument '$options{arg}' is unknown", debug => 1);
              return ;
          }
  
          $pfdata_extends->{method_name} = $1;
          my $args = $2;
          if (my $func = $self->can('parse_pfdata_' . $pfdata_extends->{method_name})) {
              (my $status, $pfdata_extends->{method_args}) = $func->($self, args => $args);
              if ($status == 1) {
                  $self->output_add(long_msg => "argument in method '$options{arg}' is unknown", debug => 1);
                  return ;
              }
          }
      }
  
      push @{$self->{pfdata_extends}}, $pfdata_extends;
  }
  
  sub apply_perfdata_explode {
      my ($self, %options) = @_;
  
      return if ($self->{explode_perfdata_total} == 0);
      foreach (@{$self->{perfdatas}}) {
          next if ($_->{max} eq '');
          if ($self->{explode_perfdata_total} == 2) {
              $self->perfdata_add(label => $_->{label} . '_max', value => $_->{max}, unit => $_->{unit});
              next;
          }
          foreach my $regexp (keys %{$self->{explode_perfdatas}}) {
              if ($_->{label} =~ /$regexp/) {
                  $self->perfdata_add(label => $self->{explode_perfdatas}->{$regexp}, value => $_->{max}, unit => $_->{unit});
                  last;
              }
          }
      }
  }
  
  sub apply_perfdata_extend {
      my ($self, %options) = @_;
  
      foreach my $extend (@{$self->{pfdata_extends}}) {
          my $new_pfdata = [];
  
          # Manage special case when type group and pfdata_match empty
          if ($extend->{type} == 3 && (!defined($extend->{pfdata_match}) || $extend->{pfdata_match} eq '')) {
              next if (!defined($extend->{pfdata_substitute}) || $extend->{pfdata_substitute} eq '');
              my $new_perf = {
                  label => $extend->{pfdata_substitute}, value => '',
                  unit => defined($extend->{uom_sub}) ? $extend->{uom_sub} : '',
                  warning => '', critical => '',
                  min => defined($extend->{min_sub}) ? $extend->{min_sub} : '',
                  max => defined($extend->{max_sub}) ? $extend->{max_sub} : ''
              };
  
              if (defined($extend->{method_name})) {
                  my $func = $self->can('apply_pfdata_' . $extend->{method_name});
                  $func->($self, perf => \$new_perf, args => $extend->{method_args});
              }
  
              $self->apply_perfdata_thresholds(
                  perf => \$new_perf,
                  warning => $extend->{warn_sub},
                  critical => $extend->{crit_sub},
                  max => $new_perf->{max}
              );
              if (length($new_perf->{value})) {
                  push @{$self->{perfdatas}}, $new_perf;
              }
              next;
          }
  
          for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
              next if ($self->{perfdatas}->[$i]->{label} !~ /$extend->{pfdata_match}/);
  
              my $new_perf = { %{$self->{perfdatas}->[$i]} };
              if ($extend->{type} == 3) {
                  $new_perf = { label => $self->{perfdatas}->[$i]->{label}, value => '', unit => '', warning => '', critical => '', min => '', max => '' };
              }
  
              if (defined($extend->{pfdata_substitute})) {
                  eval "\$new_perf->{label} =~ s{$extend->{pfdata_match}}{$extend->{pfdata_substitute}}";
              }
  
              if (defined($extend->{method_name})) {
                  my $func = $self->can('apply_pfdata_' . $extend->{method_name});
                  $func->($self, perf => \$new_perf, args => $extend->{method_args});
              }
  
              $new_perf->{unit} = $extend->{uom_sub} if (defined($extend->{uom_sub}));
              $new_perf->{min} = $extend->{min_sub} if (defined($extend->{min_sub}));
              $new_perf->{max} = $extend->{max_sub} if (defined($extend->{max_sub}));
              $self->apply_perfdata_thresholds(
                  perf => \$new_perf,
                  warning => $extend->{warn_sub},
                  critical => $extend->{crit_sub},
                  max => $new_perf->{max}
              );
  
              if ($extend->{type} == 1) {
                  $self->{perfdatas}->[$i] = $new_perf;
              } else {
                  push @$new_pfdata, $new_perf if (length($new_perf->{value}));
              }
          }
  
          push @{$self->{perfdatas}}, @$new_pfdata;
      }
  }
  
  sub change_perfdata {
      my ($self, %options) = @_;
  
      $self->apply_perfdata_extend();
      $self->apply_perfdata_explode();
  }
  
  1;
  
  
  =head1 NAME
  
  Output class
  
  =head1 SYNOPSIS
  
  -
  
  =head1 OUTPUT OPTIONS
  
  =over 8
  
  =item B<--verbose>
  
  Display extended status information (long output).
  
  =item B<--debug>
  
  Display debug messages.
  
  =item B<--filter-perfdata>
  
  Filter perfdata that match the regexp.
  Example: adding --filter-perfdata='avg' will remove all metrics that do not contain
  'avg' from performance data.
  
  =item B<--filter-perfdata-adv>
  
  Filter perfdata based on a "if" condition using the following variables:
  label, value, unit, warning, critical, min, max.
  Variables must be written either %{variable} or %(variable).
  Example: adding --filter-perfdata-adv='not (%(value) == 0 and %(max) eq "")' will
  remove all metrics whose value equals 0 and that don't have a maximum value.
  
  =item B<--explode-perfdata-max>
  
  Create a new metric for each metric that comes with a maximum limit. The new
  metric will be named identically with a '_max' suffix.
  Example: it will split 'used_prct'=26.93%;0:80;0:90;0;100
  into 'used_prct'=26.93%;0:80;0:90;0;100 'used_prct_max'=100%;;;;
  
  =item B<--change-perfdata> B<--extend-perfdata>
  
  Change or extend perfdata.
  Syntax: --extend-perfdata=searchlabel,newlabel,target[,[<new-unit-of-mesure>],[min],[max]]
  
  Common examples:
  
  =over 4
  
  Convert storage free perfdata into used: --change-perfdata='free,used,invert()'
  
  Convert storage free perfdata into used: --change-perfdata='used,free,invert()'
  
  Scale traffic values automatically: --change-perfdata='traffic,,scale(auto)'
  
  Scale traffic values in Mbps: --change-perfdata='traffic_in,,scale(Mbps),mbps'
  
  Change traffic values in percent: --change-perfdata='traffic_in,,percent()'
  
  =back
  
  =item B<--extend-perfdata-group>
  
  Add new aggregated metrics (min, max, average or sum) for groups of metrics defined by a regex match on the metrics' names.
  Syntax: --extend-perfdata-group=regex,<names-of-new-metrics>,calculation[,[<new-unit-of-mesure>],[min],[max]]
  regex: regular expression
  <names-of-new-metrics>: how the new metrics' names are composed (can use $1, $2... for groups defined by () in regex).
  calculation: how the values of the new metrics should be calculated
  <new-unit-of-mesure> (optional): unit of measure for the new metrics
  min (optional): lowest value the metrics can reach
  max (optional): highest value the metrics can reach
  
  Common examples:
  
  =over 4
  
  Sum wrong packets from all interfaces (with interface need  --units-errors=absolute): --extend-perfdata-group=',packets_wrong,sum(packets_(discard|error)_(in|out))'
  
  Sum traffic by interface: --extend-perfdata-group='traffic_in_(.*),traffic_$1,sum(traffic_(in|out)_$1)'
  
  =back
  
  =item B<--change-short-output> B<--change-long-output>
  
  Modify the short/long output that is returned by the plugin.
  Syntax: --change-short-output=pattern~replacement~modifier
  Most commonly used modifiers are i (case insensitive) and g (replace all occurrences).
  Example: adding --change-short-output='OK~Up~gi' will replace all occurrences of 'OK', 'ok', 'Ok' or 'oK' with 'Up'
  
  =item B<--change-exit>
  
  Replace an exit code with one of your choice.
  Example: adding --change-exit=unknown=critical will result in a CRITICAL state
  instead of an UNKNOWN state.
  
  =item B<--change-output-adv>
  
  Replace short output and exit code based on a "if" condition using the following variables:
  short_output, exit_code.
  Variables must be written either %{variable} or %(variable).
  Example: adding --change-output-adv='%(short_ouput) =~ /UNKNOWN: No daemon/,OK: No daemon,OK' will 
  change the following specific UNKNOWN result to an OK result.
  
  =item B<--range-perfdata>
  
  Rewrite the ranges displayed in the perfdata. Accepted values:
  0: nothing is changed.
  1: if the lower value of the range is equal to 0, it is removed.
  2: remove the thresholds from the perfdata.
  
  =item B<--filter-uom>
  
  Mask the units when they don't match the given regular expression.
  
  =item B<--opt-exit>
  
  Replace the exit code in case of an execution error (i.e. wrong option provided,
  SSH connection refused, timeout, etc). Default: unknown.
  
  =item B<--output-ignore-perfdata>
  
  Remove all the metrics from the service. The service will still have a status
  and an output.
  
  =item B<--output-ignore-label>
  
  Remove the status label ("OK:", "WARNING:", "UNKNOWN:", CRITICAL:") from the
  beginning of the output.
  Example: 'OK: Ram Total:...' will become 'Ram Total:...'
  
  =item B<--output-xml>
  
  Return the output in XML format (to send to an XML API).
  
  =item B<--output-json>
  
  Return the output in JSON format (to send to a JSON API).
  
  =item B<--output-openmetrics>
  
  Return the output in OpenMetrics format (to send to a tool expecting this
  format).
  
  =item B<--output-file>
  
  Write output in file (can be combined with JSON, XML and OpenMetrics options).
  Example: --output-file=/tmp/output.txt will write the output in /tmp/output.txt.
  
  =item B<--disco-format>
  
  Applies only to modes beginning with 'list-'.
  Returns the list of available macros to configure a service discovery rule
  (formatted in XML).
  
  =item B<--disco-show>
  
  Applies only to modes beginning with 'list-'.
  Returns the list of discovered objects (formatted in XML) for service discovery.
  
  =item B<--float-precision>
  
  Define the float precision for thresholds (default: 8).
  
  =item B<--source-encoding>
  
  Define the character encoding of the response sent by the monitored resource
  Default: 'UTF-8'.
  
  =head1 DESCRIPTION
  
  B<output>.
  
  =cut
CENTREON_PLUGINS_OUTPUT

$fatpacked{"centreon/plugins/passwordmgr/centreonvault.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_CENTREONVAULT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::passwordmgr::centreonvault;
  
  use strict;
  use warnings;
  use Data::Dumper;
  use centreon::plugins::http;
  use JSON::XS;
  use MIME::Base64;
  use Crypt::OpenSSL::AES;
  use centreon::plugins::statefile;
  
  my $VAULT_PATH_REGEX = qr/^secret::hashicorp_vault::([^:]+)::(.+)$/;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class PasswordMgr needs an 'output' argument that must be of type centreon::plugins::output.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          print "Class PasswordMgr needs an 'options' argument that must be of type centreon::plugins::options.\n";
          $options{output}->option_exit();
      }
  
      $options{options}->add_options(arguments => {
          'vault-config:s'    => { name => 'vault_config',    default => '/etc/centreon-engine/centreonvault.json'},
          'vault-cache:s'     => { name => 'vault_cache',     default => '/var/lib/centreon/centplugins/centreonvault_session'},
          'vault-env-file:s'  => { name => 'vault_env_file',  default => '/usr/share/centreon/.env'},
      });
  
      $options{options}->add_help(package => __PACKAGE__, sections => 'VAULT OPTIONS');
  
      $self->{output} = $options{output};
  
      # to access the vault, http protocol management is needed
      $self->{http} = centreon::plugins::http->new(%options, noptions => 1, default_backend => 'curl', insecure => 1);
  
      # to store the token and its expiration date, a statefile is needed
      $self->{cache} = centreon::plugins::statefile->new();
  
      return $self;
  }
  
  sub extract_map_options {
      my ($self, %options) = @_;
  
      $self->{map_option} = [];
  
      # Parse all options to find '/# .*\:\:secret\:\:(.*)/' pattern in the options values and add entries in map_option
      foreach my $option (keys %{$options{option_results}}) {
          if (defined($options{option_results}{$option})) {
              next if ($option eq 'map_option');
              if (ref($options{option_results}{$option}) eq 'ARRAY') {
                  foreach (@{$options{option_results}{$option}}) {
                      if ($_ =~ $VAULT_PATH_REGEX) {
                          push (@{$self->{request_endpoint}}, "/v1/$1::$2");
                          push (@{$self->{map_option}}, $option."=%".$_);
                      }
                  }
              } else {
  
                  if (my ($path, $secret) = $options{option_results}{$option} =~ $VAULT_PATH_REGEX) {
                      push (@{$self->{request_endpoint}}, "/v1/" . $path . "::" . $secret);
                      push (@{$self->{map_option}}, $option."=%".$options{option_results}{$option});
                  }
              }
          }
      }
  }
  
  sub vault_settings {
      my ($self, %options) = @_;
  
      if (centreon::plugins::misc::is_empty($options{option_results}->{vault_config})) {
          $self->{output}->add_option_msg(short_msg => "Please provide a Centreon Vault configuration file path with --vault-config option");
          $self->{output}->option_exit();
      }
      if (! -f $options{option_results}->{vault_config}) {
          $self->{output}->add_option_msg(short_msg => "File '$options{option_results}->{vault_config}' could not be found.");
          $self->{output}->option_exit();
      }
      $self->{vault_cache}    = $options{option_results}->{vault_cache};
      $self->{vault_env_file} = $options{option_results}->{vault_env_file};
      $self->{vault_config}   = $options{option_results}->{vault_config};
  
  
      my $file_content = do {
          local $/ = undef;
          if (!open my $fh, "<", $options{option_results}->{vault_config}) {
              $self->{output}->add_option_msg(short_msg => "Could not read file $options{option_results}->{vault_config}: $!");
              $self->{output}->option_exit();
          }
          <$fh>;
      };
  
      # decode the JSON content of the file
      my $json = centreon::plugins::misc::json_decode($file_content);
      if (!defined($json)) {
          $self->{output}->add_option_msg(short_msg => "Cannot decode JSON : $file_content\n");
          $self->{output}->option_exit();
      }
  
      # set the default values
      $self->{vault}->{protocol} = 'https';
      $self->{vault}->{url} = '127.0.0.1';
      $self->{vault}->{port} = '8100';
  
      # define the list of expected attributes in the JSON file
      my @valid_json_options = (
          'protocol',
          'url',
          'port',
          'root_path',
          'token',
          'secret_id',
          'role_id'
      );
  
      # set the object fields when the json fields are not empty
      foreach my $valid_option (@valid_json_options) {
          $self->{vault}->{$valid_option} = $json->{$valid_option}
              if ( !centreon::plugins::misc::is_empty( $json->{ $valid_option } ) );
      }
  
      return 1;
  }
  
  sub get_decryption_key {
      my ($self, %options) = @_;
  
      # try getting APP_SECRET from the environment variables
      if ( !centreon::plugins::misc::is_empty($ENV{'APP_SECRET'}) ) {
          return $ENV{'APP_SECRET'};
      }
  
      # try getting APP_SECRET defined in the env file (default: /usr/share/centreon/.env) file
      my $fh;
      return undef if (!open $fh, "<", $self->{vault_env_file});
      for my $line (<$fh>) {
          if ($line =~ /^APP_SECRET=(.*)$/) {
              return $1;
          }
      }
  
      return undef;
  }
  
  sub extract_and_decrypt {
      my ($self, %options) = @_;
  
      my $input = decode_base64($options{data});
      my $key   = $options{key};
  
      # with AES-256, the IV length must 16 bytes
      my $iv_length = 16;
      # extract the IV, the hashed data, the encrypted data
      my $iv             = substr($input, 0, $iv_length);     # initialization vector
      my $hashed_data    = substr($input, $iv_length, 64);    # hmac of the original data, for integrity control
      my $encrypted_data = substr($input, $iv_length + 64);   # data to decrypt
  
      # Creating the AES decryption object
      my $cipher;
      eval {
          $cipher = Crypt::OpenSSL::AES->new(
              $key,
              {
                  'cipher'  => 'AES-256-CBC',
                  'iv'      => $iv
              }
          );
      };
      if ($@) {
          $self->{output}->add_option_msg(short_msg => "There was an error while creating the AES object: " . $@);
          $self->{output}->option_exit();
      }
  
      # Decrypting the data
      my $decrypted_data;
      eval {$decrypted_data = $cipher->decrypt($encrypted_data);};
      if ($@) {
          $self->{output}->add_option_msg(short_msg => "There was an error while decrypting an AES-encrypted data: " . $@);
          $self->{output}->option_exit();
      }
  
      return $decrypted_data;
  }
  
  sub is_token_still_valid {
      my ($self) = @_;
      if (
              !defined($self->{auth})
              || centreon::plugins::misc::is_empty($self->{auth}->{token})
              || centreon::plugins::misc::is_empty($self->{auth}->{expiration_epoch})
              || $self->{auth}->{expiration_epoch} !~ /\d+/
              || $self->{auth}->{expiration_epoch} <= time()
      ) {
          $self->{output}->output_add(long_msg => "The token is missing or has expired or is invalid.", debug => 1);
          return undef;
      }
      $self->{output}->output_add(long_msg => "The cached token is still valid.", debug => 1);
      # Possible enhancement: check the token validity by calling this endpoint: /v1/auth/token/lookup-self
      # {"request_id":"XXXXX","lease_id":"","renewable":false,"lease_duration":0,"data":{"accessor":"XXXXXXX","creation_time":1732294406,"creation_ttl":2764800,"display_name":"approle","entity_id":"XXX","expire_time":"2024-12-24T16:53:26.932122122Z","explicit_max_ttl":0,"id":"hvs.secretToken","issue_time":"2024-11-22T16:53:26.932129132Z","meta":{"role_name":"myvault"},"num_uses":0,"orphan":true,"path":"auth/approle/login","policies":["default","myvault"],"renewable":true,"ttl":2764724,"type":"service"},"wrap_info":null,"warnings":null,"auth":null,"mount_type":"token"}
  
      return 1;
  }
  
  sub check_authentication {
      my ($self, %options) = @_;
  
      # prepare the cache (aka statefile)
      $self->{cache}->check_options(option_results => $options{option_results});
      my ($dir, $file, $suffix) = $options{option_results}->{vault_cache} =~ /^(.*\/)([^\/]+)(_.*)?$/;
  
      # Try reading the cache file
      if ($self->{cache}->read(
          statefile        => $file,
          statefile_suffix => defined($suffix) ? $suffix : '',
          statefile_dir    => $dir,
          statefile_format => 'json'
      )) {
          # if the cache file could be read, get the token and its expiration date
          $self->{auth} = {
              token            => $self->{cache}->get(name => 'token'),
              expiration_epoch => $self->{cache}->get(name => 'expiration_epoch')
          };
      }
  
      # if it is not valid, authenticate to get a new token
      if ( !$self->is_token_still_valid() ) {
          return $self->authenticate();
      }
  
      return 1;
  }
  
  sub authenticate {
      my ($self) = @_;
  
      # initial value: assuming the role and secret id might not be encrypted
      my $role_id   = $self->{vault}->{role_id};
      my $secret_id = $self->{vault}->{secret_id};
      if (centreon::plugins::misc::is_empty($role_id) || centreon::plugins::misc::is_empty($secret_id)) {
          $self->{output}->add_option_msg(short_msg => "Unable to authenticate to the vault: role_id or secret_id is empty.");
          $self->{output}->option_exit();
      }
      my $decryption_key = $self->get_decryption_key();
  
      # Decrypt the role_id and the secret_id if we have a decryption key
      if ( !centreon::plugins::misc::is_empty($decryption_key) ) {
          $role_id = $self->extract_and_decrypt(
              data => $role_id,
              key  => $decryption_key
          );
          $secret_id = $self->extract_and_decrypt(
              data => $secret_id,
              key  => $decryption_key
          );
      }
  
      # Authenticate to get the token
      my ($auth_result_json) = $self->{http}->request(
          hostname        => $self->{vault}->{url},
          port            => $self->{vault}->{port},
          proto           => $self->{vault}->{protocol},
          method          => 'POST',
          url_path        => "/v1/auth/approle/login",
          query_form_post => "role_id=$role_id&secret_id=$secret_id",
          header          => [
              'Content-Type: application/x-www-form-urlencoded',
              'Accept: */*',
              'X-Vault-Request: true',
              'User-Agent: Centreon-Plugins'
          ]
      );
  
      # Convert the response into a JSON object
      my $auth_result_obj = centreon::plugins::misc::json_decode($auth_result_json);
      if (!defined($auth_result_obj)) {
          # exit with UNKNOWN status
          $self->{output}->add_option_msg(short_msg => "Cannot decode JSON response from the vault server: $auth_result_json.");
          $self->{output}->option_exit();
      }
      # Authentication to the vault has passed
      # store the token (.auth.client_token) and its expiration date (current date + .lease_duration)
      my $expiration_epoch = -1;
      my $lease_duration = $auth_result_obj->{auth}->{lease_duration};
      if ( defined($lease_duration)
              && $lease_duration =~ /\d+/
              && $lease_duration > 0 ) {
          $expiration_epoch = time() + $lease_duration;
      }
      $self->{auth} = {
          'token'            => $auth_result_obj->{auth}->{client_token},
          'expiration_epoch' => $expiration_epoch
      };
      $self->{cache}->write(data => $self->{auth}, name => 'auth');
  
      $self->{output}->output_add(long_msg => "Authenticating worked. Token valid until "
              . localtime($self->{auth}->{expiration_epoch}), debug => 1);
  
      return 1;
  }
  
  
  
  sub request_api {
      my ($self, %options) = @_;
  
      $self->vault_settings(%options);
  
      # check the authentication
      if (!$self->check_authentication(%options)) {
          $self->{output}->add_option_msg(short_msg => "Unable to authenticate to the vault.");
          $self->{output}->option_exit();
      }
  
      $self->{lookup_values} = {};
  
      foreach my $item (@{$self->{request_endpoint}}) {
          # Extract vault name configuration from endpoint
          # 'vault::/v1/<root_path>/monitoring/hosts/7ad55afc-fa9e-4851-85b7-e26f47e421d7'
          my ($endpoint, $secret) = $item =~ /^(.*)\:\:(.*)$/;
  
  
          my ($response) = $self->{http}->request(
              hostname => $self->{vault}->{url},
              port => $self->{vault}->{port},
              proto => $self->{vault}->{protocol},
              method => 'GET',
              url_path => $endpoint,
              header => [
                  'Accept: application/json',
                  'User-Agent: Centreon-Plugins',
                  'X-Vault-Request: true',
                  'X-Vault-Token: ' . $self->{auth}->{token}
              ]
          );
  
          my $json = centreon::plugins::misc::json_decode($response);
          if (!defined($json->{data})) {
              $self->{output}->add_option_msg(short_msg => "Cannot decode Vault JSON response: $response");
              $self->{output}->option_exit();
          };
  
          foreach my $secret_name (keys %{$json->{data}->{data}}) {
              # e.g. secret::hashicorp_vault::myspace/data/snmp::PubCommunity
              $self->{lookup_values}->{'secret::hashicorp_vault::' .  substr($endpoint, index($endpoint, '/', 1) + 1) . '::' . $secret_name} = $json->{data}->{data}->{$secret_name};
          }
      }
  }
  
  sub do_map {
      my ($self, %options) = @_;
  
      foreach my $mapping (@{$self->{map_option}}) {
          my ($opt_name, $opt_value) = $mapping =~ /^(.+?)=%(.+)$/ or next;
          $opt_name =~ s/-/_/g;
          $options{option_results}->{$opt_name} = defined($self->{lookup_values}->{$opt_value}) ? $self->{lookup_values}->{$opt_value} : $opt_value;
      }
  }
  
  sub manage_options {
      my ($self, %options) = @_;
  
      $self->extract_map_options(%options);
  
      return if (scalar(@{$self->{map_option}}) <= 0);
  
      $self->request_api(%options);
      $self->do_map(%options);
  }
  
  1;
  
  
  =head1 NAME
  
  Centreon Vault password manager
  
  =head1 SYNOPSIS
  
  Centreon Vault password manager
  
  To be used with an array containing keys/values saved in a secret path by resource
  
  =head1 VAULT OPTIONS
  
  =over 8
  
  =item B<--vault-config>
  
  Path to the file defining access to the Centreon vault (default: C</etc/centreon-engine/centreonvault.json>).
  
  =item B<--vault-cache>
  
  Path to the file where the token to access the Centreon vault will be stored (default: C</var/lib/centreon/centplugins/centreonvault_session>).
  
  =item B<--vault-env-file>
  
  Path to the file containing the APP_SECRET variable (default: C</usr/share/centreon/.env>).
  
  =back
  
  =head1 DESCRIPTION
  
  B<centreonvault>.
  
  =cut
  
  =head1 NAME
  
  centreon::plugins::passwordmgr::centreonvault - Module for getting secrets from Centreon Vault.
  
  =head1 SYNOPSIS
  
    use centreon::plugins::passwordmgr::centreonvault;
  
    my $vault = centreon::plugins::passwordmgr::centreonvault->new(output => $output, options => $options);
    $vault->manage_options(option_results => \%option_results);
  
  =head1 DESCRIPTION
  
  This module provides methods to retrieve secrets (passwords, SNMP communities, ...) from Centreon Vault (adequately
  configured HashiCorp Vault).
  It extracts and decrypt the information required to login to the vault from the vault configuration file, authenticates
  to the vault, retrieves secrets, and maps them to the corresponding options for the centreon-plugins to work with.
  
  =head1 METHODS
  
  =head2 new
  
    my $vault = centreon::plugins::passwordmgr::centreonvault->new(%options);
  
  Creates a new `centreon::plugins::passwordmgr::centreonvault` object. The `%options` hash can include:
  
  =over 4
  
  =item * output
  
  The output object for displaying debug and error messages.
  
  =item * options
  
  The options object for handling command-line options.
  
  =back
  
  =head2 extract_map_options
  
    $vault->extract_map_options(option_results => \%option_results);
  
  Extracts and maps options that match the Vault path regex pattern (C</^secret::hashicorp_vault::([^:]+)::(.+)$/>). The
  `%option_results` hash should include the command-line options.
  
  =head2 vault_settings
  
    $vault->vault_settings(option_results => \%option_results);
  
  Loads and validates the Vault configuration from the specified file.
  The `%option_results` hash should include the command-line options.
  
  =head2 get_decryption_key
  
    my $key = $vault->get_decryption_key();
  
  Retrieves the decryption key from C<APP_SECRET> environment variable. It will look for it in the the specified
  environment file if it is not available in the environment variables.
  
  =head2 extract_and_decrypt
  
    my $decrypted_data = $vault->extract_and_decrypt(data => $data, key => $key);
  
  Decrypts the given data using the specified key. The options must include:
  
  =over 4
  
  =item * data
  
  The base64-encoded data to decrypt.
  
  =item * key
  
  The base64-encoded decryption key.
  
  =back
  
  =head2 is_token_still_valid
  
    my $is_valid = $vault->is_token_still_valid();
  
  Checks if there is a token in the cache and if it is still valid based on its expiration date. Returns 1 if valid, otherwise undef.
  
  =head2 check_authentication
  
    $vault->check_authentication(option_results => \%option_results);
  
  Checks the authentication status and retrieves a new token if necessary. The `%option_results` hash should include the command-line options.
  
  =head2 authenticate
  
    $vault->authenticate();
  
  Authenticates to the Vault, retrieves a new token and stores it in the dedicated cache file.
  
  =head2 request_api
  
    $vault->request_api(option_results => \%option_results);
  
  Sends requests to the Vault API to retrieve secrets. The `%option_results` hash should include the command-line options.
  
  =head2 do_map
  
    $vault->do_map(option_results => \%option_results);
  
  Maps the retrieved secrets to the corresponding options. The `%option_results` hash should include the command-line options.
  Calling this method will update the `%option_results` hash replacing vault paths with the retrieved secrets.
  
  =head2 manage_options
  
    $vault->manage_options(option_results => \%option_results);
  
  Manages the options by extracting, requesting, and mapping secrets. The `%option_results` hash should include the command-line options.
  
  NB: This is the main method to be called from outside the module. All other methods are intended to be used internally.
  
  =head1 AUTHOR
  
  Centreon
  
  =head1 LICENSE
  
  Licensed under the Apache License, Version 2.0.
  
  =cut
CENTREON_PLUGINS_PASSWORDMGR_CENTREONVAULT

$fatpacked{"centreon/plugins/passwordmgr/environment.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_ENVIRONMENT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::passwordmgr::environment;
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class PasswordMgr: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      $options{options}->add_options(arguments => {
          'environment-map-option:s@' => { name => 'environment_map_option' }
      });
      $options{options}->add_help(package => __PACKAGE__, sections => 'ENVIRONMENT OPTIONS');
  
      $self->{output} = $options{output};    
  
      return $self;
  }
  
  sub manage_options {
      my ($self, %options) = @_;
  
      return if (!defined($options{option_results}->{environment_map_option}));
  
      foreach (@{$options{option_results}->{environment_map_option}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($option, $map) = ($1, $2);
  
          $option =~ s/-/_/g;
          $options{option_results}->{$option} = defined($ENV{$map}) ? $ENV{$map} : '';
      }
  }
  
  1;
  
  
  =head1 NAME
  
  Environment global
  
  =head1 SYNOPSIS
  
  environment class
  
  =head1 ENVIRONMENT OPTIONS
  
  =over 8
  
  =item B<--environment-map-option>
  
  Overload plugin option.
  Example:
  --environment-map-option="snmp-community=SNMPCOMMUNITY"
  
  =back
  
  =head1 DESCRIPTION
  
  B<environment>.
  
  =cut
CENTREON_PLUGINS_PASSWORDMGR_ENVIRONMENT

$fatpacked{"centreon/plugins/passwordmgr/file.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_FILE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::passwordmgr::file;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  use JSON::Path;
  use JSON::XS;
  use Data::Dumper;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class PasswordMgr: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      $options{options}->add_options(arguments => {
          'secret-file:s'          => { name => 'secret_file' },
          'secret-search-value:s@' => { name => 'secret_search_value' },
          'secret-map-option:s@'   => { name => 'secret_map_option' }
      });
      $options{options}->add_help(package => __PACKAGE__, sections => 'SECRET FILE OPTIONS');
  
      $self->{output} = $options{output};    
      $JSON::Path::Safe = 0;
  
      return $self;
  }
  
  sub load {
      my ($self, %options) = @_;
  
      if (defined($options{option_results}->{secret_file}) && $options{option_results}->{secret_file} ne '') {
          if (! -f $options{option_results}->{secret_file} or ! -r $options{option_results}->{secret_file}) {
              $self->{output}->add_option_msg(short_msg => "Cannot read secret file '$options{option_results}->{secret_file}': $!");
              $self->{output}->option_exit();
          }
  
          my $content = centreon::plugins::misc::slurp_file(output => $self->{output}, file => $options{option_results}->{secret_file});
  
          my $decoded;
          eval {
              $decoded = JSON::XS->new->utf8->decode($content);
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg => "Cannot decode secret file");
              $self->{output}->option_exit();
          }
  
          return $decoded;
      }
  }
  
  sub do_lookup {
      my ($self, %options) = @_;
      
      $self->{lookup_values} = {};
      return if (!defined($options{option_results}->{secret_search_value}));
      
      foreach (@{$options{option_results}->{secret_search_value}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($map, $lookup) = ($1, $2);
  
          # Change %{xxx} options usage
          while ($lookup =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $options{option_results}->{$1} if (defined($options{option_results}->{$1}));
              $lookup =~ s/\%\{$1\}/$sub/g
          }
  
          my $jpath = JSON::Path->new($lookup);
          my $result = $jpath->value($options{json});
          $self->{output}->output_add(long_msg => 'lookup = ' . $lookup. ' - response = ' . Data::Dumper::Dumper($result), debug => 1);
          $self->{lookup_values}->{$map} = $result;
      }
  }
  
  sub do_map {
      my ($self, %options) = @_;
      
      return if (!defined($options{option_results}->{secret_map_option}));
      foreach (@{$options{option_results}->{secret_map_option}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($option, $map) = ($1, $2);
          
          # Change %{xxx} options usage
          while ($map =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
              $map =~ s/\%\{$1\}/$sub/g
          }
  
          $option =~ s/-/_/g;
          $options{option_results}->{$option} = $map;
      }
  }
  
  sub manage_options {
      my ($self, %options) = @_;
      
      my $secrets = $self->load(%options);
      return if (!defined($secrets));
  
      $self->do_lookup(%options, json => $secrets);
      $self->do_map(%options);
  }
  
  1;
  
  
  =head1 NAME
  
  Secret file global
  
  =head1 SYNOPSIS
  
  secret file class
  
  =head1 SECRET FILE OPTIONS
  
  =over 8
  
  =item B<--secret-file>
  
  Secret file.
  
  =item B<--secret-search-value>
  
  Looking for a value in the JSON. Can use JSON Path and other option values.
  Example: 
  --secret-search-value='password=$..entries.[?($_->{title} =~ /server/i)].password'
  --secret-search-value='username=$..entries.[?($_->{title} =~ /server/i)].username'
  --secret-search-value='password=$..entries.[?($_->{title} =~ /%{hostname}/i)].password'
  
  =item B<--secret-map-option>
  
  Overload plugin option.
  Example:
  --secret-map-option="password=%{password}"
  --secret-map-option="username=%{username}"
  
  =back
  
  =head1 DESCRIPTION
  
  B<secret file>.
  
  =cut
CENTREON_PLUGINS_PASSWORDMGR_FILE

$fatpacked{"centreon/plugins/passwordmgr/hashicorpvault.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_HASHICORPVAULT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::passwordmgr::hashicorpvault;
  
  use strict;
  use warnings;
  use Data::Dumper;
  use centreon::plugins::http;
  use Digest::MD5 qw(md5_hex);
  use JSON::XS;
  
  use vars qw($vault_connections);
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class PasswordMgr: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      $options{options}->add_options(arguments => {
          'auth-method:s'    => { name => 'auth_method', default => 'token' },
          'auth-path:s'      => { name => 'auth_path' },
          'auth-settings:s%' => { name => 'auth_settings' },
          'map-option:s@'    => { name => 'map_option' },
          'secret-path:s@'   => { name => 'secret_path' },
          'vault-address:s'  => { name => 'vault_address'},
          'vault-port:s'     => { name => 'vault_port', default => '8200' },
          'vault-protocol:s' => { name => 'vault_protocol', default => 'http'},
          'vault-token:s'    => { name => 'vault_token'}
      });
      $options{options}->add_help(package => __PACKAGE__, sections => 'VAULT OPTIONS');
  
      $self->{output} = $options{output};
      $self->{http} = centreon::plugins::http->new(%options, noptions => 1, default_backend => 'curl');
      return $self;
  }
  
  sub get_access_token {
      my ($self, %options) = @_;
  
      my $decoded;
      my $login = $self->parse_auth_method(method => $self->{auth_method}, settings => $self->{auth_settings});
      my $post_json = JSON::XS->new->utf8->encode($login);
      if (!defined($self->{auth_path}) || $self->{auth_path} eq '') {
          $self->{auth_path} = $self->{auth_method};
      }
      my $url_path = '/v1/auth/'. $self->{auth_path} . '/login/';
      $url_path .= $self->{auth_settings}->{username} if (defined($self->{auth_settings}->{username}) && $self->{auth_method} =~ 'userpass|login') ;
  
      my $content = $self->{http}->request(
          hostname => $self->{vault_address},
          port => $self->{vault_port},
          proto => $self->{vault_protocol},
          method => 'POST',
          header => ['Content-type: application/json'],
          query_form_post => $post_json,
          url_path => $url_path
      );
  
      if (!defined($content) || $content eq '') {
          $self->{output}->add_option_msg(short_msg => "Authentication endpoint returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']");
          $self->{output}->option_exit();
      }
  
      eval {
          $decoded = JSON::XS->new->utf8->decode($content);
      };
      if ($@) {
          $self->{output}->output_add(long_msg => $content, debug => 1);
          $self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)");
          $self->{output}->option_exit();
      }
      if (defined($decoded->{errors}[0])) {
          $self->{output}->output_add(long_msg => "Error message : " . $decoded->{errors}[0], debug => 1);
          $self->{output}->add_option_msg(short_msg => "Authentication endpoint returns error code '" . $decoded->{errors}[0] . "' (add --debug option for detailed message)");
          $self->{output}->option_exit();
      }
  
      my $access_token = $decoded->{auth}->{client_token};
      return $access_token;
  }
  
  sub parse_auth_method {
      my ($self, %options) = @_;
  
      my $login_settings;
      my $settings_mapping = {
          azure    => [ 'role', 'jwt' ],
          cert     => [ 'name' ],
          github   => [ 'token' ],
          ldap     => [ 'username', 'password' ],
          okta     => [ 'username', 'password', 'totp' ],
          radius   => [ 'username', 'password' ],
          userpass => [ 'username', 'password' ]
      };
  
      foreach (@{$settings_mapping->{$options{method}}}) {
          if (!defined($options{settings}->{$_})) {
              $self->{output}->add_option_msg(short_msg => 'Missing authentication setting: ' . $_);
              $self->{output}->option_exit();
          }
          $login_settings->{$_} = $options{settings}->{$_};
      };
  
      return $login_settings;
  }
  
  sub settings {
      my ($self, %options) = @_;
  
      if (!defined($options{option_results}->{vault_address}) || $options{option_results}->{vault_address} eq '') {
          $self->{output}->add_option_msg(short_msg => "Please set the --vault-address option");
          $self->{output}->option_exit();
      }
  
      if ($options{option_results}->{auth_method} eq 'token' && (!defined($options{option_results}->{vault_token}) || $options{option_results}->{vault_token} eq '')) {
          $self->{output}->add_option_msg(short_msg => "Please set the --vault-token option");
          $self->{output}->option_exit();
      }
  
      if (!defined($options{option_results}->{secret_path}) || $options{option_results}->{secret_path} eq '') {
          $self->{output}->add_option_msg(short_msg => "Please set the --secret-path option");
          $self->{output}->option_exit();
      }
  
      if (defined($options{option_results}->{auth_path})) {		
          $self->{auth_path} = lc($options{option_results}->{auth_path});
      }
  
      $self->{auth_method} = lc($options{option_results}->{auth_method});
      $self->{auth_settings} = defined($options{option_results}->{auth_settings}) && $options{option_results}->{auth_settings} ne '' ? $options{option_results}->{auth_settings} : {};
      $self->{vault_address} = $options{option_results}->{vault_address};
      $self->{vault_port} = $options{option_results}->{vault_port};
      $self->{vault_protocol} = $options{option_results}->{vault_protocol};
      $self->{vault_token} = $options{option_results}->{vault_token};
  
      if (lc($self->{auth_method}) !~ m/azure|cert|github|ldap|okta|radius|userpass|token/ ) {
          $self->{output}->add_option_msg(short_msg => "Incorrect or unsupported authentication method set in --auth-method");
          $self->{output}->option_exit();
      }
      foreach (@{$options{option_results}->{secret_path}}) {
          $self->{request_endpoint}->{$_} = '/v1/' . $_;
      }
  
      if (defined($options{option_results}->{auth_method}) && $options{option_results}->{auth_method} ne 'token') {
          $self->{vault_token} = $self->get_access_token(%options);
      };
  
      $self->{http}->add_header(key => 'Accept', value => 'application/json');
      if (defined($self->{vault_token})) {
          $self->{http}->add_header(key => 'X-Vault-Token', value => $self->{vault_token});
      }
  }
  
  sub request_api {
      my ($self, %options) = @_;
  
      $self->settings(%options);
      my ($raw_data, $raw_response);
      foreach my $endpoint (keys %{$self->{request_endpoint}}) {
          my $json;
          my $response = $self->{http}->request(
              hostname => $self->{vault_address},
              port => $self->{vault_port},
              proto => $self->{vault_protocol},
              method => 'GET',
              url_path => $self->{request_endpoint}->{$endpoint}
          );
          $self->{output}->output_add(long_msg => $response, debug => 1);
  
          eval {
              $json = JSON::XS->new->utf8->decode($response);
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg => "Cannot decode Vault JSON response: $@");
              $self->{output}->option_exit();
          }
  
          if ((defined($json->{data}->{metadata}->{deletion_time}) && $json->{data}->{metadata}->{deletion_time} ne '') || $json->{data}->{metadata}->{destroyed} eq 'true') {
              $self->{output}->add_option_msg(short_msg => "This secret is not valid anymore");
              $self->{output}->option_exit();
          }
  
          foreach (keys %{$json->{data}->{data}}) {
              $self->{lookup_values}->{'key_' . $endpoint} = $_;
              $self->{lookup_values}->{'value_' . $endpoint} = $json->{data}->{data}->{$_};
          }
          push(@{$raw_data}, $json);
          push(@{$raw_response}, $response);
      }
  
      return ($raw_data, $raw_response);
  }
  
  sub do_map {
      my ($self, %options) = @_;
  
      return if (!defined($options{option_results}->{map_option}));
      foreach (@{$options{option_results}->{map_option}}) {
          next if (! /^(.+?)=(.+)$/);
  
          my ($option, $map) = ($1, $2);
  
          # Change %{xxx} options usage
          while ($map =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
              $map =~ s/\%\{$1\}/$sub/g;
          }
          $option =~ s/-/_/g;
          $options{option_results}->{$option} = $map;
      }
  }
  
  sub manage_options {
      my ($self, %options) = @_;
  
      my ($content, $debug) = $self->request_api(%options);
      if (!defined($content)) {
          $self->{output}->add_option_msg(short_msg => "Cannot read Vault information");
          $self->{output}->option_exit();
      }
      $self->do_map(%options);
      $self->{output}->output_add(long_msg => Data::Dumper::Dumper($debug), debug => 1) if ($self->{output}->is_debug());
  }
  
  1;
  
  
  =head1 NAME
  
  HashiCorp Vault global
  
  =head1 SYNOPSIS
  
  HashiCorp Vault class
  To be used with K/V engines
  
  =head1 VAULT OPTIONS
  
  =over 8
  
  =item B<--vault-address>
  
  IP address of the HashiCorp Vault server (mandatory).
  
  =item B<--vault-port>
  
  Port of the HashiCorp Vault server (default: '8200').
  
  =item B<--vault-protocol>
  
  HTTP of the HashiCorp Vault server.
  Can be: 'http', 'https' (default: http).
  
  =item B<--auth-method>
  
  Authentication method to log in against the Vault server.
  Can be: 'azure', 'cert', 'github', 'ldap', 'okta', 'radius', 'userpass' (default: 'token');
  
  =item B<--auth-path>
  
  Authentication path for 'userpass'. Is an optional setting.
  
  More information here: https://developer.hashicorp.com/vault/docs/auth/userpass#configuration
  
  =item B<--vault-token>
  
  Directly specify a valid token to log in (only for --auth-method='token').
  
  =item B<--auth-settings>
  
  Required information to log in according to the selected method.
  Examples:
  for 'userpass': --auth-settings='username=user1' --auth-settings='password=my_password'
  for 'azure': --auth-settings='role=my_azure_role' --auth-settings='jwt=my_azure_token'
  
  More information here: https://www.vaultproject.io/api-docs/auth
  
  =item B<--secret-path>
  
  Location of the secret in the Vault K/V engine (mandatory - Can be multiple).
  Examples:
  for v1 engine: --secret-path='mysecrets/servicecredentials'
  for v2 engine: --secret-path='mysecrets/data/servicecredentials?version=12'
  
  More information here: https://www.vaultproject.io/api-docs/secret/kv
  
  =item B<--map-option>
  
  Overload Plugin option with K/V values.
  Use the following syntax:
  the_option_to_overload='%{key_$secret_path$}' or
  the_option_to_overload='%{value_$secret_path$}'
  Example:
  --map-option='username=%{key_mysecrets/servicecredentials}'
  --map-option='password=%{value_mysecrets/servicecredentials}'
  
  =back
  
  =head1 DESCRIPTION
  
  B<hashicorpvault>.
  
  =cut
CENTREON_PLUGINS_PASSWORDMGR_HASHICORPVAULT

$fatpacked{"centreon/plugins/passwordmgr/keepass.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_KEEPASS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::passwordmgr::keepass;
  
  use strict;
  use warnings;
  use JSON::Path;
  use Data::Dumper;
  use KeePass::Reader;
  
  use vars qw($keepass_connections);
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class PasswordMgr: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      $options{options}->add_options(arguments => {
          'keepass-endpoint:s'        => { name => 'keepass_endpoint' },
          'keepass-endpoint-file:s'   => { name => 'keepass_endpoint_file' },
          'keepass-file:s'            => { name => 'keepass_file' },
          'keepass-password:s'        => { name => 'keepass_password' },
          'keepass-search-value:s@'   => { name => 'keepass_search_value' },
          'keepass-map-option:s@'     => { name => 'keepass_map_option' }
      });
      $options{options}->add_help(package => __PACKAGE__, sections => 'KEEPASS OPTIONS');
  
      $self->{output} = $options{output};    
      $JSON::Path::Safe = 0;
  
      return $self;
  }
  
  sub build_api_args {
      my ($self, %options) = @_;
      
      $self->{connection_info} = { file => undef, password => undef };
      if (defined($options{option_results}->{keepass_endpoint_file}) && $options{option_results}->{keepass_endpoint_file} ne '') {
          if (! -f $options{option_results}->{keepass_endpoint_file} or ! -r $options{option_results}->{keepass_endpoint_file}) {
              $self->{output}->add_option_msg(short_msg => "Cannot read keepass endpoint file: $!");
              $self->{output}->option_exit();
          }
          
          require $options{option_results}->{keepass_endpoint_file};
          if (defined($keepass_connections) && defined($options{option_results}->{keepass_endpoint}) && $options{option_results}->{keepass_endpoint} ne '') {
              if (!defined($keepass_connections->{$options{option_results}->{keepass_endpoint}})) {
                  $self->{output}->add_option_msg(short_msg => "Endpoint $options{option_results}->{keepass_endpoint} doesn't exist in keepass endpoint file");
                  $self->{output}->option_exit();
              }
              
              $self->{connection_info} = $keepass_connections->{$options{option_results}->{keepass_endpoint}};
          }
      }
      
      foreach (['keepass_file', 'file'], ['keepass_password', 'password']) {
          if (defined($options{option_results}->{$_->[0]}) && $options{option_results}->{$_->[0]} ne '') {
              $self->{connection_info}->{$_->[1]} = $options{option_results}->{$_->[0]};
          }
      }
      
      if (defined($self->{connection_info}->{file}) && $self->{connection_info}->{file} ne '') {
          if (!defined($self->{connection_info}->{password}) || $self->{connection_info}->{password} eq '') {
              $self->{output}->add_option_msg(short_msg => "Please set keepass-password option");
              $self->{output}->option_exit();
          }
      }
  }
  
  sub do_lookup {
      my ($self, %options) = @_;
      
      $self->{lookup_values} = {};
      return if (!defined($options{option_results}->{keepass_search_value}));
      
      foreach (@{$options{option_results}->{keepass_search_value}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($map, $lookup) = ($1, $2);
                  
          # Change %{xxx} options usage
          while ($lookup =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $options{option_results}->{$1} if (defined($options{option_results}->{$1}));
              $lookup =~ s/\%\{$1\}/$sub/g
          }
          
          my $jpath = JSON::Path->new($lookup);
          my $result = $jpath->value($options{json});
          $self->{output}->output_add(long_msg => 'lookup = ' . $lookup. ' - response = ' . Data::Dumper::Dumper($result), debug => 1);
          $self->{lookup_values}->{$map} = $result;
      }
  }
  
  sub do_map {
      my ($self, %options) = @_;
      
      return if (!defined($options{option_results}->{keepass_map_option}));
      foreach (@{$options{option_results}->{keepass_map_option}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($option, $map) = ($1, $2);
          
          # Change %{xxx} options usage
          while ($map =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
              $map =~ s/\%\{$1\}/$sub/g
          }
  
          $option =~ s/-/_/g;
          $options{option_results}->{$option} = $map;
      }
  }
  
  sub manage_options {
      my ($self, %options) = @_;
      
      $self->build_api_args(%options);
      return if (!defined($self->{connection_info}->{file}));
      
      my $keepass = KeePass::Reader->new();
      my $content = $keepass->load_db(file => $self->{connection_info}->{file}, password => $self->{connection_info}->{password});
      if (!defined($content)) {
          $self->{output}->add_option_msg(short_msg => "Cannot read keepass file: " . $keepass->error());
          $self->{output}->option_exit();
      }
      $self->{output}->output_add(long_msg => Data::Dumper::Dumper($content), debug => 1) if ($self->{output}->is_debug());
  
      $self->do_lookup(%options, json => $content);
      $self->do_map(%options);
  }
  
  1;
  
  
  =head1 NAME
  
  Keepass global
  
  =head1 SYNOPSIS
  
  keepass class
  
  =head1 KEEPASS OPTIONS
  
  =over 8
  
  =item B<--keepass-endpoint>
  
  Connection information to be used in keepass file.
  
  =item B<--keepass-endpoint-file>
  
  File with keepass connection informations.
  
  =item B<--keepass-file>
  
  Keepass file.
  
  =item B<--keepass-password>
  
  Keepass master password.
  
  =item B<--keepass-search-value>
  
  Looking for a value in the JSON keepass. Can use JSON Path and other option values.
  Example: 
  --keepass-search-value='password=$..entries.[?($_->{title} =~ /serveurx/i)].password'
  --keepass-search-value='username=$..entries.[?($_->{title} =~ /serveurx/i)].username'
  --keepass-search-value='password=$..entries.[?($_->{title} =~ /%{hostname}/i)].password'
  
  =item B<--keepass-map-option>
  
  Overload plugin option.
  Example:
  --keepass-map-option="password=%{password}"
  --keepass-map-option="username=%{username}"
  
  =back
  
  =head1 DESCRIPTION
  
  B<keepass>.
  
  =cut
CENTREON_PLUGINS_PASSWORDMGR_KEEPASS

$fatpacked{"centreon/plugins/passwordmgr/teampass.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_TEAMPASS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::passwordmgr::teampass;
  
  use strict;
  use warnings;
  use JSON::Path;
  use JSON::XS;
  use Data::Dumper;
  use centreon::plugins::http;
  
  use vars qw($teampass_connections);
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class PasswordMgr: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      $options{options}->add_options(arguments => {
          "teampass-endpoint:s"       => { name => 'teampass_endpoint' },
          "teampass-endpoint-file:s"  => { name => 'teampass_endpoint_file' },
          "teampass-api-key:s"        => { name => 'teampass_api_key' },
          "teampass-api-address:s"    => { name => 'teampass_api_address' },
          "teampass-api-request:s"    => { name => 'teampass_api_request' },
          "teampass-search-value:s@"  => { name => 'teampass_search_value' },
          "teampass-map-option:s@"    => { name => 'teampass_map_option' },
          "teampass-timeout:s"        => { name => 'teampass_timeout' },
      });
      $options{options}->add_help(package => __PACKAGE__, sections => 'TEAMPASS OPTIONS');
  
      $self->{output} = $options{output};    
      $self->{http} = centreon::plugins::http->new(%options, noptions => 1, default_backend => 'curl');
      $JSON::Path::Safe = 0;
      
      return $self;
  }
  
  sub build_api_args {
      my ($self, %options) = @_;
      
      $self->{connection_info} = { address => undef, key => undef, request => undef };
      if (defined($options{option_results}->{teampass_endpoint_file}) && $options{option_results}->{teampass_endpoint_file} ne '') {
          if (! -f $options{option_results}->{teampass_endpoint_file} or ! -r $options{option_results}->{teampass_endpoint_file}) {
              $self->{output}->add_option_msg(short_msg => "Cannot read teampass file: $!");
              $self->{output}->option_exit();
          }
          
          require $options{option_results}->{teampass_endpoint_file};
          if (defined($teampass_connections) && defined($options{option_results}->{teampass_endpoint}) && $options{option_results}->{teampass_endpoint} ne '') {
              if (!defined($teampass_connections->{$options{option_results}->{teampass_endpoint}})) {
                  $self->{output}->add_option_msg(short_msg => "Endpoint $options{option_results}->{teampass_endpoint} doesn't exist in teampass file");
                  $self->{output}->option_exit();
              }
              
              $self->{connection_info} = $teampass_connections->{$options{option_results}->{teampass_endpoint}};
          }
      }
      
      foreach (['teampass_api_address', 'address'], ['teampass_api_key', 'key'], ['teampass_api_request', 'request']) {
          if (defined($options{option_results}->{$_->[0]}) && $options{option_results}->{$_->[0]} ne '') {
              $self->{connection_info}->{$_->[1]} = $options{option_results}->{$_->[0]};
          }
      }
      
      if (defined($self->{connection_info}->{address}) && $self->{connection_info}->{address} ne '') {
          foreach ('key', 'request') {
              if (!defined($self->{connection_info}->{$_}) || $self->{connection_info}->{$_} eq '') {
                  $self->{output}->add_option_msg(short_msg => "Please set teampass-api-$_ option");
                  $self->{output}->option_exit();
              }
          }
      }
  }
  
  sub do_lookup {
      my ($self, %options) = @_;
      
      $self->{lookup_values} = {};
      return if (!defined($options{option_results}->{teampass_search_value}));
      
      foreach (@{$options{option_results}->{teampass_search_value}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($map, $lookup) = ($1, $2);
                  
          # Change %{xxx} options usage
          while ($lookup =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $options{option_results}->{$1} if (defined($options{option_results}->{$1}));
              $lookup =~ s/\%\{$1\}/$sub/g
          }
          
          my $jpath = JSON::Path->new($lookup);
          my $result = $jpath->value($options{json});
          $self->{output}->output_add(long_msg => 'lookup = ' . $lookup. ' - response = ' . Data::Dumper::Dumper($result), debug => 1);
          $self->{lookup_values}->{$map} = $result;
      }
  }
  
  sub do_map {
      my ($self, %options) = @_;
      
      return if (!defined($options{option_results}->{teampass_map_option}));
      foreach (@{$options{option_results}->{teampass_map_option}}) {
          next if (! /^(.+?)=(.+)$/);
          my ($option, $map) = ($1, $2);
          
          # Change %{xxx} options usage
          while ($map =~ /\%\{(.*?)\}/g) {
              my $sub = '';
              $sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
              $map =~ s/\%\{$1\}/$sub/g
          }
  
          $option =~ s/-/_/g;
          $options{option_results}->{$option} = $map;
      }
  }
  
  sub manage_options {
      my ($self, %options) = @_;
      
      $self->build_api_args(%options);
      return if (!defined($self->{connection_info}->{address}));
      
      $self->{http}->set_options(
          timeout => $options{option_results}->{teampass_timeout},
          unknown_status => '%{http_code} < 200 or %{http_code} >= 300',
      );
      my $response = $self->{http}->request(method => 'GET', 
          full_url => $self->{connection_info}->{address} . $self->{connection_info}->{request}, 
          hostname => '',
          get_param => ['apikey=' . $self->{connection_info}->{key}],
      );
      $self->{output}->output_add(long_msg => $response, debug => 1);
      
      my $json;
      eval {
          $json = JSON::XS->new->utf8->decode($response);
      };
      if ($@) {
          $self->{output}->add_option_msg(short_msg => "Cannot decode teampass json response: $@");
          $self->{output}->option_exit();
      }
      
      $self->do_lookup(%options, json => $json);
      $self->do_map(%options);
  }
  
  1;
  
  
  =head1 NAME
  
  Teampass global
  
  =head1 SYNOPSIS
  
  teampass class
  
  =head1 TEAMPASS OPTIONS
  
  =over 8
  
  =item B<--teampass-endpoint>
  
  Connection information to be used in teampass file.
  
  =item B<--teampass-endpoint-file>
  
  File with teampass connection informations.
  
  =item B<--teampass-timeout>
  
  Set HTTP Rest API timeout (default: 5).
  
  =item B<--teampass-api-key>
  
  Teampass API Key.
  
  =item B<--teampass-api-address>
  
  Teampass URL (example: http://10.0.0.1/teampass).
  
  =item B<--teampass-api-request>
  
  Teampass request (example: /api/index.php/folder/3).
  
  =item B<--teampass-search-value>
  
  Looking for a value in the JSON teampass response. Can use JSON Path and other option values.
  Example: 
  --teampass-search-value='password=$.[?($_->{label} =~ /serveur1/i)].pw'
  --teampass-search-value='login=$.[?($_->{label} =~ /serveur1/i)].login'
  --teampass-search-value='password=$.[?($_->{label} =~ /%{hostname}/i)].pw'
  
  =item B<--teampass-map-option>
  
  Overload plugin option.
  Example:
  --teampass-map-option="password=%{password}"
  --teampass-map-option="username=%{login}"
  
  =back
  
  =head1 DESCRIPTION
  
  B<teampass>.
  
  =cut
CENTREON_PLUGINS_PASSWORDMGR_TEAMPASS

$fatpacked{"centreon/plugins/perfdata.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PERFDATA';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::perfdata;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      $self->{output} = $options{output};
      # Typical Nagios Perfdata 'with ~ @ ..'
      $self->{threshold_label} = {};
      $self->{float_precision} = defined($self->{output}->{option_results}->{float_precision}) && $self->{output}->{option_results}->{float_precision} =~ /\d+/ ?  
          int($self->{output}->{option_results}->{float_precision}) : 8;
  
      return $self;
  }
  
  sub get_perfdata_for_output {
      my ($self, %options) = @_;
      # $options{label} : threshold label
      # $options{total} : percent threshold to transform in global
      # $options{cast_int} : cast absolute to int
      # $options{op} : operator to apply to start/end value (uses with 'value'})
      # $options{value} : value to apply with 'op' option
      
      if (!defined($self->{threshold_label}->{$options{label}}->{value}) || $self->{threshold_label}->{$options{label}}->{value} eq '') {
          return '';
      }
      
      my %perf_value = %{$self->{threshold_label}->{$options{label}}};
      
      if (defined($options{op}) && defined($options{value})) {
          eval "\$perf_value{start} = \$perf_value{start} $options{op} \$options{value}" if ($perf_value{infinite_neg} == 0);
          eval "\$perf_value{end} = \$perf_value{end} $options{op} \$options{value}" if ($perf_value{infinite_pos} == 0);
      }
      if (defined($options{total})) {
          $perf_value{start} = $perf_value{start} * $options{total} / 100 if ($perf_value{infinite_neg} == 0);
          $perf_value{end} = $perf_value{end} * $options{total} / 100 if ($perf_value{infinite_pos} == 0);
          $perf_value{start} = sprintf("%.2f", $perf_value{start}) if ($perf_value{infinite_neg} == 0 && (!defined($options{cast_int}) || $options{cast_int} != 1));
          $perf_value{end} = sprintf("%.2f", $perf_value{end}) if ($perf_value{infinite_pos} == 0 && (!defined($options{cast_int}) || $options{cast_int} != 1));
      }
      
      $perf_value{start} = int($perf_value{start}) if ($perf_value{infinite_neg} == 0 && defined($options{cast_int}) && $options{cast_int} == 1);
      $perf_value{end} = int($perf_value{end}) if ($perf_value{infinite_pos} == 0 && defined($options{cast_int}) && $options{cast_int} == 1);
      
      my $perf_output = ($perf_value{arobase} == 1 ? '@' : '') . 
                        (($perf_value{infinite_neg} == 0) ? $perf_value{start} : '~') . 
                        ':' . 
                        (($perf_value{infinite_pos} == 0) ? $perf_value{end} : '');
  
      return $perf_output;
  }
  
  sub threshold_validate {
      my ($self, %options) = @_;
      # $options{label} : threshold label
      # $options{value} : threshold value
  
      my $status = 1;
      $self->{threshold_label}->{$options{label}} = { value => $options{value}, start => undef, end => undef, arobase => undef, infinite_neg => undef, infinite_pos => undef };
      if (!defined($options{value}) || $options{value} eq '') {
          return $status;
      }
  
      ($status, my $result_perf) = 
          centreon::plugins::misc::parse_threshold(threshold => $options{value});
      $self->{threshold_label}->{$options{label}} = { %{$self->{threshold_label}->{$options{label}}}, %$result_perf };
      
      $self->{threshold_label}->{$options{label}}->{start_precision} = $self->{threshold_label}->{$options{label}}->{start};
      if ($self->{threshold_label}->{$options{label}}->{start} =~ /[.,]/) {
          $self->{threshold_label}->{$options{label}}->{start_precision} = sprintf("%.$self->{output}->{option_results}->{float_precision}f", $self->{threshold_label}->{$options{label}}->{start});
      }
      
      $self->{threshold_label}->{$options{label}}->{end_precision} = $self->{threshold_label}->{$options{label}}->{end};
      if ($self->{threshold_label}->{$options{label}}->{end} =~ /[.,]/) {
          $self->{threshold_label}->{$options{label}}->{end_precision} = sprintf("%.$self->{output}->{option_results}->{float_precision}f", $self->{threshold_label}->{$options{label}}->{end});
      }
      
      return $status;
  }
  
  sub threshold_check {
      my ($self, %options) = @_;
      # Can check multiple threshold. First match: out. Order is important
      # options{value}: value to compare
      # options{threshold}: ref to an array (example: [ {label => 'warning', exit_litteral => 'warning' }, {label => 'critical', exit_litteral => 'critical'} ]
      if ($options{value} =~ /[.,]/) {
          $options{value} = sprintf("%.$self->{output}->{option_results}->{float_precision}f", $options{value});
      }
  
      foreach (@{$options{threshold}}) {
          next if (!defined($self->{threshold_label}->{$_->{label}}));
          next if (!defined($self->{threshold_label}->{$_->{label}}->{value}) || $self->{threshold_label}->{$_->{label}}->{value} eq '');
          if ($self->{threshold_label}->{$_->{label}}->{arobase} == 0 && ($options{value} < $self->{threshold_label}->{$_->{label}}->{start_precision} || $options{value} > $self->{threshold_label}->{$_->{label}}->{end_precision})) {
              return $_->{exit_litteral};
          } elsif ($self->{threshold_label}->{$_->{label}}->{arobase}  == 1 && ($options{value} >= $self->{threshold_label}->{$_->{label}}->{start_precision} && $options{value} <= $self->{threshold_label}->{$_->{label}}->{end_precision})) {
              return $_->{exit_litteral};
          }
      }
  
      return 'ok';
  }
  
  sub trim {
      my ($self, $value) = @_;
      
      $value =~ s/^[ \t]+//;
      $value =~ s/[ \t]+$//;
      return $value;
  }
  
  sub change_bytes {
      my ($self, %options) = @_;
  
      my $value = $options{value};
      my $divide = defined($options{network}) ? 1000 : 1024;
      my @units = ('K', 'M', 'G', 'T');
      my $unit = '';
      my $sign = '';
  
      $sign = '-' if ($value != abs($value));
      $value = abs($value);
      
      for (my $i = 0; $i < scalar(@units); $i++) {
          last if (($value / $divide) < 1);
          $unit = $units[$i];
          $value = $value / $divide;
      }
  
      return (sprintf('%.2f', $sign . $value), $unit . (defined($options{network}) ? 'b' : 'B'));
  }
  
  1;
  
  
  =head1 NAME
  
  Perfdata class
  
  =head1 SYNOPSIS
  
  -
  
  =head1 DESCRIPTION
  
  B<perfdata>.
  
  =cut
CENTREON_PLUGINS_PERFDATA

$fatpacked{"centreon/plugins/script.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_SCRIPT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::script;
  
  use strict;
  use warnings;
  use centreon::plugins::output;
  use centreon::plugins::misc;
  use Pod::Usage;
  
  my %handlers = (DIE => {}, ALRM => {});
  
  my $global_version = '20250900 (446490d)';
  my $alternative_fatpacker = 1;
  
  sub new {
      my ($class) = @_;
      my $self  = {};
      bless $self, $class;
  
      $self->{options} = undef;
      $self->{plugin} = undef;
      $self->{help} = undef;
  
      # Avoid to destroy because it keeps a ref on the object. 
      # A problem if we execute it multiple times in the same perl execution
      # Use prepare_destroy
      $self->set_signal_handlers();
      return $self;
  }
  
  sub prepare_destroy {
      my ($self) = @_;
  
      %handlers = ();
  }
  
  sub set_signal_handlers {
      my ($self) = @_;
  
      $SIG{__DIE__} = \&class_handle_DIE;
      $handlers{DIE}->{$self} = sub { $self->handle_DIE($_[0]) };
  }
  
  sub class_handle_DIE {
      my ($msg) = @_;
  
      foreach (keys %{$handlers{DIE}}) {
          &{$handlers{DIE}->{$_}}($msg);
      }
  }
  
  sub class_handle_ALRM {
      foreach (keys %{$handlers{ALRM}}) {
          &{$handlers{ALRM}->{$_}}();
      }
  }
  
  sub handle_DIE {
      my ($self, $msg) = @_;
  
      return unless defined $^S and $^S == 0; # Ignore errors in eval
      $self->{output}->add_option_msg(short_msg => $msg);
      $self->{output}->die_exit();
  }
  
  sub handle_ALRM {
      my ($self) = @_;
  
      $self->{output}->add_option_msg(short_msg => 'script global timeout');
      $self->{output}->option_exit();
  }
  
  sub get_global_version {
      return $global_version;
  }
  
  sub get_plugin {
      my ($self) = @_;
  
      # Need to load global 'Output' and 'Options'
      if ($alternative_fatpacker == 0) {
          require centreon::plugins::options;
          $self->{options} = centreon::plugins::options->new();
      } else {
          require centreon::plugins::alternative::FatPackerOptions;
          $self->{options} = centreon::plugins::alternative::FatPackerOptions->new();
      }
      $self->{output} = centreon::plugins::output->new(options => $self->{options});
      $self->{options}->set_output(output => $self->{output});
  
      $self->{options}->add_options(arguments => {
          'plugin:s'          => { name => 'plugin' },
          'list-plugin'       => { name => 'list_plugin' }, 
          'help'              => { name => 'help' },
          'ignore-warn-msg'   => { name => 'ignore_warn_msg' },
          'version'           => { name => 'version' },
          'runas:s'           => { name => 'runas' },
          'global-timeout:s'  => { name => 'global_timeout' },
          'environment:s%'    => { name => 'environment' },
          'convert-args:s'    => { name => 'convert_args' },
      });
  
      $self->{options}->parse_options();
  
      $self->{plugin} = $self->{options}->get_option(argument => 'plugin');
      $self->{list_plugin} = $self->{options}->get_option(argument => 'list_plugin');
      $self->{help} = $self->{options}->get_option(argument => 'help');
      $self->{version} = $self->{options}->get_option(argument => 'version');
      $self->{runas} = $self->{options}->get_option(argument => 'runas');
      $self->{environment} = $self->{options}->get_option(argument => 'environment');
      $self->{ignore_warn_msg} = $self->{options}->get_option(argument => 'ignore_warn_msg');
      $self->{convert_args} = $self->{options}->get_option(argument => 'convert_args');
  
      my $global_timeout = $self->{options}->get_option(argument => 'global_timeout');
      if (defined($global_timeout) && $global_timeout =~ /(\d+)/) {
          $SIG{ALRM} = \&class_handle_ALRM;
          $handlers{ALRM}->{$self} = sub { $self->handle_ALRM() };
          alarm($1);
      }
  
      $self->{output}->plugin(name => $self->{plugin});
      $self->{output}->check_options(option_results => $self->{options}->get_options());
  
      $self->{options}->clean();
  }
  
  sub convert_args {
      my ($self) = @_;
  
      if ($self->{convert_args} =~ /^(.+?),(.*)/) {
          my ($search, $replace) = ($1, $2);
          for (my $i = 0; $i <= $#ARGV; $i++) {
              eval "\$ARGV[\$i] =~ s/$search/$replace/g";
          }
      }
  }
  
  sub display_local_help {
      my ($self) = @_;
  
      my $stdout;
      if ($self->{help}) {
          local *STDOUT;
          open STDOUT, '>', \$stdout;
  
          if ($alternative_fatpacker == 0) {
              pod2usage(-exitval => 'NOEXIT', -input => $self->{options}->pod_where(package => __PACKAGE__));
          } else {
              my $pp = __PACKAGE__ . '.pm';
              $pp =~ s{::}{/}g;
              my $content_class = $INC{$pp}->{$pp};
              open my $str_fh, '<', \$content_class;
              pod2usage(-exitval => 'NOEXIT', -input => $str_fh);
              close $str_fh;
          }
      }
  
      $self->{output}->add_option_msg(long_msg => $stdout) if (defined($stdout));
  }
  
  sub check_directory {
      my ($self, $directory) = @_;
  
      opendir(my $dh, $directory) || return ;
      while (my $filename = readdir $dh) {
          $self->check_directory($directory . '/' . $filename) if ($filename !~ /^\./ && -d $directory . '/' . $filename);
          if ($filename eq 'plugin.pm') {
              my $stdout = '';
  
              {
                  local *STDOUT;
                  open STDOUT, '>', \$stdout;
                  pod2usage(
                      -exitval => 'NOEXIT',
                      -input => $directory . "/" . $filename,
                      -verbose => 99, 
                      -sections => 'PLUGIN DESCRIPTION'
                  );
              }
              $self->{plugins_result}->{$directory . '/' . $filename} = $stdout;
          }
      }
      closedir $dh;
  }
  
  sub fatpacker_find_plugin {
      my ($self) = @_;
      
      my $plugins = [];
      foreach (@INC) {
          next if (ref($_) !~ /FatPacked/);
          foreach my $name (keys %$_) {
              if ($name =~ /plugin.pm$/) {
                  push @$plugins, $name;
              }
          }
      }
  
      return $plugins;
  }
  
  sub check_plugin_option {
      my ($self) = @_;
  
      if (defined($self->{version})) {
          $self->{output}->add_option_msg(short_msg => 'Global Version: ' . $global_version);
          $self->{output}->option_exit(nolabel => 1);
      }
  
      my $no_plugin = 1;
      if ($alternative_fatpacker == 1) {
          my $integrated_plugins = $self->fatpacker_find_plugin();
          if (scalar(@$integrated_plugins) == 1) {
              $self->{plugin} = $integrated_plugins->[0];
              $no_plugin = 0;
          }
      }
  
      if ($no_plugin == 1) {
          $self->{output}->add_option_msg(short_msg => "Need to specify '--plugin' option.");
          $self->{output}->option_exit();
      }
  }
  
  sub display_list_plugin {
      my ($self) = @_;
      $self->{plugins_result} = {};
  
      if ($alternative_fatpacker == 1) {
          my $integrated_plugins = $self->fatpacker_find_plugin();
  
          foreach my $key (sort @$integrated_plugins) {
              # Need to load it to get the description
              centreon::plugins::misc::mymodule_load(
                  output => $self->{output}, module => $key, 
                  error_msg => 'Cannot load module --plugin.'
              );
  
              my $name = $key;
              $name =~ s/\.pm//g;
              $name =~ s/\//::/g;
              $self->{output}->add_option_msg(long_msg => '-----------------');
              $self->{output}->add_option_msg(long_msg => 'PLUGIN: ' . $name);
              {
                  my $stdout = '';
                  local *STDOUT;
                  open STDOUT, '>', \$stdout;
                  my $content_class = $INC{$key}->{$key};
                  open my $str_fh, '<', \$content_class;
                  pod2usage(-exitval => 'NOEXIT', -input => $str_fh, -verbose => 99, -sections => 'PLUGIN DESCRIPTION');
                  close $str_fh;
                  $self->{output}->add_option_msg(long_msg => $stdout);
              }
          }
          return ;
      }
  
      centreon::plugins::misc::mymodule_load(
          output => $self->{output}, module => 'FindBin', 
          error_msg => "Cannot load module 'FindBin'."
      );
      my $directory = $FindBin::Bin;
      if (defined($ENV{PAR_TEMP})) {
          $directory = $ENV{PAR_TEMP} . '/inc/lib';
      }
      # Search file 'plugin.pm'
      $self->check_directory($directory);
      foreach my $key (sort keys %{$self->{plugins_result}}) {
          my $name = $key;
          $name =~ s/^\Q$directory\E\/(.*)\.pm/$1/;
          $name =~ s/\//::/g;
          $self->{plugins_result}->{$key} =~ s/^Plugin Description/DESCRIPTION/i;
  
          $self->{output}->add_option_msg(long_msg => '-----------------');
          $self->{output}->add_option_msg(long_msg => 'PLUGIN: ' . $name);
          $self->{output}->add_option_msg(long_msg => $self->{plugins_result}->{$key});
      }
  }
  
  sub check_relaunch_get_args {
      my ($self) = @_;
  
      my $args = ['--plugin=' . $self->{plugin}, @ARGV];
      push @$args, '--ignore-warn-msg' if (defined($self->{ignore_warn_msg}));
      push @$args, '--help' if (defined($self->{help}));
      push @$args, '--global-timeout', $self->{global_timeout} if (defined($self->{global_timeout}));
      foreach ((
          ['output_xml', 0], ['output_json', 0], ['output_openmetrics', 0], 
          ['disco_format', 0], ['disco_show', 0], ['use_new_perfdata', 0], ['debug', 0], ['verbose', 0],
          ['range_perfdata', 1], ['filter_uom', 1], ['opt_exit', 1], ['filter_perfdata', 1],
          ['output_file', 1], ['float_precision', 1]
      )) {
          my $option = $self->{output}->get_option(option => $_->[0]);
          if (defined($option)) {
              my $option_label = $_->[0];
              $option_label =~ s/_/-/g;
              push @$args, "--$option_label" if ($_->[1] == 0);
              push @$args, "--$option_label", $option if ($_->[1] == 1);
          }
      }
  
      return $args;
  }
  
  sub check_relaunch {
      my $self = shift;
  
      centreon::plugins::misc::mymodule_load(
          output => $self->{output}, module => 'FindBin', 
          error_msg => "Cannot load module 'FindBin'."
      );
  
      my $need_restart = 0;
      my $cmd = $FindBin::Bin . '/' . $FindBin::Script;
      my $args = [];
  
      if (defined($self->{environment})) {
          foreach (keys %{$self->{environment}}) {
              if ($_ ne '' && (!defined($ENV{$_}) || $ENV{$_} ne $self->{environment}->{$_})) {
                  $ENV{$_} = $self->{environment}->{$_};
                  $need_restart = 1;
              }
          }
      }
  
      my $rebuild_args = $self->check_relaunch_get_args();
  
      if (defined($self->{runas}) && $self->{runas} ne '') {
          # Check if it's already me and user exist ;)
          my ($name, $passwd, $uid) = getpwnam($self->{runas});
          if (!defined($uid)) {
              $self->{output}->add_option_msg(short_msg => "Runas user '" . $self->{runas} . "' not exist.");
              $self->{output}->option_exit();
          }
          if ($uid != $>) {
              if ($> == 0) {
                  unshift @$args, '-s', '/bin/bash', '-l', $self->{runas}, '-c', join(' ', $cmd, @$rebuild_args);
                  $cmd = 'su';
              } else {
                  unshift @$args, '-S', '-u', $self->{runas}, $cmd, @$rebuild_args;
                  $cmd = 'sudo';
              }
              $need_restart = 1;
          }
      }
  
      if ($need_restart == 1) {
          if (scalar(@$args) <= 0) {
              unshift @$args, @$rebuild_args;
          }
  
          my ($lerror, $stdout, $exit_code) = centreon::plugins::misc::backtick(
              command => $cmd,
              arguments => $args,
              timeout => 30,
              wait_exit => 1
          );
  
          if ($exit_code <= -1000) {
              if ($exit_code == -1000) {
                  $self->{output}->output_add(
                      severity => 'UNKNOWN', 
                      short_msg => $stdout
                  );
              }
              $self->{output}->display();
              $self->{output}->exit();
          }
          chomp $stdout;
          print $stdout . "\n";
          # We put unknown
          if (!($exit_code >= 0 && $exit_code <= 4)) {
              exit 3;
          }
          exit $exit_code;
      }
  }
  
  sub run {
      my ($self) = @_;
  
      $self->get_plugin();
  
      if (defined($self->{help}) && !defined($self->{plugin})) {
          $self->display_local_help();
          $self->{output}->option_exit();
      }
      if (defined($self->{list_plugin})) {
          $self->display_list_plugin();
          $self->{output}->option_exit();
      }
      $self->check_plugin_option() if (!defined($self->{plugin}) || $self->{plugin} eq '');
      if (defined($self->{ignore_warn_msg})) {
          $SIG{__WARN__} = sub {};
      }
      $self->convert_args() if (defined($self->{convert_args}));
  
      $self->check_relaunch();
  
      (undef, $self->{plugin}) = 
          centreon::plugins::misc::mymodule_load(
              output => $self->{output}, module => $self->{plugin}, 
              error_msg => 'Cannot load module --plugin.'
          );
      my $plugin = $self->{plugin}->new(options => $self->{options}, output => $self->{output});
      $plugin->init(
          help => $self->{help},
          version => $self->{version}
      );
      $plugin->run();
  }
  
  1;
  
  
  =head1 NAME
  
  centreon_plugins.pl - main program to call Centreon plugins.
  
  =head1 SYNOPSIS
  
  centreon_plugins.pl [options]
  
  =head1 OPTIONS
  
  =over 8
  
  =item B<--plugin>
  
  Specify the path to the plugin.
  
  =item B<--list-plugin>
  
  List all available plugins.
  
  =item B<--version>
  
  Return the version of the plugin.
  
  =item B<--help>
  
  Return the help message for the plugin and exit.
  
  =item B<--ignore-warn-msg>
  
  Ignore Perl warning messages (they will not be displayed).
  
  =item B<--runas>
  
  Run the script as a different user.
  
  =item B<--global-timeout>
  
  Define the script's timeout.
  
  =item B<--environment>
  
  Define environment variables for the script (set them in the execution environment
  before running it for better performance).
  
  =item B<--convert-args>
  
  Replace a pattern in the provided arguments. Useful to bypass forbidden characters.
  E.g.: when a password transmitted via the NRPE protocol contains '!' (which is
  interpreted as a separator), you can send '##' instead of the '!' and the plugin will replace '##' with '!', using the --convert-args='##,\x21' option.
  
  =back
  
  =head1 DESCRIPTION
  
  B<centreon_plugins.pl> .
  
  =cut
CENTREON_PLUGINS_SCRIPT

$fatpacked{"centreon/plugins/script_custom.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_SCRIPT_CUSTOM';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::script_custom;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
      $self->{options} = $options{options};
      $self->{output} = $options{output};
      
      $self->{options}->add_options(
          arguments => {
              'mode:s'            => { name => 'mode_name' },
              'dyn-mode:s'        => { name => 'dynmode_name' },
              'list-mode'         => { name => 'list_mode' },
              'custommode:s'      => { name => 'custommode_name' },
              'list-custommode'   => { name => 'list_custommode' },
              'multiple'          => { name => 'multiple' },
              'no-sanity-options' => { name => 'no_sanity_options' },
              'pass-manager:s'    => { name => 'pass_manager' }
          }
      );
      $self->{version} = '1.0';
      $self->{modes} = {};
      $self->{custom_modes} = {};
      $self->{default} = undef;
      $self->{customdefault} = {};
      $self->{custommode_current} = undef;
      $self->{custommode_stored} = [];
      
      $self->{options}->parse_options();
      $self->{option_results} = $self->{options}->get_options();
      foreach (keys %{$self->{option_results}}) {
          $self->{$_} = $self->{option_results}->{$_};
      }
      $self->{options}->clean();
  
      $self->{options}->add_help(package => $options{package}, sections => 'PLUGIN DESCRIPTION');
      $self->{options}->add_help(package => __PACKAGE__, sections => 'GLOBAL OPTIONS');
      $self->{output}->mode(name => $self->{mode_name});
  
      return $self;
  }
  
  sub load_custom_mode {
      my ($self, %options) = @_;
      
      $self->is_custommode(custommode => $self->{custommode_name});
      centreon::plugins::misc::mymodule_load(
          output => $self->{output}, module => $self->{custom_modes}->{$self->{custommode_name}}, 
          error_msg => 'Cannot load module --custommode.'
      );
      $self->{custommode_current} = $self->{custom_modes}->{$self->{custommode_name}}->new(
          options => $self->{options},
          output => $self->{output},
          custommode_name => $self->{custommode_name},
          mode_name => $self->{mode_name}
      );
  }
  
  sub init {
      my ($self, %options) = @_;
  
      # add meta mode
      $self->{modes}->{multi} = 'centreon::plugins::multi';
      if (defined($options{help}) && !defined($self->{mode_name}) && !defined($self->{dynmode_name})) {
          $self->{options}->display_help();
          $self->{output}->option_exit();
      }
      if (defined($options{version}) && !defined($self->{mode_name})&& !defined($self->{dynmode_name})) {
          $self->version();
      }
      if (defined($self->{list_mode})) {
          $self->list_mode();
      }
      if (defined($self->{list_custommode})) {
          $self->list_custommode();
      }
      $self->{options}->set_sanity() if (!defined($self->{no_sanity_options}));
  
      # Output HELP
      $self->{options}->add_help(package => 'centreon::plugins::output', sections => 'OUTPUT OPTIONS');
      
      $self->load_password_mgr();
  
      if (defined($self->{custommode_name}) && $self->{custommode_name} ne '') {
          $self->load_custom_mode();
      } elsif (scalar(keys %{$self->{custom_modes}}) == 1) {
          $self->{custommode_name} = (keys(%{$self->{custom_modes}}))[0];
          $self->load_custom_mode();
      } else {
          $self->{output}->add_option_msg(short_msg => "Need to specify '--custommode'.");
          $self->{output}->option_exit();
      }
      
      # Load mode
      if (defined($self->{mode_name}) && $self->{mode_name} ne '') {
          $self->is_mode(mode => $self->{mode_name});
          centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $self->{modes}{$self->{mode_name}}, 
                                                 error_msg => "Cannot load module --mode.");
          $self->{mode} = $self->{modes}{$self->{mode_name}}->new(options => $self->{options}, output => $self->{output}, mode => $self->{mode_name});
      } elsif (defined($self->{dynmode_name}) && $self->{dynmode_name} ne '') {
          (undef, $self->{dynmode_name}) = centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $self->{dynmode_name}, 
                                                                                  error_msg => "Cannot load module --dyn-mode.");
          $self->{mode} = $self->{dynmode_name}->new(options => $self->{options}, output => $self->{output}, mode => $self->{dynmode_name});
      } else {
          $self->{output}->add_option_msg(short_msg => "Need to specify '--mode' or '--dyn-mode' option.");
          $self->{output}->option_exit();
      }
  
      if (defined($options{help})) {
          if (defined($self->{mode_name}) && $self->{mode_name} ne '') {
              $self->{options}->add_help(package => $self->{modes}{$self->{mode_name}}, sections => 'MODE');
          } else {
              $self->{options}->add_help(package => $self->{dynmode_name}, sections => 'MODE');
          }
          $self->{options}->display_help();
          $self->{output}->option_exit();
      }
      if (defined($options{version})) {
          $self->{mode}->version();
          $self->{output}->option_exit(nolabel => 1);
      }
      
      $self->{options}->parse_options();
      $self->{option_results} = $self->{options}->get_options();
      $self->{pass_mgr}->manage_options(option_results => $self->{option_results}) if (defined($self->{pass_mgr}));
  
      push @{$self->{custommode_stored}}, $self->{custommode_current};
      $self->{custommode_current}->set_options(option_results => $self->{option_results});
      $self->{custommode_current}->set_defaults(default => $self->{customdefault});
  
      while ($self->{custommode_current}->check_options()) {
          $self->{custommode_current} = $self->{custom_modes}->{$self->{custommode_name}}->new(noptions => 1, options => $self->{options}, output => $self->{output}, mode => $self->{custommode_name});
          $self->{custommode_current}->set_options(option_results => $self->{option_results});
          push @{$self->{custommode_stored}}, $self->{custommode_current};
      }
      $self->{mode}->check_options(
          option_results => $self->{option_results},
          default => $self->{default},
          modes => $self->{modes} # for meta mode multi
      );
  }
  
  sub load_password_mgr {
      my ($self, %options) = @_;
      
      return if (!defined($self->{option_results}->{pass_manager}) || $self->{option_results}->{pass_manager} eq '');
  
      (undef, my $pass_mgr_name) = centreon::plugins::misc::mymodule_load(
          output => $self->{output}, module => "centreon::plugins::passwordmgr::" . $self->{option_results}->{pass_manager}, 
          error_msg => "Cannot load module 'centreon::plugins::passwordmgr::" . $self->{option_results}->{pass_manager} . "'"
      );
      $self->{pass_mgr} = $pass_mgr_name->new(options => $self->{options}, output => $self->{output});
  }
  
  sub run {
      my $self = shift;
  
      if ($self->{output}->is_disco_format()) {
          $self->{mode}->disco_format();
          $self->{output}->display_disco_format();
          $self->{output}->exit(exit_litteral => 'ok');
      }
  
      if ($self->{output}->is_disco_show()) {
          if (defined($self->{multiple})) {
              $self->{mode}->disco_show(custom => $self->{custommode});
          } else {
              $self->{mode}->disco_show(custom => $self->{custommode_stored}[0]);
          }
          $self->{output}->display_disco_show();
          $self->{output}->exit(exit_litteral => 'ok');
      } else {
          if (defined($self->{multiple})) {
              $self->{mode}->run(custom => $self->{custommode_stored});
          } else {
              $self->{mode}->run(custom => $self->{custommode_stored}[0]);
          }
      }
  }
  
  sub is_mode {
      my ($self, %options) = @_;
      
      if (!defined($self->{modes}{$options{mode}})) {
          $self->{output}->add_option_msg(short_msg => "mode '" . $options{mode} . "' doesn't exist (use --list-mode option to show available modes).");
          $self->{output}->option_exit();
      }
  }
  
  sub is_custommode {
      my ($self, %options) = @_;
      
      if (!defined($self->{custom_modes}->{$options{custommode}})) {
          $self->{output}->add_option_msg(short_msg => "mode '" . $options{custommode} . "' doesn't exist (use --list-custommode option to show available modes).");
          $self->{output}->option_exit();
      }
  }
  
  sub version {
      my $self = shift;    
      $self->{output}->add_option_msg(short_msg => "Plugin Version: " . $self->{version});
      $self->{output}->option_exit(nolabel => 1);
  }
  
  sub list_mode {
      my $self = shift;
      $self->{options}->display_help();
      
      $self->{output}->add_option_msg(long_msg => 'Modes Meta:');
      $self->{output}->add_option_msg(long_msg => '   multi');
      $self->{output}->add_option_msg(long_msg => '');
      $self->{output}->add_option_msg(long_msg => 'Modes Available:');
      foreach (sort keys %{$self->{modes}}) {
          next if ($_ eq 'multi');
          $self->{output}->add_option_msg(long_msg => '   ' . $_);
      }
      $self->{output}->option_exit(nolabel => 1);
  }
  
  sub list_custommode {
      my $self = shift;
      $self->{options}->display_help();
      
      $self->{output}->add_option_msg(long_msg => "Custom Modes Available:");
      foreach (keys %{$self->{custom_modes}}) {
          $self->{output}->add_option_msg(long_msg => "   " . $_);
      }
      $self->{output}->option_exit(nolabel => 1);
  }
  
  1;
  
  
  =head1 NAME
  
  -
  
  =head1 SYNOPSIS
  
  -
  
  =head1 GLOBAL OPTIONS
  
  =over 8
  
  =item B<--mode>
  
  Define the mode in which you want the plugin to be executed (see --list-mode).
  
  =item B<--dyn-mode>
  
  Specify a mode with the module's path (advanced).
  
  =item B<--list-mode>
  
  List all available modes.
  
  =item B<--mode-version>
  
  Check minimal version of mode. If not, unknown error.
  
  =item B<--version>
  
  Return the version of the plugin.
  
  =item B<--custommode>
  
  When a plugin offers several ways (CLI, library, etc.) to get information
  the desired one must be defined with this option.
  
  =item B<--list-custommode>
  
  List all available custom modes.
  
  =item B<--multiple>
  
  Multiple custom mode objects. This may be required by some specific modes (advanced).
  
  =item B<--pass-manager>
  
  Define the password manager you want to use.
  Supported managers are: environment, file, keepass, hashicorpvault and teampass.
  
  =back
  
  =head1 DESCRIPTION
  
  B<>.
  
  =cut
CENTREON_PLUGINS_SCRIPT_CUSTOM

$fatpacked{"centreon/plugins/snmp.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_SNMP';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::snmp;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  use SNMP;
  use Socket;
  use POSIX;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
      # $options{options} = options object
      # $options{output} = output object
      # $options{exit_value} = integer
  
      if (!defined($options{output})) {
          print "Class SNMP: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class SNMP: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      if (!defined($options{noptions})) {
          $options{options}->add_options(arguments => {
              'hostname|host:s'    => { name => 'host' },
              'snmp-community:s'   => { name => 'snmp_community', default => 'public' },
              'snmp-version:s'     => { name => 'snmp_version', default => 1 },
              'snmp-port:s'        => { name => 'snmp_port', default => 161 },
              'snmp-timeout:s'     => { name => 'snmp_timeout', default => 1 },
              'snmp-retries:s'     => { name => 'snmp_retries', default => 5 },
              'maxrepetitions:s'   => { name => 'maxrepetitions', default => 50 },
              'subsetleef:s'       => { name => 'subsetleef', default => 50 },
              'subsettable:s'      => { name => 'subsettable', default => 100 },
              'snmp-cache-file:s'  => { name => 'snmp_cache_file' },
              'snmp-autoreduce:s'  => { name => 'snmp_autoreduce' },
              'snmp-force-getnext' => { name => 'snmp_force_getnext' },
              'snmp-username:s'    => { name => 'snmp_security_name' },
              'authpassphrase:s'   => { name => 'snmp_auth_passphrase' },
              'authprotocol:s'     => { name => 'snmp_auth_protocol' },
              'privpassphrase:s'   => { name => 'snmp_priv_passphrase' },
              'privprotocol:s'     => { name => 'snmp_priv_protocol' },
              'contextname:s'      => { name => 'snmp_context_name' },
              'contextengineid:s'  => { name => 'snmp_context_engine_id' },
              'securityengineid:s' => { name => 'snmp_security_engine_id' },
              'snmp-tls-transport:s'      => { name => 'snmp_tls_transport' },
              'snmp-tls-our-identity:s'   => { name => 'snmp_tls_our_identity' },
              'snmp-tls-their-identity:s' => { name => 'snmp_tls_their_identity' },
              'snmp-tls-their-hostname:s' => { name => 'snmp_tls_their_hostname' },
              'snmp-tls-trust-cert:s    ' => { name => 'snmp_tls_trust_cert' },
              'snmp-errors-exit:s'        => { name => 'snmp_errors_exit', default => 'unknown' },
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'SNMP OPTIONS');
      }
  
      #####
      $self->{session} = undef;
      $self->{output} = $options{output};
      $self->{snmp_params} = {};
  
      $self->{use_snmp_cache} = 0;
  
      # Dont load MIB
      $SNMP::auto_init_mib = 0;
      $ENV{MIBS} = '';
      # For snmpv v1 - get request retries when you have "NoSuchName"
      $self->{RetryNoSuch} = 1;
      # Dont try to translate OID (we keep value)
      $self->{UseNumeric} = 1;
  
      $self->{error_msg} = undef;
      $self->{error_status} = 0;
  
      return $self;
  }
  
  sub connect {
      my ($self, %options) = @_;
  
      $self->{snmp_params}->{RetryNoSuch} = $self->{RetryNoSuch};
      $self->{snmp_params}->{UseNumeric} = $self->{UseNumeric};
  
      if (!$self->{output}->is_litteral_status(status => $self->{snmp_errors_exit})) {
          $self->{output}->add_option_msg(short_msg => "Unknown value '" . $self->{snmp_errors_exit}  . "' for --snmp-errors-exit.");
          $self->{output}->option_exit(exit_litteral => 'unknown');
      }
  
      $self->{session} = new SNMP::Session(%{$self->{snmp_params}});
      if (!defined($self->{session})) {
          if (defined($options{dont_quit}) && $options{dont_quit} == 1) {
              $self->set_error(error_status => -1, error_msg => 'SNMP Session: unable to create');
              return 1;
          }
          $self->{output}->add_option_msg(short_msg => 'SNMP Session: unable to create');
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
      if ($self->{session}->{ErrorNum}) {
          if (defined($options{dont_quit}) && $options{dont_quit} == 1) {
              $self->set_error(error_status => -1, error_msg => 'SNMP Session: ' . $self->{session}->{ErrorStr});
              return 1;
          }
          $self->{output}->add_option_msg(short_msg => 'SNMP Session: ' . $self->{session}->{ErrorStr});
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
  
      return 0;
  }
  
  sub load {
      my ($self, %options) = @_;
      # $options{oids} = ref to array of oids (example: ['.1.2', '.1.2'])
      # $options{instances} = ref to array of oids instances
      # $options{begin}, $args->{end} = integer instance end
      # $options{instance_regexp} = str
      # 3 way to use: with instances, with end, none
      
      if (defined($options{end})) {
          for (my $i = $options{begin}; $i <= $options{end}; $i++) {
              foreach (@{$options{oids}}) {
                  push @{$self->{oids_loaded}}, $_ . "." . $i;
              }
          }
          return ;
      }
      
      if (defined($options{instances})) {
          $options{instance_regexp} = defined($options{instance_regexp}) ? $options{instance_regexp} : '(\d+)$';
          foreach my $instance (@{$options{instances}}) {
              $instance =~ /$options{instance_regexp}/;
              foreach (@{$options{oids}}) {
                  push @{$self->{oids_loaded}}, $_ . "." . $1;
              }
          }
          return ;
      }
      
      push @{$self->{oids_loaded}}, @{$options{oids}};
  }
  
  sub autoreduce_table {
      my ($self, %options) = @_;
      
      return 1 if (defined($self->{snmp_force_getnext}) || $self->is_snmpv1());
      if ($self->{snmp_params}->{Retries} > 1) {
          $self->{snmp_params}->{Retries} = 1;
          $self->connect();
      }
      
      return 1 if (${$options{repeat_count}} == 1);
      ${$options{repeat_count}} = int(${$options{repeat_count}} / $self->{snmp_autoreduce_divisor});
      ${$options{repeat_count}} = 1 if (${$options{repeat_count}} < 1);
      return 0;
  }
  
  sub autoreduce_multiple_table {
      my ($self, %options) = @_;
      
      if ($self->{snmp_params}->{Retries} > 1) {
          $self->{snmp_params}->{Retries} = 1;
          $self->connect();
      }
      return 1 if (${$options{repeat_count}} == 1);
      
      ${$options{repeat_count}} = int(${$options{repeat_count}} / $self->{snmp_autoreduce_divisor});
      $self->{subsettable} = int($self->{subsettable} / $self->{snmp_autoreduce_divisor});
      ${$options{repeat_count}} = 1 if (${$options{repeat_count}} < 1);
      return 0;
  }
  
  sub autoreduce_leef {
      my ($self, %options) = @_;
      
      if ($self->{snmp_params}->{Retries} > 1) {
          $self->{snmp_params}->{Retries} = 1;
          $self->connect();
      }
      
      return 1 if ($self->{subsetleef} == 1);
      $self->{subsetleef} = int($self->{subsetleef} / $self->{snmp_autoreduce_divisor});
      $self->{subsetleef} = 1 if ($self->{subsetleef} < 1);
      
      my $array_ref = [];
      my $subset_current = 0;
      my $subset_construct = [];
      foreach ([@{$options{current}}], @{$self->{array_ref_ar}}) {
          foreach my $entry (@$_) {;
              push @$subset_construct, [$entry->[0], $entry->[1]];
              $subset_current++;
              if ($subset_current == $self->{subsetleef}) {
                  push @$array_ref, \@$subset_construct;
                  $subset_construct = [];
                  $subset_current = 0;
              }
          }
      }
      
      if ($subset_current) {
          push @$array_ref, \@$subset_construct;
      }
  
      $self->{array_ref_ar} = \@$array_ref;
      return 0;
  }
  
  sub get_leef_cache {
      my ($self, %options) = @_;
  
      my $results = {};
      foreach my $oid (@{$options{oids}}) {
          if (defined($self->{snmp_cache}->{$oid})) {
              $results->{$oid} = $self->{snmp_cache}->{$oid};
          }
      }
  
      if ($options{nothing_quit} == 1 && scalar(keys %$results) <= 0) {
          $self->{output}->add_option_msg(short_msg => 'SNMP GET Request: Cant get a single value.');
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
  
      return $results;
  }
  
  sub get_leef {
      my ($self, %options) = @_;
      # $options{dont_quit} = integer
      # $options{nothing_quit} = integer
      # $options{oids} = ref to array of oids (example: ['.1.2', '.1.2'])
  
      # Returns array
      #    'undef' value for an OID means NoSuchValue
  
      my ($dont_quit) = (defined($options{dont_quit}) && $options{dont_quit} == 1) ? 1 : 0;
      my ($nothing_quit) = (defined($options{nothing_quit}) && $options{nothing_quit} == 1) ? 1 : 0;
      $self->set_error();
  
      if (!defined($options{oids})) {
          if ($#{$self->{oids_loaded}} < 0) {
              if ($dont_quit == 1) {
                  $self->set_error(error_status => -1, error_msg => "Need to specify OIDs");
                  return undef;
              }
              $self->{output}->add_option_msg(short_msg => 'Need to specify OIDs');
              $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
          }
          push @{$options{oids}}, @{$self->{oids_loaded}};
          @{$self->{oids_loaded}} = ();
      }
  
      if ($self->{use_snmp_cache} == 1) {
          return $self->get_leef_cache(oids => $options{oids}, nothing_quit => $nothing_quit);
      }
  
      my $results = {};
      $self->{array_ref_ar} = [];
      my $subset_current = 0;
      my $subset_construct = [];
      foreach my $oid (@{$options{oids}}) {
          # Get last value
          next if ($oid !~ /(.*)\.(\d+)([\.\s]*)$/);
          
          my ($oid, $instance) = ($1, $2);
          $results->{$oid . "." . $instance} = undef;
          push @$subset_construct, [$oid, $instance];
          $subset_current++;
          if ($subset_current == $self->{subsetleef}) {
              push @{$self->{array_ref_ar}}, \@$subset_construct;
              $subset_construct = [];
              $subset_current = 0;
          }
      }
      if ($subset_current) {
          push @{$self->{array_ref_ar}}, \@$subset_construct;
      }
  
      ############################
      # If wrong oid with SNMP v1, packet resent (2 packets more). Not the case with SNMP > 1.
      # Can have "NoSuchName", if nothing works...
      # = v1: wrong oid
      #   bless( [
      #       '.1.3.6.1.2.1.1.3',
      #       '0',
      #       '199720062',
      #       'TICKS'
      #       ], 'SNMP::Varbind' ),
      #   bless( [
      #       '.1.3.6.1.2.1.1.999',
      #       '0'
      #       ], 'SNMP::Varbind' ),
      #   bless( [
      #       '.1.3.6.1.2.1.1',
      #       '1000'
      #       ], 'SNMP::Varbind' )
      # > v1: wrong oid
      #   bless( [
      #        '.1.3.6.1.2.1.1.3',
      #        '0',
      #        '199728713',
      #        'TICKS'
      #       ], 'SNMP::Varbind' ),
      #   bless( [
      #         '.1.3.6.1.2.1.1',
      #         '3',
      #         'NOSUCHINSTANCE',
      #        'TICKS'
      #    ], 'SNMP::Varbind' )
      #   bless( [
      #        '.1.3.6.1.2.1.1.999',
      #        '0',
      #        'NOSUCHOBJECT',
      #        'NOSUCHOBJECT'
      #       ], 'SNMP::Varbind' ),
      #   bless( [
      #        '.1.3.6.1.2.1.1',
      #        '1000',
      #        'NOSUCHOBJECT',
      #        'NOSUCHOBJECT'
      #       ], 'SNMP::Varbind' )
      ############################
  
      my $total = 0;
      while (my $entry = shift(@{$self->{array_ref_ar}})) {
          my $vb = new SNMP::VarList(@{$entry});
          $self->{session}->get($vb);
  
          if ($self->{session}->{ErrorNum}) {
              # 0    noError       Pas d'erreurs.
              # 1    tooBig        Reponse de taille trop grande.
              # 2    noSuchName    Variable inexistante.
              # -24  Timeout
              if ($self->{session}->{ErrorNum} == 2) {
                  # We are at the end with snmpv1. We next.
                  next;
              }
  
              if ($self->{snmp_autoreduce} == 1 && 
                  ($self->{session}->{ErrorNum} == 1 || $self->{session}->{ErrorNum} == 5 || $self->{session}->{ErrorNum} == -24)) {
                  next if ($self->autoreduce_leef(current => $entry) == 0);
              }
              my $msg = 'SNMP GET Request: ' . $self->{session}->{ErrorStr};    
              if ($dont_quit == 0) {
                  $self->{output}->add_option_msg(short_msg => $msg);
                  $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
              }
  
              $self->set_error(error_status => -1, error_msg => $msg);
              return undef;
          }
  
          # Some equipments gives a partial response and no error.
          # We look the last value if it's empty or not
          # In snmpv1 we have the retryNoSuch
          if (((scalar(@$vb) != scalar(@{$entry})) || (scalar(@{@$vb[-1]}) < 3)) && !$self->is_snmpv1()) {
              next if ($self->{snmp_autoreduce} == 1 && $self->autoreduce_leef(current => $entry) == 0);
              if ($dont_quit == 0) {
                  $self->{output}->add_option_msg(short_msg => 'SNMP partial response. Please try --snmp-autoreduce option');
                  $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
              }
  
              $self->set_error(error_status => -1, error_msg => 'SNMP partial response');
              return undef;
          }
  
          foreach my $entry (@$vb) {
              if ($#$entry < 3) {
                  # Can be snmpv1 not find
                  next;
              }
              if (${$entry}[2] eq 'NOSUCHOBJECT' || ${$entry}[2] eq 'NOSUCHINSTANCE') {
                  # Error in snmp > 1
                  next;
              }
  
              $total++;
              $results->{${$entry}[0] . "." . ${$entry}[1]} = ${$entry}[2];
          }
      }
  
      if ($nothing_quit == 1 && $total == 0) {
          $self->{output}->add_option_msg(short_msg => 'SNMP GET Request: Cant get a single value.');
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
  
      $self->debug(results => $results) if ($self->{output}->is_debug());
  
      return $results;
  }
  
  sub multiple_find_bigger {
      my ($self, %options) = @_;
      
      my $getting = {};
      my @values = ();
      foreach my $key (keys %{$options{working_oids}}) {
          push @values, $options{working_oids}->{$key}->{start};
          $getting->{ $options{working_oids}->{$key}->{start} } = $key;
      }
      @values = $self->oid_lex_sort(@values);
      
      return $getting->{pop(@values)};
  }
  
  sub get_multiple_table_cache {
      my ($self, %options) = @_;
  
      my $results = {};
      foreach my $entry (@{$options{oids}}) {
          my $result = $self->get_table_cache(
              oid => $entry->{oid},
              start => $entry->{start},
              end => $entry->{end},
              nothing_quit => 0
          );
          if ($options{return_type} == 0) {
              $results->{ $entry->{oid} } = $result;
          } else {
              $results = { %$results, %$result };
          }
      }
  
      my $total = 0;
      if ($options{nothing_quit} == 1) {
          if ($options{return_type} == 1) {
              $total = scalar(keys %$results);
          } else {
              foreach (keys %$results) {
                  $total += scalar(keys %{$results->{$_}});
              }
          }
  
          if ($total == 0) {
              $self->{output}->add_option_msg(short_msg => 'SNMP Table Request: Cant get a single value.');
              $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
          }
      }
  
      return $results;
  }
  
  sub get_multiple_table {
      my ($self, %options) = @_;
      # $options{dont_quit} = integer
      # $options{oids} = refs array
      #     [ { oid => 'x.x.x.x', start => '', end => ''}, { oid => 'y.y.y.y', start => '', end => ''} ]
      # $options{return_type} = integer
  
      my ($return_type) = (defined($options{return_type}) && $options{return_type} == 1) ? 1 : 0;
      my ($dont_quit) = (defined($options{dont_quit}) && $options{dont_quit} == 1) ? 1 : 0;
      my ($nothing_quit) = (defined($options{nothing_quit}) && $options{nothing_quit} == 1) ? 1 : 0;
      $self->set_error();
  
      if ($self->{use_snmp_cache} == 1) {
          return $self->get_multiple_table_cache(
              oids => $options{oids},
              return_type => $return_type,
              nothing_quit => $nothing_quit
          );
      }
  
      my $working_oids = {};
      my $results = {};
      # Check overlap
      foreach my $entry (@{$options{oids}}) {
          # Transform asking
          if ($entry->{oid} !~ /(.*)\.(\d+)([\.\s]*)$/) {
              if ($dont_quit == 1) {
                  $self->set_error(error_status => -1, error_msg => "Method 'get_multiple_table': Wrong OID '" . $entry->{oid} . "'.");
                  return undef;
              }
              $self->{output}->add_option_msg(short_msg => "Method 'get_multiple_table': Wrong OID '" . $entry->{oid} . "'.");
              $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
          }
  
          if (defined($entry->{start})) {
              $working_oids->{$entry->{oid}} = { start => $entry->{start}, end => $entry->{end} }; # last in it
          } else {
              $working_oids->{$entry->{oid}} = { start => $entry->{oid}, end => $entry->{end} };
          }
  
          if ($return_type == 0) {
              $results->{ $entry->{oid} } = {};
          }
      }
  
      # we use a medium (UDP have a PDU limit. SNMP protcol cant send multiples for one request)
      # So we need to manage
      # It's for "bulk". We ask 50 next values. If you set 1, it's like a getnext (snmp v1)
      my $repeat_count = 50;
      if (defined($self->{maxrepetitions}) && 
          $self->{maxrepetitions} =~ /^\d+$/) {
          $repeat_count = $self->{maxrepetitions};
      }
  
      # Quit if base not the same or 'ENDOFMIBVIEW' value. Need all oid finish otherwise we continue :)
      while (1) {
          my $current_oids = 0;
          my @bindings = ();
          my @bases = ();
          foreach my $key (keys %{$working_oids}) {
              $working_oids->{$key}->{start} =~ /(.*)\.(\d+)([\.\s]*)$/;
              push @bindings, [$1, $2];
              push @bases, $key;
  
              $current_oids++;
              last if ($current_oids > $self->{subsettable});
          }
  
          # Nothing more to check. We quit
          last if ($current_oids == 0);
  
          my $vb = new SNMP::VarList(@bindings);
  
          if ($self->is_snmpv1() || defined($self->{snmp_force_getnext})) {
              $self->{session}->getnext($vb);
          } else {
              my $current_repeat_count = floor($repeat_count / $current_oids);
              $current_repeat_count = 1 if ($current_repeat_count == 0);
              $self->{session}->getbulk(0, $current_repeat_count, $vb);
          }
  
          # Error
          if ($self->{session}->{ErrorNum}) {
              # 0    noError       Pas d'erreurs.
              # 1    tooBig        Reponse de taille trop grande.
              # 2    noSuchName    Variable inexistante.
              if ($self->{session}->{ErrorNum} == 2) {
                  # We are at the end with snmpv1. Need to find the most up oid ;)
                  my $oid_base = $self->multiple_find_bigger(working_oids => $working_oids);
                  delete $working_oids->{$oid_base};
                  next;
              }
  
              if ($self->{snmp_autoreduce} == 1 && 
                  ($self->{session}->{ErrorNum} == 1 || $self->{session}->{ErrorNum} == 5 || $self->{session}->{ErrorNum} == -24)) {
                  next if ($self->autoreduce_multiple_table(repeat_count => \$repeat_count) == 0);
              }
  
              my $msg = 'SNMP Table Request: ' . $self->{session}->{ErrorStr};
              if ($dont_quit == 0) {
                  $self->{output}->add_option_msg(short_msg => $msg);
                  $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
              }
  
              $self->set_error(error_status => -1, error_msg => $msg);
              return undef;
          }
  
          # Manage
          # step by step: [ 1 => 1, 2 => 1, 3 => 1 ], [ 1 => 2, 2 => 2, 3 => 2 ],...
  
          my $pos = -1;
          foreach my $entry (@$vb) {
              $pos++;
  
              # Already destruct. we continue
              next if (!defined($working_oids->{ $bases[$pos % $current_oids] }));
  
              # ENDOFMIBVIEW is on each iteration. So we need to delete and skip after that
              if (${$entry}[2] eq 'ENDOFMIBVIEW') {
                  delete $working_oids->{ $bases[$pos % $current_oids] };
                  # END mib
                  next;
              }
  
              # Not in same table
              my $complete_oid = ${$entry}[0] . "." . ${$entry}[1];
              my $base = $bases[$pos % $current_oids];
              if ($complete_oid !~ /^$base\./ ||
                  (defined($working_oids->{ $bases[$pos % $current_oids] }->{end}) && 
                   $self->check_oid_up(current => $complete_oid, end => $working_oids->{ $bases[$pos % $current_oids] }->{end}))) {
                  delete $working_oids->{ $bases[$pos % $current_oids] };
                  next;
              }
  
              if ($return_type == 0) {
                  $results->{$bases[$pos % $current_oids]}->{$complete_oid} = ${$entry}[2];
              } else {
                  $results->{$complete_oid} = ${$entry}[2];
              }
  
              $working_oids->{ $bases[$pos % $current_oids] }->{start} = $complete_oid;
          }
  
          # infinite loop. Some equipments it returns nothing!!??
          if ($pos == -1) {
              $self->{output}->add_option_msg(short_msg => 'SNMP Table Request: problem to get values (try --snmp-force-getnext option)');
              $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
          }
      }
  
      my $total = 0;
      if ($nothing_quit == 1) {
          if ($return_type == 1) {
              $total = scalar(keys %{$results});
          } else {
              foreach (keys %{$results}) {
                  $total += scalar(keys %{$results->{$_}});
              }
          }
  
          if ($total == 0) {
              $self->{output}->add_option_msg(short_msg => 'SNMP Table Request: Cant get a single value.');
              $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
          }
      }
  
      $self->debug(results => $results) if ($self->{output}->is_debug());
  
      return $results;
  }
  
  sub get_table_cache {
      my ($self, %options) = @_;
  
      my $branch = defined($options{start}) ? $options{start} : $options{oid};
  
      my $results = {};
      foreach my $oid ($self->oid_lex_sort(keys %{$self->{snmp_cache}})) {
          if ($oid =~ /^$branch\./) {
              $results->{$oid} = $self->{snmp_cache}->{$oid};
              if (defined($options{end}) && $self->check_oid_up(current => $oid, end => $options{end})) {
                  last;
              } 
          }
      }
  
      if ($options{nothing_quit} == 1 && scalar(keys %$results) <= 0) {
          $self->{output}->add_option_msg(short_msg => 'SNMP Table Request: Cant get a single value.');
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
  
      return $results;
  }
  
  sub get_table {
      my ($self, %options) = @_;
      # $options{dont_quit} = integer
      # $options{oid} = string (example: '.1.2')
      # $options{start} = string (example: '.1.2')
      # $options{end} = string (example: '.1.2')
  
      my ($dont_quit) = (defined($options{dont_quit}) && $options{dont_quit} == 1) ? 1 : 0;
      my ($nothing_quit) = (defined($options{nothing_quit}) && $options{nothing_quit} == 1) ? 1 : 0;
      $self->set_error();
  
      if (defined($options{start})) {
          $options{start} = $self->clean_oid($options{start});
      }
      if (defined($options{end})) {
          $options{end} = $self->clean_oid($options{end});
      }
  
      if ($self->{use_snmp_cache} == 1) {
          return $self->get_table_cache(
              oid => $options{oid},
              start => $options{start},
              end => $options{end},
              nothing_quit => $nothing_quit
          );
      }
  
      # we use a medium (UDP have a PDU limit. SNMP protcol cant send multiples for one request)
      # So we need to manage
      # It's for "bulk". We ask 50 next values. If you set 1, it's like a getnext (snmp v1)
      my $repeat_count = 50;
      if (defined($self->{maxrepetitions}) && 
          $self->{maxrepetitions} =~ /^\d+$/) {
          $repeat_count = $self->{maxrepetitions};
      }
  
      # Transform asking
      if ($options{oid} !~ /(.*)\.(\d+)([\.\s]*)$/) {
          if ($dont_quit == 1) {
              $self->set_error(error_status => -1, error_msg => "Method 'get_table': Wrong OID '" . $options{oid} . "'.");
              return undef;
          }
          $self->{output}->add_option_msg(short_msg => "Method 'get_table': Wrong OID '" . $options{oid} . "'.");
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
  
      my $main_indice = $1 . '.' . $2;
      my $results = {};
  
      # Quit if base not the same or 'ENDOFMIBVIEW' value
      my $leave = 1;
      my $last_oid;
  
      if (defined($options{start})) {
          $last_oid = $options{start};
      } else {
          $last_oid = $options{oid};
      }
      while ($leave) {
          $last_oid =~ /(.*)\.(\d+)([\.\s]*)$/;
          my $vb = new SNMP::VarList([$1, $2]);
  
          if ($self->is_snmpv1() || defined($self->{snmp_force_getnext})) {
              $self->{session}->getnext($vb);
          } else {
              $self->{session}->getbulk(0, $repeat_count, $vb);
          }
  
          # Error
          if ($self->{session}->{ErrorNum}) {
              # 0    noError       Pas d'erreurs.
              # 1    tooBig        Reponse de taille trop grande.
              # 2    noSuchName    Variable inexistante.
              # -24  Timeout
              if ($self->{session}->{ErrorNum} == 2) {
                  # We are at the end with snmpv1. We quit.
                  last;
              }
              if ($self->{snmp_autoreduce} == 1 && 
                  ($self->{session}->{ErrorNum} == 1 || $self->{session}->{ErrorNum} == 5 || $self->{session}->{ErrorNum} == -24)) {
                  next if ($self->autoreduce_table(repeat_count => \$repeat_count) == 0);
              }
  
              my $msg = 'SNMP Table Request: ' . $self->{session}->{ErrorStr};
  
              if ($dont_quit == 0) {
                  $self->{output}->add_option_msg(short_msg => $msg);
                  $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
              }
  
              $self->set_error(error_status => -1, error_msg => $msg);
              return undef;
          }
  
          # Manage
          foreach my $entry (@$vb) {
              if (${$entry}[2] eq 'ENDOFMIBVIEW') {
                  # END mib
                  $leave = 0;
                  last;
              }
  
              # Not in same table
              my $complete_oid = ${$entry}[0] . "." . ${$entry}[1];
              if ($complete_oid !~ /^$main_indice\./ ||
                  (defined($options{end}) && $self->check_oid_up(current => $complete_oid, end => $options{end}))) {
                  $leave = 0;
                  last;
              }
  
              $results->{$complete_oid} = ${$entry}[2];
              $last_oid = $complete_oid;
          }
      }
  
      if ($nothing_quit == 1 && scalar(keys %$results) == 0) {
          $self->{output}->add_option_msg(short_msg => 'SNMP Table Request: Cant get a single value.');
          $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
      }
  
      $self->debug(results => $results) if ($self->{output}->is_debug());
  
      return $results;
  }
  
  sub set {
      my ($self, %options) = @_;
      # $options{dont_quit} = integer
      # $options{oids} = ref to hash table
      my ($dont_quit) = (defined($options{dont_quit}) && $options{dont_quit} == 1) ? 1 : 0;
      $self->set_error();
  
      my $vars = [];
      foreach my $oid (keys %{$options{oids}}) {
          # Get last value
          next if ($oid !~ /(.*)\.(\d+)([\.\s]*)$/);
  
          my $value = $options{oids}->{$oid}->{value};
          my $type = $options{oids}->{$oid}->{type};
          my ($oid, $instance) = ($1, $2);
  
          push @$vars, [$oid, $instance, $value, $type];
      }
  
      $self->{session}->set($vars);
      if ($self->{session}->{ErrorNum}) {
          # 0    noError       Pas d'erreurs.
          # 1    tooBig        Reponse de taille trop grande.
          # 2    noSuchName    Variable inexistante.
  
          my $msg = 'SNMP SET Request: ' . $self->{session}->{ErrorStr};
          if ($dont_quit == 0) {
              $self->{output}->add_option_msg(short_msg => $msg);
              $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
          }
  
          $self->set_error(error_status => -1, error_msg => $msg);
          return undef;
      }
  
      return 0;
  }
  
  sub is_snmpv1 {
      my ($self) = @_;
  
      if ($self->{snmp_params}->{Version} eq '1') {
          return 1;
      }
      return 0;
  }
  
  sub clean_oid {
      my ($self, $oid) = @_;
  
      $oid =~ s/\.$//;
      $oid =~ s/^(\d)/\.$1/;
      return $oid;
  }
  
  sub check_oid_up {
      my ($self, %options) = @_;
  
      my $current_oid = $options{current};
      my $end_oid = $options{end};
  
      my @current_oid_splitted = split /\./, $current_oid;
      my @end_oid_splitted = split /\./, $end_oid;
      # Skip first value (before first '.' empty)
      for (my $i = 1; $i <= $#current_oid_splitted && $i <= $#end_oid_splitted; $i++) {
          if (int($current_oid_splitted[$i]) > int($end_oid_splitted[$i])) {
              return 1;
          }
      }
  
      return 0;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      $self->{snmp_errors_exit} = $options{option_results}->{snmp_errors_exit};
  
      if (defined($options{option_results}->{snmp_cache_file}) && $options{option_results}->{snmp_cache_file} ne '') {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'JSON::XS',
              error_msg => "Cannot load module 'JSON::XS'."
          );
          my $content = centreon::plugins::misc::slurp_file(output => $self->{output}, file => $options{option_results}->{snmp_cache_file});
          eval {
              $self->{snmp_cache} = JSON::XS->new->decode($content);
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg => "Cannot decode json cache file: $@");
              $self->{output}->option_exit();
          }
  
          $self->{use_snmp_cache} = 1;
          return ;
      }
  
      if (!defined($options{option_results}->{host})) {
          $self->{output}->add_option_msg(short_msg => 'Missing parameter --hostname.');
          $self->{output}->option_exit();
      }
  
      $options{option_results}->{snmp_version} =~ s/^v//;
      if ($options{option_results}->{snmp_version} !~ /1|2c|2|3/) {
          $self->{output}->add_option_msg(short_msg => 'Unknown snmp version.');
          $self->{output}->option_exit();
      }
  
      $self->{snmp_force_getnext} = $options{option_results}->{snmp_force_getnext};
      $self->{maxrepetitions} = $options{option_results}->{maxrepetitions};
      $self->{subsetleef} = (defined($options{option_results}->{subsetleef}) && $options{option_results}->{subsetleef} =~ /^[0-9]+$/) ? $options{option_results}->{subsetleef} : 50;
      $self->{subsettable} = (defined($options{option_results}->{subsettable}) && $options{option_results}->{subsettable} =~ /^[0-9]+$/) ? $options{option_results}->{subsettable} : 100;
      $self->{snmp_autoreduce} = 0;
      $self->{snmp_autoreduce_divisor} = 2;
      if (defined($options{option_results}->{snmp_autoreduce})) {
          $self->{snmp_autoreduce} = 1;
          $self->{snmp_autoreduce_divisor} = $1 if ($options{option_results}->{snmp_autoreduce} =~ /(\d+(\.\d+)?)/ && $1 > 1);
      }
  
      %{$self->{snmp_params}} = (
          DestHost => $options{option_results}->{host},
          Community => $options{option_results}->{snmp_community},
          Version => $options{option_results}->{snmp_version},
          RemotePort => $options{option_results}->{snmp_port},
          Retries => 5
      );
  
      if (defined($options{option_results}->{snmp_timeout}) && $options{option_results}->{snmp_timeout} =~ /^[0-9]+$/) {
          $self->{snmp_params}->{Timeout} = $options{option_results}->{snmp_timeout} * (10**6);
      }
  
      if (defined($options{option_results}->{snmp_retries}) && $options{option_results}->{snmp_retries} =~ /^[0-9]+$/) {
          $self->{snmp_params}->{Retries} = $options{option_results}->{snmp_retries};
      }
  
      if ($options{option_results}->{snmp_version} eq '3') {
          delete $self->{snmp_params}->{Community};
  
          $self->{snmp_params}->{Context} = $options{option_results}->{snmp_context_name} if (defined($options{option_results}->{snmp_context_name}));
          $self->{snmp_params}->{ContextEngineId} = $options{option_results}->{snmp_context_engine_id} if (defined($options{option_results}->{snmp_context_engine_id}));
          $self->{snmp_params}->{SecEngineId} = $options{option_results}->{snmp_security_engine_id} if (defined($options{option_results}->{snmp_security_engine_id}));
          $self->{snmp_params}->{SecName} = $options{option_results}->{snmp_security_name} if (defined($options{option_results}->{snmp_security_name}));
  
          # Certificate SNMPv3. Need net-snmp > 5.6
          if (defined($options{option_results}->{snmp_tls_transport}) && $options{option_results}->{snmp_tls_transport} =~ /^dtlsudp|tlstcp$/) {
              $self->{snmp_params}->{DestHost} = $options{option_results}->{snmp_tls_transport} . ':' . $options{option_results}->{host};
              $self->{snmp_params}->{OurIdentity} = $options{option_results}->{snmp_tls_our_identity} if (defined($options{option_results}->{snmp_tls_our_identity}));
              $self->{snmp_params}->{TheirIdentity} = $options{option_results}->{snmp_tls_their_identity} if (defined($options{option_results}->{snmp_tls_their_identity}));
              $self->{snmp_params}->{TheirHostname} = $options{option_results}->{snmp_tls_their_hostname} if (defined($options{option_results}->{snmp_tls_their_hostname}));
              $self->{snmp_params}->{TrustCert} = $options{option_results}->{snmp_tls_trust_cert} if (defined($options{option_results}->{snmp_tls_trust_cert}));
              $self->{snmp_params}->{SecLevel} = 'authPriv';
              return ;
          }
  
          if (!defined($options{option_results}->{snmp_security_name}) || $options{option_results}->{snmp_security_name} eq '') {
              $self->{output}->add_option_msg(short_msg => 'Missing parameter Security Name.');
              $self->{output}->option_exit();
          }
  
          # unauthenticated and unencrypted
          $self->{snmp_params}->{SecLevel} = 'noAuthNoPriv';
  
          my $user_activate = 0;
          if (defined($options{option_results}->{snmp_auth_passphrase}) && $options{option_results}->{snmp_auth_passphrase} ne '') {
              if (!defined($options{option_results}->{snmp_auth_protocol})) {
                  $self->{output}->add_option_msg(short_msg => 'Missing parameter authenticate protocol.');
                  $self->{output}->option_exit();
              }
              $options{option_results}->{snmp_auth_protocol} = uc($options{option_results}->{snmp_auth_protocol});
              if ($options{option_results}->{snmp_auth_protocol} !~ /^(?:MD5|SHA|SHA224|SHA256|SHA384|SHA512)$/) {
                  $self->{output}->add_option_msg(short_msg => 'Wrong authentication protocol.');
                  $self->{output}->option_exit();
              }
  
              $self->{snmp_params}->{SecLevel} = 'authNoPriv';
              $self->{snmp_params}->{AuthProto} = $options{option_results}->{snmp_auth_protocol};
              $self->{snmp_params}->{AuthPass} = $options{option_results}->{snmp_auth_passphrase};
              $user_activate = 1;
          }
  
          if (defined($options{option_results}->{snmp_priv_passphrase}) && $options{option_results}->{snmp_priv_passphrase} ne '') {
              if (!defined($options{option_results}->{snmp_priv_protocol})) {
                  $self->{output}->add_option_msg(short_msg => 'Missing parameter privacy protocol.');
                  $self->{output}->option_exit();
              }
  
              $options{option_results}->{snmp_priv_protocol} = uc($options{option_results}->{snmp_priv_protocol});
              if ($options{option_results}->{snmp_priv_protocol} !~ /^(?:DES|AES|AES192|AES192C|AES256|AES256C)$/) {
                  $self->{output}->add_option_msg(short_msg => 'Wrong privacy protocol.');
                  $self->{output}->option_exit();
              }
              if ($user_activate == 0) {
                  $self->{output}->add_option_msg(short_msg => 'Cannot use snmp v3 privacy option without snmp v3 authentification options.');
                  $self->{output}->option_exit();
              }
              $self->{snmp_params}->{SecLevel} = 'authPriv';
              $self->{snmp_params}->{PrivPass} = $options{option_results}->{snmp_priv_passphrase};
              $self->{snmp_params}->{PrivProto} = $options{option_results}->{snmp_priv_protocol};
          }
      }
  }
  
  sub set_snmp_connect_params {
      my ($self, %options) = @_;
  
      foreach (keys %options) {
          $self->{snmp_params}->{$_} = $options{$_};
      }
  }
  
  sub set_snmp_params {
      my ($self, %options) = @_;
  
      foreach (keys %options) {
          $self->{$_} = $options{$_};
      }
  }
  
  sub set_error {
      my ($self, %options) = @_;
      # $options{error_msg} = string error
      # $options{error_status} = integer status
  
      $self->{error_status} = defined($options{error_status}) ? $options{error_status} : 0;
      $self->{error_msg} = defined($options{error_msg}) ? $options{error_msg} : undef;
  }
  
  sub error_status {
      my ($self) = @_;
  
      return $self->{error_status};
  }
  
  sub error {
      my ($self) = @_;
  
      return $self->{error_msg};
  }
  
  sub get_hostname {
      my ($self) = @_;
  
      my $host = $self->{snmp_params}->{DestHost};
      $host =~ s/.*://;
      return $host;
  }
  
  sub get_port {
      my ($self) = @_;
  
      return $self->{snmp_params}->{RemotePort};
  }
  
  sub map_instance {
      my ($self, %options) = @_;
  
      my $results = {};
      my $instance = '';
      $instance = '.' . $options{instance} if (defined($options{instance}));
      foreach my $name (keys %{$options{mapping}}) {
          my $entry = $options{mapping}->{$name}->{oid} . $instance;
          if (defined($options{results}->{$entry})) {
              $results->{$name} = $options{results}->{$entry};
          } elsif (defined($options{results}->{$options{mapping}->{$name}->{oid}}->{$entry})) {
              $results->{$name} = $options{results}->{$options{mapping}->{$name}->{oid}}->{$entry};
          } else {
              $results->{$name} = defined($options{default}) ? $options{default} : undef;
          }
  
          if (defined($options{mapping}->{$name}->{map})) {
              if (defined($results->{$name})) {
                  $results->{$name} = defined($options{mapping}->{$name}->{map}->{$results->{$name}}) ? $options{mapping}->{$name}->{map}->{$results->{$name}} : (defined($options{default}) ? $options{default} : 'unknown');
              }
          }
      }
  
      return $results;
  }
  
  sub debug {
      my ($self, %options) = @_;
  
      foreach my $oid1 ($self->oid_lex_sort(keys %{$options{results}})) {
          if (ref($options{results}->{$oid1}) eq 'HASH') {
              foreach my $oid2 ($self->oid_lex_sort(keys %{$options{results}->{$oid1}})) {
                  $self->{output}->output_add(long_msg => $oid2 . ' = ' . (defined($options{results}->{$oid1}->{$oid2}) ? $options{results}->{$oid1}->{$oid2} : 'undef'), debug => 1);
              }
          } else {
              $self->{output}->output_add(long_msg => $oid1 . ' = ' . (defined($options{results}->{$oid1}) ? $options{results}->{$oid1} : 'undef'), debug => 1);
          }
      }
  }
  
  sub oid_lex_sort {
      my $self = shift;
  
      if (@_ <= 1) {
          return @_;
      }
  
      return map { $_->[0] }
          sort { $a->[1] cmp $b->[1] }
          map {
             my $oid = $_;
             $oid =~ s/^\.//;
             $oid =~ s/ /\.0/g;
             [$_, pack 'N*', split m/\./, $oid]
          } @_;
  }
  
  1;
  
  
  =head1 NAME
  
  SNMP global
  
  =head1 SYNOPSIS
  
  snmp class
  
  =head1 SNMP OPTIONS
  
  =over 8
  
  =item B<--hostname>
  
  Name or address of the host to monitor (mandatory).
  
  =item B<--snmp-community>
  
  SNMP community (default value: public). It is recommended to use a read-only
  community.
  
  =item B<--snmp-version>
  
  Version of the SNMP protocol. 1 for SNMP v1 (default), 2 for SNMP v2c, 3 for SNMP v3.
  
  =item B<--snmp-port>
  
  UDP port to send the SNMP request to (default: 161).
  
  =item B<--snmp-timeout>
  
  Time to wait before sending the request again if no reply has been received,
  in seconds (default: 1). See also --snmp-retries.
  
  =item B<--snmp-retries>
  
  Maximum number of retries (default: 5).
  
  =item B<--maxrepetitions>
  
  Max repetitions value (default: 50) (only for SNMP v2 and v3).
  
  =item B<--subsetleef>
  
  How many OID values per SNMP request (default: 50) (for get_leef method. Be cautious when you set it. Prefer to let the default value).
  
  =item B<--snmp-autoreduce>
   
  Progressively reduce the number of requested OIDs in bulk mode. Use it in case of
  SNMP errors (by default, the number is divided by 2).
  
  =item B<--snmp-force-getnext>
  
  Use SNMP getnext function in SNMP v2c and v3. This will request one OID at a
  time.
  
  =item B<--snmp-cache-file>
  
  Use SNMP cache file.
  
  =item B<--snmp-username>
  
  SNMP v3 only:
  User name (securityName). 
  
  =item B<--authpassphrase>
  
  SNMP v3 only:
  Pass phrase hashed using the authentication protocol defined in the 
  --authprotocol option.
  
  =item B<--authprotocol>
  
  SNMP v3 only:
  Authentication protocol: MD5|SHA. Since net-snmp 5.9.1: SHA224|SHA256|SHA384|SHA512.
  
  =item B<--privpassphrase>
  
  SNMP v3 only:
  Privacy pass phrase (privPassword) to encrypt messages using the protocol
  defined in the --privprotocol option.
  
  =item B<--privprotocol>
  
  SNMP v3 only:
  Privacy protocol (privProtocol) used to encrypt messages.
  Supported protocols are: DES|AES and since net-snmp 5.9.1: AES192|AES192C|AES256|AES256C.
  
  =item B<--contextname>
  
  SNMP v3 only:
  Context name (contextName), if relevant for the monitored host.
  
  =item B<--contextengineid>
  
  SNMP v3 only:
  Context engine ID (contextEngineID), if relevant for the monitored host, given 
  as a hexadecimal string.
  
  =item B<--securityengineid>
  
  SNMP v3 only:
  Security engine ID, given as a hexadecimal string.
  
  =item B<--snmp-errors-exit>
  
  Expected status in case of SNMP error or timeout.
  Possible values are warning, critical and unknown (default).
  
  =item B<--snmp-tls-transport>
  
  Transport protocol for TLS communication (can be: 'dtlsudp', 'tlstcp').
  
  =item B<--snmp-tls-our-identity>
  
  X.509 certificate to identify ourselves. Can be the path to the certificate file
  or its contents.
  
  =item B<--snmp-tls-their-identity>
  
  X.509 certificate to identify the remote host. Can be the path to the 
  certificate file or its contents. This option is unnecessary if the certificate
  is already trusted by your system.
  
  =item B<--snmp-tls-their-hostname>
  
  Common Name (CN) expected in the certificate sent by the host if it differs from
  the value of the --hostname parameter.
  
  =item B<--snmp-tls-trust-cert>
  
  A trusted CA certificate used to verify a remote host's certificate. 
  If you use this option, you must also  define --snmp-tls-their-hostname.
  
  =back
  
  =head1 DESCRIPTION
  
  B<snmp>.
  
  =cut
CENTREON_PLUGINS_SNMP

$fatpacked{"centreon/plugins/ssh.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_SSH';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::ssh;
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = {};
      bless $self, $class;
  
      if (!defined($options{noptions}) || $options{noptions} != 1) {
          $options{options}->add_options(arguments => {
              'ssh-backend:s'  => { name => 'ssh_backend', default => 'sshcli' },
              'ssh-port:s'     => { name => 'ssh_port' },
              'ssh-priv-key:s' => { name => 'ssh_priv_key' },
              'ssh-username:s' => { name => 'ssh_username' },
              'ssh-password:s' => { name => 'ssh_password' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'SSH GLOBAL OPTIONS');
      }
  
      centreon::plugins::misc::mymodule_load(
          output    => $options{output},
          module    => 'centreon::plugins::backend::ssh::sshcli',
          error_msg => "Cannot load module 'centreon::plugins::backend::ssh::sshcli'."
      );
      $self->{backend_sshcli} = centreon::plugins::backend::ssh::sshcli->new(%options);
  
      centreon::plugins::misc::mymodule_load(
          output    => $options{output},
          module    => 'centreon::plugins::backend::ssh::plink',
          error_msg => "Cannot load module 'centreon::plugins::backend::ssh::plink'."
      );
      $self->{backend_plink} = centreon::plugins::backend::ssh::plink->new(%options);
  
      centreon::plugins::misc::mymodule_load(
          output    => $options{output},
          module    => 'centreon::plugins::backend::ssh::libssh',
          error_msg => "Cannot load module 'centreon::plugins::backend::ssh::libssh'."
      );
      $self->{backend_libssh} = centreon::plugins::backend::ssh::libssh->new(%options);
  
      $self->{output} = $options{output};
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      $self->{ssh_backend} = $options{option_results}->{ssh_backend};
      my $default_port = 22;
      if (defined($options{default_ssh_port}) && $options{default_ssh_port} =~ /\d+/) {
          $default_port = $options{default_ssh_port};
      }
      $self->{ssh_port} = defined($options{option_results}->{ssh_port}) && $options{option_results}->{ssh_port} =~ /(\d+)/ ? $1 : $default_port;
      $self->{ssh_backend} = 'sshcli'
          if (!defined($options{option_results}->{ssh_backend}) || $options{option_results}->{ssh_backend} eq '');
      if (!defined($self->{'backend_' . $self->{ssh_backend}})) {
          $self->{output}->add_option_msg(short_msg => 'unknown ssh backend: ' . $self->{ssh_backend});
          $self->{output}->option_exit();
      }
      $self->{'backend_' . $self->{ssh_backend}}->check_options(%options);
  }
  
  sub get_port {
      my ($self, %options) = @_;
  
      return $self->{ssh_port};
  }
  
  sub get_ssh_backend {
      my ($self, %options) = @_;
  
      return $self->{ssh_backend};
  }
  
  sub execute {
      my ($self, %options) = @_;
  
      return $self->{'backend_' . $self->{ssh_backend}}->execute(%options);
  }
  
  1;
  
  
  =head1 NAME
  
  SSH abstraction layer.
  
  =head1 SYNOPSIS
  
  SSH abstraction layer for SSH CLI, Plink and libSSH backends
  
  =head1 SSH GLOBAL OPTIONS
  
  =over 8
  
  =item B<--ssh-backend>
  
  Define the backend you want to use.
  It can be: C<sshcli> (default), C<plink> and C<libssh>.
  
  =item B<--ssh-username>
  
  Define the user name to log in to the host.
  
  =item B<--ssh-password>
  
  Define the password associated with the user name.
  Cannot be used with the C<sshcli> backend.
  Warning: using a password is not recommended. Use C<--ssh-priv-key> instead.
  
  =item B<--ssh-port>
  
  Define the TCP port on which SSH is listening.
  
  =item B<--ssh-priv-key>
  
  Define the private key file to use for user authentication.
  
  =back
  
  =head1 DESCRIPTION
  
  B<ssh>.
  
  =cut
CENTREON_PLUGINS_SSH

$fatpacked{"centreon/plugins/statefile.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_STATEFILE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::statefile;
  
  use strict;
  use warnings;
  use Data::Dumper;
  use centreon::plugins::misc;
  
  my $default_dir = '/var/lib/centreon/centplugins';
  if ($^O eq 'MSWin32') {
      $default_dir = 'C:/Windows/Temp';
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (defined($options{options})) {
          $options{options}->add_options(arguments => {
              'memcached:s'          => { name => 'memcached' },
              'redis-server:s'       => { name => 'redis_server' },
              'redis-attribute:s%'   => { name => 'redis_attribute' },
              'redis-db:s'           => { name => 'redis_db' },
              'memexpiration:s'      => { name => 'memexpiration', default => 86400 },
              'statefile-dir:s'      => { name => 'statefile_dir', default => $default_dir },
              'statefile-suffix:s'   => { name => 'statefile_suffix', default => '' },
              'statefile-concat-cwd' => { name => 'statefile_concat_cwd' },
              'statefile-storable'   => { name => 'statefile_storable' }, # legacy
              'failback-file'        => { name => 'failback_file' },
              'statefile-format:s'   => { name => 'statefile_format' },
              'statefile-key:s'      => { name => 'statefile_key' },
              'statefile-cipher:s'   => { name => 'statefile_cipher' }
          });
          $options{options}->add_help(package => __PACKAGE__, sections => 'RETENTION OPTIONS', once => 1);
      }
  
      $self->{error} = 0;
      $self->{output} = $options{output};
      $self->{datas} = {};
      $self->{storable} = 0;
      $self->{memcached_ok} = 0;
      $self->{memcached} = undef;
  
      $self->{statefile_dir} = undef;
      $self->{statefile_suffix} = undef;
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      if (defined($options{option_results}) && defined($options{option_results}->{memcached})) {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Memcached::libmemcached',
              error_msg => "Cannot load module 'Memcached::libmemcached'."
          );
          $self->{memcached} = Memcached::libmemcached->new();
          Memcached::libmemcached::memcached_server_add($self->{memcached}, $options{option_results}->{memcached});
      }
  
      # Check redis
      if (defined($options{option_results}->{redis_server})) {
          $self->{redis_attributes} = '';
          if (defined($options{option_results}->{redis_attribute})) {
              foreach (keys %{$options{option_results}->{redis_attribute}}) {
                  $self->{redis_attributes} .= "$_ => " . $options{option_results}->{redis_attribute}->{$_} . ', ';
              }
          }
  
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Redis',
              error_msg => "Cannot load module 'Redis'."
          );
          eval {
              $options{option_results}->{redis_server} .= ':6379' if ($options{option_results}->{redis_server} !~ /:\d+$/);
              $self->{redis_cnx} = Redis->new(
                  server => $options{option_results}->{redis_server}, 
                  eval $self->{redis_attributes}
              );
              if (defined($self->{redis_cnx}) && 
                  defined($options{option_results}->{redis_db}) &&
                  $options{option_results}->{redis_db} ne ''
                  ) {
                  $self->{redis_cnx}->select($options{option_results}->{redis_db});
              }
          };
          if (!defined($self->{redis_cnx}) && !defined($options{option_results}->{failback_file})) {
              $self->{output}->add_option_msg(short_msg => "redis connection issue: $@");
              $self->{output}->option_exit();
          }
      }
  
      $self->{statefile_format} = 'json';
      if (defined($options{option_results}->{statefile_format}) && $options{option_results}->{statefile_format} ne '' && 
          $options{option_results}->{statefile_format} =~ /^(?:dumper|json|storable)$/) {
          $self->{statefile_format} = $options{option_results}->{statefile_format};
      } elsif (defined($options{default_format}) && $options{default_format} =~ /^(?:dumper|json|storable)$/) {
          $self->{statefile_format} = $options{default_format};
      }
  
      if (defined($options{option_results}->{statefile_storable})) {
          $self->{statefile_format} = 'storable';
      }
  
      if ($self->{statefile_format} eq 'dumper') {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output}, module => 'Safe', 
              no_quit => 1
          );
          $self->{safe} = Safe->new();
          $self->{safe}->share('$datas');
      } elsif ($self->{statefile_format} eq 'storable') {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Storable',
              error_msg => "Cannot load module 'Storable'."
          );
      } elsif ($self->{statefile_format} eq 'json') {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'JSON::XS',
              error_msg => "Cannot load module 'JSON::XS'."
          );
      }
  
      $self->{statefile_cipher} = defined($options{option_results}->{statefile_cipher}) && $options{option_results}->{statefile_cipher} ne '' ?    
          $options{option_results}->{statefile_cipher} : 'AES';
      $self->{statefile_key} = defined($options{option_results}->{statefile_key}) && $options{option_results}->{statefile_key} ne '' ?    
          $options{option_results}->{statefile_key} : '';
  
      if ($self->{statefile_key} ne '') {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Crypt::Mode::CBC',
              error_msg => "Cannot load module 'Crypt::Mode::CBC'."
          );
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Crypt::PRNG',
              error_msg => "Cannot load module 'Crypt::PRNG'."
          );
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'MIME::Base64',
              error_msg => "Cannot load module 'MIME::Base64'."
          );
      }
  
      $self->{statefile_dir} = $options{option_results}->{statefile_dir};
      if (defined($self->{statefile_dir})
              && $self->{statefile_dir} ne $default_dir
              && defined($options{option_results}->{statefile_concat_cwd})
      ) {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'Cwd',
              error_msg => "Cannot load module 'Cwd'."
          );
          $self->{statefile_dir} = Cwd::cwd() . '/' . $self->{statefile_dir};
      }
  
      $self->{statefile_suffix} = $options{option_results}->{statefile_suffix};
      $self->{memexpiration} = $options{option_results}->{memexpiration};
  }
  
  sub error {
      my ($self) = shift;
  
      if (@_) {
          $self->{error} = $_[0];
      }
      return $self->{error};
  }
  
  sub get_key {
      my ($self, %options) = @_;
  
      my $key = $options{key};
  
      {
          use bytes;
  
          my $size = length($key);
          my $minsize = Crypt::Cipher->min_keysize($options{cipher});
          if ($minsize > $size) {
              $key .= "0" x ($minsize - $size);
          }
      }
  
      return $key;
  }
  
  sub decrypt {
      my ($self, %options) = @_;
  
      return (1, $options{data}) if (!defined($options{data}->{encrypted}));
  
      my $plaintext;
      eval {
          my $cipher = Crypt::Mode::CBC->new($options{data}->{cipher}, 1);
          $plaintext = $cipher->decrypt(
              MIME::Base64::decode_base64($options{data}->{ciphertext}),
              $self->get_key(key => $self->{statefile_key}, cipher => $options{data}->{cipher}),
              pack('H*', $options{data}->{iv})
          );
      };
  
      if ($@) {
          return 0;
      }
  
      return $self->deserialize(data => $plaintext, nocipher => 1);
  }
  
  sub deserialize {
      my ($self, %options) = @_;
  
      my $deserialized = '';
      if ($self->{statefile_format} eq 'dumper') {
          our $datas;
          $self->{safe}->reval($options{data}, 1);
          return 0 if ($@);
  
          $deserialized = $datas;
      } elsif ($self->{statefile_format} eq 'storable') {
          eval {
              $deserialized = Storable::thaw($options{data});
          };
          return 0 if ($@);
      } elsif ($self->{statefile_format} eq 'json') {
          eval {
              $deserialized = JSON::XS->new->decode($options{data});
          };
          return 0 if ($@);
      }
  
      return 0 if (!defined($deserialized) || ref($deserialized) ne 'HASH');
  
      my $rv = 1;
      if ($self->{statefile_key} ne '' && !defined($options{nocipher})) {
          ($rv, $deserialized) = $self->decrypt(data => $deserialized);
      }
  
      return ($rv, $deserialized);
  }
  
  sub slurp {
      my ($self, %options) = @_;
  
      my $content = do {
          local $/ = undef;
          if (!open my $fh, '<', $options{file}) {
              $self->{output}->add_option_msg(short_msg => "Could not open file $options{file}: $!");
              $self->{output}->option_exit();
          }
          <$fh>;
      };
  
      return $content;
  }
  
  sub read {
      my ($self, %options) = @_;
      $self->{statefile_suffix} = defined($options{statefile_suffix}) ? $options{statefile_suffix} : $self->{statefile_suffix};
      $self->{statefile_dir} = defined($options{statefile_dir}) ? $options{statefile_dir} : $self->{statefile_dir};
      $self->{statefile} = defined($options{statefile}) ? $options{statefile} . $self->{statefile_suffix} : $self->{statefile};
      $self->{no_quit} = defined($options{no_quit}) && $options{no_quit} == 1 ? 1 : 0;
  
      my ($data, $rv);
  
      if (defined($self->{memcached})) {
          # if "SUCCESS" or "NOT FOUND" is ok. Other with use the file
          my $val = Memcached::libmemcached::memcached_get($self->{memcached}, $self->{statefile_dir} . '/' . $self->{statefile});
          if (defined($self->{memcached}->errstr) && $self->{memcached}->errstr =~ /^SUCCESS|NOT FOUND$/i) {
              $self->{memcached_ok} = 1;
              if (defined($val)) {
                  ($rv, $data) = $self->deserialize(data => $val);
                  $self->{datas} = defined($data) ? $data : {};
                  return $rv;
              }
  
              return 0;
          }
      }
  
      if (defined($self->{redis_cnx})) {
          my $val = $self->{redis_cnx}->get($self->{statefile_dir} . "/" . $self->{statefile});
          if (defined($val)) {
              ($rv, $data) = $self->deserialize(data => $val);
              $self->{datas} = defined($data) ? $data : {};
              return $rv;
          }
  
          return 0;
      }
  
      if (! -e $self->{statefile_dir} . '/' . $self->{statefile}) {
          if (! -w $self->{statefile_dir} || ! -x $self->{statefile_dir}) {
              $self->error(1);
              $self->{output}->add_option_msg(short_msg =>  "Cannot write statefile '" . $self->{statefile_dir} . "/" . $self->{statefile} . "'. Need write/exec permissions on directory.");
              if ($self->{no_quit} == 0) {
                  $self->{output}->option_exit();
              }
          }
          return 0;
      } elsif (! -w $self->{statefile_dir} . '/' . $self->{statefile}) {
          $self->error(1);
          $self->{output}->add_option_msg(short_msg => "Cannot write statefile '" . $self->{statefile_dir} . "/" . $self->{statefile} . "'. Need write permissions on file.");
          if ($self->{no_quit} == 0) {
              $self->{output}->option_exit();
          }
          return 1;
      } elsif (! -s $self->{statefile_dir} . '/' . $self->{statefile}) {
          # Empty file. Not a problem. Maybe plugin not manage not values
          return 0;
      }
  
      $data = $self->slurp(file => $self->{statefile_dir} . '/' . $self->{statefile});
      ($rv, $data) = $self->deserialize(data => $data);
      $self->{datas} = defined($data) ? $data : {};
  
      return $rv;
  }
  
  sub get_string_content {
      my ($self, %options) = @_;
  
      return Data::Dumper::Dumper($self->{datas});
  }
  
  sub get {
      my ($self, %options) = @_;
  
      if (defined($self->{datas}->{ $options{name} })) {
          return $self->{datas}->{ $options{name} };
      }
      return undef;
  }
  
  sub encrypt {
      my ($self, %options) = @_;
  
      my $data = {
          encrypted => 1,
          cipher => $self->{statefile_cipher},
          iv => Crypt::PRNG::random_bytes_hex(16)
      };
  
      eval {
          my $cipher = Crypt::Mode::CBC->new($self->{statefile_cipher}, 1);
          $data->{ciphertext} = MIME::Base64::encode_base64(
              $cipher->encrypt(
                  $options{data},
                  $self->get_key(key => $self->{statefile_key}, cipher => $self->{statefile_cipher}),
                  pack('H*', $data->{iv})
              ),
              ''
          );
      };
      if ($@) {
          $self->{output}->add_option_msg(short_msg => "cipher encrypt error: $@");
          $self->{output}->option_exit();
      }
  
      return $self->serialize(data => $data, nocipher => 1);
  }
  
  sub serialize {
      my ($self, %options) = @_;
  
      my $serialized = '';
      if ($self->{statefile_format} eq 'dumper') {
          $serialized = Data::Dumper->Dump([$options{data}], ['datas']);
      } elsif ($self->{statefile_format} eq 'storable') {
          $serialized = Storable::freeze($options{data});
      } elsif ($self->{statefile_format} eq 'json') {
          eval {
              $serialized = JSON::XS->new->encode($options{data});
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg =>  "Cannot serialize statefile '" . $self->{statefile_dir} . "/" . $self->{statefile} . "'");
              $self->{output}->option_exit();
          }
      }
  
      if ($self->{statefile_key} ne '' && !defined($options{nocipher})) {
          $serialized = $self->encrypt(data => $serialized);
      }
  
      return $serialized;
  }
  
  sub write {
      my ($self, %options) = @_;
  
      my $serialized = $self->serialize(data => $options{data});
      if ($self->{memcached_ok} == 1) {
          Memcached::libmemcached::memcached_set(
              $self->{memcached},
              $self->{statefile_dir} . '/' . $self->{statefile}, 
              $serialized,
              $self->{memexpiration}
          );
          if (defined($self->{memcached}->errstr) && $self->{memcached}->errstr =~ /^SUCCESS$/i) {
              return ;
          }
      }
      if (defined($self->{redis_cnx})) {
          return if (defined($self->{redis_cnx}->set(
              $self->{statefile_dir} . '/' . $self->{statefile},
              $serialized,
              'EX', $self->{memexpiration}))
          );
      }
      open FILE, '>', $self->{statefile_dir} . '/' . $self->{statefile};
      print FILE $serialized;
      close FILE;
  }
  
  1;
  
  
  =head1 NAME
  
  centreon::plugins::statefile - A module for managing state files with various storage backends.
  
  =head1 SYNOPSIS
  
      use centreon::plugins::statefile;
  
      my $statefile = centreon::plugins::statefile->new(
          output => $output,
          options => $options
      );
  
      $statefile->check_options(option_results => $option_results);
      $statefile->read(statefile => 'my_statefile');
      my $data = $statefile->get(name => 'some_key');
      $statefile->write(data => { some_key => 'some_value' });
  
  =head1 DESCRIPTION
  
  The `centreon::plugins::statefile` module provides methods to manage state files (files storing the data to keep from an
  execution to the next one), supporting various storage backends such as local files, Memcached, and Redis. It also supports encryption and different serialization formats.
  
  =head1 METHODS
  
  =head2 new
  
      my $statefile = centreon::plugins::statefile->new(%options);
  
  Creates a new `centreon::plugins::statefile` object.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<output> - An C<centreon::plugins::output> object to log error messages.
  
  =item * C<options> - A C<centreon::plugins::options> object to add command-line options.
  
  =back
  
  =back
  
  =head2 check_options
  
      $statefile->check_options(%options);
  
  Checks and processes the provided options.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<option_results> - A hash of option results.
  
  =back
  
  =back
  
  =head2 read
  
      $statefile->read(%options);
  
  Reads the state file.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<statefile> - The name of the state file to read.
  
  =item * C<statefile_suffix> - An optional suffix for the state file name.
  
  =item * C<statefile_dir> - An optional directory for the state file.
  
  =item * C<no_quit> - An optional flag to prevent the program from exiting on error.
  
  =back
  
  =back
  
  =head2 write
  
      $statefile->write(%options);
  
  Writes data to the state file.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<data> - A hash reference containing the data to write.
  
  =back
  
  =back
  
  =head2 get
  
      my $value = $statefile->get(%options);
  
  Retrieves a value from the state file data.
  
  =over 4
  
  =item * C<%options> - A hash of options. The following keys are supported:
  
  =over 8
  
  =item * C<name> - The key name of the value to retrieve.
  
  =back
  
  =back
  
  =head2 get_string_content
  
      my $string = $statefile->get_string_content();
  
  Returns the state file data as a string.
  
  =head2 error
  
      my $error = $statefile->error();
  
  Gets or sets the error state.
  
  =over 4
  
  =item * C<$error> - An optional error value to set.
  
  =back
  
  =head1 EXAMPLES
  
  =head2 Creating a Statefile Object
  
      use centreon::plugins::statefile;
  
      my $statefile = centreon::plugins::statefile->new(
          output => $output,
          options => $options
      );
  
  =head2 Checking Options
  
      $statefile->check_options(option_results => $option_results);
  
  =head2 Reading a Statefile
  
      $statefile->read(statefile => 'my_statefile');
  
  =head2 Writing to a Statefile
  
      $statefile->write(data => { some_key => 'some_value' });
  
  =head2 Retrieving a Value
  
      my $value = $statefile->get(name => 'some_key');
  
  =head2 Getting Statefile Data as a String
  
      my $string = $statefile->get_string_content();
  
  =head1 AUTHOR
  
  Centreon
  
  =head1 LICENSE
  
  Licensed under the Apache License, Version 2.0.
  
  =cut
  
  =head1 RETENTION OPTIONS
  
  =over 8
  
  =item B<--memcached>
  
  Memcached server to use (only one server).
  
  =item B<--redis-server>
  
  Redis server to use (only one server). Syntax: address[:port]
  
  =item B<--redis-attribute>
  
  Set Redis Options (--redis-attribute="cnx_timeout=5").
  
  =item B<--redis-db>
  
  Set Redis database index.
  
  =item B<--failback-file>
  
  Fall back on a local file if Redis connection fails.
  
  =item B<--memexpiration>
  
  Time to keep data in seconds (default: 86400).
  
  =item B<--statefile-dir>
  
  Define the cache directory (default: '/var/lib/centreon/centplugins').
  
  =item B<--statefile-suffix>
  
  Define a suffix to customize the statefile name (default: '').
  
  =item B<--statefile-concat-cwd>
  
  If used with the '--statefile-dir' option, the latter's value will be used as
  a sub-directory of the current working directory.
  Useful on Windows when the plugin is compiled, as the file system and permissions are different from Linux.
  
  =item B<--statefile-format>
  
  Define the format used to store the cache. Available formats: 'dumper', 'storable', 'json' (default).
  
  =item B<--statefile-key>
  
  Define the key to encrypt/decrypt the cache.
  
  =item B<--statefile-cipher>
  
  Define the cipher algorithm to encrypt the cache (default: 'AES').
  
  =back
  
  =head1 DESCRIPTION
  
  B<statefile>.
  
  =cut
CENTREON_PLUGINS_STATEFILE

$fatpacked{"centreon/plugins/templates/catalog_functions.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_TEMPLATES_CATALOG_FUNCTIONS';
  #
  # Copyright 2018 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::templates::catalog_functions;
  
  use strict;
  use warnings;
  use Exporter;
  
  our @ISA = qw(Exporter);
  our @EXPORT_OK = qw(catalog_status_threshold catalog_status_threshold_ng catalog_status_calc);
  
  sub catalog_status_threshold {
      my ($self, %options) = @_;
      my $status = 'ok';
  
      my $label = $self->{label};
      $label =~ s/-/_/g;
      if (defined($self->{instance_mode}->{option_results}->{'ok_' . $label}) && $self->{instance_mode}->{option_results}->{'ok_' . $label} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'ok_' . $label})) {
          $status = 'ok';
      } elsif (defined($self->{instance_mode}->{option_results}->{'critical_' . $label}) && $self->{instance_mode}->{option_results}->{'critical_' . $label} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'critical_' . $label})) {
          $status = 'critical';
      } elsif (defined($self->{instance_mode}->{option_results}->{'warning_' . $label}) && $self->{instance_mode}->{option_results}->{'warning_' . $label} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'warning_' . $label})) {
          $status = 'warning';
      } elsif (defined($self->{instance_mode}->{option_results}->{'unknown_' . $label}) && $self->{instance_mode}->{option_results}->{'unknown_' . $label} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'unknown_' . $label})) {
          $status = 'unknown';
      }
  
      return $status;
  }
  
  sub catalog_status_threshold_ng {
      my ($self, %options) = @_;
      my $status = 'ok';
      my $message;
  
      if (defined($self->{instance_mode}->{option_results}->{'critical-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'critical-' . $self->{label}} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'critical-' . $self->{label}})) {
          $status = 'critical';
      } elsif (defined($self->{instance_mode}->{option_results}->{'warning-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'warning-' . $self->{label}} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'warning-' . $self->{label}})) {
          $status = 'warning';
      } elsif (defined($self->{instance_mode}->{option_results}->{'unknown-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'unknown-' . $self->{label}} ne '' &&
          $self->eval(value => $self->{instance_mode}->{option_results}->{'unknown-' . $self->{label}})) {
          $status = 'unknown';
      }
  
      return $status;
  }
  
  sub catalog_status_calc {
      my ($self, %options) = @_;
  
      foreach (keys %{$options{new_datas}}) {
          if (/^\Q$self->{instance}\E_(.*)/) {
              $self->{result_values}->{$1} = $options{new_datas}->{$_};
          }
      }
  }
  
  1;
  
  
CENTREON_PLUGINS_TEMPLATES_CATALOG_FUNCTIONS

$fatpacked{"centreon/plugins/templates/counter.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_TEMPLATES_COUNTER';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  
  package centreon::plugins::templates::counter;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use centreon::plugins::values;
  use centreon::plugins::misc;
  use JSON::XS;
  
  my $sort_subs = {
      num => sub { $a <=> $b },
      cmp => sub { $a cmp $b },
  };
  
  sub set_counters {
      my ($self, %options) = @_;
      
      if (!defined($self->{maps_counters})) {
          $self->{maps_counters} = {};
      }
      
      $self->{maps_counters_type} = [];
      
      # 0 = mode total
      # 1 = mode instances
      #push @{$self->{maps_counters_type}}, { 
      #    name => 'global', type => 0, message_separator => ', ', cb_prefix_output => undef, cb_init => undef,
      #};
  
      #$self->{maps_counters}->{global} = [
      #    { label => 'client', set => {
      #           key_values => [ { name => 'client' } ],
      #           output_template => 'Current client connections : %s',
      #           perfdatas => [
      #               { label => 'Client', value => 'client', template => '%s', 
      #                 min => 0, unit => 'con' },
      #           ],
      #       }
      #    },
      #];
      
      # Example for instances
      #push @{$self->{maps_counters_type}}, { 
      #    name => 'cpu', type => 1, message_separator => ', ', cb_prefix_output => undef, cb_init => undef,
      #    message_multiple => 'All CPU usages are ok',
      #};    
  }
  
  sub get_callback {
      my ($self, %options) = @_;
  
      if (defined($options{method_name})) {
          return $self->can($options{method_name});
      }
      
      return undef;
  }
  
  sub call_object_callback {
      my ($self, %options) = @_;
      
      if (defined($options{method_name})) {
          my $method = $self->can($options{method_name});
          if ($method) {
              return $self->$method(%options);
          }
      }
      
      return undef;
  }
  
  sub get_threshold_prefix {
      my ($self, %options) = @_;
      
      my $prefix = '';
      END_LOOP: foreach (@{$self->{maps_counters_type}}) {
          if ($_->{name} eq $options{name}) {
              $prefix = 'instance-' if ($_->{type} == 1);
              last;
          }
          
          if ($_->{type} == 3) {
              foreach (@{$_->{group}}) {
                  if ($_->{name} eq $options{name}) {
                      $prefix = 'instance-' if ($_->{type} == 0);
                      $prefix = 'subinstance-' if ($_->{type} == 1);
                      last END_LOOP;
                  }
              }
          }
      }
  
      return $prefix;
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $self->{version} = '1.0';
      $options{options}->add_options(arguments => {
          'filter-counters-block:s' => { name => 'filter_counters_block' },
          'filter-counters:s'       => { name => 'filter_counters' },
          'display-ok-counters:s'   => { name => 'display_ok_counters' },
          'list-counters'           => { name => 'list_counters' }
      });
      $self->{statefile_value} = undef;
      if (defined($options{statefile}) && $options{statefile}) {
          centreon::plugins::misc::mymodule_load(
              output => $self->{output},
              module => 'centreon::plugins::statefile',
              error_msg => "Cannot load module 'centreon::plugins::statefile'."
          );
          $self->{statefile_value} = centreon::plugins::statefile->new(%options);
      }
  
      $self->{maps_counters} = {} if (!defined($self->{maps_counters}));
      $self->set_counters(%options);
      
      foreach my $key (keys %{$self->{maps_counters}}) {
          foreach (@{$self->{maps_counters}->{$key}}) {
              my $label = $_->{label};
              my $thlabel = $label;
              if ($self->{output}->use_new_perfdata() && defined($_->{nlabel})) {
                  $label = $_->{nlabel};
                  $thlabel = $self->get_threshold_prefix(name => $key) . $label;
              }
              $thlabel =~ s/\./-/g;
  
              if (!defined($_->{threshold}) || $_->{threshold} != 0) {
                  $options{options}->add_options(arguments => {
                      'unknown-' . $thlabel . ':s'  => { name => 'unknown-' . $thlabel, default => $_->{unknown_default} },
                      'warning-' . $thlabel . ':s'  => { name => 'warning-' . $thlabel, default => $_->{warning_default} },
                      'critical-' . $thlabel . ':s' => { name => 'critical-' . $thlabel, default => $_->{critical_default} }
                  });
  
                  if (defined($_->{nlabel})) {
                      $options{options}->add_options(arguments => {
                          'unknown-' . $_->{label} . ':s'  => { name => 'unknown-' . $_->{label}, redirect => 'unknown-' . $thlabel },
                          'warning-' . $_->{label} . ':s'  => { name => 'warning-' . $_->{label}, redirect => 'warning-' . $thlabel },
                          'critical-' . $_->{label} . ':s' => { name => 'critical-' . $_->{label}, redirect => 'critical-' . $thlabel }
                      });
                  }
              }
  
              $_->{obj} = centreon::plugins::values->new(
                  statefile => $self->{statefile_value},
                  output => $self->{output}, perfdata => $self->{perfdata},
                  label => $_->{label}, nlabel => $_->{nlabel}, thlabel => $thlabel
              );
              $_->{obj}->set(%{$_->{set}});
          }
      }
  
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
      
      if (defined($self->{option_results}->{list_counters})) {
          my $list_counter = '';
          my $th_counter = '';
          my $counters;
          foreach my $key (keys %{$self->{maps_counters}}) {
              foreach (@{$self->{maps_counters}->{$key}}) {
                  $counters->{metrics}->{$_->{label}}->{nlabel} ="";
                  $counters->{metrics}->{$_->{label}}->{min}="";
                  $counters->{metrics}->{$_->{label}}->{max}="";
                  $counters->{metrics}->{$_->{label}}->{unit}="";
                  $counters->{metrics}->{$_->{label}}->{output_template}="";
                  if(defined($_->{nlabel})) {
                      $counters->{metrics}->{$_->{label}}->{nlabel} = $_->{nlabel};
                  }
                  if(defined($_->{set}->{perfdatas}->[0]->{min})) {
                      $counters->{metrics}->{$_->{label}}->{min} = $_->{set}->{perfdatas}->[0]->{min};
                  }
                  if(defined($_->{set}->{perfdatas}->[0]->{max})) {
                      $counters->{metrics}->{$_->{label}}->{max} = $_->{set}->{perfdatas}->[0]->{max};
                  }
                  if(defined($_->{set}->{perfdatas}->[0]->{unit})) {
                      $counters->{metrics}->{$_->{label}}->{unit} = $_->{set}->{perfdatas}->[0]->{unit};
                  }
                  if(defined($_->{set}->{perfdatas}->[0]->{template})) {
                      $counters->{metrics}->{$_->{label}}->{output_template} = $_->{set}->{perfdatas}->[0]->{template};
                  }
                  my $label = $_->{label};
                  $label =~ s/-//g;
                  $list_counter .= $_->{label}." ";
                  $th_counter .= " --warning-$_->{label}='\$_SERVICEWARNING" . uc($label) . "\$' --critical-$_->{label}='\$_SERVICECRITICAL" . uc($label) . "\$'";
  
              }
          }
          $counters->{"counter list"}=$list_counter;
          $counters->{"pack configuration"}=$th_counter." \$_SERVICEEXTRAOPTIONS\$";
  
          my $result_data ="";
          eval {
              $result_data = JSON::XS->new->indent->space_after->canonical->utf8->encode($counters);
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg => "Cannot use \$counters as it is a malformed JSON: " . $@);
              $self->{output}->option_exit();
          }
  
          $self->{output}->output_add(short_msg => "counter list: ".$list_counter);
          $self->{output}->output_add(long_msg => $result_data);
          $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1);
          $self->{output}->exit();
      }
  
      my $change_macros_opt = [];
      foreach my $key (keys %{$self->{maps_counters}}) {
          foreach (@{$self->{maps_counters}->{$key}}) {
              push @$change_macros_opt, 'unknown-' . $_->{label}, 'warning-' . $_->{label}, 'critical-' . $_->{label}
                  if (defined($_->{type}) && $_->{type} == 2);
              $_->{obj}->{instance_mode} = $self;
              $_->{obj}->init(option_results => $self->{option_results}) if (!defined($_->{type}) || $_->{type} != 2);
          }
      }
  
      $self->change_macros(macros => $change_macros_opt) if (scalar(@$change_macros_opt) > 0);
  
      if (defined($self->{statefile_value})) {
          $self->{statefile_value}->check_options(%options);
      }
  }
  
  sub run_global {
      my ($self, %options) = @_;
  
      return undef if (defined($self->{option_results}->{filter_counters_block}) && $self->{option_results}->{filter_counters_block} ne '' 
          && $options{config}->{name} =~ /$self->{option_results}->{filter_counters_block}/);
      return undef if (defined($options{config}->{cb_init}) && $self->call_object_callback(method_name => $options{config}->{cb_init}) == 1);
      my $resume = defined($options{resume}) && $options{resume} == 1 ? 1 : 0;
      my $display_short = (!defined($options{config}->{display_short}) || $options{config}->{display_short} != 0) ? 1 : 0;
      # Can be set when it comes from type 3 counters
      my $called_multiple = defined($options{called_multiple}) && $options{called_multiple} == 1 ? 1 : 0;
      my $multiple_parent = defined($options{multiple_parent}) && $options{multiple_parent} == 1 ? 1 : 0;
      my $force_instance = defined($options{force_instance}) ? $options{force_instance} : undef;
  
      my $message_separator = defined($options{config}->{message_separator}) ? 
          $options{config}->{message_separator}: ', ';
      my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
      my @exits;
      foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
          my $obj = $_->{obj};
  
          next if (defined($self->{option_results}->{filter_counters}) && $self->{option_results}->{filter_counters} ne '' &&
              $_->{label} !~ /$self->{option_results}->{filter_counters}/);
  
          $obj->set(instance => defined($force_instance) ? $force_instance : $options{config}->{name});
  
          my ($value_check) = $obj->execute(new_datas => $self->{new_datas}, values => $self->{$options{config}->{name}});
  
          next if (defined($options{config}->{skipped_code}) && defined($options{config}->{skipped_code}->{$value_check}));
          if ($value_check != 0) {
              $long_msg .= $long_msg_append . $obj->output_error();
              $long_msg_append = $message_separator;
              next;
          }
          my $exit2 = $obj->threshold_check();
          push @exits, $exit2;
  
          my $output = $obj->output();
          if (!defined($_->{display_ok}) || $_->{display_ok} != 0 ||
              (defined($self->{option_results}->{display_ok_counters}) && $self->{option_results}->{display_ok_counters} ne '' &&
               $_->{label} =~ /$self->{option_results}->{display_ok_counters}/)) {
              $long_msg .= $long_msg_append . $output;
              $long_msg_append = $message_separator;
          }
  
          if (!$self->{output}->is_status(litteral => 1, value => $exit2, compare => 'ok')) {
              $short_msg .= $short_msg_append . $output;
              $short_msg_append = $message_separator;
          }
  
          $obj->perfdata(extra_instance => $multiple_parent);
      }
  
      my ($prefix_output, $suffix_output);
      $prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance_value => $self->{$options{config}->{name}}) 
          if (defined($options{config}->{cb_prefix_output}));
      $prefix_output = '' if (!defined($prefix_output));
  
      $suffix_output = $self->call_object_callback(method_name => $options{config}->{cb_suffix_output}, instance_value => $self->{$options{config}->{name}}) 
          if (defined($options{config}->{cb_suffix_output}));
      $suffix_output = '' if (!defined($suffix_output));
  
      if ($called_multiple == 1 && $long_msg ne '') {
          $self->{output}->output_add(long_msg => $options{indent_long_output} . $prefix_output. $long_msg . $suffix_output);
      }
  
      my $exit = $self->{output}->get_most_critical(status => [ @exits ]);
      if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
          if ($called_multiple == 0) {
              $self->{output}->output_add(
                  severity => $exit,
                  short_msg => $prefix_output . $short_msg . $suffix_output
              );
          } else {
              $self->run_multiple_prefix_output(
                  severity => $exit,
                  short_msg => $prefix_output . $short_msg . $suffix_output
              );
          }
      } else {
          if ($long_msg ne '' && $multiple_parent == 0) {
              if ($called_multiple == 0) {
                  $self->{output}->output_add(short_msg => $prefix_output . $long_msg . $suffix_output)
                      if ($display_short == 1);
              } else {
                  $self->run_multiple_prefix_output(
                      severity => 'ok',
                      short_msg => $prefix_output . $long_msg . $suffix_output
                  ) if ($display_short == 1);
              }
          }
      }
  }
  
  sub run_instances {
      my ($self, %options) = @_;
  
      return undef if (defined($self->{option_results}->{filter_counters_block}) && $self->{option_results}->{filter_counters_block} ne '' 
          && $options{config}->{name} =~ /$self->{option_results}->{filter_counters_block}/);
      return undef if (defined($options{config}->{cb_init}) && $self->call_object_callback(method_name => $options{config}->{cb_init}) == 1);
      my $cb_init_counters = $self->get_callback(method_name => $options{config}->{cb_init_counters});
      my $display_status_lo = defined($options{display_status_long_output}) && $options{display_status_long_output} == 1 ? 1 : 0;
      my $display_short = (!defined($options{config}->{display_short}) || $options{config}->{display_short} != 0) ? 1 : 0;
      my $display_long = (!defined($options{config}->{display_long}) || $options{config}->{display_long} != 0) ? 1 : 0;
      my $resume = defined($options{resume}) && $options{resume} == 1 ? 1 : 0;
      my $no_message_multiple = 1;
      
      $self->{lproblems} = 0;
      $self->{multiple} = 1;
      if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
          $self->{multiple} = 0;
      }
      
      my $message_separator = defined($options{config}->{message_separator}) ? 
          $options{config}->{message_separator}: ', ';
  
      # The default sort method is cmp (string comparison)
      my $sort_method = 'cmp';
      # If configured otherwise, we take it from the counter (only other method is 'num' for '<=>')
      $sort_method = $options{config}->{sort_method}
          if (defined($options{config}->{sort_method}));
  
      # In the absence of sort_attribute the sort method is set now
      my $sort_sub = $sort_subs->{$sort_method};
  
      # If sort_attribute is set, then we'll redefine how things are sorted depending on the specified sort_method
      if (defined($options{config}->{sort_attribute})) {
          my $sort_attribute = $options{config}->{sort_attribute};
          if ($sort_method eq 'cmp') {
              $sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} cmp $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
          } else {
              $sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} <=> $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
          }
      }
  
      # Now the loop begins with the desired sorting method
      foreach my $id (sort { $sort_sub->() } keys %{$self->{$options{config}->{name}}}) {
          my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
          my @exits = ();
          foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
              my $obj = $_->{obj};
  
              next if (defined($self->{option_results}->{filter_counters}) && $self->{option_results}->{filter_counters} ne '' &&
                  $_->{label} !~ /$self->{option_results}->{filter_counters}/);
              next if ($cb_init_counters && $self->$cb_init_counters(%$_) == 1);
  
              $no_message_multiple = 0;
              $obj->set(instance => $id);
          
              my ($value_check) = $obj->execute(
                  new_datas => $self->{new_datas},
                  values => $self->{$options{config}->{name}}->{$id}
              );
              next if (defined($options{config}->{skipped_code}) && defined($options{config}->{skipped_code}->{$value_check}));
              if ($value_check != 0) {
                  $long_msg .= $long_msg_append . $obj->output_error();
                  $long_msg_append = $message_separator;
                  next;
              }
              my $exit2 = $obj->threshold_check();
              push @exits, $exit2;
  
              my $output = $obj->output();
              if (!defined($_->{display_ok}) || $_->{display_ok} != 0 ||
                  (defined($self->{option_results}->{display_ok_counters}) && $self->{option_results}->{display_ok_counters} ne '' &&
                   $_->{label} =~ /$self->{option_results}->{display_ok_counters}/)) {
                  $long_msg .= $long_msg_append . $output;
                  $long_msg_append = $message_separator;
              }
              
              if (!$self->{output}->is_status(litteral => 1, value => $exit2, compare => 'ok')) {
                  $self->{lproblems}++;
                  $short_msg .= $short_msg_append . $output;
                  $short_msg_append = $message_separator;
              }
              
              $obj->perfdata(extra_instance => $self->{multiple});
          }
  
          my ($prefix_output, $suffix_output);
          $prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $id, instance_value => $self->{$options{config}->{name}}->{$id})
              if (defined($options{config}->{cb_prefix_output}));
          $prefix_output = '' if (!defined($prefix_output));
          
          $suffix_output = $self->call_object_callback(method_name => $options{config}->{cb_suffix_output}) 
          if (defined($options{config}->{cb_suffix_output}));
          $suffix_output = '' if (!defined($suffix_output));
  
          my $exit = $self->{output}->get_most_critical(status => [ @exits ]);
          # in mode grouped, we don't display 'ok'
          my $debug = 0;
          $debug = 1 if ($display_status_lo == 1 && $self->{output}->is_status(value => $exit, compare => 'OK', litteral => 1));
          if (scalar @{$self->{maps_counters}->{$options{config}->{name}}} > 0 && $long_msg ne '') {
              $self->{output}->output_add(long_msg => ($display_status_lo == 1 ? lc($exit) . ': ' : '') . $prefix_output . $long_msg . $suffix_output, debug => $debug)
                  if ($display_long == 1);
          }
          if ($resume == 1) {
              $self->{most_critical_instance} = $self->{output}->get_most_critical(status => [ $self->{most_critical_instance},  $exit ]);  
              next;
          }
          
          if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
              $self->{output}->output_add(
                  severity => $exit,
                  short_msg => $prefix_output . $short_msg . $suffix_output
              );
          }
          
          if ($self->{multiple} == 0)  {
              $self->{output}->output_add(short_msg => $prefix_output . $long_msg . $suffix_output)
                  if ($display_short == 1);
          }
      }
      
      if ($no_message_multiple == 0 && $self->{multiple} == 1 && $resume == 0) {
          $self->{output}->output_add(short_msg => $options{config}->{message_multiple})
              if ($display_short == 1);
      }
  }
  
  sub run_group {
      my ($self, %options) = @_;
  
      my $multiple = 1;
      return if (scalar(keys %{$self->{$options{config}->{name}}}) <= 0);
      if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
          $multiple = 0;
      }
      
      if ($multiple == 1) {
          $self->{output}->output_add(
              severity => 'OK',
              short_msg => $options{config}->{message_multiple}
          );
      }
  
      my $format_output = defined($options{config}->{format_output}) ? $options{config}->{format_output} : '%s problem(s) detected';
  
      my ($global_exit, $total_problems) = ([], 0);
      foreach my $id (sort keys %{$self->{$options{config}->{name}}}) {
          $self->{most_critical_instance} = 'ok';
          if (defined($options{config}->{cb_long_output})) {
              $self->{output}->output_add(
                  long_msg => $self->call_object_callback(
                      method_name => $options{config}->{cb_long_output},
                      instance => $id,
                      instance_value => $self->{$options{config}->{name}}->{$id}
                  )
              );
          }
  
          foreach my $group (@{$options{config}->{group}}) {
              $self->{$group->{name}} = $self->{$options{config}->{name}}->{$id}->{$group->{name}};
              
              # we resume datas
              $self->run_instances(config => $group, display_status_long_output => 1, resume => 1);
              
              push @{$global_exit}, $self->{most_critical_instance};
              $total_problems += $self->{lproblems};
              
              my $prefix_output;
              $prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $id, instance_value => $self->{$options{config}->{name}}->{$id})
                  if (defined($options{config}->{cb_prefix_output}));
              $prefix_output = '' if (!defined($prefix_output));
              
              if ($multiple == 0 && (!defined($group->{display}) || $group->{display} != 0)) {
                  $self->{output}->output_add(
                      severity => $self->{most_critical_instance},
                      short_msg => sprintf("${prefix_output}" . $format_output, $self->{lproblems})
                  );
              }
          }
      }
      
      if ($multiple == 1) {
          my $exit = $self->{output}->get_most_critical(status => [ @{$global_exit} ]);
          if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
              $self->{output}->output_add(
                  severity => $exit,
                  short_msg => sprintf($format_output, $total_problems)
              );
          }
      }
      
      if (defined($options{config}->{display_counter_problem})) {
          $self->{output}->perfdata_add(
              label => $options{config}->{display_counter_problem}->{label},
              nlabel => $options{config}->{display_counter_problem}->{nlabel},
              unit => $options{config}->{display_counter_problem}->{unit},
              value => $total_problems,
              min => $options{config}->{display_counter_problem}->{min}, max => $options{config}->{display_counter_problem}->{max}
          );
      }
  }
  
  sub run_multiple_instances {
      my ($self, %options) = @_;
  
      return undef if (defined($self->{option_results}->{filter_counters_block}) && $self->{option_results}->{filter_counters_block} ne '' 
          && $options{config}->{name} =~ /$self->{option_results}->{filter_counters_block}/);
      return undef if (defined($options{config}->{cb_init}) && $self->call_object_callback(method_name => $options{config}->{cb_init}) == 1);
      my $use_new_perfdata = $self->{output}->use_new_perfdata();
      my $multiple_parent = defined($options{multiple_parent}) && $options{multiple_parent} == 1 ? $options{multiple_parent} : 0;
      my $indent_long_output = defined($options{indent_long_output}) ? $options{indent_long_output} : '';
      my $no_message_multiple = 1;
      my $display_long = (!defined($options{config}->{display_long}) || $options{config}->{display_long} != 0) ? 1 : 0;
      my $display_short = (!defined($options{config}->{display_short}) || $options{config}->{display_short} != 0) ? 1 : 0;
  
      my $multiple = 1;
      if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
          $multiple = 0;
      }
  
      my $message_separator = defined($options{config}->{message_separator}) ? 
          $options{config}->{message_separator} : ', ';
  
      # The default sort method is cmp (string comparison)
      my $sort_method = 'cmp';
      # If configured otherwise, we take it from the counter (only other method is 'num' for '<=>')
      $sort_method = $options{config}->{sort_method}
          if (defined($options{config}->{sort_method}));
  
      # In the absence of sort_attribute the sort method is set now
      my $sort_sub = $sort_subs->{$sort_method};
  
      # If sort_attribute is set, then we'll redefine how things are sorted depending on the specified sort_method
      if (defined($options{config}->{sort_attribute})) {
          my $sort_attribute = $options{config}->{sort_attribute};
          if ($sort_method eq 'cmp') {
              $sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} cmp $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
          } else {
              $sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} <=> $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
          }
      }
  
      # Now the loop begins with the desired sorting method
      foreach my $id (sort { $sort_sub->() } keys %{$self->{$options{config}->{name}}}) {
          my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
          my @exits = ();
          foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
              my $obj = $_->{obj};
              
              next if (defined($self->{option_results}->{filter_counters}) && $self->{option_results}->{filter_counters} ne '' &&
                  $_->{label} !~ /$self->{option_results}->{filter_counters}/);
              
              my $instance = $id;
              if ($use_new_perfdata || ($multiple_parent == 1 && $multiple == 1)) {
                  $instance = $options{instance_parent} . ($self->{output}->get_instance_perfdata_separator()) . $id;
              } elsif ($multiple_parent == 1 && $multiple == 0) {
                  $instance = $options{instance_parent};
              }
              
              $no_message_multiple = 0;
              $obj->set(instance => $instance);
          
              my ($value_check) = $obj->execute(
                  new_datas => $self->{new_datas},
                  values => $self->{$options{config}->{name}}->{$id}
              );
              next if (defined($options{config}->{skipped_code}) && defined($options{config}->{skipped_code}->{$value_check}));
              if ($value_check != 0) {
                  $long_msg .= $long_msg_append . $obj->output_error();
                  $long_msg_append = $message_separator;
                  next;
              }
              my $exit2 = $obj->threshold_check();
              push @exits, $exit2;
  
              my $output = $obj->output();
              if (!defined($_->{display_ok}) || $_->{display_ok} != 0 ||
                  (defined($self->{option_results}->{display_ok_counters}) && $self->{option_results}->{display_ok_counters} ne '' &&
                   $_->{label} =~ /$self->{option_results}->{display_ok_counters}/)) {
                  $long_msg .= $long_msg_append . $output;
                  $long_msg_append = $message_separator;
              }
  
              if (!$self->{output}->is_status(litteral => 1, value => $exit2, compare => 'ok')) {
                  $short_msg .= $short_msg_append . $output;
                  $short_msg_append = $message_separator;
              }
              
              if ($multiple_parent == 1 && $multiple == 0) {
                  $obj->perfdata(extra_instance => 1);
              } else {
                  $obj->perfdata(extra_instance => $multiple);
              }
          }
  
          my ($prefix_output, $suffix_output);
          $prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $id, instance_value => $self->{$options{config}->{name}}->{$id})
              if (defined($options{config}->{cb_prefix_output}));
          $prefix_output = '' if (!defined($prefix_output));
  
          $suffix_output = $self->call_object_callback(method_name => $options{config}->{cb_suffix_output}) 
          if (defined($options{config}->{cb_suffix_output}));
          $suffix_output = '' if (!defined($suffix_output));
  
          my $exit = $self->{output}->get_most_critical(status => [ @exits ]);
          if (scalar @{$self->{maps_counters}->{$options{config}->{name}}} > 0 && $long_msg ne '') {
              $self->{output}->output_add(long_msg => $indent_long_output . $prefix_output . $long_msg . $suffix_output)
                  if ($display_long == 1);
          }
          
          if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
              $self->run_multiple_prefix_output(
                  severity => $exit,
                  short_msg => $prefix_output . $short_msg . $suffix_output
              );
          }
  
          if ($multiple == 0 && $multiple_parent == 0) {
              $self->run_multiple_prefix_output(severity => 'ok', short_msg => $prefix_output . $long_msg . $suffix_output)
                  if ($display_short == 1);
          }
      }
  
      if ($no_message_multiple == 0 && $multiple == 1 && $multiple_parent == 0) {
          $self->run_multiple_prefix_output(severity => 'ok', short_msg => $options{config}->{message_multiple})
              if ($display_short == 1);
      }
  }
  
  sub run_multiple_prefix_output {
      my ($self, %options) = @_;
      
      my %separator;
      if ($self->{prefix_multiple_output_done}->{lc($options{severity})} == 0) {
          $self->{output}->output_add(severity => $options{severity}, short_msg => $self->{prefix_multiple_output});
          $self->{prefix_multiple_output_done}->{lc($options{severity})} = 1;
          $separator{separator} = '';
      }
  
      $self->{output}->output_add(severity => $options{severity}, short_msg => $options{short_msg}, %separator);
  }
  
  sub run_multiple {
      my ($self, %options) = @_;
  
      my $multiple = 1;
      if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
          $multiple = 0;
      }
  
      if ($multiple == 1) {
          $self->{output}->output_add(
              severity => 'OK',
              short_msg => $options{config}->{message_multiple}
          );
      }
  
      foreach my $instance (sort keys %{$self->{$options{config}->{name}}}) {
          if (defined($options{config}->{cb_long_output})) {
              $self->{output}->output_add(
                  long_msg => $self->call_object_callback(
                      method_name => $options{config}->{cb_long_output},
                      instance => $instance,
                      instance_value => $self->{$options{config}->{name}}->{$instance}
                  )
              );
          }
  
          $self->{prefix_multiple_output} = '';
          $self->{prefix_multiple_output_done} = { ok => 0, warning => 0, critical => 0, unknown => 0 };
          $self->{prefix_multiple_output} = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $instance, instance_value => $self->{$options{config}->{name}}->{$instance})
               if (defined($options{config}->{cb_prefix_output}));
          my $indent_long_output = '';
          $indent_long_output = $options{config}->{indent_long_output}
              if (defined($options{config}->{indent_long_output}));
  
          foreach my $group (@{$options{config}->{group}}) {
              next if (!defined($self->{$options{config}->{name}}->{$instance}->{$group->{name}}));
              $self->{$group->{name}} = $self->{$options{config}->{name}}->{$instance}->{$group->{name}};
  
              if ($group->{type} == 1) {
                  $self->run_multiple_instances(config => $group, multiple_parent => $multiple, instance_parent => $instance, indent_long_output => $indent_long_output);
              } elsif ($group->{type} == 0) {
                  $self->run_global(
                      config => $group,
                      multiple_parent => $multiple,
                      called_multiple => 1,
                      force_instance => $instance,
                      indent_long_output => $indent_long_output
                  );
              }
          }
      }
  }
  
  sub read_statefile_key {
      my ($self, %options) = @_;
  
      $self->{statefile_value}->read(statefile => $self->{cache_name});
      return $self->{statefile_value}->get(name => $options{key});
  }
  
  sub set_timestamp {
      my ($self, %options) = @_;
  
      $self->{override_timestamp} = $options{timestamp};
  }
  
  sub run {
      my ($self, %options) = @_;
      
      $self->manage_selection(%options);
      
      $self->{new_datas} = undef;
      if (defined($self->{statefile_value})) {
          $self->{new_datas} = {};
          $self->{statefile_value}->read(statefile => $self->{cache_name}) if (defined($self->{cache_name}));
          $self->{new_datas}->{last_timestamp} = defined($self->{override_timestamp}) ? $self->{override_timestamp} : time();
      }
  
      foreach my $entry (@{$self->{maps_counters_type}}) {
          if ($entry->{type} == 0) {
              $self->run_global(config => $entry);
          } elsif ($entry->{type} == 1) {
              $self->run_instances(config => $entry);
          } elsif ($entry->{type} == 2) {
              $self->run_group(config => $entry);
          } elsif ($entry->{type} == 3) {
              $self->run_multiple(config => $entry);
          }
      }
  
      if (defined($self->{statefile_value})) {
          $self->{statefile_value}->write(data => $self->{new_datas});
      }
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      # example for snmp
      #use Digest::MD5 qw(md5_hex);
      #$self->{cache_name} = "choose_name_" . $options{snmp}->get_hostname()  . '_' . $options{snmp}->get_port() . '_' . $self->{mode} . '_' . 
      #    (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
  }
  
  sub compat_threshold_counter {
      my ($self, %options) = @_;
  
      foreach ('warning', 'critical') {
          foreach my $th (@{$options{compat}->{th}}) {
              next if (!defined($options{option_results}->{$_ . '-' . $th->[0]}) || $options{option_results}->{$_ . '-' . $th->[0]} eq '');
  
              my $src_threshold = $options{option_results}->{$_ . '-' . $th->[0]};
              if (defined($options{compat}->{units}) && $options{compat}->{units} eq '%') {
                  $options{option_results}->{$_ . '-' . $th->[0]} = undef;
                  if (defined($options{compat}->{free})) {
                      $options{option_results}->{$_ . '-' . $th->[0]} = undef;
                      my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => $src_threshold);
                      next if ($status == 0);
  
                      my $tmp = { arobase => $result->{arobase}, infinite_pos => 0, infinite_neg => 0, start => $result->{start}, end => $result->{end} };
                      $tmp->{infinite_neg} = 1 if ($result->{infinite_pos} == 1);
                      $tmp->{infinite_pos} = 1 if ($result->{infinite_neg} == 1);
  
                      if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
                          $tmp->{end} = 100 - $result->{start};
                      }
                      if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
                          $tmp->{start} = 100 - $result->{end};
                      }
  
                      $options{option_results}->{$_ . '-' . $th->[1]->{prct}} = centreon::plugins::misc::get_threshold_litteral(%$tmp);
                  } else {
                      $options{option_results}->{$_ . '-' . $th->[1]->{prct}} = $src_threshold;
                  }
              } elsif (defined($options{compat}->{free})) {
                  $options{option_results}->{$_ . '-' . $th->[1]->{free}} = $options{option_results}->{$_ . '-' . $th->[0]};
                  $options{option_results}->{$_ . '-' . $th->[0]} = undef;
              }
          }
      }
  }
  
  sub change_macros {
      my ($self, %options) = @_;
  
      foreach (@{$options{macros}}) {
          if (defined($self->{option_results}->{$_}) && $self->{option_results}->{$_} ne '') {
              $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$values->{$1}/g;
          }
      }
  }
  
  sub custom_perfdata_instances {
      my ($self, %options) = @_;
  
      my $instances = [];
      foreach (split(/\s+/, $options{instances})) {
          while (/%\((.+?)\)/g) {
              my $name = $1;
              if (!defined($options{labels}->{$name})) {
                  $self->{output}->add_option_msg(short_msg => "option $options{option_name} unsupported label: %($name)");
                  $self->{output}->option_exit();
              }
  
              push @$instances, $name;
          }
      }
  
      if (scalar(@$instances) <= 0) {
          $self->{output}->add_option_msg(short_msg => "option $options{option_name} need at least one label");
          $self->{output}->option_exit();
      }
  
      return $instances;
  }
  
  1;
  
  
  =head1 MODE
  
  Default template for counters. Should be extended.
  
  =over 8
  
  =item B<--filter-counters>
  
  Only display some counters (regexp can be used).
  Example to check SSL connections only : --filter-counters='^xxxx|yyyy$'
  
  =item B<--warning-*>
  
  Warning threshold.
  Can be: 'xxx', 'xxx'.
  
  =item B<--critical-*>
  
  Critical threshold.
  Can be: 'xxx', 'xxx'.
  
  =back
  
  =cut
CENTREON_PLUGINS_TEMPLATES_COUNTER

$fatpacked{"centreon/plugins/templates/hardware.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_TEMPLATES_HARDWARE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::templates::hardware;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub set_system {
      my ($self, %options) = @_;
  
      #$self->{regexp_threshold_numeric_check_section_option} = '';
      #$self->{cb_threshold_numeric_check_section_option} = 'callbackname';
  
      # Some callbacks 
      #$self->{cb_hook1} = 'callbackname'; # before the loads
      #$self->{cb_hook2} = 'callbackname'; # between loads and requests
      #$self->{cb_hook3} = 'callbackname'; # after requests
      #$self->{cb_hook4} = 'callbackname'; # after output
  
      # Example for threshold:
      #$self->{thresholds} = {
      #    fan => [
      #        ['bad', 'CRITICAL'],
      #        ['good', 'OK'],
      #        ['notPresent', 'OK'],
      #    ],
      #};
      
      # Unset the call to load components
      #$self->{components_exec_load} = 0;
  
      # Set the path_info
      #$self->{components_path} = 'network::xxxx::mode::components';
  
      # Set the components
      #$self->{components_module} = ['cpu', 'memory', ...];
  }
  
  sub call_object_callback {
      my ($self, %options) = @_;
  
      if (defined($options{method_name})) {
          my $method = $self->can($options{method_name});
          if ($method) {
              return $self->$method(%options);
          }
      }
  
      return undef;
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $self->{version} = '1.0';
      $options{options}->add_options(arguments => {
          'component:s'            => { name => 'component', default => '.*' },
          'no-component:s'         => { name => 'no_component', default => 'critical' },
          'threshold-overload:s@'  => { name => 'threshold_overload' },
          'add-name-instance'      => { name => 'add_name_instance' },
          'no-component-count'     => { name => 'no_component_count' }
      });
  
      $self->{performance} = (defined($options{no_performance}) && $options{no_performance} == 1) ?
          0 : 1;
      if ($self->{performance} == 1) {
          $options{options}->add_options(arguments => {
              'warning:s@'  => { name => 'warning' },
              'critical:s@' => { name => 'critical' }
          });
      }
  
      $self->{filter_exclude} = (defined($options{no_filter_exclude}) && $options{no_filter_exclude} == 1) ?
          0 : 1;
      if ($self->{filter_exclude} == 1) {
          $options{options}->add_options(arguments => {
              'exclude:s'     => { name => 'exclude' },
              'filter:s@'     => { name => 'filter' }
          });
      }
      $self->{absent} = (defined($options{no_absent}) && $options{no_absent} == 1) ?
          0 : 1;
      if ($self->{absent} == 1) {
          $options{options}->add_options(arguments => {
              'absent-problem:s@' => { name => 'absent_problem' }
          });
      }
  
      $self->{load_components} = (defined($options{no_load_components}) && $options{no_load_components} == 1) ?
          0 : 1;
      $self->{components} = {};
      $self->{no_components} = undef;
  
      $self->{components_module} = [];
      $self->{components_exec_load} = 1;
      $self->set_system();
  
      $self->{count} = (defined($options{no_count}) && $options{no_count} == 1) ? 0 : 1;
      if ($self->{count} == 1) {
          foreach my $component (@{$self->{components_module}}) {
              $options{options}->add_options(arguments => {
                  'unknown-count-' . $component . ':s'  => { name => 'unknown_count_' . $component },
                  'warning-count-' . $component . ':s'  => { name => 'warning_count_' . $component },
                  'critical-count-' . $component . ':s' => { name => 'critical_count_' . $component }
              });
          }
      }
  
      $self->{request} = [];
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      # For compatibility both $self->{option_results}->{no_component} and $self->{no_components} are initialized.
      # If unset or empty the default value 'critical' is used.
      $self->{option_results}->{no_component} = $self->{no_components} = $self->{option_results}->{no_component} || 'critical';
  
      if ($self->{filter_exclude} == 1) {
          $self->{filter} = [];
          foreach my $val (@{$self->{option_results}->{filter}}) {
              next if (!defined($val) || $val eq '');
              my @values = split (/,/, $val);
              push @{$self->{filter}}, { filter => $values[0], instance => $values[1] }; 
          }
      }
  
      if ($self->{absent} == 1) {
          $self->{absent_problem} = [];
          foreach my $val (@{$self->{option_results}->{absent_problem}}) {
              next if (!defined($val) || $val eq '');
              my @values = split (/,/, $val);
              push @{$self->{absent_problem}}, { filter => $values[0], instance => $values[1] }; 
          }
      }
  
      $self->{overload_th} = [];
      foreach my $val (@{$self->{option_results}->{threshold_overload}}) {
          next if (!defined($val) || $val eq '');
          my @values = split (/,/, $val);
          if (scalar(@values) < 3) {
              $self->{output}->add_option_msg(short_msg => "Wrong threshold-overload option '" . $val . "'.");
              $self->{output}->option_exit();
          }
          my ($section, $instance, $status, $filter);
          if (scalar(@values) == 3) {
              ($section, $status, $filter) = @values;
              $instance = '.*';
          } else {
               ($section, $instance, $status, $filter) = @values;
          }
  
          if ($self->{output}->is_litteral_status(status => $status) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong threshold-overload status '" . $val . "'.");
              $self->{output}->option_exit();
          }
          push @{$self->{overload_th}}, { section => $section, filter => $filter, status => $status, instance => $instance };
      }
  
      if ($self->{performance} == 1) {
          $self->{numeric_threshold} = {};
          foreach my $option (('warning', 'critical')) {
              foreach my $val (@{$self->{option_results}->{$option}}) {
                  next if (!defined($val) || $val eq '');
                  if ($val !~ /^(.*?),(.*?),(.*)$/) {
                      $self->{output}->add_option_msg(short_msg => "Wrong $option option '" . $val . "'.");
                      $self->{output}->option_exit();
                  }
                  my ($section, $instance, $value) = ($1, $2, $3);                
                  if (defined($self->{regexp_threshold_numeric_check_section_option}) && 
                      $section !~ /$self->{regexp_threshold_numeric_check_section_option}/) {
                      $self->{output}->add_option_msg(short_msg => "Wrong $option option '" . $val . "'.");
                      $self->{output}->option_exit();
                  }   
                  $self->call_object_callback(
                      method_name => $self->{cb_threshold_numeric_check_section_option}, 
                      section => $section,
                      option_name => $option,
                      option_value => $val
                  );
  
                  my $position = 0;
                  if (defined($self->{numeric_threshold}->{$section})) {
                      $position = scalar(@{$self->{numeric_threshold}->{$section}});
                  }
                  if (($self->{perfdata}->threshold_validate(label => $option . '-' . $section . '-' . $position, value => $value)) == 0) {
                      $self->{output}->add_option_msg(short_msg => "Wrong $option threshold '" . $value . "'.");
                      $self->{output}->option_exit();
                  }
                  $self->{numeric_threshold}->{$section} = [] if (!defined($self->{numeric_threshold}->{$section}));
                  push @{$self->{numeric_threshold}->{$section}}, { label => $option . '-' . $section . '-' . $position, threshold => $option, instance => $instance };
              }
          }
      }
  
      if ($self->{count} == 1) {
          foreach my $comp (@{$self->{components_module}}) {
              foreach my $threshold (('warning', 'critical', 'unknown')) {
                  if (($self->{perfdata}->threshold_validate(label => $threshold . '-count-' . $comp, value => $self->{option_results}->{$threshold . '_count_' . $comp})) == 0) {
                      $self->{output}->add_option_msg(short_msg => "Wrong " . $threshold . " threshold '" . $self->{option_results}->{$threshold . '_count_' . $comp} . "'.");
                      $self->{output}->option_exit();
                  }
              }
          }
      }
  }
  
  sub load_components {
      my ($self, %options) = @_;
  
      foreach (@{$self->{components_module}}) {
          if (/$self->{option_results}->{component}/) {
              my $mod_name = $self->{components_path} . "::$_";
              centreon::plugins::misc::mymodule_load(
                  output => $self->{output}, module => $mod_name,
                  error_msg => "Cannot load module '$mod_name'.") if ($self->{load_components} == 1);
              $self->{loaded} = 1;
              if ($self->{components_exec_load} == 1) {
                  my $func = $mod_name->can('load');
                  $func->($self);
              }
          }
      }
  }
  
  sub exec_components {
      my ($self, %options) = @_;
  
      foreach (@{$self->{components_module}}) {
          if (/$self->{option_results}->{component}/) {
              my $mod_name = $self->{components_path} . "::$_";
              my $func = $mod_name->can('check');
              $func->($self); 
          }
      }
  }
  
  sub display {
      my ($self, %options) = @_;
  
      my $total_components = 0;
      my $display_by_component = '';
      my $display_by_component_append = '';
      my $exit = 'OK';
      my $exits = [];
      my ($warn, $crit);
  
      foreach my $comp (sort(keys %{$self->{components}})) {
          # Skipping short msg when no components
          next if (!defined($self->{option_results}->{no_component_count}) && $self->{components}->{$comp}->{total} == 0 && $self->{components}->{$comp}->{skip} == 0);
          next if (defined($self->{option_results}->{component}) && $comp !~ /$self->{option_results}->{component}/ );
  
          if ($self->{count} == 1) {
              ($exit, $warn, $crit) = $self->get_severity_count(label => $comp, value => $self->{components}->{$comp}->{total});
              if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
                  $self->{output}->output_add(
                      severity => $exit,
                      short_msg => sprintf(
                          "'%s' components '%s' checked",
                          $self->{components}->{$comp}->{total},
                          $comp
                      )
                  );
              }
              $self->{output}->perfdata_add(
                  label => 'count_' . $comp,
                  nlabel => 'hardware.' . $comp . '.count',
                  value => $self->{components}->{$comp}->{total},
                  warning => $warn,
                  critical => $crit
              );
              push @{$exits}, $exit;
          }
  
          $total_components += $self->{components}->{$comp}->{total} + $self->{components}->{$comp}->{skip};
          my $count_by_components = $self->{components}->{$comp}->{total} + $self->{components}->{$comp}->{skip}; 
          $display_by_component .= $display_by_component_append . $self->{components}->{$comp}->{total} . '/' . $count_by_components . ' ' . $self->{components}->{$comp}->{name};
          $display_by_component_append = ', ';
      }
  
      $exit = $self->{output}->get_most_critical(status => $exits) if (scalar(@{$exits}) > 0);
  
      if ($self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
          $self->{output}->output_add(
              short_msg => sprintf(
                  'All %s components are ok [%s].', 
                  $total_components,
                  $display_by_component
              )
          );
      }
  
      if (defined($self->{option_results}->{no_component}) && $total_components == 0) {
          $self->{output}->output_add(
              severity => $self->{no_components},
              short_msg => 'No components are checked.'
          );
      }
  }
  
  sub run {
      my ($self, %options) = @_;
  
      $self->{loaded} = 0;  
      $self->call_object_callback(method_name => $self->{cb_hook1}, %options);
  
      $self->load_components(%options);
      if ($self->{loaded} == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong option. Cannot find component '" . $self->{option_results}->{component} . "'.");
          $self->{output}->option_exit();
      }
  
      $self->call_object_callback(method_name => $self->{cb_hook2}, %options);
      $self->exec_components(%options);
      $self->call_object_callback(method_name => $self->{cb_hook3}, %options);
  
      $self->display();
  
      $self->call_object_callback(method_name => $self->{cb_hook4}, %options);
  
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  sub disco_format {
      my ($self, %options) = @_;
  
      $self->{output}->add_disco_format(elements => ['component', 'instance', 'description']);
  }
  
  sub disco_show {
      my ($self, %options) = @_;
  
      $self->{loaded} = 0;  
      $self->call_object_callback(method_name => $self->{cb_hook1}, %options);
  
      $self->load_components(%options);
      if ($self->{loaded} == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong option. Cannot find component '" . $self->{option_results}->{component} . "'.");
          $self->{output}->option_exit();
      }
      
      $self->call_object_callback(method_name => $self->{cb_hook2}, %options);
      
      foreach (@{$self->{components_module}}) {
          if (/$self->{option_results}->{component}/) {
              my $mod_name = $self->{components_path} . "::$_";
              if (my $func = $mod_name->can('disco_show')) {
                  $func->($self);
              }
          }
      }
  
      $self->call_object_callback(method_name => $self->{cb_hook3}, %options);
  }
  
  sub check_filter {
      my ($self, %options) = @_;
  
      # Old compatibility variable. We'll be deleted
      if (defined($self->{option_results}->{exclude})) {
          if (defined($options{instance})) {
              if ($self->{option_results}->{exclude} =~ /(^|\s|,)${options{section}}[^,]*#\Q$options{instance}\E#/) {
                  $self->{components}->{$options{section}}->{skip}++;
                  $self->{output}->output_add(long_msg => sprintf("skipping $options{section} section $options{instance} instance."));
                  return 1;
              }
          } elsif (defined($self->{option_results}->{exclude}) && $self->{option_results}->{exclude} =~ /(^|\s|,)$options{section}(\s|,|$)/) {
              $self->{output}->output_add(long_msg => sprintf("skipping $options{section} section."));
              return 1;
          }
      }
  
      $options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));   
      foreach (@{$self->{filter}}) {
          if ($options{section} =~ /$_->{filter}/) {
              if (!defined($options{instance}) && !defined($_->{instance})) {
                  $self->{output}->output_add(long_msg => sprintf("skipping $options{section} section."));
                  return 1;
              } elsif (defined($options{instance}) && $options{instance} =~ /$_->{instance}/) {
                  $self->{output}->output_add(long_msg => sprintf("skipping $options{section} section $options{instance} instance."));
                  return 1;
              }
          }
      }
  
      return 0;
  }
  
  sub absent_problem {
      my ($self, %options) = @_;
  
      $options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
      foreach (@{$self->{absent_problem}}) {
          if ($options{section} =~ /$_->{filter}/) {
              if (!defined($_->{instance}) || $options{instance} =~ /$_->{instance}/) {
                  $self->{output}->output_add(
                      severity => 'CRITICAL',
                      short_msg => sprintf(
                          "Component '%s' instance '%s' is not present", 
                          $options{section},
                          $options{instance}
                      )
                  );
                  $self->{output}->output_add(long_msg => sprintf("Skipping $options{section} section $options{instance} instance (not present)"));
                  $self->{components}->{$options{section}}->{skip}++;
                  return 1;
              }
          }
      }
  
      return 0;
  }
  
  sub get_severity_count {
      my ($self, %options) = @_;
      my $status = 'OK'; # default
      my $thresholds = { warning => undef, critical => undef };
  
      $status = $self->{perfdata}->threshold_check(
          value => $options{value},
          threshold => [
              { label => 'critical-count-' . $options{label}, exit_litteral => 'critical' }, 
              { label => 'warning-count-' . $options{label}, exit_litteral => 'warning' },
              { label => 'unknown-count-' . $options{label}, exit_litteral => 'unknown' },
          ]
      );
      $thresholds->{critical} = $self->{perfdata}->get_perfdata_for_output(label => 'critical-count-' . $options{label});
      $thresholds->{warning} = $self->{perfdata}->get_perfdata_for_output(label => 'warning-count-' . $options{label});
  
      return ($status, $thresholds->{warning}, $thresholds->{critical});
  }
  
  sub get_severity_numeric {
      my ($self, %options) = @_;
      my $status = 'OK'; # default
      my $thresholds = { warning => undef, critical => undef };
      my $checked = 0;
  
      $options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
      if (defined($self->{numeric_threshold}->{$options{section}})) {
          my $exits = [];
          foreach (@{$self->{numeric_threshold}->{$options{section}}}) {
              if ($options{instance} =~ /$_->{instance}/) {
                  push @{$exits}, $self->{perfdata}->threshold_check(value => $options{value}, threshold => [ { label => $_->{label}, exit_litteral => $_->{threshold} } ]);
                  $thresholds->{$_->{threshold}} = $self->{perfdata}->get_perfdata_for_output(label => $_->{label});
                  $checked = 1;
              }
          }
          $status = $self->{output}->get_most_critical(status => $exits) if (scalar(@{$exits}) > 0);
      }
  
      return ($status, $thresholds->{warning}, $thresholds->{critical}, $checked);
  }
  
  sub get_severity {
      my ($self, %options) = @_;
      my $status = 'UNKNOWN'; # default 
  
      $options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
  
      foreach (@{$self->{overload_th}}) {
          if ($options{section} =~ /$_->{section}/i) {
              if ($options{value} =~ /$_->{filter}/i &&
                  (!defined($options{instance}) || $options{instance} =~ /$_->{instance}/)) {
                  $status = $_->{status};
                  return $status;
              }
          }
      }
  
      my $label = defined($options{label}) ? $options{label} : $options{section};
      foreach (@{$self->{thresholds}->{$label}}) {
          if ($options{value} =~ /$$_[0]/i) {
              $status = $$_[1];
              return $status;
          }
      }
  
      return $status;
  }
      
  1;
  
  
  =head1 MODE
  
  Default template for hardware. Should be extended.
  
  =over 8
  
  =item B<--component>
  
  Define which component should be monitored based on their names. 
  This option will be treated as a regular expression. 
  All components are included by default ('.*').
  
  =item B<--filter>
  
  Exclude some components. This option can be called several times (example: --filter=component1 --filter=component2). 
  You can also exclude components from a specific instance (example: --filter=component_name,instance_value).
  
  =item B<--absent-problem>
  
  Return an error if a component is not 'present' (default is skipping). 
  It can be set globally or for a specific instance: --absent-problem='component_name' or --absent-problem='component_name,instance_value'.
  
  =item B<--no-component>
  
  Define the expected status if no components are found (default: critical).
  
  =item B<--threshold-overload>
  
  Use this option to override the status returned by the plugin when the status label matches a regular expression (syntax: section,[instance,]status,regexp).
  Example: --threshold-overload='xxxxx,CRITICAL,^(?!(normal)$)'
  
  =item B<--warning>
  
  Define the warning threshold for temperatures (syntax: type,instance,threshold)
  Example: --warning='temperature,.*,30'
  
  =item B<--critical>
  
  Define the critical threshold for temperatures (syntax: type,instance,threshold)
  Example: --critical='temperature,.*,40'
  
  =item B<--warning-count-*>
  
  Define the warning threshold for the number of components of one type (replace '*' with the component type).
  
  =item B<--critical-count-*>
  
  Define the critical threshold for the number of components of one type (replace '*' with the component type).
  
  =back
  
  =cut
CENTREON_PLUGINS_TEMPLATES_HARDWARE

$fatpacked{"centreon/plugins/values.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_VALUES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package centreon::plugins::values;
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  # Warning message with sprintf and too much arguments.
  # Really annoying. Need to disable that warning
  no if ($^V gt v5.22.0), 'warnings' => 'redundant';
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      $self->{statefile} = $options{statefile};
      $self->{output} = $options{output};
      $self->{perfdata} = $options{perfdata};
      $self->{label} = $options{label};
      $self->{nlabel} = $options{nlabel};
      $self->{thlabel} = defined($options{thlabel}) ? $options{thlabel} : $self->{label};
  
      $self->{perfdatas} = [];
      
      $self->{output_template} = $self->{label} . ' : %s';
      $self->{output_use} = undef;
      $self->{output_change_bytes} = 0;
      
      $self->{output_error_template} = $self->{label} . ' : %s';
      
      $self->{threshold_use} = undef;
      $self->{threshold_warn} = undef;
      $self->{threshold_crit} = undef;
  
      $self->{per_second} = 0;
      $self->{manual_keys} = 0;
      $self->{last_timestamp} = undef;
  
      $self->{result_values} = {};
      $self->{safe_test} = 0;
  
      return $self;
  }
  
  sub init {
      my ($self, %options) = @_;
      my $unkn = defined($self->{threshold_unkn}) ? $self->{threshold_unkn} : 'unknown-' . $self->{thlabel};
      my $warn = defined($self->{threshold_warn}) ? $self->{threshold_warn} : 'warning-' . $self->{thlabel};
      my $crit = defined($self->{threshold_crit}) ? $self->{threshold_crit} : 'critical-' . $self->{thlabel}; 
  
      if (($self->{perfdata}->threshold_validate(label => $unkn, value => $options{option_results}->{$unkn})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong $unkn threshold '" . $options{option_results}->{$unkn} . "'.");
          $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => $warn, value => $options{option_results}->{$warn})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong $warn threshold '" . $options{option_results}->{$warn} . "'.");
          $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => $crit, value => $options{option_results}->{$crit})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong $crit threshold '" . $options{option_results}->{$crit} . "'.");
          $self->{output}->option_exit();
      }
  }
  
  sub set {
      my ($self, %options) = @_;
  
      foreach (keys %options) {
          $self->{$_} = $options{$_};
      }
  }
  
  sub calc {
      my ($self, %options) = @_;
  
      # manage only one value ;)
      foreach my $value (@{$self->{key_values}}) {
          if (defined($value->{diff}) && $value->{diff} == 1)  {
              $self->{result_values}->{$value->{name}} = $options{new_datas}->{$self->{instance} . '_' . $value->{name}} - $options{old_datas}->{$self->{instance} . '_' . $value->{name}};
          } elsif (defined($value->{per_second}) && $value->{per_second} == 1) {
              $self->{result_values}->{$value->{name}} = ($options{new_datas}->{$self->{instance} . '_' . $value->{name}} - $options{old_datas}->{$self->{instance} . '_' . $value->{name}}) / $options{delta_time};
          } elsif (defined($value->{per_minute}) && $value->{per_minute} == 1) {
              $self->{result_values}->{$value->{name}} = ($options{new_datas}->{$self->{instance} . '_' . $value->{name}} - $options{old_datas}->{$self->{instance} . '_' . $value->{name}}) / ($options{delta_time} / 60);
          } else {
              $self->{result_values}->{$value->{name}} = $options{new_datas}->{$self->{instance} . '_' . $value->{name}};
          }
      }
  
      return 0;
  }
  
  sub threshold_check {
      my ($self, %options) = @_;
      
      if (defined($self->{closure_custom_threshold_check})) {
          return &{$self->{closure_custom_threshold_check}}($self, %options);
      }
  
      my $unkn = defined($self->{threshold_unkn}) ? $self->{threshold_unkn} : 'unknown-' . $self->{thlabel};
      my $warn = defined($self->{threshold_warn}) ? $self->{threshold_warn} : 'warning-' . $self->{thlabel};
      my $crit = defined($self->{threshold_crit}) ? $self->{threshold_crit} : 'critical-' . $self->{thlabel};
      
      my $value = '';
      if (defined($self->{threshold_use})) {
          $value = $self->{result_values}->{ $self->{threshold_use} };
      } else {
          $value = defined($self->{key_values}->[0]) ? $self->{result_values}->{ $self->{key_values}->[0]->{name} } : '';
      }
  
      return $self->{perfdata}->threshold_check(
          value => $value, threshold => [
              { label => $crit, exit_litteral => 'critical' },
              { label => $warn, exit_litteral => 'warning' },
              { label => $unkn, exit_litteral => 'unknown' }
          ]
      );
  }
  
  sub output_error {
      my ($self, %options) = @_;
  
      return sprintf($self->{output_error_template}, $self->{error_msg});
  }
  
  sub output {
      my ($self, %options) = @_;
       
      if (defined($self->{closure_custom_output})) {
          return $self->{closure_custom_output}->($self);
      }
  
      my ($value, $unit, $name) = ('', '');
      if (defined($self->{output_use})) {
          $name = $self->{output_use};
      } else {
          $name = defined($self->{key_values}->[0]) ? $self->{key_values}->[0]->{name} : undef;
      }
  
      if (defined($name)) {
          $value = $self->{result_values}->{$name};
          if ($self->{output_change_bytes} == 1) {
              ($value, $unit) = $self->{perfdata}->change_bytes(value => $value);
          } elsif ($self->{output_change_bytes} == 2) {
              ($value, $unit) = $self->{perfdata}->change_bytes(value => $value, network => 1);
          }
      }
  
      return sprintf($self->{output_template}, $value, $unit);
  }
  
  sub use_instances {
      my ($self, %options) = @_;
  
      if (!defined($options{extra_instance}) || $options{extra_instance} != 0 || $self->{output}->use_new_perfdata()) {
          return 1;
      }
      
      return 0;
  }
  
  sub perfdata {
      my ($self, %options) = @_;
      
      if (defined($self->{closure_custom_perfdata})) {
          return &{$self->{closure_custom_perfdata}}($self, %options);
      }
      
      my $warn = defined($self->{threshold_warn}) ? $self->{threshold_warn} : 'warning-' . $self->{thlabel};
      my $crit = defined($self->{threshold_crit}) ? $self->{threshold_crit} : 'critical-' . $self->{thlabel}; 
      
      foreach my $perf (@{$self->{perfdatas}}) {
          my ($label, $extra_label, $min, $max, $th_total) = ($self->{label}, '');
          my $cast_int = (defined($perf->{cast_int}) && $perf->{cast_int} == 1) ? 1 : 0;
          my $template = '%s';
          
          $template = $perf->{template} if (defined($perf->{template}));
          $label = $perf->{label} if (defined($perf->{label}));
          if (defined($perf->{min})) {
              $min = ($perf->{min} =~ /[^0-9.-]/) ? $self->{result_values}->{$perf->{min}} : $perf->{min};
          }
          if (defined($perf->{max})) {
              $max = ($perf->{max} =~ /[^0-9.-]/) ? $self->{result_values}->{$perf->{max}} : $perf->{max};
          }
          if (defined($perf->{threshold_total})) {
              $th_total = ($perf->{threshold_total} =~ /[^0-9.-]/) ? $self->{result_values}->{$perf->{threshold_total}} : $perf->{threshold_total};
          }
          
          my $instances;
          if (defined($perf->{label_extra_instance}) && $perf->{label_extra_instance} == 1) {
              my $instance = '';
              if (defined($perf->{instance_use})) {
                  $instance = $self->{result_values}->{$perf->{instance_use}};
              } else {
                  $instance = $self->{instance};
              }
              
              if (!defined($options{extra_instance}) || $options{extra_instance} != 0 || $self->{output}->use_new_perfdata()) {
                  $instances = $instance;
              }
          }
  
          my $value = defined($perf->{value}) ? $perf->{value} : $self->{key_values}->[0]->{name};
          $self->{output}->perfdata_add(
              label => $label,
              instances => $instances,
              nlabel => $self->{nlabel},
              unit => $perf->{unit},
              value => $cast_int == 1 ? int($self->{result_values}->{$value}) : sprintf($template, $self->{result_values}->{$value}),
              warning => $self->{perfdata}->get_perfdata_for_output(label => $warn, total => $th_total, cast_int => $cast_int),
              critical => $self->{perfdata}->get_perfdata_for_output(label => $crit, total => $th_total, cast_int => $cast_int),
              min => $min,
              max => $max
          );
      }
  }
  
  sub eval {
      my ($self, %options) = @_;
  
      if ($self->{safe_test} == 0) {
          my ($code) = centreon::plugins::misc::mymodule_load(
              output => $self->{output}, module => 'Safe', 
              no_quit => 1
          );
          if ($code == 0) {
              $self->{safe} = Safe->new();
              $self->{safe}->share('$values');
          }
          $self->{safe_test} = 1;
      }
  
      my $result;
      if (defined($self->{safe})) {
          our $values = $self->{result_values};
          $result = $self->{safe}->reval($options{value}, 1);
          if ($@) {
              die 'Unsafe code evaluation: ' . $@;
          }
      } else {
          my $values = $self->{result_values};
          {
              local $SIG{__WARN__} = sub {}; # ignore
              
              $result = eval "$options{value}";
              if ($@) {
                  die 'Code evaluation error: ' . $@;
              }
          }
      }
  
      return $result;
  }
  
  sub execute {
      my ($self, %options) = @_;
      my $old_datas = {};
  
      $self->{result_values} = {},
      $self->{error_msg} = undef;
      my $quit = 0;
      
      $options{new_datas} = {} if (!defined($options{new_datas}));
      foreach my $value (@{$self->{key_values}}) {
          if (!defined($options{values}->{$value->{name}}) || 
              defined($value->{no_value}) && $options{values}->{$value->{name}} eq $value->{no_value}) {
              $quit = 2;
              last;
          }
      
          if ((defined($value->{diff}) && $value->{diff} == 1) ||
              (defined($value->{per_minute}) && $value->{per_minute} == 1) ||
              (defined($value->{per_second}) && $value->{per_second} == 1)) {
              $options{new_datas}->{$self->{instance} . '_' . $value->{name}} = $options{values}->{$value->{name}};
              $old_datas->{$self->{instance} . '_' . $value->{name}} = $self->{statefile}->get(name => $self->{instance} . '_' . $value->{name});
              if (!defined($old_datas->{$self->{instance} . '_' . $value->{name}})) {
                  $quit = 1;
                  next;
              }
              if ($old_datas->{$self->{instance} . '_' . $value->{name}} > $options{new_datas}->{$self->{instance} . '_' . $value->{name}}) {
                  $old_datas->{$self->{instance} . '_' . $value->{name}} = 0;
              }
          } else {
              $options{new_datas}->{$self->{instance} . '_' . $value->{name}} = $options{values}->{$value->{name}};
              if (defined($self->{statefile})) {
                  $old_datas->{$self->{instance} . '_' . $value->{name}} = $self->{statefile}->get(name => $self->{instance} . '_' . $value->{name});
              }
          }
      }
      
      # Very manual
      if ($self->{manual_keys} == 1) {
          foreach my $name (keys %{$options{values}}) {
              $options{new_datas}->{$self->{instance} . '_' . $name} = $options{values}->{$name};
              if (defined($self->{statefile})) {
                  $old_datas->{$self->{instance} . '_' . $name} = $self->{statefile}->get(name => $self->{instance} . '_' . $name);
              }
          }
      }
  
      if ($quit == 2) {
          $self->{error_msg} = 'skipped (no value(s))';
          return -10;
      }
  
      if (defined($self->{statefile})) {
          $self->{last_timestamp} = $self->{statefile}->get(name => 'last_timestamp');
      }
  
      if ($quit == 1) {
          $self->{error_msg} = 'Buffer creation';
          return -1;
      }
  
      my $delta_time;
      if (defined($self->{statefile}) && defined($self->{last_timestamp})) {
          $delta_time = $options{new_datas}->{last_timestamp} - $self->{last_timestamp};
          if ($delta_time <= 0) {
              $delta_time = 1;
          }
      }
  
      if (defined($self->{closure_custom_calc})) {
          return $self->{closure_custom_calc}->(
              $self,
              old_datas => $old_datas,
              new_datas => $options{new_datas},
              delta_time => $delta_time,
              extra_options => $self->{closure_custom_calc_extra_options}
          );
      }
      return $self->calc(old_datas => $old_datas, new_datas => $options{new_datas}, delta_time => $delta_time);
  }
  
  1;
  
  
CENTREON_PLUGINS_VALUES

$fatpacked{"os/linux/local/custom/cli.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_CUSTOM_CLI';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::custom::cli;
  
  use strict;
  use warnings;
  use centreon::plugins::ssh;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self  = {};
      bless $self, $class;
  
      if (!defined($options{output})) {
          print "Class Custom: Need to specify 'output' argument.\n";
          exit 3;
      }
      if (!defined($options{options})) {
          $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument.");
          $options{output}->option_exit();
      }
  
      $self->{mode_name} = $options{mode_name};
      # discovery-snmp cannot be used at distance.
      return $self if (defined($options{mode_name}) && $options{mode_name} =~ /discovery-snmp|check-plugin/);
  
      if (!defined($options{noptions})) {
          $options{options}->add_options(arguments => {                      
              'hostname:s'        => { name => 'hostname' },
              'timeout:s'         => { name => 'timeout' },
              'command:s'         => { name => 'command' },
              'command-path:s'    => { name => 'command_path' },
              'command-options:s' => { name => 'command_options' },
              'sudo:s'            => { name => 'sudo' }
          });
      }
  
      $options{options}->add_help(package => __PACKAGE__, sections => 'CLI OPTIONS', once => 1);
  
      $self->{output} = $options{output};
      $self->{ssh} = centreon::plugins::ssh->new(%options);
  
      return $self;
  }
  
  sub set_options {
      my ($self, %options) = @_;
  
      $self->{option_results} = $options{option_results};
  }
  
  sub set_defaults {}
  
  sub check_options {
      my ($self, %options) = @_;
  
      return 0 if ($self->{mode_name} =~ /discovery-snmp|check-plugin/);
  
      if (defined($self->{option_results}->{timeout}) && $self->{option_results}->{timeout} =~ /(\d+)/) {
          $self->{timeout} = $1;
      }
      if (defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') {
          $self->{ssh}->check_options(option_results => $self->{option_results});
      }
  
      centreon::plugins::misc::check_security_command(
          output => $self->{output},
          command => $self->{option_results}->{command},
          command_options => $self->{option_results}->{command_options},
          command_path => $self->{option_results}->{command_path}
      );
  
      return 0;
  }
  
  sub get_identifier {
      my ($self, %options) = @_;
  
      my $id = defined($self->{option_results}->{hostname}) ? $self->{option_results}->{hostname} : 'me';
      if (defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') {
          $id .= ':' . $self->{ssh}->get_port();
      }
      return $id;
  }
  
  sub execute_command {
      my ($self, %options) = @_;
  
      centreon::plugins::misc::check_security_command(
          output => $self->{output},
          command => $self->{option_results}->{command},
          command_options => $self->{option_results}->{command_options},
          command_path => $self->{option_results}->{command_path}
      );
  
      my $timeout = $self->{timeout};
      if (!defined($timeout)) {
          $timeout = defined($options{timeout}) ? $options{timeout} : 45;
      }
      my $command_options = defined($self->{option_results}->{command_options}) && $self->{option_results}->{command_options} ne '' ? $self->{option_results}->{command_options} : $options{command_options};
      if (defined($options{command_options_suffix})) {
          $command_options .= $options{command_options_suffix};
      }
  
      my ($stdout, $exit_code);
      if (defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') {
          ($stdout, $exit_code) = $self->{ssh}->execute(
              hostname => $self->{option_results}->{hostname},
              sudo => $self->{option_results}->{sudo},
              command => defined($self->{option_results}->{command}) && $self->{option_results}->{command} ne '' ? $self->{option_results}->{command} : $options{command},
              command_path => defined($self->{option_results}->{command_path}) && $self->{option_results}->{command_path} ne '' ? $self->{option_results}->{command_path} : $options{command_path},
              command_options => $command_options,
              timeout => $timeout,
              no_quit => $options{no_quit}
          );
      } else {
          ($stdout, $exit_code) = centreon::plugins::misc::execute(
              output => $self->{output},
              sudo => $self->{option_results}->{sudo},
              options => { timeout => $timeout },
              command => defined($self->{option_results}->{command}) && $self->{option_results}->{command} ne '' ? $self->{option_results}->{command} : $options{command},
              command_path => defined($self->{option_results}->{command_path}) && $self->{option_results}->{command_path} ne '' ? $self->{option_results}->{command_path} : $options{command_path},
              command_options => $command_options,
              no_quit => $options{no_quit}
          );
      }
  
      $self->{output}->output_add(long_msg => "command response: $stdout", debug => 1);
  
      return ($stdout, $exit_code);
  }
  
  1;
  
  
  =head1 NAME
  
  ssh
  
  =head1 SYNOPSIS
  
  my ssh
  
  =head1 CLI OPTIONS
  
  =over 8
  
  =item B<--hostname>
  
  Hostname to query.
  
  =item B<--timeout>
  
  Timeout in seconds for the command (default: 45). Default value can be override by the mode.
  
  =item B<--command>
  
  Command to get information. Used it you have output in a file.
  
  =item B<--command-path>
  
  Command path.
  
  =item B<--command-options>
  
  Command options.
  
  =item B<--sudo>
  
  sudo command.
  
  =back
  
  =head1 DESCRIPTION
  
  B<custom>.
  
  =cut
OS_LINUX_LOCAL_CUSTOM_CLI

$fatpacked{"os/linux/local/mode/checkplugin.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_CHECKPLUGIN';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::checkplugin;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
  use Time::HiRes qw(gettimeofday tv_interval);
  use centreon::plugins::ssh;
  use centreon::plugins::misc;
  
  sub custom_status_output {
      my ($self, %options) = @_;
  
      return $self->{result_values}->{short_message};
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'commands', type => 1, message_separator => ' - ', display_long => 0 }
      ];
  
      $self->{maps_counters}->{commands} = [
           {
               label => 'status', type => 2, 
               unknown_default => '%{exit_code} == 3',
               warning_default => '%{exit_code} == 1',
               critical_default => '%{exit_code} == 2',
               set => {
                  key_values => [
                      { name => 'short_message' }, { name => 'exit_code' }
                  ],
                  closure_custom_output => $self->can('custom_status_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => \&catalog_status_threshold_ng
              }
          },
          { label => 'time', nlabel => 'ssh.response.time.seconds', display_ok => 0, set => {
                  key_values => [ { name => 'time' } ],
                  output_template => 'response time: %.3fs',
                  perfdatas => [
                      { template => '%.3f', min => 0, unit => 's', label_extra_instance => 1 }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'hostname:s' => { name => 'hostname' },
          'timeout:s'  => { name => 'timeout' },
          'command:s@' => { name => 'command' }
      });
  
      $self->{ssh} = centreon::plugins::ssh->new(%options);
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::check_options(%options);
  
      if (!defined($self->{option_results}->{hostname}) || $self->{option_results}->{hostname} eq '') {
          $self->{output}->add_option_msg(short_msg => 'Set --hostname option');
          $self->{output}->option_exit();
      }
      if (!defined($self->{option_results}->{command})) {
          $self->{output}->add_option_msg(short_msg => 'Need to specify at least one --command option');
          $self->{output}->option_exit();
      }
  
      $self->{ssh}->check_options(option_results => $self->{option_results});
  }
  
  sub parse_perfdatas {
      my ($self, %options) = @_;
  
      while ($options{perfdatas} =~ /(.*?)=([0-9\.]+)([^0-9;]+?)?([0-9.@;:]+?)?(?:\s+|\Z)/g) {
          my ($label, $value, $unit, $extra) = ($1, $2, $3, $4);
          $label = centreon::plugins::misc::trim($label);
          $label =~ s/^'//;
          $label =~ s/'$//;
          my @extras = split(';', $extra);
          $self->{output}->perfdata_add(
              nlabel => $label,
              unit => $unit,
              value => $value,
              warning => $extras[1],
              critical => $extras[2],
              min => $extras[3],
              max => $extras[4]
          );
      }
  }
  
  sub parse_plugin_output {
      my ($self, %options) = @_;
  
      my @lines = split(/\n/, $options{output});
      my $short = 'no output';
      my $line = shift(@lines);
      if (defined($line) && $line =~ /^(.*?)(?:\|(.*)|\Z)/) {
          $short = $1;
          if (defined($2)) {
              $self->parse_perfdatas(perfdatas => $2);
          }
      }
  
      $self->{commands}->{ $options{cmd} }->{short_message} = $short;
      foreach (@lines) {
          $self->{output}->output_add(long_msg => $_);
      }
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      $self->{output}->set_ignore_label();
  
      my $timeout = $self->{option_results}->{timeout};
      $timeout = 45 if (!defined($timeout) || $timeout !~ /\d+/);
  
      $self->{commands} = {};
      my $i = 1;
      foreach my $command (@{$self->{option_results}->{command}}) {
          my $timing0 = [gettimeofday];
          my ($stdout, $exit_code) = $self->{ssh}->execute(
              hostname => $self->{option_results}->{hostname},
              command => $command,
              timeout => $timeout,
              no_quit => 1
          );
          my $cmd = 'command' . $i;
          $self->{commands}->{$cmd} = {
              time => tv_interval($timing0, [gettimeofday]),
              exit_code => $exit_code
          };
          $self->parse_plugin_output(cmd => $cmd, output => $stdout);
          $i++;
      }
  }
  
  1;
  
  
  =head1 MODE
  
  SSH execution commands in a remote host.
  
  =over 8
  
  =item B<--hostname>
  
  Hostname to query.
  
  =item B<--timeout>
  
  Timeout in seconds for the command (default: 45).
  
  =item B<--command>
  
  command to execute on the remote machine
  
  =item B<--unknown-status>
  
  Define the conditions to match for the status to be UNKNOWN (default: '%{exit_code} == 3').
  You can use the following variables: %{short_message}, %{exit_code}
  
  =item B<--warning-status>
  
  Define the conditions to match for the status to be WARNING (default: '%{exit_code} == 1').
  You can use the following variables: %{short_message}, %{exit_code}
  
  =item B<--critical-status>
  
  Define the conditions to match for the status to be CRITICAL (default: '%{exit_code} == 2').
  You can use the following variables: %{short_message}, %{exit_code}
  
  =item B<--warning-time>
  
  Warning threshold in seconds.
  
  =item B<--critical-time>
  
  Critical threshold in seconds.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_CHECKPLUGIN

$fatpacked{"os/linux/local/mode/cmdreturn.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_CMDRETURN';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::cmdreturn;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'exec-command:s'         => { name => 'exec_command' },
          'exec-command-path:s'    => { name => 'exec_command_path' },
          'exec-command-options:s' => { name => 'exec_command_options' },
          'manage-returns:s'       => { name => 'manage_returns', default => '' },
          'separator:s'            => { name => 'separator', default => '#' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (!defined($self->{option_results}->{exec_command})) {
          $self->{output}->add_option_msg(short_msg => "Need to specify exec-command option.");
          $self->{output}->option_exit();
      }
  
      $self->{expressions} = [];
      foreach my $entry (split(/$self->{option_results}->{separator}/, $self->{option_results}->{manage_returns})) {
          next if (!($entry =~ /(.*?),(.*?),(.*)/));
          next if (!$self->{output}->is_litteral_status(status => $2));
          my ($expr, $rv, $msg) = ($1, $2, $3);
  
          if ($expr ne '') {
              if ($expr =~ /^\s*([0-9]+)\s*$/) {
                  push @{$self->{expressions}}, { test => "%(code) == $1", rv => $rv, msg => $msg };
              } else {
                  push @{$self->{expressions}}, { test => $expr, rv => $rv, msg => $msg };
              }
          } else {
              $self->{expression_default} = { rv => $rv, msg => $msg };
          }
      }
  
      if ($self->{option_results}->{manage_returns} eq '' ||
          (scalar(@{$self->{expressions}}) == 0 && !defined($self->{expression_default}))) {
          $self->{output}->add_option_msg(short_msg => "Need to specify manage-returns option correctly.");
          $self->{output}->option_exit();
      }
  
      for (my $i = 0; $i < scalar(@{$self->{expressions}}); $i++) {
          $self->{expressions}->[$i]->{test} =~ s/%\{(.*?)\}/\$values->{$1}/g;
          $self->{expressions}->[$i]->{test} =~ s/%\((.*?)\)/\$values->{$1}/g;
      }
  
      centreon::plugins::misc::check_security_whitelist(
          output => $self->{output},
          command => $self->{option_results}->{exec_command},
          command_path => $self->{option_results}->{exec_command_path},
          command_options => $self->{option_results}->{exec_command_options}
      );
  }
  
  sub run {
      my ($self, %options) = @_;
  
      my ($stdout, $exit_code) = $options{custom}->execute_command(
          command => $self->{option_results}->{exec_command},
          command_path => $self->{option_results}->{exec_command_path},
          command_options => $self->{option_results}->{exec_command_options},
          no_quit => 1
      );
  
      my $long_msg = $stdout;
      $long_msg =~ s/\|/~/mg;
      $self->{output}->output_add(long_msg => $long_msg);
  
      my $matched = 0;
      my $values = { code => $exit_code, output => $stdout };
      foreach (@{$self->{expressions}}) {
          if ($self->{output}->test_eval(test => $_->{test}, values => $values)) {
              $self->{output}->output_add(
                  severity => $_->{rv}, 
                  short_msg => $_->{msg}
              );
              $matched = 1;
              last;
          }
      }
  
      if ($matched == 0 && defined($self->{expression_default})) {
          $self->{output}->output_add(
              severity => $self->{expression_default}->{rv}, 
              short_msg => $self->{expression_default}->{msg}
          );
      } elsif ($matched == 0) {
          $self->{output}->output_add(
              severity => 'UNKNOWN', 
              short_msg => "Command exit code ($exit_code)"
          );
      }
  
      if (defined($exit_code)) {
          $self->{output}->perfdata_add(
              nlabel => 'command.exit.code.count',
              value => $exit_code
          );
      }
  
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check command returns.
  
  =over 8
  
  =item B<--manage-returns>
  
  Set action according command exit code.
  Example: %(code) == 0,OK,File xxx exist#%(code) == 1,CRITICAL,File xxx not exist#,UNKNOWN,Command problem
  
  =item B<--separator>
  
  Set the separator used in --manage-returns (default : #)
  
  =item B<--exec-command>
  
  Command to test (default: none).
  You can use 'sh' to use '&&' or '||'.
  
  =item B<--exec-command-path>
  
  Command path (default: none).
  
  =item B<--exec-command-options>
  
  Command options (default: none).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_CMDRETURN

$fatpacked{"os/linux/local/mode/connections.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_CONNECTIONS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::connections;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  my %map_ss_states = (
      UNCONN => 'closed',
      LISTEN => 'listen',
      'SYN-SENT' => 'synSent',
      'SYN-RECV' => 'synReceived',
      ESTAB => 'established',
      'FIN-WAIT-1' => 'finWait1',
      'FIN-WAIT-2' => 'finWait2',
      'CLOSE-WAIT' => 'closeWait',
      'LAST-ACK' => 'lastAck',
      CLOSING => 'closing',
      'TIME-WAIT' => 'timeWait',
      UNKNOWN => 'unknown',
  );
  
  my %map_states = (
      CLOSED => 'closed',
      LISTEN => 'listen',
      SYN_SENT => 'synSent',
      SYN_RECV => 'synReceived',
      ESTABLISHED => 'established',
      FIN_WAIT1 => 'finWait1',
      FIN_WAIT2 => 'finWait2',
      CLOSE_WAIT => 'closeWait',
      LAST_ACK => 'lastAck',
      CLOSING => 'closing',
      TIME_WAIT => 'timeWait',
      UNKNOWN => 'unknown',
  );
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => { 
          'warning:s'      => { name => 'warning', },
          'critical:s'     => { name => 'critical', },
          'service:s@'     => { name => 'service', },
          'application:s@' => { name => 'application', },
          'con-mode:s'     => { name => 'con_mode', default => 'netstat' }
      });
  
      $self->{connections} = [];
      $self->{services} = { total => { filter => '(?!(udp*))#.*?#.*?#.*?#.*?#(?!(listen))', builtin => 1, number => 0, msg => 'Total connections: %d' } };
      $self->{applications} = {};
      $self->{states} = {
          closed => 0, listen => 0, synSent => 0, synReceived => 0,
          established => 0, finWait1 => 0, finWait2 => 0, closeWait => 0,
          lastAck => 0, closing => 0, timeWait => 0
      };
  
      return $self;
  }
  
  sub netstat_build {
      my ($self, %options) = @_;
  
      foreach my $line (split /\n/, $self->{stdout}) {
          next if ($line !~ /^(tcp|udp|tcp6|udp6)\s+\S+\s+\S+\s+(\S+)\s+(\S+)\s*(\S*)/);
          my ($type, $src, $dst, $state) = ($1, $2, $3, $4);
          $src =~ /(.*):(\d+|\*)$/;
          my ($src_addr, $src_port) = ($1, $2);
          $dst =~ /(.*):(\d+|\*)$/;
          my ($dst_addr, $dst_port) = ($1, $2);
          
          if ($type =~ /^udp/) {
              if ($dst_port eq '*') {
                  $state = 'listen';
              } else {
                  $state = 'established';
              }
          } else {
              $state = $map_states{$state};
              $self->{states}->{$state}++;
          }
  
          push @{$self->{connections}}, $type . "#$src_addr#$src_port#$dst_addr#$dst_port#" . lc($state);
      }
  }
  
  sub ss_build {
      my ($self, %options) = @_;
  
      foreach my $line (split /\n/, $self->{stdout}) {
          next if ($line !~ /^(tcp|udp)\s+(\S+)\s+\S+\s+\S+\s+(\S+)\s*(\S+)/);
          my ($type, $src, $dst, $state) = ($1, $3, $4, $2);
          $src =~ /(.*):(\d+|\*)$/;
          my ($src_addr, $src_port) = ($1, $2);
          $dst =~ /(.*):(\d+|\*)$/;
          my ($dst_addr, $dst_port) = ($1, $2);
          $type .= '6' if ($src_addr !~ /^\d+\.\d+\.\d+\.\d+$/);
          
          if ($type =~ /^udp/) {
              if ($dst_port eq '*') {
                  $state = 'listen';
              } else {
                  $state = 'established';
              }
          } else {
              $state = $map_ss_states{$state};
              $self->{states}->{$state}++;
          }
          
          push @{$self->{connections}}, $type . "#$src_addr#$src_port#$dst_addr#$dst_port#" . lc($state);
      }
  }
  
  sub build_connections {
      my ($self, %options) = @_;
  
      if ($self->{option_results}->{con_mode} !~ /^ss|netstat$/) {
          $self->{output}->add_option_msg(short_msg => "Unknown --con-mode option.");
          $self->{output}->option_exit();
      }
  
      my ($command, $command_options) = ('netstat', '-antu 2>&1');
      if ($self->{option_results}->{con_mode} eq 'ss') {
          $command = 'ss';
          $command_options = '-a -A tcp,udp -n 2>&1';
      }
  
      ($self->{stdout}) = $options{custom}->execute_command(
          command => $command,
          command_options => $command_options
      );
  
      if ($self->{option_results}->{con_mode} eq 'ss') {
          $self->ss_build();
      } else {
          $self->netstat_build();
      }
  }
  
  sub check_services {
      my ($self, %options) = @_;
  
      foreach my $service (@{$self->{option_results}->{service}}) {
          my ($tag, $ipv, $state, $port_src, $port_dst, $filter_ip_src, $filter_ip_dst, $warn, $crit) = split /,/, $service;
  
          if (!defined($tag) || $tag eq '') {
              $self->{output}->add_option_msg(short_msg => "Tag for service '" . $service . "' must be defined.");
              $self->{output}->option_exit();
          }
          if (defined($self->{services}->{$tag})) {
              $self->{output}->add_option_msg(short_msg => "Tag '" . $tag . "' (service) already exists.");
              $self->{output}->option_exit();
          }
  
          $self->{services}->{$tag} = {
              filter => 
                  ((defined($ipv) && $ipv ne '') ? $ipv : '.*?') . '#' . 
                  ((defined($filter_ip_src) && $filter_ip_src ne '') ? $filter_ip_src : '.*?') . '#' . 
                  ((defined($port_src) && $port_src ne '') ? $port_src : '.*?') . '#' . 
                  ((defined($filter_ip_dst) && $filter_ip_dst ne '') ? $filter_ip_dst : '.*?') . '#' . 
                  ((defined($port_dst) && $port_dst ne '') ? $port_dst : '.*?') . '#' . 
                  ((defined($state) && $state ne '') ? lc($state) : '(?!(listen))'), 
              builtin => 0,
              number => 0
          };
          if (($self->{perfdata}->threshold_validate(label => 'warning-service-' . $tag, value => $warn)) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $warn . "' for service '$tag'.");
              $self->{output}->option_exit();
          }
          if (($self->{perfdata}->threshold_validate(label => 'critical-service-' . $tag, value => $crit)) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $crit . "' for service '$tag'.");
              $self->{output}->option_exit();
          }
      }
  }
  
  sub check_applications {
      my ($self, %options) = @_;
  
      foreach my $app (@{$self->{option_results}->{application}}) {
          my ($tag, $services, $warn, $crit) = split /,/, $app;
  
          if (!defined($tag) || $tag eq '') {
              $self->{output}->add_option_msg(short_msg => "Tag for application '" . $app . "' must be defined.");
              $self->{output}->option_exit();
          }
          if (defined($self->{applications}->{$tag})) {
              $self->{output}->add_option_msg(short_msg => "Tag '" . $tag . "' (application) already exists.");
              $self->{output}->option_exit();
          }
          
          $self->{applications}->{$tag} = {
              services => {}
          };
          foreach my $service (split /\|/, $services) {
              if (!defined($self->{services}->{$service})) {
                  $self->{output}->add_option_msg(short_msg => "Service '" . $service . "' is not defined.");
                  $self->{output}->option_exit();
              }
              $self->{applications}->{$tag}->{services}->{$service} = 1;
          }
  
          if (($self->{perfdata}->threshold_validate(label => 'warning-app-' . $tag, value => $warn)) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $warn . "' for application '$tag'.");
              $self->{output}->option_exit();
          }
          if (($self->{perfdata}->threshold_validate(label => 'critical-app-' . $tag, value => $crit)) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $crit . "' for application '$tag'.");
              $self->{output}->option_exit();
          }
      }
  }
  
  sub test_services {
      my ($self, %options) = @_;
  
      foreach my $tag (keys %{$self->{services}}) {
          foreach (@{$self->{connections}}) {
              if (/$self->{services}->{$tag}->{filter}/) {
                  $self->{services}->{$tag}->{number}++;
              }
          }
  
          my $exit_code = $self->{perfdata}->threshold_check(
              value => $self->{services}->{$tag}->{number}, 
              threshold => [ { label => 'critical-service-' . $tag, 'exit_litteral' => 'critical' }, { label => 'warning-service-' . $tag, exit_litteral => 'warning' } ]
          );
          my ($perf_label, $msg) = ('service_' . $tag, "Service '$tag' connections: %d");
          if ($self->{services}->{$tag}->{builtin} == 1) {
              ($perf_label, $msg) = ($tag, $self->{services}->{$tag}->{msg});
          }
  
          $self->{output}->output_add(
              severity => $exit_code,
              short_msg => sprintf($msg, $self->{services}->{$tag}->{number})
          );
          $self->{output}->perfdata_add(
              label => $perf_label,
              value => $self->{services}->{$tag}->{number},
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-service-' . $tag),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-service-' . $tag),
              min => 0
          );
      }
  }
  
  sub test_applications {
      my ($self, %options) = @_;
  
      foreach my $tag (keys %{$self->{applications}}) {
          my $number = 0;
  
          foreach (keys %{$self->{applications}->{$tag}->{services}}) {
              $number += $self->{services}->{$_}->{number};
          }
  
          my $exit_code = $self->{perfdata}->threshold_check(
              value => $number, 
              threshold => [ { label => 'critical-app-' . $tag, 'exit_litteral' => 'critical' }, { label => 'warning-app-' . $tag, exit_litteral => 'warning' } ]
          );
          $self->{output}->output_add(
              severity => $exit_code,
              short_msg => sprintf("Applicatin '%s' connections: %d", $tag, $number)
          );
          $self->{output}->perfdata_add(
              label => 'app_' . $tag,
              value => $number,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-app-' . $tag),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-app-' . $tag),
              min => 0
          );
      }
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (($self->{perfdata}->threshold_validate(label => 'warning-service-total', value => $self->{option_results}->{warning})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
          $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'critical-service-total', value => $self->{option_results}->{critical})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
          $self->{output}->option_exit();
      }
      $self->check_services();
      $self->check_applications();
  }
  
  sub run {
      my ($self, %options) = @_;
  
      $self->build_connections(custom => $options{custom});
      $self->test_services();
      $self->test_applications();
  
      foreach (keys %{$self->{states}}) {
          $self->{output}->perfdata_add(
              label => 'con_' . $_,
              value => $self->{states}->{$_},
              min => 0
          );
      }
  
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check tcp/udp connections (udp connections are not in total. Use option '--service' to check it).
  'ipx', 'x25' connections are not checked (need output to do it. If you have, you can post it in github :)
  
  Command used: 'netstat -antu 2>&1' or 'ss -a -A tcp,udp -n 2>&1'
  
  =over 8
  
  =item B<--warning>
  
  Warning threshold for total connections.
  
  =item B<--critical>
  
  Critical threshold for total connections.
  
  =item B<--service>
  
  Check tcp connections following rules:
  tag,[type],[state],[port-src],[port-dst],[filter-ip-src],[filter-ip-dst],[threshold-warning],[threshold-critical]
  
  Example to test SSH connections on the server: --service="ssh,,,22,,,,10,20" 
  
  =over 16
  
  =item <tag>
  
  Name to identify service (must be unique and couldn't be 'total').
  
  =item <type>
  
  regexp - can use 'ipv4', 'ipv6', 'udp', 'udp6'. Empty means all.
  
  =item <state>
  
  regexp - can use 'finWait1', 'established',... Empty means all (minus listen).
  For udp connections, there are 'established' and 'listen'.
  
  =item <filter-ip-*>
  
  regexp - can use to exclude or include some IPs.
  
  =item <threshold-*>
  
  nagios-perfdata - number of connections.
  
  =back
  
  =item B<--application>
  
  Check tcp connections of mutiple services:
  tag,[services],[threshold-warning],[threshold-critical]
  
  Example:
  --application="web,http|https,100,200"
  
  =over 16
  
  =item <tag>
  
  Name to identify application (must be unique).
  
  =item <services>
  
  List of services (used the tag name. Separated by '|').
  
  =item <threshold-*>
  
  nagios-perfdata - number of connections.
  
  =back
  
  =item B<--con-mode>
  
  Default mode for parsing and command: 'netstat' (default) or 'ss'.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_CONNECTIONS

$fatpacked{"os/linux/local/mode/cpu.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_CPU';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::cpu;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use Digest::MD5 qw(md5_hex);
  
  sub custom_cpu_avg_calc {
      my ($self, %options) = @_;
  
      my ($skipped, $buffer) = (1, 1);
      my ($count, $total_cpu) = (0, 0);
      foreach (keys %{$options{new_datas}}) {
          if (/^(.*?cpu\d+)_idle/) {
              my $prefix = $1;
              $skipped = 0;
              next if (!defined($options{old_datas}->{$_}));
              $buffer = 0;
  
              my ($old_total, $old_cpu_idle) = (0, 0);
              if ($options{new_datas}->{$_} >= $options{old_datas}->{$_}) {
                  $old_total = $options{old_datas}->{$_} + $options{old_datas}->{$prefix . '_system'} + 
                      $options{old_datas}->{$prefix . '_user'} + $options{old_datas}->{$prefix . '_iowait'};
                  $old_cpu_idle = $options{old_datas}->{$_};
              }
              my $total_elapsed = ($options{new_datas}->{$_} + $options{new_datas}->{$prefix . '_system'} + 
                  $options{new_datas}->{$prefix . '_user'} + $options{new_datas}->{$prefix . '_iowait'}) -
                  $old_total;
              if ($total_elapsed == 0) {
                  $self->{error_msg} = 'no new values for cpu counters';
                  return -12;
              }
  
              my $idle_elapsed = $options{new_datas}->{$_} - $old_cpu_idle;
              $total_cpu += 100 - (100 * $idle_elapsed / $total_elapsed);
              $count++;
          }
      }
  
      return -10 if ($skipped == 1);
      if ($buffer == 1) {
          $self->{error_msg} = "Buffer creation";
          return -1;
      }
  
      $self->{result_values}->{prct_used} = $total_cpu / $count;
      return 0;
  }
  
  sub custom_cpu_core_calc {
      my ($self, %options) = @_;
  
      my ($old_total, $old_cpu_idle) = (0, 0);
      if ($options{new_datas}->{$self->{instance} . '_idle'} >= $options{old_datas}->{$self->{instance} . '_idle'}) {
          $old_total = $options{old_datas}->{$self->{instance} . '_idle'} + $options{old_datas}->{$self->{instance} . '_system'} + 
              $options{old_datas}->{$self->{instance} . '_user'} + $options{old_datas}->{$self->{instance} . '_iowait'};
          $old_cpu_idle = $options{old_datas}->{$self->{instance} . '_idle'};
      }
      my $total_elapsed = ($options{new_datas}->{$self->{instance} . '_idle'} + $options{new_datas}->{$self->{instance} . '_system'} + 
          $options{new_datas}->{$self->{instance} . '_user'} + $options{new_datas}->{$self->{instance} . '_iowait'}) -
          $old_total;
      if ($total_elapsed == 0) {
          $self->{error_msg} = 'no new values for cpu counters';
          return -12;
      }
  
      my $idle_elapsed = $options{new_datas}->{$self->{instance} . '_idle'} - $old_cpu_idle;
      $self->{result_values}->{prct_used} = 100 - (100 * $idle_elapsed / $total_elapsed);
  
      return 0;
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'cpu_avg', type => 0 },
          { name => 'cpu_core', type => 1, cb_prefix_output => 'prefix_cpu_core_output' }
      ];
  
      $self->{maps_counters}->{cpu_avg} = [
          { label => 'average', nlabel => 'cpu.utilization.percentage', set => {
                  key_values => [],
                  closure_custom_calc => $self->can('custom_cpu_avg_calc'),
                  manual_keys => 1, 
                  output_template => 'CPU(s) average usage is %.2f %%',
                  output_use => 'prct_used', threshold_use => 'prct_used',
                  perfdatas => [
                      { value => 'prct_used', template => '%.2f',
                        min => 0, max => 100, unit => '%' }
                  ]
              }
          }
      ];
  
      $self->{maps_counters}->{cpu_core} = [
          { label => 'core', nlabel => 'core.cpu.utilization.percentage', set => {
                  key_values => [
                      { name => 'idle', diff => 1 }, { name => 'user', diff => 1 }, 
                      { name => 'system', diff => 1 }, { name => 'iowait', diff => 1 }, { name => 'display' }
                  ],
                  closure_custom_calc => $self->can('custom_cpu_core_calc'),
                  output_template => 'usage : %.2f %%',
                  output_use => 'prct_used', threshold_use => 'prct_used',
                  perfdatas => [
                      { value => 'prct_used', template => '%.2f',
                        min => 0, max => 100, unit => '%', label_extra_instance => 1 }
                  ]
              }
          }
      ];
  }
  
  sub prefix_cpu_core_output {
      my ($self, %options) = @_;
  
      return "CPU '" . $options{instance_value}->{display} . "' ";
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/stat 2>&1'
      );
  
      $self->{cpu_avg} = {};
      $self->{cpu_core} = {};
      foreach (split(/\n/, $stdout)) {
          next if (!/cpu(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
          my $cpu_number = $1;
          
          $self->{cpu_core}->{$cpu_number} = {
              display => $cpu_number,
              idle => $5,
              system => $4,
              user => $2,
              iowait => $6
          };
          $self->{cpu_avg}->{'cpu' . $cpu_number . '_idle'} = $5;
          $self->{cpu_avg}->{'cpu' . $cpu_number . '_system'} = $4;
          $self->{cpu_avg}->{'cpu' . $cpu_number . '_user'} = $2;
          $self->{cpu_avg}->{'cpu' . $cpu_number . '_iowait'} = $6;
      }
   
      $self->{cache_name} = 'cache_linux_local_' . $options{custom}->get_identifier()  . '_' . $self->{mode} . '_' .
          (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
  }
  
  1;
  
  
  =head1 MODE
  
  Check system CPUs (need '/proc/stat' file).
  Command used: cat /proc/stat 2>&1
  
  =over 8
  
  =item B<--warning-average>
  
  Warning threshold average CPU utilization. 
  
  =item B<--critical-average>
  
  Critical  threshold average CPU utilization. 
  
  =item B<--warning-core>
  
  Warning thresholds for each CPU core
  
  =item B<--critical-core>
  
  Critical thresholds for each CPU core
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_CPU

$fatpacked{"os/linux/local/mode/cpudetailed.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_CPUDETAILED';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::cpudetailed;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use centreon::plugins::statefile;
  
  my $maps = [
      { counter => 'user', output => 'User %.2f %%', position => 1 },
      { counter => 'nice', output => 'Nice %.2f %%', position => 2 }, 
      { counter => 'system', output => 'System %.2f %%', position => 3 },
      { counter => 'idle', output => 'Idle %.2f %%', position => 4 },
      { counter => 'wait', output => 'Wait %.2f %%', position => 5 },
      { counter => 'interrupt', output => 'Interrupt %.2f %%', position => 6 },
      { counter => 'softirq', output => 'Soft Irq %.2f %%', position => 7 },
      { counter => 'steal', output => 'Steal %.2f %%', position => 8 },
      { counter => 'guest', output => 'Guest %.2f %%', position => 9 },
      { counter => 'guestnice', output => 'Guest Nice %.2f %%', position => 10 }
  ];
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
      });
  
      foreach (@{$maps}) {
          $options{options}->add_options(arguments => {
              'warning-' . $_->{counter} . ':s'    => { name => 'warning_' . $_->{counter} },
              'critical-' . $_->{counter} . ':s'    => { name => 'critical_' . $_->{counter} }
          });
      }
  
      $self->{statefile_cache} = centreon::plugins::statefile->new(%options);
      $self->{hostname} = undef;
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      foreach (@{$maps}) {
          if (($self->{perfdata}->threshold_validate(label => 'warning-' . $_->{counter}, value => $self->{option_results}->{'warning_' . $_->{counter}})) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong warning-" . $_->{counter} . " threshold '" . $self->{option_results}->{'warning_' . $_->{counter}} . "'.");
              $self->{output}->option_exit();
          }
          if (($self->{perfdata}->threshold_validate(label => 'critical-' . $_->{counter}, value => $self->{option_results}->{'critical_' . $_->{counter}})) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong critical-" . $_->{counter} . " threshold '" . $self->{option_results}->{'critical_' . $_->{counter}} . "'.");
              $self->{output}->option_exit();
          }
      }
      
      $self->{statefile_cache}->check_options(%options);
      $self->{hostname} = $self->{option_results}->{hostname};
      if (!defined($self->{hostname})) {
          $self->{hostname} = 'me';
      }
  }
  
  sub run {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/stat 2>&1'
      );
  
      $self->{statefile_cache}->read(statefile => 'cache_linux_local_' . $options{custom}->get_identifier()  . '_' .  $self->{mode});
      # Manage values
      my ($buffer_creation, $exit) = (0, 0);
      my $save_datas = {};
      my $new_datas = {};
      my $old_datas = {};
      my ($total_datas, $total_cpu_num) = ({}, 0);
  
      foreach my $line (split(/\n/, $stdout)) {
          next if ($line !~ /cpu(\d+)\s+/);
          my $cpu_number = $1;
          my @values = split /\s+/, $line;
          
          foreach (@{$maps}) {
              next if (!defined($values[$_->{position}]));
              if (!defined($new_datas->{$cpu_number})) {
                  $new_datas->{$cpu_number} = { total => 0 };
                  $old_datas->{$cpu_number} = { total => 0 };
              }
  
              $new_datas->{$cpu_number}->{$_->{counter}} = $values[$_->{position}];
              $save_datas->{'cpu' . $cpu_number . '_' . $_->{counter}} = $values[$_->{position}];
              my $tmp_value = $self->{statefile_cache}->get(name => 'cpu' . $cpu_number . '_' . $_->{counter});
              if (!defined($tmp_value)) {
                  $buffer_creation = 1;
                  next;
              }
  
              if ($new_datas->{$cpu_number}->{$_->{counter}} < $tmp_value) {
                  $buffer_creation = 1;
                  next;
              }
              
              $exit = 1;
              $old_datas->{$cpu_number}->{$_->{counter}} = $tmp_value;
              $new_datas->{$cpu_number}->{total} += $new_datas->{$cpu_number}->{$_->{counter}};
              $old_datas->{$cpu_number}->{total} += $old_datas->{$cpu_number}->{$_->{counter}};
          }
      }
  
      $self->{statefile_cache}->write(data => $save_datas);
      if ($buffer_creation == 1) {
          $self->{output}->output_add(
              severity => 'OK',
              short_msg => "Buffer creation..."
          );
          if ($exit == 0) {
              $self->{output}->display();
              $self->{output}->exit();
          }
      }
  
      $self->{output}->output_add(
          severity => 'OK', 
          short_msg => "CPUs usages are ok."
      );
  
      foreach my $cpu_number (sort keys(%$new_datas)) {
          # In buffer creation. New cpu
          next if (scalar(keys %{$old_datas->{$cpu_number}}) <= 1);
          
          if ($new_datas->{$cpu_number}->{total} - $old_datas->{$cpu_number}->{total} == 0) {
              $self->{output}->output_add(
                  severity => 'OK',
                  short_msg => "Counter not moved. Have to wait."
              );
              $self->{output}->display();
              $self->{output}->exit();
          }
          $total_cpu_num++;
          
          my @exits;
          foreach (@{$maps}) {
              next if (!defined($new_datas->{$cpu_number}->{$_->{counter}}));
              my $value = (($new_datas->{$cpu_number}->{$_->{counter}} - $old_datas->{$cpu_number}->{$_->{counter}}) * 100) / 
                           ($new_datas->{$cpu_number}->{total} - $old_datas->{$cpu_number}->{total});
              push @exits, $self->{perfdata}->threshold_check(value => $value, threshold => [ { label => 'critical-' . $_->{counter}, 'exit_litteral' => 'critical' }, { label => 'warning-' . $_->{counter}, 'exit_litteral' => 'warning' }]);
          }
  
          $exit = $self->{output}->get_most_critical(status => [ @exits ]);
          my $str_output = "CPU '$cpu_number' Usage: ";
          my $str_append = '';
          foreach (@{$maps}) {
              next if (!defined($new_datas->{$cpu_number}->{$_->{counter}}));
          
              my $value = (($new_datas->{$cpu_number}->{$_->{counter}} - $old_datas->{$cpu_number}->{$_->{counter}}) * 100) / 
                           ($new_datas->{$cpu_number}->{total} - $old_datas->{$cpu_number}->{total});
              $total_datas->{$_->{counter}} = 0 if (!defined($total_datas->{$_->{counter}}));
              $total_datas->{$_->{counter}} += $value;
              $str_output .= $str_append . sprintf($_->{output}, $value);
              $str_append = ', ';
              my $warning = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $_->{counter});
              my $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $_->{counter});
  
              $self->{output}->perfdata_add(
                  nlabel => 'core.cpu.utilization.percentage',
                  unit => '%',
                  instances => [$cpu_number, $_->{counter}],
                  value => sprintf("%.2f", $value),
                  warning => $warning,
                  critical => $critical,
                  min => 0, max => 100
              );
          }
  
          $self->{output}->output_add(long_msg => $str_output);
          if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
              $self->{output}->output_add(
                  severity => $exit,
                  short_msg => $str_output
              );
          }
      }
      
      # We can display a total (some buffer creation and counters have moved)
      if ($total_cpu_num != 0) {
          foreach my $counter (sort keys %{$total_datas}) {
              $self->{output}->perfdata_add(
                  nlabel => 'cpu.utilization.percentage',
                  instances => $counter,
                  unit => '%',
                  value => sprintf("%.2f", $total_datas->{$counter} / $total_cpu_num),
                  min => 0, max => 100
              );
          }
      }
   
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check average usage for each CPUs (need '/proc/stat' file)
  (User, Nice, System, Idle, Wait, Interrupt, SoftIRQ, Steal, Guest, GuestNice)
  Command used: cat /proc/stat 2>&1
  
  =over 8
  
  =item B<--warning-*>
  
  Warning threshold in percent.
  Can be: 'user', 'nice', 'system', 'idle', 'wait', 'interrupt', 'softirq', 'steal', 'guest', 'guestnice'.
  
  =item B<--critical-*>
  
  Critical threshold in percent.
  Can be: 'user', 'nice', 'system', 'idle', 'wait', 'interrupt', 'softirq', 'steal', 'guest', 'guestnice'.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_CPUDETAILED

$fatpacked{"os/linux/local/mode/discoverysnmp.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_DISCOVERYSNMP';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::discoverysnmp;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use os::linux::local::mode::resources::discovery qw($discovery_match);
  use centreon::plugins::snmp;
  use NetAddr::IP;
  use JSON::XS;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'subnet:s'          => { name => 'subnet' },
          'snmp-port:s'       => { name => 'snmp_port', default => 161 },
          'snmp-version:s@'   => { name => 'snmp_version' },
          'snmp-community:s@' => { name => 'snmp_community' },
          'snmp-timeout:s'    => { name => 'snmp_timeout', default => 1 },
          'prettify'          => { name => 'prettify' },
          'extra-oids:s'      => { name => 'extra_oids' }
      });
  
      $self->{snmp} = centreon::plugins::snmp->new(%options, noptions => 1);
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (!defined($self->{option_results}->{subnet}) ||
          $self->{option_results}->{subnet} !~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/) {
          $self->{output}->add_option_msg(short_msg => "Need to specify --subnet option (<ip>/<cidr>).");
          $self->{output}->option_exit();
      }
      if (!defined($self->{option_results}->{snmp_community}) || $self->{option_results}->{snmp_community} eq '') {
          $self->{output}->add_option_msg(short_msg => "Need to specify --snmp-community option.");
          $self->{output}->option_exit();
      }
      if (!defined($self->{option_results}->{snmp_version}) || $self->{option_results}->{snmp_version} eq '') {
          $self->{output}->add_option_msg(short_msg => "Need to specify --snmp-version option.");
          $self->{output}->option_exit();
      }
      if (!defined($self->{option_results}->{snmp_timeout}) || $self->{option_results}->{snmp_timeout} !~ /(\d+)/) {
          $self->{output}->add_option_msg(short_msg => "Need to specify --snmp-timeout option.");
          $self->{output}->option_exit();
      }
  
      $self->{snmp}->set_snmp_connect_params(Timeout => $self->{option_results}->{snmp_timeout} * (10**6));
      $self->{snmp}->set_snmp_connect_params(Retries => 0);
      $self->{snmp}->set_snmp_params(subsetleef => 1);
      $self->{snmp}->set_snmp_params(snmp_autoreduce => 0);
      $self->{snmp}->set_snmp_params(snmp_errors_exit => 'unknown');
  
      $self->{oid_sysDescr} = '.1.3.6.1.2.1.1.1.0';
      $self->{oid_sysName} = '.1.3.6.1.2.1.1.5.0';
  
      $self->{oids} = [$self->{oid_sysDescr}, $self->{oid_sysName}];
      $self->{extra_oids} = {};
      if (defined($self->{option_results}->{extra_oids})) {
          my @extra_oids = split(/,/, $self->{option_results}->{extra_oids});
          foreach my $extra_oid (@extra_oids) {
              next if ($extra_oid eq '');
  
              my @values = split(/=/, $extra_oid);
              my ($name, $oid) = ('', $values[0]);
              if (defined($values[1])) {
                  $name = $values[0];
                  $oid = $values[1];
              }
  
              $oid =~ s/^(\d+)/\.$1/;
              $self->{extra_oids}->{$oid} = $name;
              push @{$self->{oids}}, $oid;
          }
      }
  }
  
  sub define_type {
      my ($self, %options) = @_;
  
      return 'unknown' unless (defined($options{desc}) && $options{desc} ne '');
      foreach (@$discovery_match) {
          if ($options{desc} =~ /$_->{re}/) {
              return $_->{type};
          }
      }
  
      return 'unknown';
  }
  
  sub snmp_request {
      my ($self, %options) = @_;
  
      $self->{snmp}->set_snmp_connect_params(DestHost => $options{ip});
      $self->{snmp}->set_snmp_connect_params(Community => $options{community});
      $self->{snmp}->set_snmp_connect_params(Version => $options{version});
      $self->{snmp}->set_snmp_connect_params(RemotePort => $options{port});
      return undef if ($self->{snmp}->connect(dont_quit => 1) != 0);
      return $self->{snmp}->get_leef(
          oids => $self->{oids},
          nothing_quit => 0, dont_quit => 1
      );
  }
  
  sub run {
      my ($self, %options) = @_;
  
      my @disco_data;
      my $disco_stats;
      
      my $last_version;
      my $last_community;
      my $subnet = NetAddr::IP->new($self->{option_results}->{subnet});
  
      $disco_stats->{start_time} = time();
  
      foreach my $ip (@{$subnet->splitref($subnet->bits())}) {
          my $result;
          foreach my $community (@{$self->{option_results}->{snmp_community}}) {
              foreach my $version (@{$self->{option_results}->{snmp_version}}) {
                  $result = $self->snmp_request(
                      ip => $ip->addr,
                      community => $community,
                      version => $version,
                      port => $self->{option_results}->{snmp_port}
                  );
                  $last_version = $version;
                  $last_community = $community;
                  last if (defined($result));
              }
          }
          next if (!defined($result) || $result eq '');
  
          my %host;
          $host{type} = $self->define_type(desc => $result->{$self->{oid_sysDescr}});
          $host{desc} = $result->{$self->{oid_sysDescr}};
          $host{desc} =~ s/\n/ /g if (defined($host{desc}));
          $host{ip} = $ip->addr;
          $host{hostname} = $result->{$self->{oid_sysName}};
          $host{snmp_version} = $last_version;
          $host{snmp_community} = $last_community;
          $host{snmp_port} = $self->{option_results}->{snmp_port};
          $host{extra_oids} = [];
          foreach (keys %{$self->{extra_oids}}) {
              my $label = defined($self->{extra_oids}->{$_}) && $self->{extra_oids}->{$_} ne '' ? $self->{extra_oids}->{$_} : $_;
              my $value = defined($result->{$_}) ? $result->{$_} : 'unknown';
              push @{$host{extra_oids}}, { oid => $label, value => $value };
          }
  
          push @disco_data, \%host;
      }
      
      $disco_stats->{end_time} = time();
      $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time};
      $disco_stats->{discovered_items} = @disco_data;
      $disco_stats->{results} = \@disco_data;
  
      my $encoded_data;
      eval {
          if (defined($self->{option_results}->{prettify})) {
              $encoded_data = JSON::XS->new->utf8->pretty->encode($disco_stats);
          } else {
              $encoded_data = JSON::XS->new->utf8->encode($disco_stats);
          }
      };
      if ($@) {
          $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}';
      }
      
      $self->{output}->output_add(short_msg => $encoded_data);
      $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1);
      $self->{output}->exit();
  }
      
  1;
  
  
  =head1 MODE
  
  Resources discovery.
  
  =over 8
  
  =item B<--subnet>
  
  Specify subnet from which discover
  resources (must be <ip>/<cidr> format) (mandatory).
  
  =item B<--snmp-port>
  
  Specify SNMP port (default: 161).
  
  =item B<--snmp-version>
  
  Specify SNMP version (can be defined multiple times) (mandatory).
  
  =item B<--snmp-community>
  
  Specify SNMP community (can be defined multiple times) (mandatory).
  
  =item B<--snmp-timeout>
  
  Specify SNMP timeout in second (default: 1).
  
  =item B<--prettify>
  
  Prettify JSON output.
  
  =item B<--extra-oids>
  
  Specify extra OIDs to get (example: --extra-oids='hrSystemInitialLoadParameters=1.3.6.1.2.1.25.1.4.0,sysDescr=.1.3.6.1.2.1.1.1.0').
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_DISCOVERYSNMP

$fatpacked{"os/linux/local/mode/discoverysnmpv3.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_DISCOVERYSNMPV3';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::discoverysnmpv3;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use os::linux::local::mode::resources::discovery qw($discovery_match);
  use centreon::plugins::snmp;
  use NetAddr::IP;
  use JSON::XS;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'subnet:s'          => { name => 'subnet' },
          'authpassphrase:s'  => { name => 'snmp_auth_passphrase' },
          'privpassphrase:s'  => { name => 'snmp_priv_passphrase' },
          'authprotocol:s'    => { name => 'snmp_auth_protocol' },
          'privprotocol:s'    => { name => 'snmp_priv_protocol' },
          'snmp-username:s'   => { name => 'snmp_security_name' },
          'snmp-port:s'       => { name => 'snmp_port', default => 161 },
          'snmp-timeout:s'    => { name => 'snmp_timeout', default => 1 },
          'prettify'          => { name => 'prettify' },
          'extra-oids:s'      => { name => 'extra_oids' }
      });
  
      $self->{snmp} = centreon::plugins::snmp->new(%options, noptions => 1);
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (!defined($self->{option_results}->{subnet}) ||
          $self->{option_results}->{subnet} !~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/) {
          $self->{output}->add_option_msg(short_msg => "Need to specify --subnet option (<ip>/<cidr>).");
          $self->{output}->option_exit();
      }
  
      delete $self->{snmp}->{snmp_params}->{Community};
      $self->{snmp}->set_snmp_connect_params(SecName => $self->{option_results}->{snmp_security_name}) if (defined($self->{option_results}->{snmp_security_name}));
  
      if (!defined($self->{option_results}->{snmp_security_name}) || $self->{option_results}->{snmp_security_name} eq '') {
          $self->{output}->add_option_msg(short_msg => 'Missing parameter Security Name.');
          $self->{output}->option_exit();
      }
  
      # unauthenticated and unencrypted
      $self->{snmp}->set_snmp_connect_params(SecLevel => 'noAuthNoPriv');
      my $user_activate = 0;
      if (defined($self->{option_results}->{snmp_auth_passphrase}) && $self->{option_results}->{snmp_auth_passphrase} ne '') {
          if (!defined($self->{option_results}->{snmp_auth_protocol})) {
              $self->{output}->add_option_msg(short_msg => 'Missing parameter authenticate protocol.');
              $self->{output}->option_exit();
          }
          $self->{option_results}->{snmp_auth_protocol} = uc($self->{option_results}->{snmp_auth_protocol});
          if ($self->{option_results}->{snmp_auth_protocol} !~ /^(?:MD5|SHA|SHA224|SHA256|SHA384|SHA512)$/) {
              $self->{output}->add_option_msg(short_msg => 'Wrong authentication protocol.');
              $self->{output}->option_exit();
          }
          $self->{snmp}->set_snmp_connect_params(SecLevel => 'authNoPriv');
          $self->{snmp}->set_snmp_connect_params(AuthProto => $self->{option_results}->{snmp_auth_protocol});
          $self->{snmp}->set_snmp_connect_params(AuthPass => $self->{option_results}->{snmp_auth_passphrase});
          $user_activate = 1;
      }
  
      if (defined($self->{option_results}->{snmp_priv_passphrase}) && $self->{option_results}->{snmp_priv_passphrase} ne '') {
          if (!defined($self->{option_results}->{snmp_priv_protocol})) {
              $self->{output}->add_option_msg(short_msg => 'Missing parameter privacy protocol.');
              $self->{output}->option_exit();
          }
          $self->{option_results}->{snmp_priv_protocol} = uc($self->{option_results}->{snmp_priv_protocol});
          if ($self->{option_results}->{snmp_priv_protocol} !~ /^(?:DES|AES|AES192|AES192C|AES256|AES256C)$/) {
              $self->{output}->add_option_msg(short_msg => 'Wrong privacy protocol.');
              $self->{output}->option_exit();
          }
          if ($user_activate == 0) {
              $self->{output}->add_option_msg(short_msg => 'Cannot use snmp v3 privacy option without snmp v3 authentification options.');
              $self->{output}->option_exit();
          }
          $self->{snmp}->set_snmp_connect_params(SecLevel => 'authPriv');
          $self->{snmp}->set_snmp_connect_params(PrivPass => $self->{option_results}->{snmp_priv_passphrase});
          $self->{snmp}->set_snmp_connect_params(PrivProto => $self->{option_results}->{snmp_priv_protocol});
      }
  
      $self->{snmpv3_combo} = '';
      my $options_mapping = {
          snmp_auth_passphrase    => "--authpassphrase",
          snmp_priv_passphrase    => "--privpassphrase" ,
          snmp_auth_protocol      => "--authprotocol",
          snmp_priv_protocol      => "--privprotocol",
          snmp_security_name      => "--snmp-username"
      };
      foreach my $option (keys %$options_mapping) {
          next if (!defined($self->{option_results}->{$option}) || $self->{option_results}->{$option} eq "");
          $self->{snmpv3_combo} .= " " . $options_mapping->{$option} . "='" .  $self->{option_results}->{$option} . "'";
      }
  
      $self->{snmp}->set_snmp_connect_params(Timeout => $self->{option_results}->{snmp_timeout} * (10**6));
      $self->{snmp}->set_snmp_connect_params(Retries => 0);
      $self->{snmp}->set_snmp_connect_params(RemotePort => $self->{option_results}->{port});
      $self->{snmp}->set_snmp_connect_params(Version => 3);
      $self->{snmp}->set_snmp_params(subsetleef => 1);
      $self->{snmp}->set_snmp_params(snmp_autoreduce => 0);
      $self->{snmp}->set_snmp_params(snmp_errors_exit => 'unknown');
  
      $self->{oid_sysDescr} = '.1.3.6.1.2.1.1.1.0';
      $self->{oid_sysName} = '.1.3.6.1.2.1.1.5.0';
  
      $self->{oids} = [$self->{oid_sysDescr}, $self->{oid_sysName}];
      $self->{extra_oids} = {};
      if (defined($self->{option_results}->{extra_oids})) {
          my @extra_oids = split(/,/, $self->{option_results}->{extra_oids});
          foreach my $extra_oid (@extra_oids) {
              next if ($extra_oid eq '');
  
              my @values = split(/=/, $extra_oid);
              my ($name, $oid) = ('', $values[0]);
              if (defined($values[1])) {
                  $name = $values[0];
                  $oid = $values[1];
              }
  
              $oid =~ s/^(\d+)/\.$1/;
              $self->{extra_oids}->{$oid} = $name;
              push @{$self->{oids}}, $oid;
          }
      }
  }
  
  sub define_type {
      my ($self, %options) = @_;
  
      return 'unknown' unless (defined($options{desc}) && $options{desc} ne '');
      foreach (@$discovery_match) {
          if ($options{desc} =~ /$_->{re}/) {
              return $_->{type};
          }
      }
  
      return 'unknown';
  }
  
  sub snmp_request {
      my ($self, %options) = @_;
  
      $self->{snmp}->set_snmp_connect_params(DestHost => $options{ip});
      return undef if ($self->{snmp}->connect(dont_quit => 1) != 0);
      return $self->{snmp}->get_leef(
          oids => $self->{oids},
          nothing_quit => 0, dont_quit => 1
      );
  }
  
  sub run {
      my ($self, %options) = @_;
  
      my @disco_data;
      my $disco_stats;
  
      my $subnet = NetAddr::IP->new($self->{option_results}->{subnet});
      $disco_stats->{start_time} = time();
  
      foreach my $ip (@{$subnet->splitref($subnet->bits())}) {
          my $snmp_result = $self->snmp_request(ip => $ip->addr);
          next if (!defined($snmp_result));
          next if (!defined($snmp_result->{$self->{oid_sysDescr}}) && !defined($snmp_result->{$self->{oid_sysName}})); 
  
          my %host;
          $host{type} = $self->define_type(desc => $snmp_result->{$self->{oid_sysDescr}});
          $host{desc} = $snmp_result->{$self->{oid_sysDescr}};
          $host{desc} =~ s/\n/ /g if (defined($host{desc}));
          $host{ip} = $ip->addr;
          $host{hostname} = $snmp_result->{$self->{oid_sysName}};
          $host{snmp_version} = '3';
          $host{snmp_port} = $self->{option_results}->{snmp_port};
          $host{snmpv3_extraopts} = $self->{snmpv3_combo};
          $host{extra_oids} = [];
          foreach (keys %{$self->{extra_oids}}) {
              my $label = defined($self->{extra_oids}->{$_}) && $self->{extra_oids}->{$_} ne '' ? $self->{extra_oids}->{$_} : $_;
              my $value = defined($snmp_result->{$_}) ? $snmp_result->{$_} : 'unknown';
              push @{$host{extra_oids}}, { oid => $label, value => $value };
          }
  
          push @disco_data, \%host;
      }
      
      $disco_stats->{end_time} = time();
      $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time};
      $disco_stats->{discovered_items} = @disco_data;
      $disco_stats->{results} = \@disco_data;
  
      my $encoded_data;
      eval {
          if (defined($self->{option_results}->{prettify})) {
              $encoded_data = JSON::XS->new->utf8->pretty->encode($disco_stats);
          } else {
              $encoded_data = JSON::XS->new->utf8->encode($disco_stats);
          }
      };
      if ($@) {
          $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}';
      }
      
      $self->{output}->output_add(short_msg => $encoded_data);
      $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1);
      $self->{output}->exit();
  }
      
  1;
  
  
  =head1 MODE
  
  Resources discovery.
  
  =over 8
  
  =item B<--subnet>
  
  Specify subnet from which discover
  resources (must be <ip>/<cidr> format) (mandatory).
  
  Specify SNMP community (can be defined multiple times) (mandatory).
  
  =item B<--snmp-timeout>
  
  Specify SNMP timeout in second (default: 1).
  
  =item B<--prettify>
  
  Prettify JSON output.
  
  =item B<--extra-oids>
  
  Specify extra OIDs to get (example: --extra-oids='hrSystemInitialLoadParameters=1.3.6.1.2.1.25.1.4.0,sysDescr=.1.3.6.1.2.1.1.1.0').
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_DISCOVERYSNMPV3

$fatpacked{"os/linux/local/mode/diskio.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_DISKIO';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::diskio;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use Digest::MD5 qw(md5_hex);
  
  sub custom_usage_calc {
      my ($self, %options) = @_;
  
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      $self->{result_values}->{usage_persecond} = ($options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_sectors'} - $options{old_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_sectors'}) 
          * $self->{instance_mode}->{option_results}->{bytes_per_sector} / $options{delta_time};
      return 0;
  }
  
  sub custom_wait_calc {
      my ($self, %options) = @_;
  
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      $self->{result_values}->{wait} = 0;
      my $tput = ($options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_ios'} - $options{old_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_ios'});
      if ($tput) {
          $self->{result_values}->{wait} = ($options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_ticks'} - $options{old_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_ticks'}) / $tput;
      }
  
      return 0;
  }
  
  sub custom_utils_calc {
      my ($self, %options) = @_;
  
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      my $delta_ms =
          $self->{instance_mode}->{option_results}->{interrupt_frequency} *
          (
              ($options{new_datas}->{$self->{instance} . '_cpu_idle'} - $options{old_datas}->{$self->{instance} . '_cpu_idle'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_user'} - $options{old_datas}->{$self->{instance} . '_cpu_user'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_iowait'} - $options{old_datas}->{$self->{instance} . '_cpu_iowait'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_system'} - $options{old_datas}->{$self->{instance} . '_cpu_system'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_hardirq'} - $options{old_datas}->{$self->{instance} . '_cpu_hardirq'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_softirq'} - $options{old_datas}->{$self->{instance} . '_cpu_softirq'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_nice'} - $options{old_datas}->{$self->{instance} . '_cpu_nice'}) +
              ($options{new_datas}->{$self->{instance} . '_cpu_steal'} - $options{old_datas}->{$self->{instance} . '_cpu_steal'})
          )
          / $options{new_datas}->{$self->{instance} . '_cpu_total'} / 100;
      $self->{result_values}->{utils} = 0;
      if ($delta_ms != 0) {
          $self->{result_values}->{utils} = 100 * ($options{new_datas}->{$self->{instance} . '_ticks'} - $options{old_datas}->{$self->{instance} . '_ticks'}) / $delta_ms;
          $self->{result_values}->{utils} = 100 if ($self->{result_values}->{utils} > 100);
      }
      return 0;
  }
  
  sub custom_svctm_calc {
      my ($self, %options) = @_;
  
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      my $nr_ios = ($options{new_datas}->{$self->{instance} . '_rd_ios'} - $options{old_datas}->{$self->{instance} . '_rd_ios'}) +
          ($options{new_datas}->{$self->{instance} . '_wr_ios'} - $options{old_datas}->{$self->{instance} . '_wr_ios'});
      my $itv = (
          ($options{new_datas}->{$self->{instance} . '_cpu_idle'} - $options{old_datas}->{$self->{instance} . '_cpu_idle'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_user'} - $options{old_datas}->{$self->{instance} . '_cpu_user'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_iowait'} - $options{old_datas}->{$self->{instance} . '_cpu_iowait'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_system'} - $options{old_datas}->{$self->{instance} . '_cpu_system'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_hardirq'} - $options{old_datas}->{$self->{instance} . '_cpu_hardirq'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_softirq'} - $options{old_datas}->{$self->{instance} . '_cpu_softirq'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_nice'} - $options{old_datas}->{$self->{instance} . '_cpu_nice'}) +
          ($options{new_datas}->{$self->{instance} . '_cpu_steal'} - $options{old_datas}->{$self->{instance} . '_cpu_steal'})
          ) / $options{new_datas}->{$self->{instance} . '_cpu_total'} * 100;
      $self->{result_values}->{svctm} = 0;
      if ($itv > 0) {
          my $tput = $nr_ios * $self->{instance_mode}->{option_results}->{interrupt_frequency} / $itv;
          my $util = ($options{new_datas}->{$self->{instance} . '_ticks'} - $options{old_datas}->{$self->{instance} . '_ticks'}) / $itv * $self->{instance_mode}->{option_results}->{interrupt_frequency};
  
          $self->{result_values}->{svctm} = $tput > 0 ? $util / $tput : 0;
      }
  
      return 0;
  }
  
  sub prefix_device_output {
      my ($self, %options) = @_;
      
      return "Device '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'device', type => 1, cb_prefix_output => 'prefix_device_output', message_multiple => 'All devices are ok', skipped_code => { -10 => 1 } }
      ];
      
      $self->{maps_counters}->{device} = [
          { label => 'read-usage', nlabel => 'device.io.read.usage.bytespersecond', set => {
                  key_values => [ { name => 'read_sectors', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_usage_calc'), closure_custom_calc_extra_options => { label_ref => 'read' },
                  output_template => 'read I/O : %s %s/s',
                  output_change_bytes => 1,
                  output_use => 'usage_persecond', threshold_use => 'usage_persecond',
                  perfdatas => [
                      { label => 'readio', value => 'usage_persecond', template => '%d',
                        unit => 'B/s', min => 0, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'write-usage', nlabel => 'device.io.write.usage.bytespersecond', set => {
                  key_values => [ { name => 'write_sectors', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_usage_calc'), closure_custom_calc_extra_options => { label_ref => 'write' },
                  output_template => 'write I/O : %s %s/s',
                  output_change_bytes => 1,
                  output_use => 'usage_persecond', threshold_use => 'usage_persecond',
                  perfdatas => [
                      { label => 'writeio', value => 'usage_persecond', template => '%d',
                        unit => 'B/s', min => 0, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'read-wait', nlabel => 'device.io.read.wait.milliseconds', set => {
                  key_values => [ { name => 'rd_ios', diff => 1 }, { name => 'rd_ticks', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_wait_calc'), closure_custom_calc_extra_options => { label_ref => 'rd' },
                  output_template => 'read wait: %.2f ms',
                  output_change_bytes => 1,
                  output_use => 'wait', threshold_use => 'wait',
                  perfdatas => [
                      { label => 'readwait', value => 'wait', template => '%.2f',
                        unit => 'ms', min => 0, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'write-wait', nlabel => 'device.io.write.wait.milliseconds', set => {
                  key_values => [ { name => 'wr_ios', diff => 1 }, { name => 'wr_ticks', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_wait_calc'), closure_custom_calc_extra_options => { label_ref => 'wr' },
                  output_template => 'write wait: %.2f ms',
                  output_change_bytes => 1,
                  output_use => 'wait', threshold_use => 'wait',
                  perfdatas => [
                      { label => 'writewait', value => 'wait', template => '%.2f',
                        unit => 'ms', min => 0, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'svctime', nlabel => 'device.io.servicetime.count', set => {
                  key_values => [
                      { name => 'cpu_total', diff => 1 },
                      { name => 'cpu_iowait', diff => 1 },
                      { name => 'cpu_user', diff => 1 },
                      { name => 'cpu_system', diff => 1 },
                      { name => 'cpu_idle', diff => 1 },
                      { name => 'cpu_hardirq', diff => 1 },
                      { name => 'cpu_softirq', diff => 1 },
                      { name => 'cpu_steal', diff => 1 },
                      { name => 'cpu_nice', diff => 1 },
                      { name => 'ticks', diff => 1 },
                      { name => 'rd_ios', diff => 1 },
                      { name => 'wr_ios', diff => 1 },
                      { name => 'display' }
                  ],
                  closure_custom_calc => $self->can('custom_svctm_calc'),
                  output_template => 'svctm: %.2f',
                  output_use => 'svctm', threshold_use => 'svctm',
                  perfdatas => [
                      { label => 'svctm', value => 'svctm', template => '%.2f', min => 0, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'utils', nlabel => 'device.io.utils.percentage', set => {
                  key_values => [
                      { name => 'cpu_total', diff => 1 },
                      { name => 'cpu_iowait', diff => 1 },
                      { name => 'cpu_user', diff => 1 },
                      { name => 'cpu_system', diff => 1 },
                      { name => 'cpu_idle', diff => 1 },
                      { name => 'cpu_hardirq', diff => 1 },
                      { name => 'cpu_softirq', diff => 1 },
                      { name => 'cpu_steal', diff => 1 },
                      { name => 'cpu_nice', diff => 1 },
                      { name => 'ticks', diff => 1 },
                      { name => 'display' }
                  ],
                  closure_custom_calc => $self->can('custom_utils_calc'),
                  output_template => '%%utils: %.2f %%',
                  output_use => 'utils', threshold_use => 'utils',
                  perfdatas => [
                      { label => 'utils', value => 'utils', template => '%.2f',
                        unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-partition-name:s'  => { name => 'filter_partition_name' },
          'exclude-partition-name:s' => { name => 'exclude_partition_name' },
          'interrupt-frequency:s'    => { name => 'interrupt_frequency', default => 1000 },
          'bytes-per-sector:s'       => { name => 'bytes_per_sector', default => 512 },
          'skip'                     => { name => 'skip' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'tail',
          command_options => '-n +1 /proc/stat /proc/diskstats 2>&1'
      );
      
      $stdout =~ /\/proc\/stat(.*?)\/proc\/diskstats.*?\n(.*)/msg;
      my ($cpu_parts, $disk_parts) = ($1, $2);
  
      # Manage CPU Parts
      $cpu_parts =~ /^cpu\s+(.*)$/ms;
      my @stats = split(/\s+/, $1);
  
      my ($cpu_idle, $cpu_nice, $cpu_system, $cpu_user) = ($stats[3], $stats[1], $stats[2], $stats[0]);
      my ($cpu_iowait, $cpu_hardirq, $cpu_softirq, $cpu_steal) = (0, 0, 0, 0);
      $cpu_iowait = $stats[4] if (defined($stats[4]));
      $cpu_hardirq = $stats[5] if (defined($stats[5]));
      $cpu_softirq = $stats[6] if (defined($stats[6]));
      $cpu_steal = $stats[7] if (defined($stats[7]));
  
      my $cpu_total = 0;
      while ($cpu_parts =~ /^cpu(\d+)/msg) {
          $cpu_total++;
      }
  
      $self->{device} = {};
      while ($disk_parts =~ /^\s*\S+\s+\S+\s+(\S+)\s+(\d+)\s+\d+\s+(\d+)\s+(\d+)\s+(\d+)\s+\d+\s+(\d+)\s+(\d+)\s+\d+\s+(\d+)\s+/mg) {
          my ($partition_name, $read_sector, $write_sector, $rd_ios, $rd_ticks, $wr_ios, $wr_ticks, $ms_ticks) = ($1, $3, $6, $2, $4, $5, $7, $8);
  
          next if (defined($self->{option_results}->{filter_partition_name}) && $self->{option_results}->{filter_partition_name} ne '' &&
              $partition_name !~ /$self->{option_results}->{filter_partition_name}/);
          next if (defined($self->{option_results}->{exclude_partition_name}) && $self->{option_results}->{exclude_partition_name} ne '' &&
              $partition_name =~ /$self->{option_results}->{exclude_partition_name}/);
  
          if (defined($self->{option_results}->{skip}) && $read_sector == 0 && $write_sector == 0) {
              $self->{output}->output_add(long_msg => "skipping device '" . $partition_name . "': no read/write IO.", debug => 1);
              next;
          }
  
          $self->{device}->{$partition_name} = {
              display => $partition_name,
              read_sectors => $read_sector, 
              write_sectors => $write_sector,
              rd_ios => $rd_ios,
              rd_ticks => $rd_ticks,
              wr_ios => $wr_ios,
              wr_ticks => $wr_ticks,
              ticks => $ms_ticks,
              cpu_total => $cpu_total,
              cpu_system => $cpu_system,
              cpu_idle => $cpu_idle,
              cpu_user => $cpu_user,
              cpu_iowait => $cpu_iowait,
              cpu_hardirq => $cpu_hardirq,
              cpu_softirq => $cpu_softirq,
              cpu_steal => $cpu_steal,
              cpu_nice => $cpu_nice
          };
      }
  
      if (scalar(keys %{$self->{device}}) <= 0) {
          if (defined($self->{option_results}->{name})) {
              $self->{output}->add_option_msg(short_msg => "No device found for name '" . $self->{option_results}->{name} . "'.");
          } else {
              $self->{output}->add_option_msg(short_msg => "No device found.");
          }
          $self->{output}->option_exit();
      }
  
      $self->{cache_name} = 'cache_linux_local_' . $options{custom}->get_identifier()  . '_' . $self->{mode} . '_' .
          (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_partition_name}) ? md5_hex($self->{option_results}->{filter_partition_name}) : md5_hex('all'));
  }
  
  1;
  
  
  =head1 MODE
  
  Check some disk io counters:
  read and writes bytes per seconds, milliseconds time spent reading and writing, %util (like iostat)
  
  Command used: tail -n +1 /proc/stat /proc/diskstats 2>&1
  
  =over 8
  
  =item B<--warning-*> B<--critical-*>
  
  Thresholds.
  Can be: 'read-usage', 'write-usage', 'read-wait', 'write-wait', 'svctime', 'utils'.
  
  =item B<--filter-partition-name>
  
  Filter partition name (regexp can be used).
  
  =item B<--exclude-partition-name>
  
  Exclude partition name (regexp can be used).
  
  =item B<--bytes-per-sector>
  
  Bytes per sector (default: 512)
  
  =item B<--interrupt-frequency>
  
  Linux Kernel Timer Interrupt Frequency (default: 1000)
  
  =item B<--skip>
  
  Skip partitions with 0 sectors read/write.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_DISKIO

$fatpacked{"os/linux/local/mode/filesdate.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_FILESDATE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::filesdate;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'warning:s'       => { name => 'warning' },
          'critical:s'      => { name => 'critical' },
          'separate-dirs'   => { name => 'separate_dirs' },
          'max-depth:s'     => { name => 'max_depth' },
          'exclude-du:s@'   => { name => 'exclude_du' },
          'filter-plugin:s' => { name => 'filter_plugin' },
          'files:s'         => { name => 'files' },
          'time:s'          => { name => 'time' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
         $self->{output}->option_exit();
      }
      if (!defined($self->{option_results}->{files}) || $self->{option_results}->{files} eq '') {
         $self->{output}->add_option_msg(short_msg => "Need to specify files option.");
         $self->{output}->option_exit();
      }
  
      #### Create command_options
      $self->{command_options} = '-x --time-style=+%s';
      if (defined($self->{option_results}->{separate_dirs})) {
          $self->{command_options} .= ' --separate-dirs';
      }
      if (defined($self->{option_results}->{max_depth})) {
          $self->{command_options} .= ' --max-depth=' . $self->{option_results}->{max_depth};
      }
      if (defined($self->{option_results}->{time})) {
          $self->{command_options} .= ' --time=' . $self->{option_results}->{time};
      } else {
          $self->{command_options} .= ' --time';
      }
      foreach my $exclude (@{$self->{option_results}->{exclude_du}}) {
          $self->{command_options} .= " --exclude='" . $exclude . "'";
      }
      $self->{command_options} .= ' ' . $self->{option_results}->{files};
      $self->{command_options} .= ' 2>&1';
  }
  
  sub run {
      my ($self, %options) = @_;
      my $total_size = 0;
      my $current_time = time();
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'du',
          command_options => $self->{command_options}
      );
  
      $self->{output}->output_add(
          severity => 'OK', 
          short_msg => 'All file/directory times are ok.'
      );
      foreach (split(/\n/, $stdout)) {
          next if (!/(\d+)\t+(\d+)\t+(.*)/);
          my ($size, $time, $name) = ($1, $2, centreon::plugins::misc::trim($3));
          my $diff_time = $current_time - $time;
          
          next if (defined($self->{option_results}->{filter_plugin}) && $self->{option_results}->{filter_plugin} ne '' &&
                   $name !~ /$self->{option_results}->{filter_plugin}/);
          
          my $exit_code = $self->{perfdata}->threshold_check(
              value => $diff_time, 
              threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]
          );
          $self->{output}->output_add(long_msg => sprintf("%s: %s seconds (time: %s)", $name, $diff_time, scalar(localtime($time))));
          if (!$self->{output}->is_status(litteral => 1, value => $exit_code, compare => 'ok')) {
              $self->{output}->output_add(
                  severity => $exit_code,
                  short_msg => sprintf('%s: %s seconds (time: %s)', $name, $diff_time, scalar(localtime($time)))
              );
          }
  
          $self->{output}->perfdata_add(
              nlabel => 'file.mtime.last.seconds',
              instances => $name,
              unit => 's',
              value => $diff_time,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical')
          );
      }
        
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check time (modified, creation,...) of files/directories.
  
  =over 8
  
  =item B<--files>
  
  Files/Directories to check. (Shell expansion is ok)
  
  =item B<--warning>
  
  Warning threshold in seconds for each files/directories (diff time).
  
  =item B<--critical>
  
  Critical threshold in seconds for each files/directories (diff time).
  
  =item B<--separate-dirs>
  
  Do not include size of subdirectories.
  
  =item B<--max-depth>
  
  Don't check fewer levels. (can be use --separate-dirs)
  
  =item B<--time>
  
  Check another time than modified time.
  
  =item B<--exclude-du>
  
  Exclude files/directories with 'du' command. Values from exclude files/directories are not counted in parent directories.
  Shell pattern can be used.
  
  =item B<--filter-plugin>
  
  Filter files/directories in the plugin. Values from exclude files/directories are counted in parent directories!!!
  Perl Regexp can be used.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_FILESDATE

$fatpacked{"os/linux/local/mode/filessize.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_FILESSIZE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::filessize;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'warning-one:s'    => { name => 'warning_one' },
          'critical-one:s'   => { name => 'critical_one' },
          'warning-total:s'  => { name => 'warning_total' },
          'critical-total:s' => { name => 'critical_total' },
          'separate-dirs'    => { name => 'separate_dirs' },
          'max-depth:s'      => { name => 'max_depth' },
          'all-files'        => { name => 'all_files' },
          'exclude-du:s@'    => { name => 'exclude_du' },
          'filter-plugin:s'  => { name => 'filter_plugin' },
          'files:s'          => { name => 'files' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (($self->{perfdata}->threshold_validate(label => 'warning_one', value => $self->{option_results}->{warning_one})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong warning-one threshold '" . $self->{warning_one} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'critical_one', value => $self->{option_results}->{critical_one})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong critical-one threshold '" . $self->{critical_one} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'warning_total', value => $self->{option_results}->{warning_total})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong warning-total threshold '" . $self->{warning_total} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'critical_total', value => $self->{option_results}->{critical_total})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong critical-total threshold '" . $self->{critical_total} . "'.");
         $self->{output}->option_exit();
      }
      if (!defined($self->{option_results}->{files}) || $self->{option_results}->{files} eq '') {
         $self->{output}->add_option_msg(short_msg => "Need to specify files option.");
         $self->{output}->option_exit();
      }
  
      $self->{command_options} = '-x -b';
      if (defined($self->{option_results}->{separate_dirs})) {
          $self->{command_options} .= ' --separate-dirs';
      }
      if (defined($self->{option_results}->{max_depth})) {
          $self->{command_options} .= ' --max-depth=' . $self->{option_results}->{max_depth};
      }
      if (defined($self->{option_results}->{all_files})) {
          $self->{command_options} .= ' --all';
      }
      foreach my $exclude (@{$self->{option_results}->{exclude_du}}) {
          $self->{command_options} .= " --exclude='" . $exclude . "'";
      }
      $self->{command_options} .= ' ' . $self->{option_results}->{files};
      $self->{command_options} .= ' 2>&1';
  }
  
  sub run {
      my ($self, %options) = @_;
      my $total_size = 0;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'du',
          command_options => $self->{command_options}
      );
      
      $self->{output}->output_add(
          severity => 'OK', 
          short_msg => "All file/directory sizes are ok."
      );
      foreach (split(/\n/, $stdout)) {
          next if (!/(\d+)\t+(.*)/);
          my ($size, $name) = ($1, centreon::plugins::misc::trim($2));
          
          next if (defined($self->{option_results}->{filter_plugin}) && $self->{option_results}->{filter_plugin} ne '' &&
                   $name !~ /$self->{option_results}->{filter_plugin}/);
          
          $total_size += $size;
          my $exit_code = $self->{perfdata}->threshold_check(
              value => $size, 
              threshold => [ { label => 'critical_one', exit_litteral => 'critical' }, { label => 'warning_one', exit_litteral => 'warning' } ]
          );
          my ($size_value, $size_unit) = $self->{perfdata}->change_bytes(value => $size);
          $self->{output}->output_add(long_msg => sprintf("%s: %s", $name, $size_value . ' ' . $size_unit));
          if (!$self->{output}->is_status(litteral => 1, value => $exit_code, compare => 'ok')) {
              $self->{output}->output_add(
                  severity => $exit_code,
                  short_msg => sprintf("'%s' size is %s", $name, $size_value . ' ' . $size_unit)
              );
          }
          $self->{output}->perfdata_add(
              nlabel => 'file.size.bytes',
              instances => $name,
              unit => 'B',
              value => $size,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning_one'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical_one'),
              min => 0
          );
      }
   
      # Total Size
      my $exit_code = $self->{perfdata}->threshold_check(
          value => $total_size, 
          threshold => [ { label => 'critical_total', exit_litteral => 'critical' }, { label => 'warning_total', exit_litteral => 'warning' } ]
      );
      my ($size_value, $size_unit) = $self->{perfdata}->change_bytes(value => $total_size);
      $self->{output}->output_add(long_msg => sprintf("Total: %s", $size_value . ' ' . $size_unit));
      if (!$self->{output}->is_status(litteral => 1, value => $exit_code, compare => 'ok')) {
          $self->{output}->output_add(
              severity => $exit_code,
              short_msg => sprintf('Total size is %s', $size_value . ' ' . $size_unit)
          );
      }
      $self->{output}->perfdata_add(
          nlabel => 'files.size.bytes',
          unit => 'B',
          value => $total_size,
          warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning_total'),
          critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical_total'),
          min => 0
      );
        
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check size of files/directories.
  
  =over 8
  
  =item B<--files>
  
  Files/Directories to check. (Shell expansion is ok)
  
  =item B<--warning-one>
  
  Warning threshold in bytes for each files/directories.
  
  =item B<--critical-one>
  
  Critical threshold in bytes for each files/directories.
  
  =item B<--warning-total>
  
  Warning threshold in bytes for all files/directories.
  
  =item B<--critical-total>
  
  Critical threshold in bytes for all files/directories.
  
  =item B<--separate-dirs>
  
  Do not include size of subdirectories.
  
  =item B<--max-depth>
  
  Don't check fewer levels. (can be use --separate-dirs)
  
  =item B<--all-files>
  
  Add files when you check directories.
  
  =item B<--exclude-du>
  
  Exclude files/directories with 'du' command. Values from exclude files/directories are not counted in parent directories.
  Shell pattern can be used.
  
  =item B<--filter-plugin>
  
  Filter files/directories in the plugin. Values from exclude files/directories are counted in parent directories!!!
  Perl Regexp can be used.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_FILESSIZE

$fatpacked{"os/linux/local/mode/inodes.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_INODES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::inodes;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub prefix_inodes_output {
      my ($self, %options) = @_;
  
      return "Inodes partition '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
      
      $self->{maps_counters_type} = [
          { name => 'inodes', type => 1, cb_prefix_output => 'prefix_inodes_output', message_multiple => 'All inode partitions are ok' }
      ];
      
      $self->{maps_counters}->{inodes} = [
          { label => 'usage', nlabel => 'storage.inodes.usage.percentage', set => {
                  key_values => [ { name => 'used' }, { name => 'display' } ],
                  output_template => 'used: %s %%',
                  perfdatas => [
                      { label => 'used', template => '%d',
                        unit => '%', min => 0, max => 100, label_extra_instance => 1 }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-type:s'        => { name => 'filter_type' },
          'filter-fs:s'          => { name => 'filter_fs' },
          'exclude-fs:s'         => { name => 'exclude_fs' },
          'filter-mountpoint:s'  => { name => 'filter_mountpoint' },
          'exclude-mountpoint:s' => { name => 'exclude_mountpoint' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout, $exit_code) = $options{custom}->execute_command(
          command => 'df',
          command_options => '-P -i -T 2>&1',
          no_quit => 1
      );
  
      $self->{inodes} = {};
      my @lines = split /\n/, $stdout;
      foreach my $line (@lines) {
          next if ($line !~ /^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.*)/);
          my ($fs, $type, $size, $used, $available, $percent, $mount) = ($1, $2, $3, $4, $5, $6, $7);
  
          next if (defined($self->{option_results}->{filter_fs}) && $self->{option_results}->{filter_fs} ne '' &&
              $fs !~ /$self->{option_results}->{filter_fs}/);
          next if (defined($self->{option_results}->{exclude_fs}) && $self->{option_results}->{exclude_fs} ne '' &&
              $fs =~ /$self->{option_results}->{exclude_fs}/);
          next if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' &&
              $type !~ /$self->{option_results}->{filter_type}/);
          next if (defined($self->{option_results}->{filter_mountpoint}) && $self->{option_results}->{filter_mountpoint} ne '' &&
              $mount !~ /$self->{option_results}->{filter_mountpoint}/);
          next if (defined($self->{option_results}->{exclude_mountpoint}) && $self->{option_results}->{exclude_mountpoint} ne '' &&
              $mount =~ /$self->{option_results}->{exclude_mountpoint}/);
  
          $percent =~ s/%//g;
          next if ($percent eq '-');
          $self->{inodes}->{$mount} = { display => $mount, fs => $fs, type => $type, total => $size, used => $percent };
      }
  
      if (scalar(keys %{$self->{inodes}}) <= 0) {
          if ($exit_code != 0) {
              $self->{output}->output_add(long_msg => "command output:" . $stdout);
          }
          $self->{output}->add_option_msg(short_msg => "No storage found (filters or command issue)");
          $self->{output}->option_exit();
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check Inodes space usage on partitions.
  
  Command used: df -P -i -T 2>&1
  
  =over 8
  
  =item B<--warning-usage>
  
  Warning threshold in percent.
  
  =item B<--critical-usage>
  
  Critical threshold in percent.
  
  =item B<--filter-mountpoint>
  
  Filter filesystem mount point (regexp can be used).
  
  =item B<--exclude-mountpoint>
  
  Exclude filesystem mount point (regexp can be used).
  
  =item B<--filter-type>
  
  Filter filesystem type (regexp can be used).
  
  =item B<--filter-fs>
  
  Filter filesystem (regexp can be used).
  
  =item B<--exclude-fs>
  
  Exclude filesystem (regexp can be used).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_INODES

$fatpacked{"os/linux/local/mode/listinterfaces.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_LISTINTERFACES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::listinterfaces;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-name:s'  => { name => 'filter_name' },
          'filter-state:s' => { name => 'filter_state' },
          'no-loopback'    => { name => 'no_loopback' },
          'skip-novalues'  => { name => 'skip_novalues' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command_path => '/sbin',
          command => 'ip',
          command_options => '-s addr 2>&1'
      );
  
      my $mapping = {
          ifconfig => {
              get_interface => '^(\S+)(.*?)(\n\n|\n$)',
              test => 'RX bytes:\S+.*?TX bytes:\S+'
          },
          iproute => {
              get_interface => '^\d+:\s+(\S+)(.*?)(?=\n\d|\Z$)',
              test => 'RX:\s+bytes.*?\d+'
          }
      };
      
      my $type = 'ifconfig';
      if ($stdout =~ /^\d+:\s+\S+:\s+</ms) {
          $type = 'iproute';
      }
  
      my $results = {};
      while ($stdout =~ /$mapping->{$type}->{get_interface}/msg) {
          my ($interface_name, $values) = ($1, $2);
          $interface_name =~ s/:$//;
          my $states = '';
          $states .= 'R' if ($values =~ /RUNNING|LOWER_UP/ms);
          $states .= 'U' if ($values =~ /UP/ms);
          
          if (defined($self->{option_results}->{no_loopback}) && $values =~ /LOOPBACK/ms) {
              $self->{output}->output_add(long_msg => "Skipping interface '" . $interface_name . "': option --no-loopback");
              next;
          }
          if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
              $interface_name !~ /$self->{option_results}->{filter_name}/) {
              $self->{output}->output_add(long_msg => "Skipping interface '" . $interface_name . "': no matching filter name");
              next;
          }
          if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' &&
              $states !~ /$self->{option_results}->{filter_state}/) {
              $self->{output}->output_add(long_msg => "Skipping interface '" . $interface_name . "': no matching filter state");
              next;
          }
          
          if (defined($self->{option_results}->{skip_novalues}) && $values =~ /$mapping->{$type}->{test}/msi) {
              $self->{output}->output_add(long_msg => "Skipping interface '" . $interface_name . "': no values");
              next;
          }
  
          $results->{$interface_name} = { state => $states };
      }    
  
      return $results;
  }
  
  sub run {
      my ($self, %options) = @_;
  	
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {
          $self->{output}->output_add(long_msg => "'" . $name . "' [state = '" . $results->{$name}->{state} . "']");
      }
      
      $self->{output}->output_add(
          severity => 'OK',
          short_msg => 'List interfaces:'
      );
      $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
      $self->{output}->exit();
  }
  
  sub disco_format {
      my ($self, %options) = @_;
  
      $self->{output}->add_disco_format(elements => ['name', 'state']);
  }
  
  sub disco_show {
      my ($self, %options) = @_;
  
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {     
          $self->{output}->add_disco_entry(
              name => $name,
              state => $results->{$name}->{state}
          );
      }
  }
  
  1;
  
  
  =head1 MODE
  
  List storages.
  
  Command used: /sbin/ip -s addr 2>&1
  
  =over 8
  
  =item B<--filter-name>
  
  Filter interface name (regexp can be used).
  
  =item B<--filter-state>
  
  Filter state (regexp can be used).
  Can be: 'R' (running), 'U' (up).
  
  =item B<--no-loopback>
  
  Don't display loopback interfaces.
  
  =item B<--skip-novalues>
  
  Filter interface without in/out byte values.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_LISTINTERFACES

$fatpacked{"os/linux/local/mode/listpartitions.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_LISTPARTITIONS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::listpartitions;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-name:s' => { name => 'filter_name' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/partitions 2>&1'
      );
  
      my $results = {};
      my @lines = split /\n/, $stdout;
      # Header not needed
      shift @lines;
      foreach my $line (@lines) {
          next if ($line !~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/);
          my ($major, $minor, $blocks, $name) = ($1, $2, $3, $4);
          
          if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
              $name !~ /$self->{option_results}->{filter_name}/) {
              $self->{output}->output_add(long_msg => "Skipping partition '" . $name . "': no matching filter name");
              next;
          }
          
          $results->{$name} = 1;
      }
  
      return $results;
  }
  
  sub run {
      my ($self, %options) = @_;
  	
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {
          $self->{output}->output_add(long_msg => "'" . $name . "'");
      }
      
      $self->{output}->output_add(
          severity => 'OK',
          short_msg => 'List partitions:'
      );
      $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
      $self->{output}->exit();
  }
  
  sub disco_format {
      my ($self, %options) = @_;
      
      $self->{output}->add_disco_format(elements => ['name']);
  }
  
  sub disco_show {
      my ($self, %options) = @_;
  
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {     
          $self->{output}->add_disco_entry(name => $name);
      }
  }
  
  1;
  
  
  =head1 MODE
  
  List partitions.
  
  Command used: cat /proc/partitions 2>&1
  
  =over 8
  
  =item B<--filter-name>
  
  Filter partition name (regexp can be used).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_LISTPARTITIONS

$fatpacked{"os/linux/local/mode/liststorages.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_LISTSTORAGES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::liststorages;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-type:s'  => { name => 'filter_type' },
          'filter-fs:s'    => { name => 'filter_fs' },
          'filter-mount:s' => { name => 'filter_mount' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'df',
          command_options => '-P -k -T 2>&1',
          no_quit => 1
      );
  
      my $results = {};
      my @lines = split /\n/, $stdout;
      foreach my $line (@lines) {
          next if ($line !~ /^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.*)/);
          my ($fs, $type, $size, $used, $available, $percent, $mount) = ($1, $2, $3, $4, $5, $6, $7);
          
          if (defined($self->{option_results}->{filter_fs}) && $self->{option_results}->{filter_fs} ne '' &&
              $fs !~ /$self->{option_results}->{filter_fs}/) {
              $self->{output}->output_add(long_msg => "skipping storage '" . $mount . "': no matching filter filesystem", debug => 1);
              next;
          }
          if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' &&
              $type !~ /$self->{option_results}->{filter_type}/) {
              $self->{output}->output_add(long_msg => "skipping storage '" . $mount . "': no matching filter filesystem type", debug => 1);
              next;
          }
          if (defined($self->{option_results}->{filter_mount}) && $self->{option_results}->{filter_mount} ne '' &&
              $mount !~ /$self->{option_results}->{filter_mount}/) {
              $self->{output}->output_add(long_msg => "skipping storage '" . $mount . "': no matching filter mount point", debug => 1);
              next;
          }
  
          $results->{$mount} = { fs => $fs, type => $type, size => $size };
      }
  
      return $results;
  }
  
  sub run {
      my ($self, %options) = @_;
  	
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {
          $self->{output}->output_add(long_msg => "'" . $name . "' [fs = " . $results->{$name}->{fs} . '] [type = ' . $results->{$name}->{type} . '] [size = ' . $results->{$name}->{size} . ']');
      }
      
      $self->{output}->output_add(
          severity => 'OK',
          short_msg => 'List storages:'
      );
      $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
      $self->{output}->exit();
  }
  
  sub disco_format {
      my ($self, %options) = @_;
      
      $self->{output}->add_disco_format(elements => ['name', 'fs', 'type', 'size']);
  }
  
  sub disco_show {
      my ($self, %options) = @_;
  
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {
          $self->{output}->add_disco_entry(
              name => $name,
              fs => $results->{$name}->{fs},
              type => $results->{$name}->{type},
              size => $results->{$name}->{size},
          );
      }
  }
  
  1;
  
  
  =head1 MODE
  
  List storages.
  
  Command used: df -P -k -T 2>&1
  
  =over 8
  
  =item B<--filter-type>
  
  Filter filesystem type (regexp can be used).
  
  =item B<--filter-fs>
  
  Filter filesystem (regexp can be used).
  
  =item B<--filter-mount>
  
  Filter mount point (regexp can be used).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_LISTSTORAGES

$fatpacked{"os/linux/local/mode/listsystemdservices.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_LISTSYSTEMDSERVICES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::listsystemdservices;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-name:s'         => { name => 'filter_name' },
          'filter-description:s'  => { name => 'filter_description' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      # check systemctl version to convert no-legend in legend=false (change in versions >= 248)
      my $legend_format= ' --no-legend';
      my ($stdout_version) = $options{custom}->execute_command(
          command         => 'systemctl',
          command_options => '--version'
      );
      $stdout_version =~ /^systemd\s(\d+)\s/;
      my $systemctl_version=$1;
      if($systemctl_version >= 248){
          $legend_format = ' --legend=false';
      }
  
      my $command_options_1 = '-a --no-pager --plain';
      my ($stdout)  = $options{custom}->execute_command(
          command         => 'systemctl',
          command_options => $command_options_1.$legend_format
      );
  
      my $results = {};
  
      #auditd.service                                                        loaded    active   running Security Auditing Service
      #avahi-daemon.service                                                  loaded    active   running Avahi mDNS/DNS-SD Stack
      #brandbot.service                                                      loaded    inactive dead    Flexible Branding Service
      while ($stdout =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/mig) {
          my ($name, $load, $active, $sub, $desc) = ($1, $2, $3, $4, $5);
          $desc =~ s/\s+$//;
  
          next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
              $name !~ /$self->{option_results}->{filter_name}/);
          next if (defined($self->{option_results}->{filter_description}) && $self->{option_results}->{filter_description} ne '' &&
              $desc !~ /$self->{option_results}->{filter_description}/);
  
          $results->{$name} = { load => $load, active => $active, sub => $sub, desc => $desc };
      }
  
      return $results;
  }
  
  sub run {
      my ($self, %options) = @_;
  	
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {
          $self->{output}->output_add(long_msg => "'" . $name . "' [desc = " . $results->{$name}->{desc} . '] [load = ' . $results->{$name}->{load} . '] [active = ' . $results->{$name}->{active} . '] [sub = ' . $results->{$name}->{sub} . ']');
      }
      
      $self->{output}->output_add(
          severity => 'OK',
          short_msg => 'List systemd services:'
      );
      $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
      $self->{output}->exit();
  }
  
  sub disco_format {
      my ($self, %options) = @_;
      
      $self->{output}->add_disco_format(elements => ['name', 'description', 'load', 'active', 'sub']);
  }
  
  sub disco_show {
      my ($self, %options) = @_;
  
      my $results = $self->manage_selection(custom => $options{custom});
      foreach my $name (sort(keys %$results)) {     
          $self->{output}->add_disco_entry(
              name => $name,
              description => $results->{$name}->{desc},
              load => $results->{$name}->{load},
              active => $results->{$name}->{active},
              sub => $results->{$name}->{sub}
          );
      }
  }
  
  1;
  
  
  =head1 MODE
  
  List systemd services.
  
  Command used: systemctl -a --no-pager --no-legend --plain
  Command change for systemctl version >= 248 : --no-legend is converted in legend=false
  
  =over 8
  
  =item B<--filter-name>
  
  Filter services name (regexp can be used).
  
  =item B<--filter-description>
  
  Filter services description (regexp can be used).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_LISTSYSTEMDSERVICES

$fatpacked{"os/linux/local/mode/loadaverage.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_LOADAVERAGE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::loadaverage;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'warning:s'  => { name => 'warning', default => '' },
          'critical:s' => { name => 'critical', default => '' },
          'average'    => { name => 'average' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      ($self->{warn1}, $self->{warn5}, $self->{warn15}) = split /,/, $self->{option_results}->{warning};
      ($self->{crit1}, $self->{crit5}, $self->{crit15}) = split /,/, $self->{option_results}->{critical};
      
      if (($self->{perfdata}->threshold_validate(label => 'warn1', value => $self->{warn1})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong warning (1min) threshold '" . $self->{warn1} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'warn5', value => $self->{warn5})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong warning (5min) threshold '" . $self->{warn5} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'warn15', value => $self->{warn15})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong warning (15min) threshold '" . $self->{warn15} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'crit1', value => $self->{crit1})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong critical (1min) threshold '" . $self->{crit1} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'crit5', value => $self->{crit5})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong critical (5min) threshold '" . $self->{crit5} . "'.");
         $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'crit15', value => $self->{crit15})) == 0) {
         $self->{output}->add_option_msg(short_msg => "Wrong critical (15min) threshold '" . $self->{crit15} . "'.");
         $self->{output}->option_exit();
      }
  }
  
  sub run {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'tail',
          command_options => '-n +1 /proc/loadavg /proc/stat 2>&1'
      );
      
      my ($load1m, $load5m, $load15m);
      my ($msg, $cpu_load1, $cpu_load5, $cpu_load15);
  
      if ($stdout =~ /\/proc\/loadavg.*?([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/ms) {
          ($load1m, $load5m, $load15m) = ($1, $2, $3)
      }
  
      if (!defined($load1m) || !defined($load5m) || !defined($load15m)) {
          $self->{output}->add_option_msg(short_msg => "Some informations missing.");
          $self->{output}->option_exit();
      }
  
      if (defined($self->{option_results}->{average})) {    
          my $countCpu = 0;
          
          $countCpu++ while ($stdout =~ /^cpu\d+/msg);
          
          if ($countCpu == 0){
              $self->{output}->output_add(severity => 'unknown',
                                          short_msg => 'Unable to get number of CPUs');
              $self->{output}->display();
              $self->{output}->exit();    
          }
  
          $cpu_load1 = sprintf("%0.2f", $load1m / $countCpu);
          $cpu_load5 = sprintf("%0.2f", $load5m / $countCpu);
          $cpu_load15 = sprintf("%0.2f", $load15m / $countCpu);
          $msg = sprintf("Load average: %s [%s/%s CPUs], %s [%s/%s CPUs], %s [%s/%s CPUs]", $cpu_load1, $load1m, $countCpu,
                         $cpu_load5, $load5m, $countCpu,
                         $cpu_load15, $load15m, $countCpu);
          $self->{output}->perfdata_add(
              label => 'avg_load1',
              value => $cpu_load1,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn1'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit1'),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'avg_load5',
              value => $cpu_load5,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn5'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit5'),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'avg_load15',
              value => $cpu_load15,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn15'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit15'),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'load1',
              value => $load1m,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn1', op => '*', value => $countCpu),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit1', op => '*', value => $countCpu),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'load5',
              value => $load5m,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn5', op => '*', value => $countCpu),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit5', op => '*', value => $countCpu),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'load15',
              value => $load15m,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn15', op => '*', value => $countCpu),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit15', op => '*', value => $countCpu),
              min => 0
          );
      } else {
          $cpu_load1 = $load1m;
          $cpu_load5 = $load5m;
          $cpu_load15 = $load15m;
      
          $msg = sprintf("Load average: %s, %s, %s", $cpu_load1, $cpu_load5, $cpu_load15);
          $self->{output}->perfdata_add(
              label => 'load1',
              value => $cpu_load1,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn1'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit1'),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'load5',
              value => $cpu_load5,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn5'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit5'),
              min => 0
          );
          $self->{output}->perfdata_add(
              label => 'load15',
              value => $cpu_load15,
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warn15'),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'crit15'),
              min => 0
          );
      }
      
      my $exit1 = $self->{perfdata}->threshold_check(value => $cpu_load1,
                                                     threshold => [ { label => 'crit1', 'exit_litteral' => 'critical' }, { label => 'warn1', exit_litteral => 'warning' } ]);
      my $exit2 = $self->{perfdata}->threshold_check(value => $cpu_load5,
                                                     threshold => [ { label => 'crit5', 'exit_litteral' => 'critical' }, { label => 'warn5', exit_litteral => 'warning' } ]);
      my $exit3 = $self->{perfdata}->threshold_check(value => $cpu_load15,
                                                     threshold => [ { label => 'crit15', 'exit_litteral' => 'critical' }, { label => 'warn15', exit_litteral => 'warning' } ]);
      my $exit = $self->{output}->get_most_critical(status => [ $exit1, $exit2, $exit3 ]);
      $self->{output}->output_add(
          severity => $exit,
          short_msg => $msg
      );
  
      $self->{output}->display();
      $self->{output}->exit();
  }
  
  1;
  
  
  =head1 MODE
  
  Check system load-average. (need '/proc/loadavg' file).
  
  Command used: tail -n +1 /proc/loadavg /proc/stat 2>&1
  
  =over 8
  
  =item B<--warning>
  
  Warning threshold (1min,5min,15min).
  
  =item B<--critical>
  
  Critical threshold (1min,5min,15min).
  
  =item B<--average>
  
  Load average for the number of CPUs.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_LOADAVERAGE

$fatpacked{"os/linux/local/mode/lvm.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_LVM';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::lvm;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  
  sub custom_space_usage_output {
      my ($self, %options) = @_;
  
      my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total});
      my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used});
      my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free});
      return sprintf(
          'space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)',
          $total_size_value . " " . $total_size_unit,
          $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used},
          $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free}
      );
  }
  
  sub prefix_vg_output {
      my ($self, %options) = @_;
  
      return sprintf(
          "VG '%s' ",
          $options{instance_value}->{name}
      );
  }
  
  sub prefix_dlv_output {
      my ($self, %options) = @_;
  
      return sprintf(
          "direct LV '%s' [VG: %s] ",
          $options{instance_value}->{lvName},
          $options{instance_value}->{vgName}
      );
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'global', type => 0 },
          { name => 'vg', type => 1, cb_prefix_output => 'prefix_vg_output', message_multiple => 'All VGs are ok' },
          { name => 'dlv', type => 1, cb_prefix_output => 'prefix_dlv_output', message_multiple => 'All direct LVs are ok' }
      ];
  
      $self->{maps_counters}->{global} = [
          { label => 'lv-detected', display_ok => 0, nlabel => 'lv.detected.count', set => {
                  key_values => [ { name => 'lv_detected' } ],
                  output_template => 'number of direct LV detected: %s',
                  perfdatas => [
                      { template => '%s', min => 0 }
                  ]
              }
          },
          { label => 'vg-detected', display_ok => 0, nlabel => 'vg.detected.count', set => {
                  key_values => [ { name => 'vg_detected' } ],
                  output_template => 'number of direct VG detected: %s',
                  perfdatas => [
                      { template => '%s', min => 0 }
                  ]
              }
          }
      ];
  
      $self->{maps_counters}->{dlv} = [
          { label => 'lv-data-usage', nlabel => 'lv.data.usage.percentage', set => {
                  key_values => [ { name => 'data' }, { name => 'vgName' }, { name => 'lvName' } ],
                  output_template => 'data usage: %.2f %%',
                  closure_custom_perfdata => sub {
                      my ($self, %options) = @_;
  
                      $self->{output}->perfdata_add(
                          nlabel => $self->{nlabel},
                          unit => '%',
                          instances => [$self->{result_values}->{vgName}, $self->{result_values}->{lvName}],
                          value => sprintf('%.2f', $self->{result_values}->{data}),
                          warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
                          critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
                          min => 0,
                          max => 100
                      );
                  }
              }
          },
          { label => 'lv-meta-usage', nlabel => 'lv.meta.usage.percentage', set => {
                  key_values => [ { name => 'meta' }, { name => 'vgName' }, { name => 'lvName' } ],
                  output_template => 'meta usage: %.2f %%',
                  closure_custom_perfdata => sub {
                      my ($self, %options) = @_;
  
                      $self->{output}->perfdata_add(
                          nlabel => $self->{nlabel},
                          unit => '%',
                          instances => [$self->{result_values}->{vgName}, $self->{result_values}->{lvName}],
                          value => sprintf('%.2f', $self->{result_values}->{meta}),
                          warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
                          critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
                          min => 0,
                          max => 100
                      );
                  }
              }
          }
      ];
  
      $self->{maps_counters}->{vg} = [
          { label => 'vg-space-usage', nlabel => 'vg.space.usage.bytes', set => {
                  key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_space_usage_output'),
                  perfdatas => [
                      { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1 }
                  ]
              }
          },
          { label => 'vg-space-usage-free', nlabel => 'vg.space.free.bytes', display_ok => 0, set => {
                  key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_space_usage_output'),
                  perfdatas => [
                      { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1 }
                  ]
              }
          },
          { label => 'vg-space-usage-prct', nlabel => 'vg.space.usage.percentage', display_ok => 0, set => {
                  key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_space_usage_output'),
                  perfdatas => [
                      { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-lv:s'       => { name => 'filter_lv' },
          'filter-vg:s'       => { name => 'filter_vg' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout, $exit_code) = $options{custom}->execute_command(
          command => 'lvs',
          command_options => '--separator=, 2>&1',
          no_quit => 1
      );
  
      $self->{global} = { lv_detected => 0, vg_detected => 0 };
      $self->{dlv} = {};
      #  LV,VG,Attr,LSize,Pool,Origin,Data%,Meta%,Move,Log,Cpy%Sync,Convert
      #  thinpool,docker,twi-aot---,71.25g,,,1.95,0.06,,,,
      #  lv_controlm,vg_sys,-wi-ao----,5.00g,,,,,,,,
      my @lines = split(/\n/, $stdout);
      shift @lines;
      my $i = 0;
      foreach my $line (@lines) {
          my @fields = split(/,/, $line);
          next if (!defined($fields[6]) || $fields[6] !~ /[0-9]/);
          my ($vg, $lv, $data, $meta) = (centreon::plugins::misc::trim($fields[1]), centreon::plugins::misc::trim($fields[0]), $fields[6], $fields[7]);
  
          next if (defined($self->{option_results}->{filter_lv}) && $self->{option_results}->{filter_lv} ne '' &&
              $lv !~ /$self->{option_results}->{filter_lv}/);
          next if (defined($self->{option_results}->{filter_vg}) && $self->{option_results}->{filter_vg} ne '' &&
              $vg !~ /$self->{option_results}->{filter_vg}/);
  
          $self->{lv_detected}++;
          $self->{dlv}->{$i} = { lvName => $lv, vgName => $vg, data => $data, meta => $meta };
          $i++;
      }
  
      ($stdout, $exit_code) = $options{custom}->execute_command(
          command => 'vgs',
          command_options => '--separator=, --units=b 2>&1',
          no_quit => 1
      );
  
      #  VG,#PV,#LV,#SN,Attr,VSize,VFree
      #  test,1,1,0,wz--n-,5364514816B,3217031168B
      $self->{vg} = {};
      @lines = split(/\n/, $stdout);
      shift @lines;
      foreach my $line (@lines) {
          my @fields = split(/,/, $line);
          next if (!defined($fields[5]) || $fields[5] !~ /[0-9]/);
          my $vg = centreon::plugins::misc::trim($fields[0]);
          $fields[5] =~ /^(\d+)/;
          my $size = $1;
          $fields[6] =~ /^(\d+)/;
          my $free = $1;
  
          next if (defined($self->{option_results}->{filter_vg}) && $self->{option_results}->{filter_vg} ne '' &&
              $vg !~ /$self->{option_results}->{filter_vg}/);
  
          $self->{vg_detected}++;
          $self->{vg}->{$vg} = {
              name => $vg,
              total => $size,
              free => $free,
              used => $size - $free,
              prct_free => $free * 100 / $size,
              prct_used => ($size - $free) * 100 / $size
          };
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check direct LV and VG free space.
  
  Command used: lvs --separator="," 2>&1
  
  =over 8
  
  =item B<--filter-vg>
  
  Filter volume group (regexp can be used).
  
  =item B<--filter-lv>
  
  Filter logical volume (regexp can be used).
  
  =item B<--warning-*> B<--critical-*>
  
  Thresholds.
  Can be: 'lv-detected', 'vg-detected',
  'vg-space-usage', 'vg-space-usage-free', 'vg-space-usage-prct',
  'lv-data-usage', 'lv-meta-usage'.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_LVM

$fatpacked{"os/linux/local/mode/memory.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_MEMORY';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::memory;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub custom_memory_output {
      my ($self, %options) = @_;
  
      return sprintf(
          'Ram total: %s %s used (-%s): %s %s (%.2f%%) free: %s %s (%.2f%%) available: %s %s (%.2f%%)',
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}),
          $self->{result_values}->{used_desc},
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}),
          $self->{result_values}->{prct_used},
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}),
          $self->{result_values}->{prct_free},
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{available}),
          $self->{result_values}->{prct_available},
  
      );
  }
  
  sub custom_swap_output {
      my ($self, %options) = @_;
  
      return sprintf(
          'Swap total: %s %s used: %s %s (%.2f%%) free: %s %s (%.2f%%)',
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}),
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}),
          $self->{result_values}->{prct_used},
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}),
          $self->{result_values}->{prct_free}
      );
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'memory', type => 0, skipped_code => { -10 => 1 } },
          { name => 'swap', type => 0, skipped_code => { -10 => 1 } }
      ];
  
       $self->{maps_counters}->{memory} = [
          { label => 'memory-usage', nlabel => 'memory.usage.bytes', set => {
                  key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' } ],
                  closure_custom_output => $self->can('custom_memory_output'),
                  perfdatas => [
                      { template => '%d', min => 0, max => 'total', unit => 'B' }
                  ]
              }
          },
          { label => 'memory-usage-free', nlabel => 'memory.free.bytes', display_ok => 0, set => {
                  key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' } ],
                  closure_custom_output => $self->can('custom_memory_output'),
                  perfdatas => [
                      { template => '%d', min => 0, max => 'total', unit => 'B' }
                  ]
              }
          },
          { label => 'memory-usage-prct', nlabel => 'memory.usage.percentage', display_ok => 0, set => {
                  key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' } ],
                  closure_custom_output => $self->can('custom_memory_output'),
                  perfdatas => [
                      { template => '%.2f', min => 0, max => 100, unit => '%' }
                  ]
              }
          },
          { label => 'memory-available', nlabel => 'memory.available.bytes', display_ok => 0, set => {
                  key_values => [ { name => 'available' }, { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'prct_available' } ],
                  closure_custom_output => $self->can('custom_memory_output'),
                  perfdatas => [
                      { template => '%d', min => 0, max => 'total', unit => 'B' }
                  ]
              }
          },
          { label => 'memory-available-prct', nlabel => 'memory.available.percentage', display_ok => 0, set => {
                  key_values => [ { name => 'prct_available' }, { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' } ],
                  closure_custom_output => $self->can('custom_memory_output'),
                  perfdatas => [
                      { template => '%.2f', min => 0, max => 100, unit => '%' }
                  ]
              }
          },
          { label => 'buffer', nlabel => 'memory.buffer.bytes', set => {
                  key_values => [ { name => 'buffer' } ],
                  output_template => 'buffer: %s %s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%d', min => 0, unit => 'B' }
                  ]
              }
          },
          { label => 'cached', nlabel => 'memory.cached.bytes', set => {
                  key_values => [ { name => 'cached' } ],
                  output_template => 'cached: %s %s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%d', min => 0, unit => 'B' }
                  ]
              }
          },
          { label => 'slab', nlabel => 'memory.slab.bytes', set => {
                  key_values => [ { name => 'slab' } ],
                  output_template => 'slab: %s %s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%d', min => 0, unit => 'B' }
                  ]
              }
          },
      ];
  
      $self->{maps_counters}->{swap} = [
          { label => 'swap', nlabel => 'swap.usage.bytes', set => {
                  key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_swap_output'),
                  perfdatas => [
                      { label => 'swap', template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1 }
                  ]
              }
          },
          { label => 'swap-free', display_ok => 0, nlabel => 'swap.free.bytes', set => {
                  key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_swap_output'),
                  perfdatas => [
                      { label => 'swap_free', template => '%d', min => 0, max => 'total',
                        unit => 'B', cast_int => 1 }
                  ]
              }
          },
          { label => 'swap-prct', display_ok => 0, nlabel => 'swap.usage.percentage', set => {
                  key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_swap_output'),
                  perfdatas => [
                      { label => 'swap_prct', template => '%.2f', min => 0, max => 100, unit => '%' }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'swap'       => { name => 'check_swap' },
          'warning:s'  => { name => 'warning', redirect => 'warning-memory-usage-percentage' },
          'critical:s' => { name => 'critical', redirect => 'critical-memory-usage-percentage' }
      });
  
      return $self;
  }
  
  sub check_rhel_version {
      my ($self, %options) = @_;
  
      $self->{rhel_71} = 0;
      return if ($options{stdout} !~ /(?:Redhat|CentOS|Red[ \-]Hat).*?release\s+(\d+)\.(\d+)/mi);
      $self->{rhel_71} = 1 if ($1 >= 8 || ($1 == 7 && $2 >= 1));
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/meminfo /etc/redhat-release 2>&1',
          no_quit => 1
      );
  
      # Buffer can be missing. In Openvz container for example.
      my $buffer_used = 0;
      my $available = 0;
      my ($cached_used, $free, $total_size, $slab_used, $swap_total, $swap_free);
      foreach (split(/\n/, $stdout)) {
          if (/^MemTotal:\s+(\d+)/i) {
              $total_size = $1 * 1024;
          } elsif (/^Cached:\s+(\d+)/i) {
              $cached_used = $1 * 1024;
          } elsif (/^Buffers:\s+(\d+)/i) {
              $buffer_used = $1 * 1024;
          } elsif (/^Slab:\s+(\d+)/i) {
              $slab_used = $1 * 1024;
          } elsif (/^MemFree:\s+(\d+)/i) {
              $free = $1 * 1024;
          } elsif (/^SwapTotal:\s+(\d+)/i) {
              $swap_total = $1 * 1024;
          } elsif (/^SwapFree:\s+(\d+)/i) {
              $swap_free = $1 * 1024;
          } elsif (/^MemAvailable:\s+(\d+)/i) {
              $available = $1 * 1024;
          }
      }
  
      if (!defined($total_size) || !defined($cached_used) || !defined($free)) {
          $self->{output}->add_option_msg(short_msg => 'Some informations missing.');
          $self->{output}->option_exit();
      }
  
      $self->check_rhel_version(stdout => $stdout);
  
      my $physical_used = $total_size - $free;
      my $nobuf_used = $physical_used - $buffer_used - $cached_used;
      if ($self->{rhel_71} == 1) {
          $nobuf_used -= $slab_used if (defined($slab_used));
      }
  
      my $used_desc = 'buffers/cache';
      $used_desc .= '/slab' if ($self->{rhel_71} == 1 && defined($slab_used));
  
      $self->{memory} = {
          total => $total_size,
          used => $nobuf_used,
          free => $total_size - $nobuf_used,
          prct_used => $nobuf_used * 100 / $total_size,
          prct_free => 100 - ($nobuf_used * 100 / $total_size),
          used_desc => $used_desc,
          available => $available,
          prct_available => $available * 100 / $total_size,
  
          buffer => $buffer_used,
          cache => $cached_used,
          slab => $slab_used
      };
  
      if (defined($self->{option_results}->{check_swap}) &&
          defined($swap_total) && $swap_total > 0) {
          $self->{swap} = {
              total => $swap_total,
              used => $swap_total - $swap_free,
              free => $swap_free,
              prct_used => 100 - ($swap_free * 100 / $swap_total),
              prct_free => ($swap_free * 100 / $swap_total)
          };
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check physical memory (need '/proc/meminfo' file).
  
  Command used: cat /proc/meminfo /etc/redhat-release 2>&1
  
  =over 8
  
  =item B<--swap>
  
  Check swap also.
  
  =item B<--warning-*> B<--critical-*>
  
  Thresholds.
  Can be: 'memory-usage' (B), 'memory-usage-free' (B), 'memory-usage-prct' (%),
  'memory-available' (B), 'memory-available-prct' (%),
  'swap' (B), 'swap-free' (B), 'swap-prct' (%),
  'buffer' (B), 'cached' (B), 'slab' (B).
  
  =back
  
  =cut
  
OS_LINUX_LOCAL_MODE_MEMORY

$fatpacked{"os/linux/local/mode/mountpoint.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_MOUNTPOINT';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::mountpoint;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
  
  sub custom_status_output {
      my ($self, %options) = @_;
  
      return "options are '" . $self->{result_values}->{options} . "' [type: " . $self->{result_values}->{type} . "]";
  }
  
  sub prefix_output {
      my ($self, %options) = @_;
  
      return "Mount point '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'mountpoints', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All mount points options are ok' }
      ];
  
      $self->{maps_counters}->{mountpoints} = [
          {
              label => 'status',
              type => 2,
              critical_default => '%{options} !~ /^rw/i && %{type} !~ /tmpfs|squashfs/i',
              set => {
                  key_values => [ { name => 'display' }, { name => 'options' }, { name => 'type' } ],
                  closure_custom_output => $self->can('custom_status_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => \&catalog_status_threshold_ng
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-device:s'      => { name => 'filter_device' },
          'exclude-device:s'     => { name => 'exclude_device' },
          'filter-mountpoint:s'  => { name => 'filter_mountpoint' },
          'exclude-mountpoint:s' => { name => 'exclude_mountpoint' },
          'filter-type:s'        => { name => 'filter_type' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'mount',
          command_options => '2>&1',
          no_quit => 1
      );
  
      $self->{mountpoints} = {};
      
      my @lines = split /\n/, $stdout;
      foreach my $line (@lines) {
          next if ($line !~ /^\s*(.*?)\s+on\s+(.*?)\s+type\s+(\S+)\s+\((.*)\)/);
          my ($device, $mountpoint, $type, $options) = ($1, $2, $3, $4);
          
          next if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' &&
              $type !~ /$self->{option_results}->{filter_type}/);
          next if (defined($self->{option_results}->{filter_device}) && $self->{option_results}->{filter_device} ne '' &&
              $device !~ /$self->{option_results}->{filter_device}/);
          next if (defined($self->{option_results}->{exclude_device}) && $self->{option_results}->{exclude_device} ne '' &&
              $device =~ /$self->{option_results}->{exclude_device}/);
          next if (defined($self->{option_results}->{filter_mountpoint}) && $self->{option_results}->{filter_mountpoint} ne '' &&
              $mountpoint !~ /$self->{option_results}->{filter_mountpoint}/);
          next if (defined($self->{option_results}->{exclude_mountpoint}) && $self->{option_results}->{exclude_mountpoint} ne '' &&
              $mountpoint =~ /$self->{option_results}->{exclude_mountpoint}/);
  
          $self->{mountpoints}->{$mountpoint} = {
              display => $mountpoint,
              type => $type,
              options => $options
          };
      }
  
      if (scalar(keys %{$self->{mountpoints}}) <= 0) {
          $self->{output}->add_option_msg(short_msg => 'No mount points found');
          $self->{output}->option_exit();
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check mount points options.
  
  Command used: mount 2>&1
  
  =over 8
  
  =item B<--filter-mountpoint>
  
  Filter mount point name (can use regexp).
  
  =item B<--exclude-mountpoint>
  
  Exclude mount point name (can use regexp).
  
  =item B<--filter-device>
  
  Filter device name (can use regexp).
  
  =item B<--exclude-device>
  
  Exclude device name (can use regexp).
  
  =item B<--filter-type>
  
  Filter mount point type (can use regexp).
  
  =item B<--warning-status>
  
  Warning threshold.
  
  =item B<--critical-status>
  
  Critical threshold
  (default: '%{options} !~ /^rw/i && %{type} !~ /tmpfs|squashfs/i').
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_MOUNTPOINT

$fatpacked{"os/linux/local/mode/ntp.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_NTP';
  #
  # Copyright 2018 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::ntp;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
  
  my %state_map_ntpq = (
      '<sp>' => 'discarded due to high stratum and/or failed sanity checks',
      'x' => 'designated falsticker by the intersection algorithm',
      '.' => 'culled from the end of the candidate list',
      '-' => 'discarded by the clustering algorithm',
      '+' => 'included in the final selection set',
      '#' => 'selected for synchronization but distance exceeds maximum',
      '*' => 'selected for synchronization',
      'o' => 'selected for synchronization, PPS signal in use'
  );
  
  my %type_map_ntpq = (
      'l' => 'local',
      'u' => 'unicast',
      'm' => 'multicast',
      'b' => 'broadcast',
      '-' => 'netaddr'
  );
  
  my %state_map_chronyc = (
      'x' => 'time may be in error',
      '-' => 'not combined',
      '+' => 'combined',
      '?' => 'unreachable',
      '*' => 'current synced',
      '~' => 'time too variable'
  );
  
  my %type_map_chronyc = (
      '^' => 'server',
      '=' => 'peer',
      '#' => 'local clock'
  );
  
  my %unit_map_chronyc = (
      'ns' => 0.000001,
      'us' => 0.001,
      'ms' => 1,
      's'  => 1000
  );
  
  my %unit_map_timedatectl = (
      'us' => 0.001,
      'ms' => 1,
      's'  => 1000,
      'min' => 60 * 1000,
      'h' => 60 * 60 * 1000,
      'd' => 24 * 60 *60 * 1000
  );
  
  my %state_map_timedatectl = (
      'synchronized' => 'currently active and fully synchronized',
      'syncing' => 'currently active and synchronizing',
      'available' => 'configured and available for use',
      'unused' => 'configured as fallback but not used',
      'inactive' => 'not used because NTP service is inactive'
  );
  
  my %type_map_timedatectl = (
      'primary' => 'Primary NTP Server',
      'fallback' => 'Fallback NTP Server'
  );
  
  sub custom_status_output {
      my ($self, %options) = @_;
  
      return sprintf(
          '[type: %s] [reach: %s] [state: %s]',
          $self->{result_values}->{type},
          $self->{result_values}->{reach},
          $self->{result_values}->{state}
      );
  }
  
  sub custom_offset_perfdata {
      my ($self, %options) = @_;
  
      if ($self->{result_values}->{rawstate} !~ /(\*|synchronized)/) {
          $self->{output}->perfdata_add(
              nlabel => $self->{nlabel},
              unit => 'ms',
              instances => $self->{result_values}->{display},
              value => $self->{result_values}->{offset},
              min => 0
          );
      } else {
          $self->{output}->perfdata_add(
              nlabel => $self->{nlabel},
              unit => 'ms',
              instances =>  $self->{result_values}->{display},
              value => $self->{result_values}->{offset},
              warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
              critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
              min => 0
          );
      }
  }
  
  sub custom_offset_threshold {
      my ($self, %options) = @_;
  
      return 'ok' if $self->{result_values}->{rawstate} !~ /^(\*|synchronized)$/;
  
      return $self->{perfdata}->threshold_check(value => $self->{result_values}->{offset}, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' } ]);
  }
  
  sub prefix_peer_output {
      my ($self, %options) = @_;
  
      return "Peer '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'global', type => 0 },
          { name => 'peers', type => 1, cb_prefix_output => 'prefix_peer_output', message_multiple => 'All peers are ok' }
      ];
  
      $self->{maps_counters}->{global} = [
          { label => 'peers', nlabel => 'peers.detected.count', set => {
                  key_values => [ { name => 'peers' } ],
                  output_template => 'Number of ntp peers: %d',
                  perfdatas => [
                      { label => 'peers', template => '%d', min => 0 }
                  ]
              }
          }
      ];
  
      $self->{maps_counters}->{peers} = [
          { label => 'status', type => 2, set => {
                  key_values => [
                      { name => 'rawstate' }, { name => 'rawtype' },
                      { name => 'state' }, { name => 'type' },
                      { name => 'reach' }, { name => 'display' }
                  ],
                  closure_custom_output => $self->can('custom_status_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => \&catalog_status_threshold_ng
              }
          },
          { label => 'offset', nlabel => 'peer.time.offset.milliseconds', display_ok => 0, set => {
                  key_values => [ { name => 'offset' }, { name => 'rawstate' }, { name => 'display' } ],
                  output_template => 'offset: %s ms',
                  closure_custom_threshold_check => $self->can('custom_offset_threshold'),
                  closure_custom_perfdata => $self->can('custom_offset_perfdata'),
                  perfdatas => [
                      { template => '%s', min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'stratum', nlabel => 'peer.stratum.count', display_ok => 0, set => {
                  key_values => [ { name => 'stratum' }, { name => 'display' } ],
                  output_template => 'stratum: %s',
                  perfdatas => [
                      { label => 'stratum', template => '%s', min => 0, label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'ntp-mode:s'      => { name => 'ntp_mode', default => 'auto' },
          'filter-name:s'   => { name => 'filter_name', default => '' },
          'exclude-name:s'  => { name => 'exclude_name', default => '' },
          'filter-state:s'  => { name => 'filter_state', default => '' },
          'exclude-state:s' => { name => 'exclude_state', default => '' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::check_options(%options);
  
      if ($self->{option_results}->{ntp_mode} !~ /^(ntpq|chronyc|timedatectl|all|auto)$/) {
          $self->{output}->add_option_msg(short_msg => "ntp mode '" . $self->{option_results}->{ntp_mode} . "' not implemented" );
          $self->{output}->option_exit();
      }
  }
  
  sub get_ntp_modes {
      my ($self, %options) = @_;
  
      my $modes = {
          ntpq => {
              regexp => '^(\+|\*|\.|\-|\#|x|\<sp\>|o)(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)',
              command => 'ntpq',
              command_options => '-p -n 2>&1',
              type => 'ntpq'
          },
          chronyc => {
              regexp => '^(.)(\+|\*|\.|\-|\#|x|\<sp\>)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*?)(\d+)(\w+)$',
              command => 'chronyc',
              command_options => '-n sources 2>&1',
              type => 'chronyc'
          },
          timedatectl => {
              regexp => '',
              command => '',
              command_options => '',
              type => 'timedatectl'
          }
      };
  
      # Returns the selected mode or all modes
      return [ $modes->{$self->{option_results}->{ntp_mode}} ?
                  $modes->{$self->{option_results}->{ntp_mode}} :
                  ( $modes->{timedatectl}, $modes->{chronyc}, $modes->{ntpq} ) ];
  }
  
  sub skip_record {
      my ($self, %options) = @_;
  
      my $name = $options{display} // '';
      my $address = $options{address} // '';
      my $rawstate = $options{rawstate} // '';
      my $state = $options{state} // '';
  
      # filter_name includes name and address
      if ($self->{option_results}->{filter_name} ne '' && $name !~ /$self->{option_results}->{filter_name}/ && $address !~ /$self->{option_results}->{filter_name}/) {
          $self->{output}->output_add(long_msg => "skipping '$name': no matching filter peer name.", debug => 1);
          return 1;
      }
      if ($self->{option_results}->{exclude_name} ne '' && ($name =~ /$self->{option_results}->{exclude_name}/ || $address =~ /$self->{option_results}->{exclude_name}/)) {
          $self->{output}->output_add(long_msg => "skipping '$name': excluded peer name.", debug => 1);
          return 1;
      }
  
      if ($self->{option_results}->{filter_state} ne '' && $state !~ /$self->{option_results}->{filter_state}/ && $rawstate !~ /$self->{option_results}->{filter_state}/) {
          $self->{output}->output_add(long_msg => "skipping '$name': no matching filter peer state.", debug => 1);
          return 1;
      }
      if ($self->{option_results}->{exclude_state} ne '' && ($state =~ /$self->{option_results}->{exclude_state}/ || $rawstate =~ /$self->{option_results}->{exclude_state}/)) {
          $self->{output}->output_add(long_msg => "skipping '$name': excluded peer state.", debug => 1);
          return 1;
      }
  
      return 0;
  }
  
  sub manage_timedatectl {
      my ($self, %options) = @_;
  
      # With timedatectl three calls are required to retrieve all information
      #
      # timedatectl status to retrieve 'NTP service' and 'System clock synchronized'
      # Output snippet:
      #    System clock synchronized: yes
      #                  NTP service: active
      #
      # timedatectl timesync-status to retrieve 'Offset', 'Stratum' and 'Packet count'
      # Output snippet:
      #       Version: 4
      #       Stratum: 2
      #        Offset: -398us
      #        Jitter: 150us
      #  Packet count: 2
      #
      # timedatectl show-timesync to retrieve 'SystemNTPServers', 'FallbackNTPServers', 'ServerAddress' and 'ServerName'
      # Output snippet:
      # SystemNTPServers=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org
      # FallbackNTPServers=3.pool.ntp.org 4.pool.ntp.org
      # ServerName=0.pool.ntp.org
      # ServerAddress=188.125.64.7
      # PollIntervalUSec=4min 16s
      # NTPMessage={ Leap=0, Version=4, Mode=4, Stratum=2, Precision=-25, RootDelay=79.940ms, RootDispersion=1.358ms, Reference=628B853E, OriginateTimestamp=Thu 2025-08-28 14:31:58 CEST, ReceiveTimestamp=Thu 2025-08-28 14:31:58 CEST, TransmitTimestamp=Thu 2025-08-28 14:31:58 CEST, DestinationTimestamp=Thu 2025-08-28 14:31:58 CEST, Ignored=no PacketCount=3, Jitter=573us }
  
      my ($stdout_status) = $options{custom}->execute_command(    command => 'timedatectl',
                                                                  command_options => 'status 2>&1',
                                                                  no_quit => 1, );
      my ($stdout_timesync) = $options{custom}->execute_command(  command => 'timedatectl',
                                                                  command_options => 'timesync-status 2>&1',
                                                                  no_quit => 1, );
      my ($stdout_show) = $options{custom}->execute_command(      command => 'timedatectl',
                                                                  command_options => 'show-timesync 2>&1',
                                                                  no_quit => 1, );
  
      my %values = ( ( map { /^\s*(.+?): (.+)$/ } split /\n/, $stdout_timesync ),
                     ( map { /^(.+?)=(.+)$/ } split /\n/, $stdout_show ),
                     ( map { /^\s*(.+?): (.+)$/ } split /\n/, $stdout_status ) );
  
      return "timedatectl not available" unless $values{'NTP service'};
  
      $values{$_}//='' foreach ('SystemNTPServers', 'FallbackNTPServers', 'ServerAddress', 'ServerName', 'Offset', 'Stratum', 'Packet count', 'System clock synchronized');
  
      my $active_is_fallback = 0;
  
      # Primary and fallback servers are initialized, the active server is excluded as it will be initialized later with additional information
      # A server has either the type 'primary' or 'fallback'
      # A 'primary' server can have the states 'available', 'synchronized', 'syncing' or 'inactive'
      # A 'fallback' server can have the states 'unused', synchronized', 'syncing' or 'inactive'
      foreach my $srv (split /\s/, $values{SystemNTPServers}) {
          next if $values{ServerAddress} && ($srv eq $values{ServerAddress} || $srv eq $values{ServerName});
  
          $self->{peers}->{$srv} = { display => $srv, rawstate => 'available', stratum => 0, rawtype => 'primary', reach => 0, offset => 0 };
          $self->{global}->{peers}++
      }
      foreach my $srv (split /\s/, $values{FallbackNTPServers}) {
          if ($values{ServerAddress} && ($srv eq $values{ServerAddress} || $srv eq $values{ServerName})) {
              $active_is_fallback = 1;
              next
          }
          $self->{peers}->{$srv} = { display => $srv, rawstate => 'unused', stratum => 0, rawtype => 'fallback', reach => 0, offset => 0 };
          $self->{global}->{peers}++
      }
  
      if ($values{ServerAddress} ne '') {
          # If there is an active server it is initialized here with all information
          $values{ServerName} = $values{ServerAddress} if $values{ServerName} eq '';
          $values{'Offset'} = $1 * $unit_map_timedatectl{$2} if $values{'Offset'} =~ /^(.*?)([a-z]+)$/ && $unit_map_timedatectl{$2};
  
          $self->{peers}->{$values{ServerAddress}} = { display => $values{ServerName},
                                                       address => $values{ServerAddress},
                                                       rawstate => $values{'System clock synchronized'} eq 'yes' ? 'synchronized' : 'syncing',
                                                       rawtype => $active_is_fallback ? 'fallback' : 'primary',
                                                       stratum => $values{Stratum},
                                                       reach => $values{'Packet count'},
                                                       offset => $values{'Offset'} };
          $self->{global}->{peers}++;
      }
  
      foreach my $peer (keys %{$self->{peers}}) {
          $self->{peers}->{$peer}->{rawstate} = 'inactive' if $values{'NTP service'} ne 'active';
          $self->{peers}->{$peer}->{state} = $state_map_timedatectl{$self->{peers}->{$peer}->{rawstate}};
          $self->{peers}->{$peer}->{type} = $type_map_timedatectl{$self->{peers}->{$peer}->{rawtype}};
  
          # Data is only filtered here becase all states must be initialized first
          if ($self->skip_record(%{$self->{peers}->{$peer}})) {
              delete $self->{peers}->{$peer};
              $self->{global}->{peers}--;
              next
          }
      }
  
      undef;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my $request_mode = $self->{option_results}->{ntp_mode};
      my $no_quit = $self->{option_results}->{ntp_mode} =~ /^(all|auto)$/;
      my $modes = $self->get_ntp_modes();
  
      $self->{global} = { peers => 0 };
      $self->{peers} = {};
  
      foreach my $mode (@$modes) {
          # Exit if we are in auto mode and the previous mode has already found peers
          last if $self->{global}->{peers} && $request_mode eq 'auto';
  
          if ($mode->{type} eq 'timedatectl') {
              # timedatectl differs from other modes so it is handled in its own function
              my $error = $self->manage_timedatectl( custom => $options{custom} );
  
              if ($no_quit == 0 && $error) {
                  $self->{output}->add_option_msg(short_msg => $error);
                  $self->{output}->option_exit();
              }
              next
          }
  
          my ($stdout) = $options{custom}->execute_command(
              command => $mode->{command},
              command_options => $mode->{command_options},
              no_quit => $no_quit
          );
  
          my @lines = split /\n/, $stdout;
          foreach my $line (@lines) {
              if ($no_quit == 0 && $line =~ /Connection refused/) {
                  $self->{output}->add_option_msg(short_msg => "check ntp.conf and ntp daemon" );
                  $self->{output}->option_exit();
              }
              next if $line !~ /$mode->{regexp}/;
  
              my $entry = {};
              my ($remote_peer, $peer_fate) = (centreon::plugins::misc::trim($2), centreon::plugins::misc::trim($1));
              if ($mode->{type} eq 'chronyc') {
                  $remote_peer = centreon::plugins::misc::trim($3);
                  $peer_fate = centreon::plugins::misc::trim($2);
                  my ($type, $stratum, $poll, $reach, $lastRX, $offset) = ($1, $4, $5, $6, $7, $9);
                  $entry = {
                      display  => $remote_peer,
                      rawstate => $peer_fate,
                      state    => $state_map_chronyc{$peer_fate},
                      stratum  => centreon::plugins::misc::trim($stratum),
                      rawtype  => centreon::plugins::misc::trim($type),
                      type     => $type_map_chronyc{centreon::plugins::misc::trim($type)},
                      reach    => centreon::plugins::misc::trim($reach),
                      offset   => centreon::plugins::misc::trim($offset) * $unit_map_chronyc{centreon::plugins::misc::trim($10)},
                  };
              } else {
                  my ($refid, $stratum, $type, $last_time, $polling_intervall, $reach, $delay, $offset, $jitter) = ($3, $4, $5, $6, $7, $8, $9, $10, $11);
                  $entry = {
                      display  => $remote_peer,
                      rawstate => $peer_fate,
                      state    => $state_map_ntpq{$peer_fate},
                      stratum  => centreon::plugins::misc::trim($stratum),
                      rawtype  => centreon::plugins::misc::trim($type),
                      type     => $type_map_ntpq{centreon::plugins::misc::trim($type)},
                      reach    => centreon::plugins::misc::trim($reach),
                      offset   => centreon::plugins::misc::trim($offset)
                  };
              }
  
              next if $self->skip_record(%$entry);
  
              if ($mode->{type} eq 'ntpq') {
                  my ($refid, $stratum, $type, $last_time, $polling_intervall, $reach, $delay, $offset, $jitter) = ($3, $4, $5, $6, $7, $8, $9, $10, $11);
                  $self->{peers}->{$remote_peer} = $entry;
              } elsif ($mode->{type} eq 'chronyc') {
                  #210 Number of sources = 4
                  #MS Name/IP address         Stratum Poll Reach LastRx Last sample               
                  #===============================================================================
                  #^+ 212.83.187.62                 2   9   377   179   -715us[ -731us] +/-   50ms
                  #^- 129.250.35.251                2   8   377    15    -82us[  -99us] +/-   96ms
  
                  $self->{peers}->{$remote_peer} = $entry;
              }
              
              $self->{global}->{peers}++;
          }
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check NTP daemons.
  
  Command used: C<timedatectl status ; timedatectl timesync-status ; timedatectl show-timesync> or C<ntpq -p -n> or C<chronyc -n sources>
  
  =over 8
  
  =item B<--ntp-mode>
  
  Default mode for parsing and command: C<auto> (default), C<timedatectl>, C<ntpq>, C<chronyc> or C<all>.
  In 'auto' mode the data is taken from the first working mode (in order).
  In 'all' mode the data is taken and aggregated from all working mode.
  
  =item B<--filter-name>
  
  Filter peer name (can be a regexp).
  
  =item B<--exclude-name>
  
  Exclude by peer name (can be a regexp).
  
  =item B<--filter-state>
  
  Filter peer state (can be a regexp).
  
  =item B<--exclude-state>
  
  Exclude by peer state (can be a regexp).
  
  =item B<--warning-peers>
  
  Warning threshold minimum amount of NTP-Server
  
  =item B<--critical-peers>
  
  Critical threshold minimum amount of NTP-Server
  
  =item B<--warning-offset>
  
  Warning threshold offset deviation value in milliseconds
  
  =item B<--critical-offset>
  
  Critical threshold offset deviation value in milliseconds
  
  =item B<--warning-stratum>
  
  Warning threshold.
  
  =item B<--critical-stratum>
  
  Critical threshold.
  
  =item B<--unknown-status>
  
  Define the conditions to match for the status to be UNKNOWN.
  You can use the following variables: %{state}, %{rawstate}, %{type}, %{rawtype}, %{reach}, %{display}
  
  =item B<--warning-status>
  
  Define the conditions to match for the status to be WARNING.
  You can use the following variables: %{state}, %{rawstate}, %{type}, %{rawtype}, %{reach}, %{display}
  
  =item B<--critical-status>
  
  Define the conditions to match for the status to be CRITICAL.
  You can use the following variables: %{state}, %{rawstate}, %{type}, %{rawtype}, %{reach}, %{display}
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_NTP

$fatpacked{"os/linux/local/mode/openfiles.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_OPENFILES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::openfiles;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub set_counters {
      my ($self, %options) = @_;
      
      $self->{maps_counters_type} = [
          { name => 'global', type => 0 }
      ];
      
      $self->{maps_counters}->{global} = [
          { label => 'files-open', nlabel => 'system.files.open.count', set => {
                  key_values => [ { name => 'openfiles' } ],
                  output_template => 'current open files: %s',
                  perfdatas => [
                      { template => '%s', min => 0 }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-username:s' => { name => 'filter_username' },
          'filter-appname:s'  => { name => 'filter_appname' },
          'filter-pid:s'      => { name => 'filter_pid' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'lsof',
          command_options => '-a -d ^mem -d ^cwd -d ^rtd -d ^txt -d ^DEL 2>&1'
      );
  
      $self->{global} = { openfiles => 0 };
      my @lines = split /\n/, $stdout;
      shift @lines;
      foreach (@lines) {
          /^(\S+)\s+(\S+)\s+(\S+)/;
          my ($name, $pid, $user) = ($1, $2, $3);
          next if (defined($self->{option_results}->{filter_username}) && $self->{option_results}->{filter_username} ne '' &&
              $user !~ /$self->{option_results}->{filter_username}/);
          next if (defined($self->{option_results}->{filter_appname}) && $self->{option_results}->{filter_appname} ne '' &&
              $name !~ /$self->{option_results}->{filter_appname}/);
          next if (defined($self->{option_results}->{filter_pid}) && $self->{option_results}->{filter_pid} ne '' &&
              $pid !~ /$self->{option_results}->{filter_pid}/);
  
          $self->{global}->{openfiles}++;
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check open files.
  
  Command used: lsof -a -d ^mem -d ^cwd -d ^rtd -d ^txt -d ^DEL 2>&1
  
  =over 8
  
  =item B<--filter-appname>
  
  Filter application name (can be a regexp).
  
  =item B<--filter-username>
  
  Filter username name (can be a regexp).
  
  =item B<--filter-pid>
  
  Filter PID (can be a regexp).
  
  =item B<--warning-*> B<--critical-*>
  
  Thresholds.
  Can be: 'files-open'.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_OPENFILES

$fatpacked{"os/linux/local/mode/packeterrors.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_PACKETERRORS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::packeterrors;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
  use Digest::MD5 qw(md5_hex);
  
  sub custom_status_output {
      my ($self, %options) = @_;
  
      return sprintf('status : %s', $self->{result_values}->{status});
  }
  
  sub custom_packet_output {
      my ($self, %options) = @_;
  
      return sprintf(
          'Packet %s %s : %.2f %% (%s)',
          ucfirst($self->{result_values}->{type}),
          ucfirst($self->{result_values}->{label}), 
          $self->{result_values}->{result_prct},
          $self->{result_values}->{diff_value}
      );
  }
  
  sub custom_packet_calc {
      my ($self, %options) = @_;
  
      $self->{result_values}->{type} = $options{extra_options}->{type};
      $self->{result_values}->{label} = $options{extra_options}->{label_ref};
      $self->{result_values}->{diff_value} = $options{new_datas}->{$self->{instance} . '_' . $self->{result_values}->{type} . '_' . $self->{result_values}->{label}} - 
          $options{old_datas}->{$self->{instance} . '_' . $self->{result_values}->{type} . '_' . $self->{result_values}->{label}};
      my $diff_total = $options{new_datas}->{$self->{instance} . '_total_' . $self->{result_values}->{label}} - 
          $options{old_datas}->{$self->{instance} . '_total_' . $self->{result_values}->{label}};
  
      $self->{result_values}->{result_prct} = ($diff_total == 0) ? 0 : ($self->{result_values}->{diff_value} * 100 / $diff_total);
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      return 0;
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'interface', type => 1, cb_prefix_output => 'prefix_interface_output', message_multiple => 'All interfaces are ok', skipped_code => { -10 => 1 } }
      ];
  
      $self->{maps_counters}->{interface} = [
          { label => 'status', type => 2, critical_default => '%{status} ne "RU"', set => {
                  key_values => [ { name => 'status' }, { name => 'display' } ],
                  closure_custom_output => $self->can('custom_status_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => \&catalog_status_threshold_ng
              }
          },
          { label => 'in-discard', nlabel => 'interface.packets.in.discard.percentage', set => {
                  key_values => [ { name => 'discard_in', diff => 1 }, { name => 'total_in', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_packet_calc'), closure_custom_calc_extra_options => { type => 'discard', label_ref => 'in' },
                  closure_custom_output => $self->can('custom_packet_output'), output_error_template => 'Discard In : %s',
                  threshold_use => 'result_prct',
                  perfdatas => [
                      { value => 'result_prct', template => '%.2f', min => 0, max => 100,
                        unit => '%', label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'out-discard', nlabel => 'interface.packets.out.discard.percentage', set => {
                  key_values => [ { name => 'discard_out', diff => 1 }, { name => 'total_out', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_packet_calc'), closure_custom_calc_extra_options => { type => 'discard', label_ref => 'out' },
                  closure_custom_output => $self->can('custom_packet_output'), output_error_template => 'Discard Out : %s',
                  threshold_use => 'result_prct',
                  perfdatas => [
                      { value => 'result_prct', template => '%.2f', min => 0, max => 100,
                        unit => '%', label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'in-error', nlabel => 'interface.packets.in.error.percentage', set => {
                  key_values => [ { name => 'error_in', diff => 1 }, { name => 'total_in', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_packet_calc'), closure_custom_calc_extra_options => { type => 'error', label_ref => 'in' },
                  closure_custom_output => $self->can('custom_packet_output'), output_error_template => 'Error In : %s',
                  threshold_use => 'result_prct',
                  perfdatas => [
                      { value => 'result_prct', template => '%.2f', min => 0, max => 100,
                        unit => '%', label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          },
          { label => 'out-error', nlabel => 'interface.packets.out.error.percentage', set => {
                  key_values => [ { name => 'error_out', diff => 1 }, { name => 'total_out', diff => 1 }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_packet_calc'), closure_custom_calc_extra_options => { type => 'error', label_ref => 'out' },
                  closure_custom_output => $self->can('custom_packet_output'), output_error_template => 'Error In : %s',
                  threshold_use => 'result_prct',
                  perfdatas => [
                      { value => 'result_prct', template => '%.2f', min => 0, max => 100,
                        unit => '%', label_extra_instance => 1, instance_use => 'display' }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-state:s'      => { name => 'filter_state', },
          'filter-interface:s'  => { name => 'filter_interface' },
          'exclude-interface:s' => { name => 'exclude_interface' },
          'no-loopback'         => { name => 'no_loopback' }
      });
  
      return $self;
  }
  
  sub prefix_interface_output {
      my ($self, %options) = @_;
  
      return "Interface '" . $options{instance_value}->{display} . "' ";
  }
  
  sub do_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'ip',
          command_path => '/sbin',
          command_options => '-s addr 2>&1'
      );
  
      my $mapping = {
          ifconfig => {
              get_interface => '^(\S+)(.*?)(\n\n|\n$)',
              total => 'RX packets:(\d+).*?TX packets:(\d+)',
              discard_in => 'RX packets:\d+\s+?errors:\d+\s+?dropped:(\d+)',
              discard_out => 'TX packets:\d+\s+?errors:\d+\s+?dropped:(\d+)',
              error_in => 'RX packets:\d+\s+?errors:(\d+)',
              error_out => 'TX packets:\d+\s+?errors:(\d+)',
          },
          iproute => {
              get_interface => '^\d+:\s+(\S+)(.*?)(?=\n\d|\Z$)',
              total => 'RX:\s+bytes\s+packets.*?\d+\s+(\d+).*?TX:\s+bytes\s+packets.*?\d+\s+(\d+)',
              discard_in => 'RX:.*?dropped.*?\d+.*?\d+.*?\d+.*?(\d+)',
              discard_out => 'TX:.*?dropped.*?\d+.*?\d+.*?\d+.*?(\d+)',
              error_in => 'RX:.*?errors.*?\d+.*?\d+.*?(\d+)',
              error_out => 'TX:.*?errors.*?\d+.*?\d+.*?(\d+)',
          },
      };
  
      my $type = 'ifconfig';
      if ($stdout =~ /^\d+:\s+\S+:\s+</ms) {
          $type = 'iproute';
      }
  
      $self->{interface} = {};
      while ($stdout =~ /$mapping->{$type}->{get_interface}/msg) {
          my ($interface_name, $values) = ($1, $2);
          my $states = '';
          $states .= 'R' if ($values =~ /RUNNING|LOWER_UP/ms);
          $states .= 'U' if ($values =~ /UP/ms);
          $interface_name =~ s/:$//;
  
          next if (defined($self->{option_results}->{no_loopback}) && $values =~ /LOOPBACK/ms);
          next if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' &&
              $states !~ /$self->{option_results}->{filter_state}/);
          next if (defined($self->{option_results}->{filter_interface}) && $self->{option_results}->{filter_interface} ne '' &&
              $interface_name !~ /$self->{option_results}->{filter_interface}/);
          next if (defined($self->{option_results}->{exclude_interface}) && $self->{option_results}->{exclude_interface} ne '' &&
              $interface_name =~ /$self->{option_results}->{exclude_interface}/);
  
          $self->{interface}->{$interface_name} = {
              display => $interface_name,
              status => $states,
          };
          if ($values =~ /$mapping->{$type}->{total}/msi) {
              $self->{interface}->{$interface_name}->{total_in} = $1;
              $self->{interface}->{$interface_name}->{total_out} = $2;
          }
          
          foreach ('discard_in', 'discard_out', 'error_in', 'error_out') {
              if ($values =~ /$mapping->{$type}->{$_}/msi) {
                  $self->{interface}->{$interface_name}->{$_} = $1;
              }
          }
      }
  
      if (scalar(keys %{$self->{interface}}) <= 0) {
          $self->{output}->add_option_msg(short_msg => "No interface found.");
          $self->{output}->option_exit();
      }
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      $self->do_selection(custom => $options{custom});
      $self->{cache_name} = 'cache_linux_local_' . $options{custom}->get_identifier() . '_' . $self->{mode} . '_' .
          (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_interface}) ? md5_hex($self->{option_results}->{filter_interface}) : md5_hex('all'));
  }
  
  1;
  
  
  =head1 MODE
  
  Check packets errors and discards on interfaces.
  
  Command used: /sbin/ip -s addr 2>&1
  
  =over 8
  
  =item B<--unknown-status>
  
  Define the conditions to match for the status to be UNKNOWN.
  You can use the following variables: %{status}, %{display}
  
  =item B<--warning-status>
  
  Define the conditions to match for the status to be WARNING.
  You can use the following variables: %{status}, %{display}
  
  =item B<--critical-status>
  
  Define the conditions to match for the status to be CRITICAL (default: '%{status} ne "RU"').
  You can use the following variables: %%{status}, %{display}
  
  =item B<--warning-*>
  
  Warning threshold in percent of total packets. Can be:
  in-error, out-error, in-discard, out-discard
  
  =item B<--critical-*>
  
  Critical threshold in percent of total packets. Can be:
  in-error, out-error, in-discard, out-discard
  
  =item B<--filter-interface>
  
  Filter interface name (regexp can be used).
  
  =item B<--exclude-interface>
  
  Exclude interface name (regexp can be used).
  
  =item B<--filter-state>
  
  Filter filesystem type (regexp can be used).
  
  =item B<--no-loopback>
  
  Don't display loopback interfaces.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_PACKETERRORS

$fatpacked{"os/linux/local/mode/paging.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_PAGING';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::paging;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use Digest::MD5 qw(md5_hex);
  
  sub set_counters {
      my ($self, %options) = @_;
      
      $self->{maps_counters_type} = [
          { name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', skipped_code => { -10 => 1 } }
      ];
      
      $self->{maps_counters}->{global} = [
          { label => 'pgpgin', nlabel => 'system.pgpin.usage.bytespersecond', set => {
                  key_values => [ { name => 'pgpgin', per_second => 1 } ],
                  output_template => 'pgpgin : %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { label => 'pgpgin', template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          },
          { label => 'pgpgout', nlabel => 'system.pgpgout.usage.bytespersecond', set => {
                  key_values => [ { name => 'pgpgout', per_second => 1 } ],
                  output_template => 'pgpgout : %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { label => 'pgpgout', template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          },
          { label => 'pswpin', nlabel => 'system.pswpin.usage.bytespersecond', set => {
                  key_values => [ { name => 'pswpin', per_second => 1 } ],
                  output_template => 'pswpin : %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { label => 'pswpin', template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          },
          { label => 'pswpout', nlabel => 'system.pswpout.usage.bytespersecond', set => {
                  key_values => [ { name => 'pswpout', per_second => 1 } ],
                  output_template => 'pswpout : %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { label => 'pswpout', template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          },
          { label => 'pgfault', nlabel => 'system.pgfault.usage.bytespersecond', set => {
                  key_values => [ { name => 'pgfault', per_second => 1 } ],
                  output_template => 'pgfault : %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { label => 'pgfault', template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          },
          { label => 'pgmajfault', nlabel => 'system.pgmajfault.usage.bytespersecond', set => {
                  key_values => [ { name => 'pgmajfault', per_second => 1 } ],
                  output_template => 'pgmajfault : %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { label => 'pgmajfault', template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
      });
  
      return $self;
  }
  
  sub prefix_global_output {
      my ($self, %options) = @_;
      
      return 'Paging ';
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/vmstat 2>&1'
      );
          
      $self->{global} = {};
      $self->{global}->{pgpgin} = $stdout =~ /^pgpgin.*?(\d+)/msi ? $1 * 1024 : undef;
      $self->{global}->{pgpgout} = $stdout =~ /^pgpgout.*?(\d+)/msi ? $1 * 1024 : undef;
      $self->{global}->{pswpin} = $stdout =~ /^pswpin.*?(\d+)/msi ? $1 * 1024 : undef;
      $self->{global}->{pswpout} = $stdout =~ /^pswpout.*?(\d+)/msi ? $1 * 1024: undef;
      $self->{global}->{pgfault} = $stdout =~ /^pgfault.*?(\d+)/msi ? $1 * 1024: undef;
      $self->{global}->{pgmajfault} = $stdout =~ /^pgmajfault.*?(\d+)/msi ? $1 * 1014: undef;
      
      $self->{cache_name} = 'cache_linux_local_' . $options{custom}->get_identifier()  . '_' . $self->{mode} . '_' . 
          (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
  }
  
  1;
  
  
  =head1 MODE
  
  Check paging informations.
  
  Command used: cat /proc/vmstat 2>&1
  
  =over 8
  
  =item B<--warning-*>
  
  Warning threshold.
  Can be: 'pgpgin', 'pgpgout', 'pswpin', 'pswpout', 'pgfault', 'pgmajfault'.
  
  =item B<--critical-*>
  
  Critical threshold.
  Can be: 'pgpgin', 'pgpgout', 'pswpin', 'pswpout', 'pgfault', 'pgmajfault'.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_PAGING

$fatpacked{"os/linux/local/mode/pendingupdates.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_PENDINGUPDATES';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::pendingupdates;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub set_counters {
      my ($self, %options) = @_;
      
      $self->{maps_counters_type} = [
          { name => 'global', type => 0, skipped_code => { -10 => 1 } },
          { name => 'updates', type => 1 }
      ];
      
      $self->{maps_counters}->{global} = [
          { label => 'total', nlabel => 'pending.updates.total.count', set => {
                  key_values => [ { name => 'total' } ],
                  output_template => 'Number of pending updates : %d',
                  perfdatas => [
                      { label => 'total', template => '%d', min => 0 }
                  ]
              }
          },
          { label => 'security', nlabel => 'security.updates.total.count', set => {
                  key_values => [ { name => 'total_security' } ],
                  output_template => 'Number of pending security updates : %d',
                  perfdatas => [
                      { label => 'total_security', template => '%d', min => 0 }
                  ]
              }
          }
      ];
      
      $self->{maps_counters}->{updates} = [
          { label => 'update', set => {
                  key_values => [ { name => 'package' }, { name => 'version' }, { name => 'repository' } ],
                  closure_custom_output => $self->can('custom_updates_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => sub { return 'ok'; }
              }
          }
      ];
  }
  
  sub custom_updates_output {
      my ($self, %options) = @_;
      
      return sprintf(
          "Package '%s' [version: %s] [repository: %s]",
          $self->{result_values}->{package},
          $self->{result_values}->{version},
          $self->{result_values}->{repository}
      );
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'check-security'      => { name => 'check_security' },
          'os-mode:s'           => { name => 'os_mode', default => 'rhel' },
          'filter-package:s'    => { name => 'filter_package' },
          'filter-repository:s' => { name => 'filter_repository' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::check_options(%options);
  
      if ((defined($self->{option_results}->{os_mode}) && $self->{option_results}->{os_mode} ne 'rhel') && defined($self->{option_results}->{check_security})){
              $self->{output}->add_option_msg(severity => 'UNKNOWN', short_msg => "--check-security is only available with rhel.");
              $self->{output}->option_exit();
          }
  
      if (!defined($self->{option_results}->{os_mode}) ||
          $self->{option_results}->{os_mode} eq '' ||
          $self->{option_results}->{os_mode} eq 'rhel'
          ) {
          $self->{command} = 'yum';
          $self->{command_options} = 'check-update 2>&1';
          if (defined($self->{option_results}->{check_security})) {
              $self->{command_options} = '-q updateinfo list sec' 
          }
      } elsif ($self->{option_results}->{os_mode} eq 'debian') {
          $self->{command} = 'apt-get';
          $self->{command_options} = 'upgrade -sVq 2>&1';
      } elsif ($self->{option_results}->{os_mode} eq 'suse') {
          $self->{command} = 'zypper';
          $self->{command_options} = 'list-updates 2>&1';
      } else {
          $self->{output}->add_option_msg(short_msg => "os mode '" . $self->{option_results}->{os_mode} . "' not implemented" );
          $self->{output}->option_exit();
      }    
  }
  
  sub parse_updates {
      my ($self, %options) = @_;
  
      my @lines = split /\n/, $options{stdout};
      foreach my $line (@lines) {
          next if ($line !~ /^(\S+)\s+(\d+\S+)\s+(\S+)/
              && $line !~ /\s+(\S+)\s+\(\S+\s\=\>\s(\S+)\)/
              && $line !~ /.*\|.*\|\s+(\S+)\s+\|.*\|\s+(\d+\S+)\s+\|.*/
              && $line !~ /.*\|\s+(\S+)\s+\|\s+(\S+)\s+\|.*\|\s+(\d+\S+)\s+\|.*/);
          my ($package, $version, $repository) = ($1, $2, $3);
          if ($self->{option_results}->{os_mode} =~ /suse/i && $line =~ /.*\|\s+(\S+)\s+\|\s+(\S+)\s+\|.*\|\s+(\d+\S+)\s+\|.*/){
              ($repository, $package, $version) = ($1, $2, $3);
          }
          
          $repository = "-" if (!defined($repository) || $repository eq '');
  
          if (defined($self->{option_results}->{filter_package}) && $self->{option_results}->{filter_package} ne '' &&
              $package !~ /$self->{option_results}->{filter_package}/) {
              $self->{output}->output_add(long_msg => "skipping '" . $package . "': no matching filter.", debug => 1);
              next;
          }
          if (defined($self->{option_results}->{filter_repository}) && $self->{option_results}->{filter_repository} ne '' &&
              $repository !~ /$self->{option_results}->{filter_repository}/) {
              $self->{output}->output_add(long_msg => "skipping '" . $repository . "': no matching filter.", debug => 1);
              next;
          }
  
          $self->{updates}->{$package} = {
              package => $package,
              version => $version,
              repository => $repository,
          };
  
          $self->{global}->{total}++;
      }
  }
  
  sub parse_security_updates {
      my ($self, %options) = @_;
  
      my @lines = split(/\n/, $options{stdout});
      $self->{global}->{total_security} = 0;
      foreach my $line (@lines) {
          $self->{global}->{total_security}++;
      }
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => $self->{command},
          command_options => $self->{command_options},
          no_quit => 1
      );
  
      if ((defined($self->{option_results}->{check_security}) && 
          (!defined($self->{option_results}->{os_mode}) ||
          $self->{option_results}->{os_mode} eq '' ||
          $self->{option_results}->{os_mode} eq 'rhel'
          ))){
          $self->{global}->{total_security} = 0;
  
          parse_security_updates($self, stdout => $stdout);
      }
      else { 
          $self->{global}->{total} = 0;
          $self->{updates} = {};
  
          parse_updates($self, stdout => $stdout);
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check pending updates.
  
  For rhel/centos: yum check-update 2>&1
  For rhel/centos security: yum -q updateinfo list sec
  For Debian: apt-get upgrade -sVq 2>&1
  For Suse: zypper list-updates 2>&1
  
  =over 8
  
  =item B<--os-mode>
  
  Default mode for parsing and command: 'rhel' (default), 'debian', 'suse'.
  
  =item B<--warning-total>
  
  Warning threshold for total amount of pending updates.
  
  =item B<--critical-total>
  
  Critical threshold for total amount of pending updates.
  
  =item B<--warning-security>
  
  Warning threshold for total amount of pending security updates.
  
  =item B<--critical-security>
  
  Critical threshold for total amount of pending security updates.
  
  =item B<--filter-package>
  
  Filter package name.
  
  =item B<--filter-repository>
  
  Filter repository name.
  
  =item B<--check-security>
  
  Display number of pending security updates. 
  
  Only available for Red Hat-Based distributions.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_PENDINGUPDATES

$fatpacked{"os/linux/local/mode/process.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_PROCESS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::process;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  use Digest::MD5 qw(md5_hex);
  use Time::HiRes;
  
  sub custom_cpu_calc {
      my ($self, %options) = @_;
  
      my $cpu_utime = $options{new_datas}->{$self->{instance} . '_cpu_utime'} - $options{old_datas}->{$self->{instance} . '_cpu_utime'};
      my $cpu_stime = $options{new_datas}->{$self->{instance} . '_cpu_stime'} - $options{old_datas}->{$self->{instance} . '_cpu_stime'};
  
      my $total_ticks = $options{delta_time} * $self->{instance_mode}->{option_results}->{clock_ticks};
      $self->{result_values}->{cpu_prct} = 100 * ($cpu_utime + $cpu_stime) / $total_ticks;
      $self->{instance_mode}->{global}->{cpu_prct} += $self->{result_values}->{cpu_prct};
  
      return 0;
  }
  
  sub custom_disks_calc {
      my ($self, %options) = @_;
  
      my $diff = $options{new_datas}->{$self->{instance} . '_disks_' . $options{extra_options}->{label_ref}} - 
          $options{old_datas}->{$self->{instance} . '_disks_' . $options{extra_options}->{label_ref}};
      $self->{result_values}->{'disks_' . $options{extra_options}->{label_ref}} = $diff / $options{delta_time};
      $self->{instance_mode}->{global}->{'disks_' . $options{extra_options}->{label_ref}} += $self->{result_values}->{'disks_' . $options{extra_options}->{label_ref}};
  
      return 0;
  }
  
  sub prefix_process_output {
      my ($self, %options) = @_;
  
      return sprintf(
          'Process: [command => %s] [arg => %s] [state => %s] ',
          $options{instance_value}->{cmd},
          $options{instance_value}->{args},
          $options{instance_value}->{state}
      );
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'processes', type => 1, cb_prefix_output => 'prefix_process_output', skipped_code => { -10 => 1, -1 => 1 } },
          { name => 'global', type => 0, skipped_code => { -10 => 1 } }
      ];
  
      $self->{maps_counters}->{global} = [
          { label => 'total', nlabel => 'processes.total.count', set => {
                  key_values => [ { name => 'processes' } ],
                  output_template => 'Number of current processes: %s',
                  perfdatas => [
                      { template => '%s', min => 0 }
                  ]
              }
          },
          { label => 'total-memory-usage', nlabel => 'processes.memory.usage.bytes', set => {
                  key_values => [ { name => 'memory_used' } ],
                  output_template => 'memory used: %s %s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%s', min => 0, unit => 'B' }
                  ]
              }
          },
          { label => 'total-cpu-utilization', nlabel => 'processes.cpu.utilization.percentage', set => {
                  key_values => [ { name => 'cpu_prct' } ],
                  output_template => 'cpu usage: %.2f %%',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%.2f', unit => '%', min => 0 }
                  ]
              }
          },
          { label => 'total-disks-read', nlabel => 'processes.disks.io.read.usage.bytespersecond', set => {
                  key_values => [ { name => 'disks_read' } ],
                  output_template => 'disks read: %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          },
          { label => 'total-disks-write', nlabel => 'processes.disks.io.write.usage.bytespersecond', set => {
                  key_values => [ { name => 'disks_write' } ],
                  output_template => 'disks write: %s %s/s',
                  output_change_bytes => 1,
                  perfdatas => [
                      { template => '%d', unit => 'B/s', min => 0 }
                  ]
              }
          }
      ];
  
      $self->{maps_counters}->{processes} = [
          { label => 'time', set => {
                  key_values => [ { name => 'duration_seconds' }, { name => 'duration_human' } ],
                  output_template => 'duration: %s',
                  output_use => 'duration_human',
                  closure_custom_perfdata => sub { return 0; }
              }
          },
          { label => 'memory-usage', set => {
                  key_values => [ { name => 'memory_used' } ],
                  output_template => 'memory used: %s %s',
                  output_change_bytes => 1,
                  closure_custom_perfdata => sub { return 0; }
              }
          },
          { label => 'cpu-utilization', set => {
                  key_values => [ { name => 'cpu_utime', diff => 1 }, { name => 'cpu_stime', diff => 1 } ],
                  closure_custom_calc => $self->can('custom_cpu_calc'),
                  output_template => 'cpu usage: %.2f %%',
                  output_use => 'cpu_prct',
                  threshold_use => 'cpu_prct',
                  closure_custom_perfdata => sub { return 0; }
              }
          },
          { label => 'disks-read', set => {
                  key_values => [ { name => 'disks_read', diff => 1 } ],
                  closure_custom_calc => $self->can('custom_disks_calc'), closure_custom_calc_extra_options => { label_ref => 'read' },
                  output_template => 'disks read: %s %s/s',
                  output_change_bytes => 1,
                  closure_custom_perfdata => sub { return 0; }
              }
          },
          { label => 'disks-write', set => {
                  key_values => [ { name => 'disks_write', diff => 1 } ],
                  closure_custom_calc => $self->can('custom_disks_calc'), closure_custom_calc_extra_options => { label_ref => 'write' },
                  output_template => 'disks write: %s %s/s',
                  output_change_bytes => 1,
                  closure_custom_perfdata => sub { return 0; }
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-command:s'  => { name => 'filter_command' },
          'exclude-command:s' => { name => 'exclude_command' },
          'filter-arg:s'      => { name => 'filter_arg' },
          'exclude-arg:s'     => { name => 'exclude_arg' },
          'filter-state:s'    => { name => 'filter_state' },
          'filter-ppid:s'	    => { name => 'filter_ppid' },
          'add-cpu'           => { name => 'add_cpu' },
          'add-memory'        => { name => 'add_memory' },
          'add-disk-io'       => { name => 'add_disk_io' },
          'page-size:s'       => { name => 'page_size', default => 4096 },
          'clock-ticks:s'     => { name => 'clock_ticks', default => 100 }
      });
  
      return $self;
  }
  
  my $state_map = {
      Z => 'zombie',
      X => 'dead',
      W => 'paging',
      T => 'stopped',
      S => 'InterruptibleSleep',
      R => 'running',
      D => 'UninterrupibleSleep',
      I => 'IdleKernelThread'
  };
  
  sub parse_output {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'ps',
          command_options => '-e -o state -o etime:15 -o pid:10 -o ppid:10 -o comm:50 -o args -w 2>&1'
      );
  
      $self->{global} = { processes => 0 };
      $self->{processes} = {};
  
      my @lines = split(/\n/, $stdout);
      my $line = shift(@lines);
      foreach my $line (@lines) {
          next if ($line !~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.{50})\s+(.*)$/);
          my ($state, $elapsed, $pid, $ppid, $cmd, $args) = (
              centreon::plugins::misc::trim($1),
              centreon::plugins::misc::trim($2),
              centreon::plugins::misc::trim($3),
              centreon::plugins::misc::trim($4),
              centreon::plugins::misc::trim($5),
              centreon::plugins::misc::trim($6)
          );
  
          next if (defined($self->{option_results}->{filter_command}) && $self->{option_results}->{filter_command} ne '' &&
              $cmd !~ /$self->{option_results}->{filter_command}/);
          next if (defined($self->{option_results}->{exclude_command}) && $self->{option_results}->{exclude_command} ne '' &&
              $cmd =~ /$self->{option_results}->{exclude_command}/);
          next if (defined($self->{option_results}->{filter_arg}) && $self->{option_results}->{filter_arg} ne '' &&
              $args !~ /$self->{option_results}->{filter_arg}/);
          next if (defined($self->{option_results}->{exclude_arg}) && $self->{option_results}->{exclude_arg} ne '' &&
              $args =~ /$self->{option_results}->{exclude_arg}/);
          next if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' &&
              $state_map->{$state} !~ /$self->{option_results}->{filter_state}/i);
          next if (defined($self->{option_results}->{filter_ppid}) && $self->{option_results}->{filter_ppid} ne '' &&
              $ppid !~ /$self->{option_results}->{filter_ppid}/);
  
          $args =~ s/\|//g;
          my $duration_seconds = $self->get_duration(elapsed => $elapsed);
  
          $self->{processes}->{$pid} = {
              ppid => $ppid, 
              state => $state,
              duration_seconds => $duration_seconds,
              duration_human => centreon::plugins::misc::change_seconds(value => $duration_seconds),
              cmd => $cmd, 
              args => $args
          };
          $self->{global}->{processes}++;
      }
  }
  
  sub get_duration {
      my ($self, %options) = @_;
  
      # Format: [[dd-]hh:]mm:ss
      $options{elapsed} =~ /(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)/;
      my ($day, $hour, $min, $sec) = ($1, $2, $3, $4);
      my $total_seconds_elapsed = $sec + ($min * 60);
      if (defined($hour)) {
          $total_seconds_elapsed += ($hour * 60 * 60);
      }
      if (defined($day)) {
          $total_seconds_elapsed += ($day * 86400);
      }
  
      return $total_seconds_elapsed;
  }
  
  sub add_cpu {
      my ($self, %options) = @_;
  
      return if (!defined($self->{option_results}->{add_cpu}));
  
      $self->{global}->{cpu_prct} = 0;
      # /stat
      #   utime (14) Amount of time that this process has been scheduled in user mode, measured in clock ticks (divide by sysconf(_SC_CLK_TCK))
      #   stime (15) Amount of time that this process has been scheduled in kernel mode, measured in clock ticks
      foreach my $pid (keys %{$self->{processes}}) {
          next if ($options{content} !~ /==>\s*\/proc\/$pid\/stat\s+.*?\n(.*?)(?:==>|\Z)/ms);
          my @values = split(/\s+/, $1);
  
          $self->{processes}->{$pid}->{cpu_utime} = $values[13];
          $self->{processes}->{$pid}->{cpu_stime} = $values[14];
      }
  }
  
  sub add_memory {
      my ($self, %options) = @_;
  
      return if (!defined($self->{option_results}->{add_memory}));
  
      $self->{global}->{memory_used} = 0;
      # statm
      #   resident (2) resident set size (inaccurate; same as VmRSS in /proc/[pid]/status)
      #   data     (6) data + stack
      # measured in page (default: 4096). can get with: getconf PAGESIZE
      foreach my $pid (keys %{$self->{processes}}) {
          next if ($options{content} !~ /==>\s*\/proc\/$pid\/statm.*?\n(.*?)(?:==>|\Z)/ms);
          my @values = split(/\s+/, $1);
  
          my $memory_used = ($values[1] * $self->{option_results}->{page_size});
          $self->{processes}->{$pid}->{memory_used} = $memory_used;
          $self->{global}->{memory_used} += $memory_used;
      }
  }
  
  sub add_disk_io {
      my ($self, %options) = @_;
  
      return if (!defined($self->{option_results}->{add_disk_io}));
  
      $self->{global}->{disks_read} = 0;
      $self->{global}->{disks_write} = 0;
      # /io
      #   read_bytes: 2256896
      #   write_bytes: 0
      foreach my $pid (keys %{$self->{processes}}) {
          next if ($options{content} !~ /==>\s*\/proc\/$pid\/io\s+.*?\n(.*?)(?:==>|\Z)/ms);
          my $entries = $1;
          next if ($entries !~ /read_bytes:\s*(\d+)/m);
          $self->{processes}->{$pid}->{disks_read} = $1;
  
          next if ($entries !~ /write_bytes:\s*(\d+)/m);
          $self->{processes}->{$pid}->{disks_write} = $1;
      }
  }
  
  sub add_extra_metrics {
      my ($self, %options) = @_;
  
      my $files = [];
      push @$files, 'stat' if (defined($self->{option_results}->{add_cpu}));
      push @$files, 'statm' if (defined($self->{option_results}->{add_memory}));
      push @$files, 'io' if (defined($self->{option_results}->{add_disk_io}));
  
      my ($num_files, $files_arg) = (scalar(@$files), '');
      return if ($num_files <= 0);
      $files_arg = $files->[0] if ($num_files == 1);
      $files_arg = '{' . join(',', @$files) . '}' if ($num_files > 1);
      
      my ($num_proc, $proc_arg) = (scalar(keys %{$self->{processes}}), '');
      return if ($num_proc <= 0);
      $proc_arg = join(',', keys %{$self->{processes}}) if ($num_proc == 1);
      $proc_arg = '{' . join(',', keys %{$self->{processes}}) . '}' if ($num_proc > 1);
  
      $self->set_timestamp(timestamp => Time::HiRes::time());
      my ($content) = $options{custom}->execute_command(
          command => 'bash',
          command_options => "-c 'tail -vn +1 /proc/$proc_arg/$files_arg'",
          no_quit => 1
      );
  
      $self->add_cpu(content => $content);
      $self->add_memory(content => $content);
      $self->add_disk_io(content => $content);
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      $self->{cache_name} = 'linux_local_' . $options{custom}->get_identifier()  . '_' . $self->{mode} . '_' .
          (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_command}) ? md5_hex($self->{option_results}->{filter_command}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_arg}) ? md5_hex($self->{option_results}->{filter_arg}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_state}) ? md5_hex($self->{option_results}->{filter_state}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_ppid}) ? md5_hex($self->{option_results}->{filter_ppid}) : md5_hex('all'));
      $self->parse_output(custom => $options{custom});
      $self->add_extra_metrics(custom => $options{custom});
  }
  
  1;
  
  
  =head1 MODE
  
  Check linux processes.
  
  Command used:
  
  ps -e -o state -o ===%t===%p===%P=== -o comm:50 -o ===%a  -w 2>&1
  bash -c 'tail -n +1 /proc/{pid1,pid2,...}/{statm,stat,io}'
  
  =over 8
  
  =item B<--add-cpu>
  
  Monitor CPU usage.
  
  =item B<--add-memory>
  
  Monitor memory usage. It's inaccurate but it provides a trend.
  
  =item B<--add-disk-io>
  
  Monitor disk I/O.
  
  =item B<--filter-command>
  
  Define which processes should be included based on the name of the executable.
  This option will be treated as a regular expression.
  
  =item B<--exclude-command>
  
  Define which processes should be excluded based on the name of the executable.
  This option will be treated as a regular expression.
  
  =item B<--filter-arg>
  
  Define which processes should be included based on the arguments of the executable.
  This option will be treated as a regular expression.
  
  =item B<--exclude-arg>
  
  Define which processes should be excluded based on the arguments of the executable.
  This option will be treated as a regular expression.
  
  =item B<--filter-ppid>
  
  Define which processes should be excluded based on the process's parent process ID (PPID).
  This option will be treated as a regular expression.
  
  
  =item B<--filter-state>
  
  Define which processes should be excluded based on the process state.
  This option will be treated as a regular expression.
  You can use: 'zombie', 'dead', 'paging', 'stopped',
  'InterrupibleSleep', 'running', 'UninterrupibleSleep'.
  
  =item B<--warning-total>
  
  Thresholds.
  
  =item B<--critical-total>
  
  Thresholds.
  
  =item B<--warning-total-memory-usage>
  
  Thresholds.
  
  =item B<--critical-total-memory-usage>
  
  Thresholds.
  
  =item B<--warning-total-cpu-utilization>
  
  Thresholds.
  
  =item B<--critical-total-cpu-utilization>
  
  Thresholds.
  
  =item B<--warning-total-disks-read>
  
  Thresholds.
  
  =item B<--critical-total-disks-read>
  
  Thresholds.
  
  =item B<--warning-total-disks-write>
  
  Thresholds.
  
  =item B<--critical-total-disks-write>
  
  Thresholds.
  
  =item B<--warning-time>
  
  Thresholds.
  
  =item B<--critical-time>
  
  Thresholds.
  
  =item B<--warning-memory-usage>
  
  Thresholds.
  
  =item B<--critical-memory-usage>
  
  Thresholds.
  
  =item B<--warning-cpu-utilization>
  
  Thresholds.
  
  =item B<--critical-cpu-utilization>
  
  Thresholds.
  
  =item B<--warning-disks-read>
  
  Thresholds.
  
  =item B<--critical-disks-read>
  
  Thresholds.
  
  =item B<--warning-disks-write>
  
  Thresholds.
  
  =item B<--critical-disks-write>
  
  Thresholds.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_PROCESS

$fatpacked{"os/linux/local/mode/quota.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_QUOTA';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::quota;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub custom_usage_perfdata {
      my ($self, %options) = @_;
  
      my $unit = '';
      $unit = 'B' if ($self->{result_values}->{label_ref} eq 'data');
  
      $self->{output}->perfdata_add(
          nlabel => $self->{nlabel}, 
          unit => $unit,
          instances => $self->{result_values}->{display},
          value => $self->{result_values}->{used},
          warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{result_values}->{warn_label}, total => $self->{result_values}->{total}, cast_int => 1),
          critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{result_values}->{crit_label}, total => $self->{result_values}->{total}, cast_int => 1),
          min => 0
      );
  }
  
  sub custom_usage_threshold {
      my ($self, %options) = @_;
  
      my $exit = $self->{perfdata}->threshold_check(
          value => $self->{result_values}->{used}, 
          threshold => [
              { label => 'critical-' . $self->{result_values}->{crit_label}, exit_litteral => 'critical' }, 
              { label => 'warning-' . $self->{result_values}->{warn_label}, exit_litteral => 'warning' }
          ]
      );
      return $exit;
  }
  
  sub custom_usage_output {
      my ($self, %options) = @_;
  
      my $value = $self->{result_values}->{used} . ' files';
      if ($self->{result_values}->{label_ref} eq 'data') {
          my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used});
          $value = $total_used_value . " " . $total_used_unit;
      }
      my ($limit_soft, $limit_hard) = ('', '');
      if (defined($self->{result_values}->{warn_limit}) && $self->{result_values}->{warn_limit} > 0) {
          $limit_soft = sprintf(" (%.2f %% of soft limit)", $self->{result_values}->{used} * 100 / $self->{result_values}->{warn_limit});
      }
      if (defined($self->{result_values}->{crit_limit}) && $self->{result_values}->{crit_limit} > 0) {
          $limit_hard = sprintf(" (%.2f %% of hard limit)", $self->{result_values}->{used} * 100 / $self->{result_values}->{crit_limit});
      }
  
      return sprintf(
          "%s used: %s%s%s",
          ucfirst($self->{result_values}->{label_ref}),
          $value,
          $limit_soft, $limit_hard
      );
  }
  
  sub custom_usage_calc {
      my ($self, %options) = @_;
  
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      $self->{result_values}->{label_ref} = $options{extra_options}->{label_ref};
      $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_' . $self->{result_values}->{label_ref} . '_used'};
      
      $self->{result_values}->{warn_label} = $self->{label};
      if (defined($self->{instance_mode}->{option_results}->{'warning-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'warning-' . $self->{label}} ne '') {
          $self->{result_values}->{warn_limit} = $self->{instance_mode}->{option_results}->{'warning-' . $self->{label}};
      } elsif ($options{new_datas}->{$self->{instance} . '_' . $self->{result_values}->{label_ref} . '_soft'} > 0) {
          $self->{result_values}->{warn_limit} = $options{new_datas}->{$self->{instance} . '_' . $self->{result_values}->{label_ref} . '_soft'};
          $self->{perfdata}->threshold_validate(label => 'warning-' . $self->{label} . '_' . $self->{result_values}->{display}, value => $self->{result_values}->{warn_limit});
          $self->{result_values}->{warn_label} = $self->{label} . '_' . $self->{result_values}->{display};
      }
  
      $self->{result_values}->{crit_label} = $self->{label};
      if (defined($self->{instance_mode}->{option_results}->{'critical-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'critical-' . $self->{label}} ne '') {
          $self->{result_values}->{crit_limit} = $self->{instance_mode}->{option_results}->{'critical-' . $self->{label}};
      } elsif ($options{new_datas}->{$self->{instance} . '_' . $self->{result_values}->{label_ref} . '_hard'} > 0) {
          $self->{result_values}->{crit_limit} = $options{new_datas}->{$self->{instance} . '_' . $self->{result_values}->{label_ref} . '_hard'} - 1;
          $self->{perfdata}->threshold_validate(label => 'critical-' . $self->{label} . '_' . $self->{result_values}->{display}, value => $self->{result_values}->{crit_limit});
          $self->{result_values}->{crit_label} = $self->{label} . '_' . $self->{result_values}->{display};
      }
      return 0;
  }
  
  sub prefix_quota_output {
      my ($self, %options) = @_;
      
      return "Quota '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
      
      $self->{maps_counters_type} = [
          { name => 'quota', type => 1, cb_prefix_output => 'prefix_quota_output', message_multiple => 'All quotas are ok' }
      ];
      
      $self->{maps_counters}->{quota} = [
          { label => 'data-usage', nlabel => 'quota.data.usage.bytes', set => {
                  key_values => [ { name => 'display' }, { name => 'data_used' }, { name => 'data_soft' }, { name => 'data_hard' } ],
                  closure_custom_calc => $self->can('custom_usage_calc'), closure_custom_calc_extra_options => { label_ref => 'data' },
                  closure_custom_output => $self->can('custom_usage_output'),
                  closure_custom_perfdata => $self->can('custom_usage_perfdata'),
                  closure_custom_threshold_check => $self->can('custom_usage_threshold')
              }
          },
          { label => 'inode-usage', nlabel => 'quota.files.usage.count', set => {
                  key_values => [ { name => 'display' }, { name => 'inode_used' }, { name => 'inode_soft' }, { name => 'inode_hard' } ],
                  closure_custom_calc => $self->can('custom_usage_calc'), closure_custom_calc_extra_options => { label_ref => 'inode' },
                  closure_custom_output => $self->can('custom_usage_output'),
                  closure_custom_perfdata => $self->can('custom_usage_perfdata'),
                  closure_custom_threshold_check => $self->can('custom_usage_threshold')
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => {
          'filter-user:s' => { name => 'filter_user' },
          'filter-fs:s'   => { name => 'filter_fs' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout, $exit_code) = $options{custom}->execute_command(
          command => 'repquota',
          command_options => '-a -i 2>&1',
          no_quit => 1
      );
  
      #*** Report for user quotas on device /dev/xxxx
      #Block grace time: 7days; Inode grace time: 7days
      #                   Block limits                File limits
      #User            used    soft    hard  grace    used  soft  hard  grace
      #----------------------------------------------------------------------
      #root      -- 20779412       0       0              5     0     0
      #apache    -- 5721908       0       0          67076     0     0
  
      $self->{quota} = {};
      while ($stdout =~ /^\*\*\*.*?(\S+?)\n(.*?)(?=\*\*\*|\z)/msig) {
          my ($fs, $data) = ($1, $2);
  
          while ($data =~ /^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.*?)\n/msig) {
              my ($user, $grace_on, $data_used, $data_soft, $data_hard, $usage) = ($1, $2, $3 * 1024, $4 * 1024, $5 * 1024, $6);
              my @values = split /\s+/, $usage;
  
              shift @values if ($usage =~ /^\+/);
              my ($inode_used, $inode_soft, $inode_hard) = (shift @values, shift @values, shift @values);
  
              my $name = $user . '.' . $fs;
              if (defined($self->{option_results}->{filter_user}) && $self->{option_results}->{filter_user} ne '' &&
                  $user !~ /$self->{option_results}->{filter_user}/) {
                  $self->{output}->output_add(long_msg => "skipping '" . $name . "': no matching filter.", debug => 1);
                  next;
              }
              if (defined($self->{option_results}->{filter_fs}) && $self->{option_results}->{filter_fs} ne '' &&
                  $fs !~ /$self->{option_results}->{filter_fs}/) {
                  $self->{output}->output_add(long_msg => "skipping '" . $name . "': no matching filter.", debug => 1);
                  next;
              }
              next if (defined($self->{option_results}->{exclude_fs}) && $self->{option_results}->{exclude_fs} ne '' &&
                  $fs =~ /$self->{option_results}->{exclude_fs}/);
  
              $self->{quota}->{$name} = {
                  display => $name,
                  data_used => $data_used, data_soft => $data_soft, data_hard => $data_hard,
                  inode_used => $inode_used, inode_soft => $inode_soft, inode_hard => $inode_hard,
              };
          }
      }
  
      if (scalar(keys %{$self->{quota}}) <= 0) {
          $self->{output}->add_option_msg(short_msg => "No quota found (filters or command issue)");
          $self->{output}->option_exit();
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check quota usage on partitions.
  
  Command used: repquota -a -i 2>&1
  
  =over 8
  
  =item B<--warning-*> B<--critical-*>
  
  Thresholds.
  Can be: 'inode-usage', 'data-usage'.
  
  =item B<--filter-user>
  
  Filter username (regexp can be used).
  
  =item B<--filter-fs>
  
  Filter filesystem (regexp can be used).
  
  =item B<--exclude-fs>
  
  Exclude filesystem (regexp can be used).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_QUOTA

$fatpacked{"os/linux/local/mode/resources/discovery.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_RESOURCES_DISCOVERY';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::resources::discovery;
  
  use strict;
  use warnings;
  use Exporter;
  
  our $discovery_match;
  
  our @ISA = qw(Exporter);
  our @EXPORT_OK = qw($discovery_match);
  
  $discovery_match = [
      { type => 'cisco asa', re => qr/Cisco Adaptative Security Appliance/i },
      { type => 'cisco standard', re => qr/Cisco IOS Software/i },
      { type => 'emc data domain', re => qr/Data Domain/i },
      { type => 'sonicwall', re => qr/SonicWALL/i },
      { type => 'silverpeak', re => qr/Silver Peak/i },
      { type => 'stonesoft', re => qr/Forcepoint/i },
      { type => 'redback', re => qr/Redback/i },
      { type => 'palo alto', re => qr/Palo Alto/i },
      { type => 'hp procurve', re => qr/HP.*Switch/i },
      { type => 'hp procurve', re => qr/HP ProCurve/i },
      { type => 'hp standard', re => qr/HPE Comware/i },
      { type => 'hp msl', re => qr/HP MSL/i },
      { type => 'mrv optiswitch', re => qr/OptiSwitch/i },
      { type => 'netapp', re => qr/Netapp/i },
      { type => 'linux', re => qr/linux/i },
      { type => 'windows', re => qr/windows/i },
      { type => 'macos', re => qr/Darwin/i },
      { type => 'hp-ux', re => qr/HP-UX/i },
      { type => 'freebsd', re => qr/FreeBSD/i },
      { type => 'aix', re => qr/ AIX / }
  ];
  
  1;
OS_LINUX_LOCAL_MODE_RESOURCES_DISCOVERY

$fatpacked{"os/linux/local/mode/storage.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_STORAGE';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::storage;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub custom_usage_perfdata {
      my ($self, %options) = @_;
  
      my $label = $self->{nlabel};
      my $value_perf = $self->{result_values}->{used};
      if (defined($self->{instance_mode}->{option_results}->{free})) {
          $label = 'storage.space.free.bytes';
          $value_perf = $self->{result_values}->{free};
      }
  
      my %total_options = ();
      if ($self->{instance_mode}->{option_results}->{units} eq '%') {
          $total_options{total} = $self->{result_values}->{total};
          $total_options{cast_int} = 1;
      }
  
      $self->{output}->perfdata_add(
          nlabel => $label,
          unit => 'B',
          instances => $self->{result_values}->{display},
          value => $value_perf,
          warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, %total_options),
          critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, %total_options),
          min => 0, max => $self->{result_values}->{total}
      );
  }
  
  sub custom_usage_threshold {
      my ($self, %options) = @_;
  
      my ($exit, $threshold_value);
      $threshold_value = $self->{result_values}->{used};
      $threshold_value = $self->{result_values}->{free} if (defined($self->{instance_mode}->{option_results}->{free}));
      if ($self->{instance_mode}->{option_results}->{units} eq '%') {
          $threshold_value = $self->{result_values}->{prct_used};
          $threshold_value = $self->{result_values}->{prct_free} if (defined($self->{instance_mode}->{option_results}->{free}));
      }
      $exit = $self->{perfdata}->threshold_check(value => $threshold_value, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' } ]);
      return $exit;
  }
  
  sub custom_usage_output {
      my ($self, %options) = @_;
  
      my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total});
      my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used});
      my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free});
      return sprintf(
          'Usage Total: %s Used: %s (%.2f%%) Free: %s (%.2f%%)',
          $total_size_value . " " . $total_size_unit,
          $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used},
          $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free}
      );
  }
  
  sub custom_usage_calc {
      my ($self, %options) = @_;
  
      if ($options{new_datas}->{$self->{instance} . '_total'} == 0) {
          $self->{error_msg} = "total size is 0";
          return -2;
      }
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_total'};
      $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_used'};
      $self->{result_values}->{free} = $options{new_datas}->{$self->{instance} . '_free'};
      $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / ($self->{result_values}->{used} + $self->{result_values}->{free});
      $self->{result_values}->{prct_free} = 100 - $self->{result_values}->{prct_used};
  
      return 0;
  }
  
  sub prefix_disks_output {
      my ($self, %options) = @_;
      
      return "Storage '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
      
      $self->{maps_counters_type} = [
          { name => 'disks', type => 1, cb_prefix_output => 'prefix_disks_output', message_multiple => 'All storages are ok' }
      ];
      
      $self->{maps_counters}->{disks} = [
          { label => 'usage', nlabel => 'storage.space.usage.bytes', set => {
                  key_values => [ { name => 'display' }, { name => 'used' }, { name => 'free' }, { name => 'total' } ],
                  closure_custom_calc => $self->can('custom_usage_calc'),
                  closure_custom_output => $self->can('custom_usage_output'),
                  closure_custom_perfdata => $self->can('custom_usage_perfdata'),
                  closure_custom_threshold_check => $self->can('custom_usage_threshold')
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-type:s'        => { name => 'filter_type' },
          'filter-fs:s'          => { name => 'filter_fs' },
          'exclude-fs:s'         => { name => 'exclude_fs' },
          'filter-mountpoint:s'  => { name => 'filter_mountpoint' },
          'exclude-mountpoint:s' => { name => 'exclude_mountpoint' },
          'units:s'              => { name => 'units', default => '%' },
          'free'                 => { name => 'free' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout, $exit_code) = $options{custom}->execute_command(
          command => 'df',
          command_options => '-P -k -T 2>&1',
          no_quit => 1
      );
  
      $self->{disks} = {};
      my @lines = split /\n/, $stdout;
      foreach my $line (@lines) {
          next if ($line !~ /^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.*)/);
          my ($fs, $type, $used, $available, $percent, $mount) = ($1, $2, $4, $5, $6, $7);
  
          next if (defined($self->{option_results}->{filter_fs}) && $self->{option_results}->{filter_fs} ne '' &&
              $fs !~ /$self->{option_results}->{filter_fs}/);
          next if (defined($self->{option_results}->{exclude_fs}) && $self->{option_results}->{exclude_fs} ne '' &&
              $fs =~ /$self->{option_results}->{exclude_fs}/);
          next if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' &&
              $type !~ /$self->{option_results}->{filter_type}/);
          next if (defined($self->{option_results}->{filter_mountpoint}) && $self->{option_results}->{filter_mountpoint} ne '' &&
              $mount !~ /$self->{option_results}->{filter_mountpoint}/);
          next if (defined($self->{option_results}->{exclude_mountpoint}) && $self->{option_results}->{exclude_mountpoint} ne '' &&
              $mount =~ /$self->{option_results}->{exclude_mountpoint}/);
  
          $self->{disks}->{$mount} = {
              display => $mount,
              fs => $fs,
              type => $type,
              used => $used * 1024,
              free => $available * 1024,
              total => ($used + $available) * 1024
          };
      }
  
      if (scalar(keys %{$self->{disks}}) <= 0) {
          if ($exit_code != 0) {
              $self->{output}->output_add(long_msg => "command output:" . $stdout);
          }
          $self->{output}->add_option_msg(short_msg => "No storage found (filters or command issue)");
          $self->{output}->option_exit();
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check storage usages.
  
  Command used: df -P -k -T 2>&1
  
  =over 8
  
  =item B<--warning-usage>
  
  Warning threshold.
  
  =item B<--critical-usage>
  
  Critical threshold.
  
  =item B<--units>
  
  Units of thresholds (default: '%') ('%', 'B').
  
  =item B<--free>
  
  Thresholds are on free space left.
  
  =item B<--filter-mountpoint>
  
  Filter filesystem mount point (regexp can be used).
  
  =item B<--exclude-mountpoint>
  
  Exclude filesystem mount point (regexp can be used).
  
  =item B<--filter-type>
  
  Filter filesystem type (regexp can be used).
  
  =item B<--filter-fs>
  
  Filter filesystem (regexp can be used).
  
  =item B<--exclude-fs>
  
  Exclude filesystem (regexp can be used).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_STORAGE

$fatpacked{"os/linux/local/mode/swap.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_SWAP';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::swap;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  
  sub custom_swap_output {
      my ($self, %options) = @_;
      
      return sprintf(
          'Swap Total: %s %s Used: %s %s (%.2f%%) Free: %s %s (%.2f%%)',
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}),
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}),
          $self->{result_values}->{prct_used},
          $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}),
          $self->{result_values}->{prct_free}
      );
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'swap', type => 0, skipped_code => { -10 => 1 } }
      ];
  
      $self->{maps_counters}->{swap} = [
          { label => 'usage', nlabel => 'swap.usage.bytes', set => {
                  key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_swap_output'),
                  perfdatas => [
                      { label => 'used', template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1 }
                  ]
              }
          },
          { label => 'usage-free', display_ok => 0, nlabel => 'swap.free.bytes', set => {
                  key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
                  closure_custom_output => $self->can('custom_swap_output'),
                  perfdatas => [
                      { label => 'free', template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1 }
                  ]
              }
          },
          { label => 'usage-prct', display_ok => 0, nlabel => 'swap.usage.percentage', set => {
                  key_values => [ { name => 'prct_used' } ],
                  output_template => 'Swap used: %.2f %%',
                  perfdatas => [
                      { label => 'used_prct', template => '%.2f', min => 0, max => 100, unit => '%' }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
      
      $options{options}->add_options(arguments => { 
          'no-swap:s' => { name => 'no_swap' }
      });
  
      $self->{no_swap} = 'critical';
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
  
      $self->SUPER::check_options(%options);
      if (defined($self->{option_results}->{no_swap}) && $self->{option_results}->{no_swap} ne '') {
          if ($self->{output}->is_litteral_status(status => $self->{option_results}->{no_swap}) == 0) {
              $self->{output}->add_option_msg(short_msg => "Wrong --no-swap status '" . $self->{option_results}->{no_swap} . "'.");
              $self->{output}->option_exit();
          }
          $self->{no_swap} = $self->{option_results}->{no_swap};
      }
  
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/meminfo 2>&1'
      );
  
      my ($total_size, $swap_free);
      foreach (split(/\n/, $stdout)) {
          if (/^SwapTotal:\s+(\d+)/i) {
              $total_size = $1 * 1024;
          } elsif (/^SwapFree:\s+(\d+)/i) {
              $swap_free = $1 * 1024;
          }
      }
      
      if (!defined($total_size) || !defined($swap_free)) {
          $self->{output}->add_option_msg(short_msg => "Some information missing.");
          $self->{output}->option_exit();
      }
  
      if ($total_size == 0) {
          $self->{output}->output_add(
              severity => $self->{no_swap},
              short_msg => 'No active swap.'
          );
          $self->{output}->display();
          $self->{output}->exit();
      }
  
      my $swap_used = $total_size - $swap_free;
      my $prct_used = $swap_used * 100 / $total_size;
      my $prct_free = 100 - $prct_used;
      my ($total_value, $total_unit) = $self->{perfdata}->change_bytes(value => $total_size);
      my ($swap_used_value, $swap_used_unit) = $self->{perfdata}->change_bytes(value => $swap_used);
      my ($swap_free_value, $swap_free_unit) = $self->{perfdata}->change_bytes(value => ($total_size - $swap_used));
  
      $self->{swap} = {
          used => $swap_used,
          free => $swap_free,
          prct_used => $prct_used,
          prct_free => $prct_free,
          total => $total_size
      };
  }
  
  1;
  
  
  =head1 MODE
  
  Check swap memory (need '/proc/meminfo' file).
  
  Command used: cat /proc/meminfo 2>&1
  
  =over 8
  
  =item B<--no-swap>
  
  Threshold if no active swap (default: 'critical').
  
  =item B<--warning-*> B<--critical-*>
  
  Threshold, can be 'usage' (in Bytes), 'usage-free' (in Bytes), 'usage-prct' (%).
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_SWAP

$fatpacked{"os/linux/local/mode/systemdjournal.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_SYSTEMDJOURNAL';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::systemdjournal;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use DateTime;
  use Digest::MD5 qw(md5_hex);
  use JSON::XS;
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'global', type => 0 }
      ];
  
      $self->{maps_counters}->{global} = [
          { label => 'entries', nlabel => 'journal.entries.count', set => {
                  key_values => [ { name => 'entries' } ],
                  output_template => 'Journal entries: %s',
                  perfdatas => [
                      { template => '%s', min => 0 }
                  ]
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'unit:s'           => { name => 'unit' },
          'filter-message:s' => { name => 'filter_message' },
          'since:s'          => { name => 'since', default => 'cache' },
          'timezone:s'       => { name => 'timezone', default => 'local' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      $self->{cache_name} = 'cache_linux_local_' . $options{custom}->get_identifier()  . '_' . $self->{mode} . '_' .
          (defined($self->{option_results}->{filter_message}) ? md5_hex($self->{option_results}->{filter_message}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{since}) ? md5_hex($self->{option_results}->{since}) : md5_hex('all'));
  
      my ($stdout_version) = $options{custom}->execute_command(
          command         => '/usr/bin/journalctl',
          command_options => '--version'
      );
      $stdout_version =~ /^systemd\s(\d+)\s/;
      my $journalctl_version = $1;
  
      my $command_options = '--output json --no-pager';
      # --output-field option has been added in version 236
      if ($journalctl_version >= 236) {
          $command_options .= '  --output-fields MESSAGE';
      };
  
      if (defined($self->{option_results}->{unit}) && $self->{option_results}->{unit} ne '') {
          $command_options .= ' --unit ' . $self->{option_results}->{unit};
      }
  
      if (defined($self->{option_results}->{since}) && $self->{option_results}->{since} ne '') {
          if ($self->{option_results}->{since} eq "cache") {
              my $last_timestamp = $self->read_statefile_key(key => 'last_timestamp');
              $last_timestamp = time() - (5 * 60) if (!defined($last_timestamp));
              my $dt = DateTime->from_epoch(epoch => $last_timestamp);
              $dt->set_time_zone($self->{option_results}->{timezone});
              $command_options .= ' --since "' . $dt->ymd . ' ' . $dt->hms . '"';
          } elsif ($self->{option_results}->{since} =~ /\d+/) {
              $command_options .= ' --since "' . $self->{option_results}->{since} . ' minutes ago"';
          }
      }
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'journalctl',
          command_options => $command_options . ' 2>&1'
      );
  
      $self->{global} = { entries => 0 };
  
      my @lines = split /\n/, $stdout;
      foreach (@lines) {
          my $decoded;
          eval {
              $decoded = JSON::XS->new->utf8->decode($_);
          };
          if ($@) {
              $self->{output}->add_option_msg(short_msg => "Cannot decode json response");
              $self->{output}->option_exit();
          }
  
          next if (defined($self->{option_results}->{filter_message}) && $self->{option_results}->{filter_message} ne '' &&
              $decoded->{MESSAGE} !~ /$self->{option_results}->{filter_message}/);
          
          $self->{global}->{entries}++;
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Count journal entries.
  
  Command used: journalctl --output json --output-fields MESSAGE --no-pager
  
  Examples:
  
  Look for sent emails by Postfix:
  
  # perl centreon_plugins.pl --plugin=os::linux::local::plugin --mode=systemd-journal --unit=postfix.service
  --filter-message='status=sent' --since=10 --change-short-output='Journal entries~Emails sent'
  --change-perfdata='journal.entries.count,emails.sent.count'
  
  OK: Emails sent: 17 | 'emails.sent.count'=17;;;0;
  
  Look for Puppet errors:
  
  # perl centreon_plugins.pl --plugin=os::linux::local::plugin --mode=systemd-journal
  --unit=puppet.service --filter-message='error' --since=30
  
  OK: Journal entries: 1 | 'journal.entries.count'=1;;;0;
  
  Look for the number of Centreon Engine reloads
  
  # perl centreon_plugins.pl --plugin=os::linux::local::plugin --mode=systemd-journal
  --unit=centengine.service --filter-message='Reloaded.*Engine' --since=60
  --change-short-output='Journal entries~Centreon Engine reloads over the last hour'
  --change-perfdata='journal.entries.count,centreon.engine.reload.count'
  
  OK: Centreon Engine reloads over the last hour: 0 | 'centreon.engine.reload.count'=0;;;0;
  
  =over 8
  
  =item B<--unit>
  
  Only look for messages from the specified unit, i.e. the
  name of the systemd service who created the message.
  
  =item B<--filter-message>
  
  Filter on message content (can be a regexp).
  
  =item B<--since>
  
  Defines the amount of time to look back at messages.
  Can be minutes (example: 5 "minutes ago") or 'cache' to use the
  timestamp from last execution. Default: 'cache'.
  
  =item B<--timezone>
  
  Defines the timezone to use for date/time conversion when using a timestamp from the cache.
  Default: 'local'.
  
  =item B<--warning-entries>
  
  Thresholds to apply to the number of journal entries
  found with the specified parameters.
  
  =item B<--critical-entries>
  
  Thresholds to apply to the number of journal entries
  found with the specified parameters.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_SYSTEMDJOURNAL

$fatpacked{"os/linux/local/mode/systemdscstatus.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_SYSTEMDSCSTATUS';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::systemdscstatus;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::misc;
  use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
  
  sub custom_status_output {
      my ($self, %options) = @_;
  
      return sprintf(
          'status : %s/%s/%s [boot: %s]',
          $self->{result_values}->{load},
          $self->{result_values}->{active},
          $self->{result_values}->{sub},
          $self->{result_values}->{boot}
      );
  }
  
  sub prefix_sc_output {
      my ($self, %options) = @_;
  
      return "Service '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'global', type => 0 },
          { name => 'sc', type => 1, cb_prefix_output => 'prefix_sc_output', message_multiple => 'All services are ok' }
      ];
  
      $self->{maps_counters}->{global} = [
          { label => 'total-running', nlabel => 'systemd.services.running.count', set => {
                  key_values => [ { name => 'running' }, { name => 'total' } ],
                  output_template => 'Total Running: %s',
                  perfdatas => [
                      { label => 'total_running', template => '%s', min => 0, max => 'total' }
                  ]
              }
          },
          { label => 'total-failed', nlabel => 'systemd.services.failed.count', set => {
                  key_values => [ { name => 'failed' }, { name => 'total' } ],
                  output_template => 'Total Failed: %s',
                  perfdatas => [
                      { label => 'total_failed', template => '%s', min => 0, max => 'total' }
                  ]
              }
          },
          { label => 'total-dead', nlabel => 'systemd.services.dead.count', set => {
                  key_values => [ { name => 'dead' }, { name => 'total' } ],
                  output_template => 'Total Dead: %s',
                  perfdatas => [
                      { label => 'total_dead', template => '%s', min => 0, max => 'total' }
                  ]
              }
          },
          { label => 'total-exited', nlabel => 'systemd.services.exited.count', set => {
                  key_values => [ { name => 'exited' }, { name => 'total' } ],
                  output_template => 'Total Exited: %s',
                  perfdatas => [
                      { label => 'total_exited', template => '%s', min => 0, max => 'total' }
                  ]
              }
          }
      ];
  
      $self->{maps_counters}->{sc} = [
          { label => 'status', type => 2, critical_default => '%{active} =~ /failed/i', set => {
                  key_values => [ { name => 'load' }, { name => 'active' },  { name => 'sub' }, { name => 'boot' }, { name => 'display' } ],
                  closure_custom_output => $self->can('custom_status_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => \&catalog_status_threshold_ng
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-name:s'  => { name => 'filter_name' },
          'exclude-name:s' => { name => 'exclude_name' }
      });
  
      return $self;
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      # check systemctl version to convert no-legend in legend=false (change in versions >= 248)
      my $legend_format= ' --no-legend';
      my ($stdout_version) = $options{custom}->execute_command(
          command         => 'systemctl',
          command_options => '--version'
      );
      $stdout_version =~ /^systemd\s(\d+)\s/;
      my $systemctl_version=$1;
      if($systemctl_version >= 248){
          $legend_format = ' --legend=false';
      }
  
      my $command_options_1 = '-a --no-pager --plain';
      my ($stdout)  = $options{custom}->execute_command(
          command         => 'systemctl',
          command_options => $command_options_1.$legend_format
      );
  
      $self->{global} = { running => 0, exited => 0, failed => 0, dead => 0, total => 0 };
      $self->{sc} = {};
      #auditd.service                                                        loaded    active   running Security Auditing Service
      #avahi-daemon.service                                                  loaded    active   running Avahi mDNS/DNS-SD Stack
      #brandbot.service                                                      loaded    inactive dead    Flexible Branding Service
      while ($stdout =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/msig) {
          my ($name, $load, $active, $sub) = ($1, $2, $3, lc($4));
  
          next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
              $name !~ /$self->{option_results}->{filter_name}/);
          next if (defined($self->{option_results}->{exclude_name}) && $self->{option_results}->{exclude_name} ne '' &&
              $name =~ /$self->{option_results}->{exclude_name}/);
  
          $self->{sc}->{$name} = { display => $name, load => $load, active => $active, sub => $sub, boot => '-' };
          $self->{global}->{$sub} += 1 if (defined($self->{global}->{$sub}));
          $self->{global}->{total} += 1;
      }
  
      if (scalar(keys %{$self->{sc}}) <= 0) {
          $self->{output}->add_option_msg(short_msg => "No service found.");
          $self->{output}->option_exit();
      }
  
      my $command_options_2 = 'list-unit-files --no-pager --plain';
      my ($stdout_2)  = $options{custom}->execute_command(
          command         => 'systemctl',
          command_options => $command_options_2.$legend_format
      );
  
      # vendor preset is a new column
      #UNIT FILE                 STATE           VENDOR PRESET
      #runlevel4.target          enabled 
      #runlevel5.target          static  
      #runlevel6.target          disabled
      #irqbalance.service        enabled         enabled
      while ($stdout_2 =~ /^(.*?)\s+(\S+)\s*/msig) {
          my ($name, $boot) = ($1, $2);
          next if (!defined($self->{sc}->{$name}));
          $self->{sc}->{$name}->{boot} = $boot;
      }
  }
  
  1;
  
  
  =head1 MODE
  
  Check systemd services status.
  
  Command used: 'systemctl -a --no-pager --no-legend' and 'systemctl list-unit-files --no-pager --no-legend'
  Command change for systemctl version >= 248 : --no-legend is converted in legend=false
  
  =over 8
  
  =item B<--filter-name>
  
  Filter service name (can be a regexp).
  
  =item B<--exclude-name>
  
  Exclude service name (can be a regexp).
  
  =item B<--warning-*> B<--critical-*>
  
  Thresholds.
  Can be: 'total-running', 'total-dead', 'total-exited',
  'total-failed'.
  
  =item B<--warning-status>
  
  Define the conditions to match for the status to be WARNING.
  You can use the following variables: %{display}, %{active}, %{sub}, %{load}, %{boot}
  Example of statuses for the majority of these variables:
  %{active}: active, inactive
  %{sub}: waiting, plugged, mounted, dead, failed, running, exited, listening, active
  %{load}: loaded, not-found
  %{boot}: enabled, disabled, static, indirect
  
  =item B<--critical-status>
  
  Define the conditions to match for the status to be CRITICAL (default: '%{active} =~ /failed/i').
  You can use the following variables: %{display}, %{active}, %{sub}, %{load}, %{boot}
  Example of statuses for the majority of these variables:
  %{active}: active, inactive
  %{sub}: waiting, plugged, mounted, dead, failed, running, exited, listening, active
  %{load}: loaded, not-found
  %{boot}: enabled, disabled, static, indirect
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_SYSTEMDSCSTATUS

$fatpacked{"os/linux/local/mode/traffic.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_TRAFFIC';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::traffic;
  
  use base qw(centreon::plugins::templates::counter);
  
  use strict;
  use warnings;
  use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
  use Digest::MD5 qw(md5_hex);
  
  sub custom_status_output {
      my ($self, %options) = @_;
  
      return sprintf('status : %s', $self->{result_values}->{status});
  }
  
  sub custom_traffic_perfdata {
      my ($self, %options) = @_;
  
      my ($warning, $critical);
      if ($self->{instance_mode}->{option_results}->{units} eq '%' && defined($self->{result_values}->{speed})) {
          $warning = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{speed}, cast_int => 1);
          $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{speed}, cast_int => 1);
      } elsif ($self->{instance_mode}->{option_results}->{units} eq 'b/s') {
          $warning = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel});
          $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel});
      }
  
      $self->{output}->perfdata_add(
          label => $self->{nlabel},
          unit => 'b/s',
          instances => $self->{result_values}->{display},
          value => sprintf("%.2f", $self->{result_values}->{traffic_per_seconds}),
          warning => $warning,
          critical => $critical,
          min => 0, max => $self->{result_values}->{speed}
      );
  }
  
  sub custom_traffic_threshold {
      my ($self, %options) = @_;
  
      my $exit = 'ok';
      if ($self->{instance_mode}->{option_results}->{units} eq '%' && defined($self->{result_values}->{speed})) {
          $exit = $self->{perfdata}->threshold_check(value => $self->{result_values}->{traffic_prct}, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]);
      } elsif ($self->{instance_mode}->{option_results}->{units} eq 'b/s') {
          $exit = $self->{perfdata}->threshold_check(value => $self->{result_values}->{traffic_per_seconds}, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]);
      }
      return $exit;
  }
  
  sub custom_traffic_output {
      my ($self, %options) = @_;
  
      my ($traffic_value, $traffic_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{traffic_per_seconds}, network => 1);
      return sprintf(
          'Traffic %s : %s/s (%s)',
          ucfirst($self->{result_values}->{label}), $traffic_value . $traffic_unit,
          defined($self->{result_values}->{traffic_prct}) ? sprintf("%.2f%%", $self->{result_values}->{traffic_prct}) : '-'
      );
  }
  
  sub custom_traffic_calc {
      my ($self, %options) = @_;
  
      my $diff_traffic = ($options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref}} - $options{old_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref}});
  
      $self->{result_values}->{traffic_per_seconds} = $diff_traffic / $options{delta_time};
      if (defined($options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}}) &&
          $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}} ne '' && 
          $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}} > 0) {
          $self->{result_values}->{traffic_prct} = $self->{result_values}->{traffic_per_seconds} * 100 / $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}};
          $self->{result_values}->{speed} = $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}};
      }
  
      $self->{result_values}->{label} = $options{extra_options}->{label_ref};
      $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
      return 0;
  }
  
  sub prefix_interface_output {
      my ($self, %options) = @_;
  
      return "Interface '" . $options{instance_value}->{display} . "' ";
  }
  
  sub set_counters {
      my ($self, %options) = @_;
  
      $self->{maps_counters_type} = [
          { name => 'interface', type => 1, cb_prefix_output => 'prefix_interface_output', message_multiple => 'All interfaces are ok', skipped_code => { -10 => 1 } }
      ];
  
      $self->{maps_counters}->{interface} = [
          { label => 'status', type => 2, critical_default => '%{status} ne "RU"', set => {
                  key_values => [ { name => 'status' }, { name => 'display' } ],
                  closure_custom_output => $self->can('custom_status_output'),
                  closure_custom_perfdata => sub { return 0; },
                  closure_custom_threshold_check => \&catalog_status_threshold_ng
              }
          },
          { label => 'in', nlabel => 'interface.traffic.in.bitspersecond', set => {
                  key_values => [ { name => 'in', diff => 1 }, { name => 'speed_in' }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_traffic_calc'), closure_custom_calc_extra_options => { label_ref => 'in' },
                  closure_custom_output => $self->can('custom_traffic_output'), output_error_template => 'Traffic In : %s',
                  closure_custom_perfdata => $self->can('custom_traffic_perfdata'),
                  closure_custom_threshold_check => $self->can('custom_traffic_threshold')
              }
          },
          { label => 'out', nlabel => 'interface.traffic.out.bitspersecond', set => {
                  key_values => [ { name => 'out', diff => 1 }, { name => 'speed_out' }, { name => 'display' } ],
                  closure_custom_calc => $self->can('custom_traffic_calc'), closure_custom_calc_extra_options => { label_ref => 'out' },
                  closure_custom_output => $self->can('custom_traffic_output'), output_error_template => 'Traffic Out : %s',
                  closure_custom_perfdata => $self->can('custom_traffic_perfdata'),
                  closure_custom_threshold_check => $self->can('custom_traffic_threshold')
              }
          }
      ];
  }
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
      bless $self, $class;
  
      $options{options}->add_options(arguments => {
          'filter-state:s'      => { name => 'filter_state', },
          'filter-interface:s'  => { name => 'filter_interface' },
          'exclude-interface:s' => { name => 'exclude_interface' },
          'units:s'             => { name => 'units', default => 'b/s' },
          'speed:s'             => { name => 'speed' },
          'guess-speed'         => { name => 'guess_speed' },
          'no-loopback'         => { name => 'no_loopback' }
      });
      
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::check_options(%options);
  
      if (defined($self->{option_results}->{speed}) && $self->{option_results}->{speed} ne '') {
          if ($self->{option_results}->{speed} !~ /^[0-9]+(\.[0-9]+){0,1}$/) {
              $self->{output}->add_option_msg(short_msg => "Speed must be a positive number '" . $self->{option_results}->{speed} . "' (can be a float also).");
              $self->{output}->option_exit();
          } else {
              $self->{option_results}->{speed} *= 1000000;
          }
      }
      if (defined($self->{option_results}->{units}) && $self->{option_results}->{units} eq '%' && 
          (!defined($self->{option_results}->{speed}) || $self->{option_results}->{speed} eq '')) {
          $self->{output}->add_option_msg(short_msg => "To use percent, you need to set --speed option.");
          $self->{output}->option_exit();
      }
  }
  
  sub guess_speed {
      my ($self, %options) = @_;
  
      if ($self->{iwconfig_output} =~ /^$options{name}\s+IEEE.*?Bit\s+Rate=([0-9\.]+)\s+Mb/ms) {
          return $1 * 1000000;
      }
      my ($output) = $options{custom}->execute_command(
          command => 'ethtool ' . $options{name},
          no_quit => 1
      );
      if ($output =~ /Speed:\s+(.*)Mb/) {
          return $1 * 1000000;
      }
  
      return undef;
  }
  
  sub do_selection {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'ip',
          command_path => '/sbin',
          command_options => '-s addr 2>&1'
      );
  
      if (defined($self->{option_results}->{guess_speed})) {
          ($self->{iwconfig_output}) = $options{custom}->execute_command(
              command => 'iwconfig',
              no_quit => 1
          );
      }
  
      # ifconfig
      my $interface_pattern = '^(\S+)(.*?)(\n\n|\n$)';
      if ($stdout =~ /^\d+:\s+\S+:\s+</ms) {
          # ip addr
          $interface_pattern = '^\d+:\s+(\S+)(.*?)(?=\n\d|\Z$)';
      }
  
      $self->{interface} = {};
      while ($stdout =~ /$interface_pattern/msg) {
          my ($interface_name, $values) = ($1, $2);
  
          $interface_name =~ s/:$//;
          my $states = '';
          $states .= 'R' if ($values =~ /RUNNING|LOWER_UP/ms);
          $states .= 'U' if ($values =~ /UP/ms);
  
          next if (defined($self->{option_results}->{no_loopback}) && $values =~ /LOOPBACK/ms);
          next if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' &&
              $states !~ /$self->{option_results}->{filter_state}/);
          next if (defined($self->{option_results}->{filter_interface}) && $self->{option_results}->{filter_interface} ne '' &&
              $interface_name !~ /$self->{option_results}->{filter_interface}/);
           next if (defined($self->{option_results}->{exclude_interface}) && $self->{option_results}->{exclude_interface} ne '' &&
              $interface_name =~ /$self->{option_results}->{exclude_interface}/);
  
          $self->{interface}->{$interface_name} = {
              display => $interface_name,
              status => $states,
              speed_in => '',
              speed_out => ''
          };
  
          if (defined($self->{option_results}->{guess_speed})) {
              my $speed = $self->guess_speed(custom => $options{custom}, name => $interface_name);
              if (defined($speed)) {
                  $self->{interface}->{$interface_name}->{speed_in} = $speed;
                  $self->{interface}->{$interface_name}->{speed_out} = $speed;
              }
          }
  
          if (defined($self->{option_results}->{speed}) && $self->{option_results}->{speed} ne '') {
              $self->{interface}->{$interface_name}->{speed_in} = $self->{option_results}->{speed};
              $self->{interface}->{$interface_name}->{speed_out} = $self->{option_results}->{speed};
          };
  
          # ip addr patterns
          if ($values =~ /RX:\s+bytes.*?(\d+).*?TX:\s+bytes.*?(\d+)/msi) {
             $self->{interface}->{$interface_name}->{in} = $1;
             $self->{interface}->{$interface_name}->{out} = $2;
          } elsif ($values =~ /RX bytes:(\S+).*?TX bytes:(\S+)/msi || $values =~ /RX packets\s+\d+\s+bytes\s+(\S+).*?TX packets\s+\d+\s+bytes\s+(\S+)/msi) {
              $self->{interface}->{$interface_name}->{in} = $1;
              $self->{interface}->{$interface_name}->{out} = $2;
          }
      }
      
      if (scalar(keys %{$self->{interface}}) <= 0) {
          $self->{output}->add_option_msg(short_msg => "No interface found.");
          $self->{output}->option_exit();
      }
  }
  
  sub manage_selection {
      my ($self, %options) = @_;
  
      $self->do_selection(custom => $options{custom});
      $self->{cache_name} = 'cache_linux_local_' . $options{custom}->get_identifier() . '_' . $self->{mode} . '_' .
          (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')) . '_' .
          (defined($self->{option_results}->{filter_interface}) ? md5_hex($self->{option_results}->{filter_interface}) : md5_hex('all'));
  }
  
  1;
  
  
  =head1 MODE
  
  Check traffic
  
  Command used: /sbin/ip -s addr 2>&1
  
  =over 8
  
  =item B<--warning-in>
  
  Warning threshold in percent for 'in' traffic.
  
  =item B<--critical-in>
  
  Critical threshold in percent for 'in' traffic.
  
  =item B<--warning-out>
  
  Warning threshold in percent for 'out' traffic.
  
  =item B<--critical-out>
  
  Critical threshold in percent for 'out' traffic.
  
  =item B<--unknown-status>
  
  Define the conditions to match for the status to be UNKNOWN (default: '').
  You can use the following variables: %{status}, %{display}
  
  =item B<--warning-status>
  
  Define the conditions to match for the status to be WARNING (default: '').
  You can use the following variables: %{status}, %{display}
  
  =item B<--critical-status>
  
  Define the conditions to match for the status to be CRITICAL (default: '%{status} ne "RU"').
  You can use the following variables: %{status}, %{display}
  
  =item B<--units>
  
  Units of thresholds (default: 'b/s') ('%', 'b/s').
  Percent can be used only if --speed is set.
  
  =item B<--filter-interface>
  
  Filter interface name (regexp can be used).
  
  =item B<--exclude-interface>
  
  Exclude interface name (regexp can be used).
  
  =item B<--filter-state>
  
  Filter interfaces type (regexp can be used).
  
  =item B<--speed>
  
  Set interface speed (in Mb).
  
  =item B<--guess-speed>
  
  Try to guess speed with commands ethtool and iwconfig.
  
  =item B<--no-loopback>
  
  Don't display loopback interfaces.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_TRAFFIC

$fatpacked{"os/linux/local/mode/uptime.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_MODE_UPTIME';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::mode::uptime;
  
  use base qw(centreon::plugins::mode);
  
  use strict;
  use warnings;
  use POSIX;
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
      bless $self, $class;
      
      $options{options}->add_options(arguments => { 
          'warning:s'  => { name => 'warning', default => '' },
          'critical:s' => { name => 'critical', default => '' },
          'seconds'    => { name => 'seconds' }
      });
  
      return $self;
  }
  
  sub check_options {
      my ($self, %options) = @_;
      $self->SUPER::init(%options);
  
      if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
          $self->{output}->option_exit();
      }
      if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
          $self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
          $self->{output}->option_exit();
      }
  }
  
  sub run {
      my ($self, %options) = @_;
  
      my ($stdout) = $options{custom}->execute_command(
          command => 'cat',
          command_options => '/proc/uptime 2>&1'
      );
  
      my ($uptime, $idletime);
      if ($stdout =~ /^([0-9\.]+)\s+([0-9\.]+)/m) {
          ($uptime, $idletime) = ($1, $2)
      }
      
      if (!defined($uptime) || !defined($idletime)) {
          $self->{output}->add_option_msg(short_msg => 'Some informations missing.');
          $self->{output}->option_exit();
      }
  
      my $exit_code = $self->{perfdata}->threshold_check(
          value => floor($uptime),
          threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]
      );
      $self->{output}->perfdata_add(
          nlabel => 'system.uptime.seconds',
          unit => 's',
          value => floor($uptime),
          warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
          critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
          min => 0
      );
  
      $self->{output}->output_add(
          severity => $exit_code,
          short_msg => sprintf(
              "System uptime is: %s",
              defined($self->{option_results}->{seconds}) ? floor($uptime) . " seconds" : floor($uptime / 86400) . " days"
          )
      );
  
      $self->{output}->display();
      $self->{output}->exit();
      
  }
  
  1;
  
  
  =head1 MODE
  
  Check system uptime.
  
  Command used: cat /proc/uptime 2>&1
  
  =over 8
  
  =item B<--warning>
  
  Warning threshold in seconds.
  
  =item B<--critical>
  
  Critical threshold in seconds.
  
  =item B<--seconds>
  
  Display uptime in seconds.
  
  =back
  
  =cut
OS_LINUX_LOCAL_MODE_UPTIME

$fatpacked{"os/linux/local/plugin.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'OS_LINUX_LOCAL_PLUGIN';
  #
  # Copyright 2024 Centreon (http://www.centreon.com/)
  #
  # Centreon is a full-fledged industry-strength solution that meets
  # the needs in IT infrastructure and application monitoring for
  # service performance.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  package os::linux::local::plugin;
  
  use strict;
  use warnings;
  use base qw(centreon::plugins::script_custom);
  
  sub new {
      my ($class, %options) = @_;
      my $self = $class->SUPER::new(package => __PACKAGE__, %options);
      bless $self, $class;
  
      $self->{modes} = {
          'check-plugin'          => 'os::linux::local::mode::checkplugin',
          'cpu'                   => 'os::linux::local::mode::cpu',
          'cpu-detailed'          => 'os::linux::local::mode::cpudetailed',
          'cmd-return'            => 'os::linux::local::mode::cmdreturn',
          'connections'           => 'os::linux::local::mode::connections',
          'discovery-snmp'        => 'os::linux::local::mode::discoverysnmp',
          'discovery-snmpv3'      => 'os::linux::local::mode::discoverysnmpv3',
          'diskio'                => 'os::linux::local::mode::diskio',
          'files-size'            => 'os::linux::local::mode::filessize',
          'files-date'            => 'os::linux::local::mode::filesdate',
          'inodes'                => 'os::linux::local::mode::inodes',
          'load'                  => 'os::linux::local::mode::loadaverage',
          'lvm'                   => 'os::linux::local::mode::lvm',
          'list-interfaces'       => 'os::linux::local::mode::listinterfaces',
          'list-partitions'       => 'os::linux::local::mode::listpartitions',
          'list-storages'         => 'os::linux::local::mode::liststorages',
          'list-systemdservices'  => 'os::linux::local::mode::listsystemdservices',
          'memory'                => 'os::linux::local::mode::memory',
          'mountpoint'            => 'os::linux::local::mode::mountpoint',
          'open-files'            => 'os::linux::local::mode::openfiles',
          'ntp'                   => 'os::linux::local::mode::ntp',
          'packet-errors'         => 'os::linux::local::mode::packeterrors',
          'paging'                => 'os::linux::local::mode::paging',
          'pending-updates'       => 'os::linux::local::mode::pendingupdates',
          'process'               => 'os::linux::local::mode::process',
          'quota'                 => 'os::linux::local::mode::quota',
          'storage'               => 'os::linux::local::mode::storage',
          'swap'                  => 'os::linux::local::mode::swap',
          'systemd-journal'       => 'os::linux::local::mode::systemdjournal',
          'systemd-sc-status'     => 'os::linux::local::mode::systemdscstatus',
          'traffic'               => 'os::linux::local::mode::traffic',
          'uptime'                => 'os::linux::local::mode::uptime'
      };
  
      $self->{custom_modes}->{cli} = 'os::linux::local::custom::cli';
  
      return $self;
  }
  
  1;
  
  
  =head1 PLUGIN DESCRIPTION
  
  Check Linux through local commands (the plugin can use SSH).
  
  =cut
OS_LINUX_LOCAL_PLUGIN

s/^  //mg for values %fatpacked;

my $class = 'FatPacked::'.(0+\%fatpacked);
no strict 'refs';
*{"${class}::files"} = sub { keys %{$_[0]} };

if ($] < 5.008) {
  *{"${class}::INC"} = sub {
    if (my $fat = $_[0]{$_[1]}) {
      my $pos = 0;
      my $last = length $fat;
      return (sub {
        return 0 if $pos == $last;
        my $next = (1 + index $fat, "\n", $pos) || $last;
        $_ .= substr $fat, $pos, $next - $pos;
        $pos = $next;
        return 1;
      });
    }
  };
}

else {
  *{"${class}::INC"} = sub {
    if (my $fat = $_[0]{$_[1]}) {
      open my $fh, '<', \$fat
        or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
      return $fh;
    }
    return;
  };
}

unshift @INC, bless \%fatpacked, $class;
  } # END OF FATPACK CODE

#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

use strict;
use warnings;
# Not perl embedded compliant at all
use FindBin;
use lib "$FindBin::Bin";
# use lib '/usr/lib/nagios/plugins/';

use centreon::plugins::script;

centreon::plugins::script->new()->run();
openSUSE Build Service is sponsored by