#!/usr/bin/env perl
use strict;
use warnings;
use SMT::Agent::Utils;
use XML::XPath;
use XML::XPath::XMLParser;

sub jobhandler
{
  my ($jobtype, $jobid, $args, $verbose) =  @_;

  SMT::Agent::Utils::logger ("jobhandler for softwarepush called", $jobid);
  SMT::Agent::Utils::logger ("softwarepush runs jobid \"$jobid\"", $jobid);

  # check whether this handler can handle requested jobtype
  SMT::Agent::Utils::error ("wrong job handler: \"softwarepush\" cannot handle \"$jobtype\"", $jobid) if ( $jobtype ne "softwarepush" );

  my $xpQuery = XML::XPath->new(xml => $args);
  eval { SMT::Agent::Utils::error("no argument section found for this job", $jobid) unless ( $xpQuery->exists('/arguments[1]')); };
  my $argSet;
  eval { $argSet = $xpQuery->find('/arguments[1]') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  SMT::Agent::Utils::error("too many argument sections found for this job", $jobid) unless ( (defined $argSet) && ($argSet->size() == 1) );
  my $arg = $argSet->pop();

  my $agreelicenses = $arg->getAttribute('agreelicenses');
  my $force = $arg->getAttribute('force');
  my $agreeSet;
  eval { $agreeSet = $xpQuery->find('/arguments[1]/options[1]/agreelicenses[1]') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  foreach my $_n ($agreeSet->get_nodelist()) {
      $agreelicenses = $_n->string_value() if (defined $_n);
      last if defined $agreelicenses;
  }
  my $forceSet;
  eval { $forceSet = $xpQuery->find('/arguments[1]/options[1]/force[1]') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  foreach my $_n ($forceSet->get_nodelist()) {
      $force = $_n->string_value() if (defined $_n);
      last if defined $force;
  }
  $agreelicenses = (defined $agreelicenses && ($agreelicenses =~ /^1$/ || $agreelicenses =~ /^true$/)) ? 1 : 0;
  $force = (defined $force && ($force =~ /^1$/ || $force =~ /^true$/)) ? 1 : 0;

  my %installables;
  my $packageSet;
  eval { $packageSet = $xpQuery->find('/arguments[1]/packages[1]/package') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  foreach my $_n ($packageSet->get_nodelist()) {
      push @{$installables{package}}, $_n->string_value() if (defined $_n);
  }
  my $patchSet;
  eval { $patchSet = $xpQuery->find('/arguments[1]/patches[1]/patch') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  foreach my $_n ($patchSet->get_nodelist()) {
      push @{$installables{patch}}, $_n->string_value() if (defined $_n);
  }
  my $patternSet;
  eval { $patternSet = $xpQuery->find('/arguments[1]/patterns[1]/pattern') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  foreach my $_n ($patternSet->get_nodelist()) {
      push @{$installables{pattern}}, $_n->string_value() if (defined $_n);
  }
  my $productSet;
  eval { $productSet = $xpQuery->find('/arguments[1]/products[1]/product') };
  SMT::Agent::Utils::error("xml data is not parsable", $jobid) if ($@);
  foreach my $_n ($productSet->get_nodelist()) {
      push @{$installables{product}}, $_n->string_value() if (defined $_n);
  }

  # reboot
  my $reboot;
  my $rebootSet;
  eval { $rebootSet = $xpQuery->find('/arguments[1]/options[1]/reboot[1]') };
  foreach my $_n ($rebootSet->get_nodelist()) {
      $reboot = $_n->string_value() if (defined $_n);
      last if defined $reboot;
  }
  $reboot = "never" unless defined $reboot;

  my $rebootAllowed = SMT::Agent::Utils::isAgentAllowed('reboot') ? 1 : 0;
  SMT::Agent::Utils::error("softwarepush with enforced reboot denied", $jobid) if ( ($reboot eq 'enforce') && ! $rebootAllowed );

  #==  create zypper base command ==
  my $command = "/usr/bin/zypper";
  my @zypperCmd;
  push (@zypperCmd, "--no-cd");               # ignore CD/DVD repositories
  push (@zypperCmd, "-x");                    # xml output
  push (@zypperCmd, "--non-interactive");     # doesn't ask user
  push (@zypperCmd, "in");                    # install
  push (@zypperCmd, "-l") if $agreelicenses;  # agree licenses
  push (@zypperCmd, "-f") if $force;          # reinstall

  my $argumentsok = 0;
  my $retval=0;
  my @stdout=();
  my @stderr=();
  my @messages=();
  my $statuscode = 1;
  my $doReboot = ($reboot eq 'enforce') ? 1:0;
  foreach my $IN ( qw(package patch pattern product) )
  {
    next unless defined $installables{$IN};
    next unless ( ref($installables{$IN}) eq 'ARRAY' );
    $argumentsok = 1;

    my @installCmd = ( @zypperCmd, '-t', $IN, @{$installables{$IN}} );
    (my $_retval, my $_stdout, my $_stderr) = SMT::Agent::Utils::executeCommand( $command, undef, @installCmd );
    push @stdout, $_stdout;
    push @stderr, $_stderr;

    if ($_retval == 102)
    {
      push @messages, "$IN installation requires reboot ($_retval)";
      if ($reboot eq 'ifneeded' || $reboot eq 'enforce')
      {
        $doReboot = 1 if ($rebootAllowed);
        # retval remains untouched
      }
      else
      {
        $statuscode = ($statuscode == 6 || $statuscode == 1) ? 6:2;
        $retval += $_retval;
      }
    }
    elsif ($_retval == 103)
    {
      push @messages, "$IN installation succeeded (packagemanager restart needed) ($_retval)";
      # retval remains untouched
    }
    elsif ($_retval != 0)
    {
      $retval += $_retval;
      $doReboot = 0;
      $statuscode = 2;
      push @messages, "$IN installation failed ($_retval)";
    }
    else
    {
      push @messages, "$IN installation succeeded";
    }
  }

  # die if the job did not contain anything to be installed
  SMT::Agent::Utils::error( "too few arguments: need one or more of [packages, patches, patterns, products]", $jobid ) unless $argumentsok;

  if ($doReboot && $statuscode == 1)
  {
      push @messages, "reboot executed";
      `/sbin/shutdown -r +1 > /dev/null 2>&1 &`;
  }

  my $msg = join(", ", @messages);
  SMT::Agent::Utils::logger("job results: $msg");
  SMT::Agent::Utils::logger("job statuscode: $statuscode, exitcode: $retval");

  return (
    stdout   => (scalar(@stdout) && $verbose) ? join("\n", @stdout) : '',
    stderr   => (scalar(@stderr) && $verbose) ? join("\n", @stderr) : '',
    exitcode => $retval,
    success  => $statuscode,
    message  => "result: ".$msg,
    breakloop => ($doReboot && $statuscode == 1) ? 'true':'false'
  );

}

SMT::Agent::Utils::logger ("successfully loaded handler for jobtype \"softwarepush\"");

return 1;

