The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:

=encoding UTF-8

=head1 NAME

Rex - Remote Execution

=head1 DESCRIPTION

Rex is a command line tool which executes commands on remote servers.  Define
tasks in Perl and execute them on remote servers or groups of servers.

Rex can be used to:

=over 4

=item * Deploy web applications to servers sequentially or in parallel.

=item * Automate common tasks.

=item * Provision servers using Rex's builtin tools.

=back

You can find examples and howtos on L<http://rexify.org/>

=head1 GETTING HELP

=over 4

=item * Web Site: L<http://rexify.org/>

=item * IRC: irc.freenode.net #rex

=item * Bug Tracker: L<https://github.com/RexOps/Rex/issues>

=item * Twitter: L<http://twitter.com/jfried83>

=back

=head1 SYNOPSIS

    # In a Rexfile:
    use Rex -feature => [qw/1.3/];
   
    user "root";
    password "ch4ngem3";
   
    desc "Show Unix version";
    task "uname", sub {
       say run "uname -a";
    };

    1;
   
    # On the command line:
    bash# rex -H server[01..10] uname

See L<rex|https://metacpan.org/pod/distribution/Rex/bin/rex> for more information about how to use rex on the command line.

See L<Rex::Commands> for a list of all commands you can use.

=head1 CLASS METHODS

=cut

package Rex;

use strict;
use warnings;

our $VERSION = '1.6.0'; # VERSION

# development version if this variable is not set
if ( !$Rex::VERSION ) {
  $Rex::VERSION = "9999.99.99_99";
}

BEGIN {
  use Rex::Logger;
  use Rex::Interface::Cache;
  use Data::Dumper;
  use Rex::Interface::Connection;
  use Cwd qw(getcwd);
  use Rex::Config;
  use Rex::Helper::Array;
  use Rex::Report;
  use Rex::Notify;
  use Rex::Require;
  use File::Basename;
  use File::Spec;
  eval { Net::SSH2->require; };
}

our ( @EXPORT, @CONNECTION_STACK, $GLOBAL_SUDO, $MODULE_PATHS,
  $WITH_EXIT_STATUS, @FEATURE_FLAGS );

$WITH_EXIT_STATUS = 1; # since 0.50 activated by default
@FEATURE_FLAGS    = ();

my $cur_dir;

BEGIN {

  sub generate_inc {
    my @additional = @_;

    my @rex_inc = ();

# this must be the first, special handling for rex modules which uses Module.pm and not
# __module__.pm as their initial file. (rex pre 0.40 or something)
    push @rex_inc, sub {
      my $mod_to_load = $_[1];
      return search_module_path( $mod_to_load, 1 );
    };

# this adds the current directory to the lib search path.
# this must come before all other paths, because custom libraries can be project dependant
# see: #1108
    push @rex_inc, add_cwd_to_inc();
    push @rex_inc, add_libstruct_to_inc($_) for @additional;

    # we have to add the Rexfile's path to @INC FIX: #1170
    push @rex_inc, @additional;

# add home directory/.rex/recipes to the search path, so that recipes can be managed
# at a central location.
    my $home_dir = _home_dir();
    if ( -d "$home_dir/.rex/recipes" ) {
      push( @INC, "$home_dir/.rex/recipes" );
    }

    # add the default search locations
    push @rex_inc, @INC;

    # this must be the last entry, special handling to load rex modules.
    push(
      @rex_inc,
      sub {
        my $mod_to_load = $_[1];
        return search_module_path( $mod_to_load, 0 );
      }
    );

    return @rex_inc;
  }

  sub add_libstruct_to_inc {
    my ($path) = @_;
    my @ret = ();

    if ( -d File::Spec->catdir( $path, "lib" ) ) {
      push( @ret, File::Spec->catdir( $path, "lib" ) );
      push( @ret, File::Spec->catdir( $path, "lib", "perl", "lib", "perl5" ) );
      if ( $^O eq "linux" ) {
        push(
          @ret,
          File::Spec->catdir(
            $path, "lib", "perl", "lib", "perl5", "x86_64-linux"
          )
        );
      }
      if ( $^O =~ m/^MSWin/ ) {
        my ($special_win_path) = grep { m/\/MSWin32\-/ } @INC;
        if ( defined $special_win_path ) {
          my $mswin32_path = basename $special_win_path;
          push(
            @ret,
            File::Spec->catdir(
              $path, "lib", "perl", "lib", "perl5", $mswin32_path
            )
          );
        }
      }
    }

    return @ret;
  }

  sub add_cwd_to_inc {
    my $path = getcwd;
    return add_libstruct_to_inc($path);
  }

  sub _home_dir {
    if ( $^O =~ m/^MSWin/ ) {
      return $ENV{'USERPROFILE'};
    }

    return $ENV{'HOME'} || "";
  }

  my @new_inc = generate_inc();
  @INC = @new_inc;

}

my $home = $ENV{'HOME'} || "/tmp";
if ( $^O =~ m/^MSWin/ ) {
  $home = $ENV{'USERPROFILE'};
}

push( @INC, "$home/.rex/recipes" );

sub search_module_path {
  my ( $mod_to_load, $pre ) = @_;

  $mod_to_load =~ s/\.pm//g;

  my @search_in;
  if ($pre) {
    @search_in = map { ("$_/$mod_to_load.pm") }
      grep { -d } @INC;

  }
  else {
    @search_in =
      map { ( "$_/$mod_to_load/__module__.pm", "$_/$mod_to_load/Module.pm" ) }
      grep { -d } @INC;
  }

  for my $file (@search_in) {
    my $o = -f $file;
    my $fh_t;
    if ( $^O =~ m/^MSWin/i && !$o ) {

      # this is a windows workaround for if(-f ) on symlinks
      $o = open( my $fh_t, "<", $file );
    }

    if ($o) {
      close $fh_t if $fh_t;
      my ($path) = ( $file =~ m/^(.*)\/.+?$/ );
      if ( $path !~ m/\// ) {
        $path = $cur_dir . "/$path";
      }

      # module found, register path
      $MODULE_PATHS->{$mod_to_load} = { path => $path };
      my $mod_package_name = $mod_to_load;
      $mod_package_name =~ s/\//::/g;
      $MODULE_PATHS->{$mod_package_name} = { path => $path };

      if ($pre) {
        return;
      }

      open( my $fh, "<", $file );
      return $fh;
    }
  }
}

sub get_module_path {
  my ($module) = @_;
  if ( exists $MODULE_PATHS->{$module} ) {
    return $MODULE_PATHS->{$module}->{path};
  }
}

sub push_connection {
  if ( !ref $_[0]->{server} ) {
    $_[0]->{server} = Rex::Group::Entry::Server->new( name => $_[0]->{server} );
  }

  push @CONNECTION_STACK, $_[0];
  return $_[0];
}

sub pop_connection {
  pop @CONNECTION_STACK;
  Rex::Logger::debug( "Connections in queue: " . scalar(@CONNECTION_STACK) );
}

sub reconnect_lost_connections {
  if ( @CONNECTION_STACK > 0 ) {
    Rex::Logger::debug("Need to reinitialize connections.");
    for (@CONNECTION_STACK) {
      $_->{conn}->reconnect;
    }
  }
}

# ... no words
my @__modif_caller;

sub unset_modified_caller {
  @__modif_caller = ();
}

sub modified_caller {
  my (@caller) = @_;
  if (@caller) {
    @__modif_caller = @caller;
  }
  else {
    return @__modif_caller;
  }
}

=head2 get_current_connection

This function is deprecated since 0.28! See Rex::Commands::connection.

Returns the current connection as a hashRef.

=over 4

=item server

The server name

=item ssh

1 if it is a ssh connection, 0 if not.

=back

=cut

sub get_current_connection {

  # if no connection available, use local connect
  unless (@CONNECTION_STACK) {
    my $conn = Rex::Interface::Connection->create("Local");

    Rex::push_connection(
      {
        conn     => $conn,
        ssh      => $conn->get_connection_object,
        cache    => Rex::Interface::Cache->create(),
        reporter => Rex::Report->create(),
        notify   => Rex::Notify->new(),
      }
    );
  }

  $CONNECTION_STACK[-1];
}

sub get_current_connection_object {
  return Rex::get_current_connection()->{conn};
}

=head2 is_ssh

Returns 1 if the current connection is a ssh connection. 0 if not.

=cut

sub is_ssh {
  if ( $CONNECTION_STACK[-1] ) {
    my $ref = ref( $CONNECTION_STACK[-1]->{"conn"} );
    if ( $ref =~ m/SSH/ ) {
      return $CONNECTION_STACK[-1]->{"conn"}->get_connection_object();
    }
  }

  return 0;
}

=head2 is_local

Returns 1 if the current connection is local. Otherwise 0.

=cut

sub is_local {
  if ( $CONNECTION_STACK[-1] ) {
    my $ref = ref( $CONNECTION_STACK[-1]->{"conn"} );
    if ( $ref =~ m/Local/ ) {
      return $CONNECTION_STACK[-1]->{"conn"}->get_connection_object();
    }
  }

  return 0;
}

=head2 is_sudo

Returns 1 if the current operation is executed within sudo.

=cut

sub is_sudo {

  if ( exists $CONNECTION_STACK[-1]->{server}->{auth}->{sudo}
    && $CONNECTION_STACK[-1]->{server}->{auth}->{sudo} == 1 )
  {
    return 1;
  }
  elsif ( exists $CONNECTION_STACK[-1]->{server}->{auth}->{sudo}
    && $CONNECTION_STACK[-1]->{server}->{auth}->{sudo} == 0 )
  {
    return 0;
  }

  if ($GLOBAL_SUDO) { return 1; }

  if ( $CONNECTION_STACK[-1] ) {
    return $CONNECTION_STACK[-1]->{conn}->get_current_use_sudo;
  }

  return 0;
}

sub global_sudo {
  my ($on) = @_;
  $GLOBAL_SUDO = $on;

  # turn cache on
  Rex::Config->set_use_cache(1);
}

=head2 get_sftp

Returns the sftp object for the current ssh connection.

=cut

sub get_sftp {
  if ( $CONNECTION_STACK[-1] ) {
    return $CONNECTION_STACK[-1]->{"conn"}->get_fs_connection_object();
  }

  return 0;
}

sub get_cache {
  if ( $CONNECTION_STACK[-1] ) {
    return $CONNECTION_STACK[-1]->{"cache"};
  }

  return Rex::Interface::Cache->create();
}

=head2 connect

Use this function to create a connection if you use Rex as a library.

 use Rex;
 use Rex::Commands::Run;
 use Rex::Commands::Fs;

 Rex::connect(
   server    => "remotehost",
   user      => "root",
   password   => "f00b4r",
   private_key => "/path/to/private/key/file",
   public_key  => "/path/to/public/key/file",
 );

 if(is_file("/foo/bar")) {
   print "Do something...\n";
 }

 my $output = run("uptime");

=cut

sub connect {

  my ($param) = {@_};

  my $server      = $param->{server};
  my $port        = $param->{port} || 22;
  my $timeout     = $param->{timeout} || 5;
  my $user        = $param->{"user"};
  my $pass        = $param->{"password"};
  my $cached_conn = $param->{"cached_connection"};

  if ( !$cached_conn ) {
    my $conn =
      Rex::Interface::Connection->create(Rex::Config::get_connection_type);

    $conn->connect(
      user     => $user,
      password => $pass,
      server   => $server,
      port     => $port,
      timeout  => $timeout,
      %{$param},
    );

    unless ( $conn->is_connected ) {
      die("Connection error or refused.");
    }

    # push a remote connection
    my $rex_conn = Rex::push_connection(
      {
        conn     => $conn,
        ssh      => $conn->get_connection_object,
        server   => $server,
        cache    => Rex::Interface::Cache->create(),
        reporter => Rex::Report->create( Rex::Config->get_report_type ),
        notify   => Rex::Notify->new(),
      }
    );

    # auth unsuccessfull
    unless ( $conn->is_authenticated ) {
      Rex::Logger::info( "Wrong username or password. Or wrong key.", "warn" );

      # after jobs

      die("Wrong username or password. Or wrong key.");
    }

    return $rex_conn;
  }
  else {
    Rex::push_connection($cached_conn);
    return $cached_conn;
  }

}

sub deprecated {
  my ( $func, $version, @msg ) = @_;

  if ($func) {
    Rex::Logger::info("The call to $func is deprecated.");
  }

  if (@msg) {
    for (@msg) {
      Rex::Logger::info($_);
    }
  }

  Rex::Logger::info("");

  Rex::Logger::info(
    "Please rewrite your code. This function will disappear in (R)?ex version $version."
  );
  Rex::Logger::info(
    "If you need assistance please join #rex on irc.freenode.net or our google group."
  );

}

sub has_feature_version {
  my ($version) = @_;

  my @version_flags = grep { m/^\d+\./ } @FEATURE_FLAGS;
  for my $v (@version_flags) {
    if ( $version <= $v ) {
      return 1;
    }
  }

  return 0;
}

sub has_feature_version_lower {
  my ($version) = @_;

  my @version_flags = grep { m/^\d+\./ } @FEATURE_FLAGS;
  for my $v (@version_flags) {
    if ( $version > $v ) {
      return 1;
    }
  }

  return 0;
}

sub import {
  my ( $class, $what, $addition1 ) = @_;

  srand();

  if ( $addition1 && ref $addition1 eq "ARRAY" ) {
    push @FEATURE_FLAGS, $what, @{$addition1};
  }
  elsif ($addition1) {
    push @FEATURE_FLAGS, $what, $addition1;
  }

  $what ||= "";

  my ( $register_to, $file, $line ) = caller;

  # use Net::OpenSSH if present (default without feature flag)
  Rex::Config->set_use_net_openssh_if_present(1);

  if ( $what eq "-minimal" ) {
    require Rex::Commands;
    Rex::Commands->import( register_in => $register_to );

    require Rex::Helper::Rexfile::ParamLookup;
    Rex::Helper::Rexfile::ParamLookup->import( register_in => $register_to );
  }

  if ( $what eq "-base" || $what eq "base" || $what eq "-feature" ) {
    require Rex::Commands;
    Rex::Commands->import( register_in => $register_to );

    require Rex::Commands::Run;
    Rex::Commands::Run->import( register_in => $register_to );

    require Rex::Commands::Fs;
    Rex::Commands::Fs->import( register_in => $register_to );

    require Rex::Commands::File;
    Rex::Commands::File->import( register_in => $register_to );

    require Rex::Commands::Cron;
    Rex::Commands::Cron->import( register_in => $register_to );

    require Rex::Commands::Host;
    Rex::Commands::Host->import( register_in => $register_to );

    require Rex::Commands::Download;
    Rex::Commands::Download->import( register_in => $register_to );

    require Rex::Commands::Upload;
    Rex::Commands::Upload->import( register_in => $register_to );

    require Rex::Commands::Gather;
    Rex::Commands::Gather->import( register_in => $register_to );

    require Rex::Commands::Kernel;
    Rex::Commands::Kernel->import( register_in => $register_to );

    require Rex::Commands::Pkg;
    Rex::Commands::Pkg->import( register_in => $register_to );

    require Rex::Commands::Service;
    Rex::Commands::Service->import( register_in => $register_to );

    require Rex::Commands::Sysctl;
    Rex::Commands::Sysctl->import( register_in => $register_to );

    require Rex::Commands::Tail;
    Rex::Commands::Tail->import( register_in => $register_to );

    require Rex::Commands::Process;
    Rex::Commands::Process->import( register_in => $register_to );

    require Rex::Commands::Sync;
    Rex::Commands::Sync->import( register_in => $register_to );

    require Rex::Commands::Notify;
    Rex::Commands::Notify->import( register_in => $register_to );

    require Rex::Commands::User;
    Rex::Commands::User->import( register_in => $register_to );

    require Rex::Helper::Rexfile::ParamLookup;
    Rex::Helper::Rexfile::ParamLookup->import( register_in => $register_to );

    require Rex::Resource::firewall;
    Rex::Resource::firewall->import( register_in => $register_to );
  }

  if ( $what eq "-feature" || $what eq "feature" ) {

    if ( !ref($addition1) ) {
      $addition1 = [$addition1];
    }
    for my $add ( @{$addition1} ) {

      my $found_feature = 0;

      if ( $add =~ m/^(\d+\.\d+)$/ ) {
        my $vers = $1;
        my ( $major, $minor, $patch, $dev_release ) =
          $Rex::VERSION =~ m/^(\d+)\.(\d+)\.(\d+)_?(\d+)?$/;

        my ( $c_major, $c_minor ) = split( /\./, $vers );

        if ( defined $dev_release && $c_major == $major && $c_minor > $minor ) {
          Rex::Logger::info(
            "This is development release $Rex::VERSION of Rex. Enabling experimental feature flag for $vers.",
            "warn"
          );
        }
        elsif ( ( $c_major > $major )
          || ( $c_major >= $major && $c_minor > $minor ) )
        {
          Rex::Logger::info(
            "This Rexfile tries to enable features that are not supported with your version. Please update.",
            "error"
          );
          exit 1;
        }
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 1.4 ) {
        Rex::Logger::debug("Enabling task_chaining_cmdline_args feature");
        Rex::Config->set_task_chaining_cmdline_args(1);
        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 1.3 ) {
        Rex::Logger::debug("Activating new template engine.");
        Rex::Config->set_use_template_ng(1);
        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 1.0 ) {
        Rex::Logger::debug("Disabling usage of a tty");
        Rex::Config->set_no_tty(1);
        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.56 ) {
        Rex::Logger::debug("Activating autodie.");
        Rex::Config->set_autodie(1);
        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.55 ) {
        Rex::Logger::debug("Using Net::OpenSSH if present.");
        Rex::Config->set_use_net_openssh_if_present(1);
        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.54 ) {
        Rex::Logger::debug("Add service check.");
        Rex::Config->set_check_service_exists(1);

        Rex::Logger::debug("Setting set() to not append data.");
        Rex::Config->set_set_no_append(1);

        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.53 ) {
        Rex::Logger::debug("Registering CMDB as template variables.");
        Rex::Config->set_register_cmdb_template(1);
        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.51 ) {
        Rex::Logger::debug("activating featureset >= 0.51");
        Rex::Config->set_task_call_by_method(1);

        require Rex::Constants;
        Rex::Constants->import( register_in => $register_to );

        require Rex::CMDB;
        Rex::CMDB->import( register_in => $register_to );

        Rex::Commands::set(
          cmdb => {
            type => "YAML",
            path => [
              "cmdb/{operatingsystem}/{hostname}.yml",
              "cmdb/{operatingsystem}/default.yml",
              "cmdb/{environment}/{hostname}.yml",
              "cmdb/{environment}/default.yml",
              "cmdb/{hostname}.yml",
              "cmdb/default.yml",
            ],
          }
        );

        $found_feature = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.40 ) {
        Rex::Logger::debug("activating featureset >= 0.40");
        $Rex::Template::BE_LOCAL = 1;
        $Rex::WITH_EXIT_STATUS   = 1;
        $found_feature           = 1;
      }

      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.35 ) {
        Rex::Logger::debug("activating featureset >= 0.35");
        $Rex::Commands::REGISTER_SUB_HASH_PARAMETER = 1;
        $found_feature                              = 1;
      }

      # remove default task auth
      if ( $add =~ m/^\d+\.\d+$/ && $add >= 0.31 ) {
        Rex::Logger::debug("activating featureset >= 0.31");
        Rex::TaskList->create()->set_default_auth(0);
        $found_feature = 1;
      }

      if ( $add eq "no_autodie" ) {
        Rex::Logger::debug("disabling autodie");
        Rex::Config->set_autodie(0);
        $found_feature = 1;
      }

      if ( $add eq "rex_kvm_agent" ) {
        Rex::Logger::debug(
          "Activating experimental support for rex-kvm-agent.");
        Rex::Config->set_use_rex_kvm_agent(1);
        $found_feature = 1;
      }

      if ( $add eq "template_ng" ) {
        Rex::Logger::debug("Activating new template engine.");
        Rex::Config->set_use_template_ng(1);
        $found_feature = 1;
      }

      if ( $add eq "no_template_ng" ) {
        Rex::Logger::debug("Deactivating new template engine.");
        Rex::Config->set_use_template_ng(0);
        $found_feature = 1;
      }

      if ( $add eq "register_cmdb_top_scope" ) {
        Rex::Logger::debug("Registering CMDB as template variables.");
        Rex::Config->set_register_cmdb_template(1);
        $found_feature = 1;
      }

      if ( $add eq "no_local_template_vars" ) {
        Rex::Logger::debug("activating featureset no_local_template_vars");
        $Rex::Template::BE_LOCAL = 0;
        $found_feature           = 1;
      }

      if ( $add eq "exit_status" ) {
        Rex::Logger::debug("activating featureset exit_status");
        $Rex::WITH_EXIT_STATUS = 1;
        $found_feature         = 1;
      }

      if ( $add eq "sudo_without_sh" ) {
        Rex::Logger::debug(
          "using sudo without sh. this might break some things.");
        Rex::Config->set_sudo_without_sh(1);
        $found_feature = 1;
      }

      if ( $add eq "sudo_without_locales" ) {
        Rex::Logger::debug(
          "Using sudo without locales. this _will_ break things!");
        Rex::Config->set_sudo_without_locales(1);
        $found_feature = 1;
      }

      if ( $add eq "tty" ) {
        Rex::Logger::debug("Enabling pty usage for ssh");
        Rex::Config->set_no_tty(0);
        $found_feature = 1;
      }

      if ( $add eq "no_tty" ) {
        Rex::Logger::debug("Disabling pty usage for ssh");
        Rex::Config->set_no_tty(1);
        $found_feature = 1;
      }

      if ( $add eq "empty_groups" ) {
        Rex::Logger::debug("Enabling usage of empty groups");
        Rex::Config->set_allow_empty_groups(1);
        $found_feature = 1;
      }

      if ( $add eq "use_server_auth" ) {
        Rex::Logger::debug("Enabling use_server_auth");
        Rex::Config->set_use_server_auth(1);
        $found_feature = 1;
      }

      if ( $add eq "exec_and_sleep" ) {
        Rex::Logger::debug("Enabling exec_and_sleep");
        Rex::Config->set_sleep_hack(1);
        $found_feature = 1;
      }

      if ( $add eq "disable_strict_host_key_checking" ) {
        Rex::Logger::debug("Disabling strict host key checking for openssh");
        Rex::Config->set_openssh_opt( StrictHostKeyChecking => "no" );
        $found_feature = 1;
      }

  #if($add eq "reporting" || $add eq "report" || exists $ENV{REX_REPORT_TYPE}) {
  #  Rex::Logger::debug("Enabling reporting");
      Rex::Config->set_do_reporting(1);

      #  $found_feature = 1;
      #}

      if ( $add eq "source_profile" ) {
        Rex::Logger::debug("Enabling source_profile");
        Rex::Config->set_source_profile(1);
        $found_feature = 1;
      }

      if ( $add eq "source_global_profile" ) {
        Rex::Logger::debug("Enabling source_global_profile");
        Rex::Config->set_source_global_profile(1);
        $found_feature = 1;
      }

      if ( $add eq "no_path_cleanup" ) {
        Rex::Logger::debug("Enabling no_path_cleanup");
        Rex::Config->set_no_path_cleanup(1);
        $found_feature = 1;
      }

      if ( $add eq "exec_autodie" ) {
        Rex::Logger::debug("Enabling exec_autodie");
        Rex::Config->set_exec_autodie(1);
        $found_feature = 1;
      }

      if ( $add eq "no_cache" ) {
        Rex::Logger::debug("disable caching");
        Rex::Config->set_use_cache(0);
        $found_feature = 1;
      }

      if ( $add eq "verbose_run" ) {
        Rex::Logger::debug("Enabling verbose_run feature");
        Rex::Config->set_verbose_run(1);
        $found_feature = 1;
      }

      if ( $add eq "disable_taskname_warning" ) {
        Rex::Logger::debug("Enabling disable_taskname_warning feature");
        Rex::Config->set_disable_taskname_warning(1);
        $found_feature = 1;
      }

      if ( $add eq "no_task_chaining_cmdline_args" ) {
        Rex::Logger::debug("Disabling task_chaining_cmdline_args feature");
        Rex::Config->set_task_chaining_cmdline_args(0);
        $found_feature = 1;
      }

      if ( $add eq "task_chaining_cmdline_args" ) {
        Rex::Logger::debug("Enabling task_chaining_cmdline_args feature");
        Rex::Config->set_task_chaining_cmdline_args(1);
        $found_feature = 1;
      }

      if ( $found_feature == 0 ) {
        Rex::Logger::info(
          "You tried to load a feature ($add) that doesn't exists in your Rex version. Please update.",
          "warn"
        );
        exit 1;
      }

    }

  }

  if ( exists $ENV{REX_REPORT_TYPE} ) {
    Rex::Logger::debug("Enabling reporting");
    Rex::Config->set_do_reporting(1);
  }

  if ( exists $ENV{REX_SUDO} && $ENV{REX_SUDO} ) {
    Rex::global_sudo(1);
  }

  # we are always strict
  strict->import;
}

=head1 CONTRIBUTORS

Many thanks to the contributors for their work. Please see L<CONTRIBUTORS|https://github.com/RexOps/Rex/blob/master/CONTRIBUTORS> file for a complete list.

=head1 LICENSE

Rex is a free software, licensed under:
The Apache License, Version 2.0, January 2004

=cut

1;