package Process;

use System;
use strict;
use Util::Http;
use Data::Dumper;
use Util;
use Mail;

use vars qw($ERROR);

sub get_pid {
  my($host, $id, $PIDF) = @_;
  my($pid);
  $ERROR = "";
  if (!$host) {  # local
    if (open(O, $PIDF)) {
      my @L = <O>; close(O);
      $pid = join("", @L);
    }
  } else {
    ($ERROR, $pid) = Util::Http->readFile($host, "tmp/${id}_pid", 10);
  }
  my $h = Process->parse($pid);
  return $h;
}

sub parse {
  my($class, $l) = @_;
  my @L = split(/\n/, $l);
  my $h = {};
  foreach my $l (@L) {
     if ($l =~ /pid=(.+)/) {
        $h->{pid} = $1;
     } elsif ($l =~ /start=(.+)/) {
        $h->{start} = $1;
     } elsif ($l =~ /end=(.+)/) {
        $h->{end} = $1;
     }
  }
  return $h;
}
    


sub label {
  my($pro, $status) = @_;
  my $L = Labels->read("Process");
}

sub list {
  my($pro, $HIST) = @_;
  my $D =  System->get_home() . "/DATA/tmp";

  if($HIST) {
    $D =  System->get_home() . "/DATA/tmphist";
  }

  opendir(D, $D);
  my @F = readdir(D); closedir(D);
  my @LIST;
  foreach my $f (@F) {
     if ($f =~ /(.+)_pid$/) {
       my $id = $1;
       push(@LIST, $pro->details(undef,$id, undef, $HIST));

     } elsif ($f =~ /(.+)_pid\.(.+)/) {
       my $id = $1;
       my $host = $2;
       push(@LIST, $pro->details(undef,$id, $f, $HIST));
     }
  }
  return \@LIST;
}


sub details {
  my($pro, $host, $id ,$file, $HIST) = @_;

  my $D =  System->get_home() . "/DATA/tmp";
 
  if($HIST) {
    $D =  System->get_home() . "/DATA/tmphist";
  }

  my($st, $pid, $hash) = $pro->status($host, $id, $HIST, $file);
  chomp($st);

  my $f = $file || ($id . "_pid");
  if ($file) {
    $file =~ /(.+)_pid\.(.+)/;
    $host = $2;
  }

  return  {    id => $id, 
              pid => $pid, 
            host  => $host,
           status => $st,
            start => $hash->{start},
              end => $hash->{end},
          };
}


sub delete {
  my($pro, $host, $id, $HIST) = @_;

  my $D = $HIST ? "tmphist" : "tmp";

  my $PIDF = System->get_home() . "/DATA/${D}/${id}_pid";
  my $renv = System->get_renv();
  $host = "" if ($host eq $renv->{hostname});

  if ($host) {
    Util::Http->deleteFile($host, "${D}/${id}_pid", 10);
    unlink "$PIDF.$host";
  } else {
    unlink $PIDF;
  }
}


#
# For local job, support the archived name to contain a flag.
#
sub archive {
  my($pro, $host, $id, $flag) = @_;

  my $renv = System->get_renv();
  $host = "" if ($host eq $renv->{hostname});

  my $D =  System->get_home() . "/DATA/tmp";
  opendir(D, $D);
  my @F = readdir(D); closedir(D);

  my $localFileList = "${id}_pid ${id} ${id}.out ${id}_trace ${id}local.out ${id}local_trace";
  my $remoteFileList = "${id}_pid.${host} ${id}.${host} ${id}${host}.out ${id}${host}_trace";
 
  my $FROM = System->get_home() . "/DATA/tmp";
  my $TO   = System->get_home() . "/DATA/tmphist";
  my $cmd1 = "cd $FROM";
  my $cmd2 = "/bin/mv $localFileList $TO";
  my $cmd3 = "/bin/mv $remoteFileList $TO";
  my $cmd4 = "/sbin/archivejob ${id}";

  if(!$host) {
    system("${cmd1};${cmd2}");
    if (defined($flag)) {
      # the files to be archived need to have flag in the names.
      # so rename the files.
      my $cmd5  ="/bin/mv $TO/${id}_pid $TO/${id}_${flag}_pid";
      my $cmd6  ="/bin/mv $TO/${id} $TO/${id}_${flag}";
      my $cmd7  ="/bin/mv $TO/${id}.out $TO/${id}_${flag}.out";
      my $cmd8  ="/bin/mv $TO/${id}_trace $TO/${id}_${flag}_trace";
      my $cmd9  ="/bin/mv $TO/${id}local.out $TO/${id}_${flag}local.out";
      my $cmd10 ="/bin/mv $TO/${id}local_trace $TO/${id}_${flag}local_trace";
      system("${cmd5};${cmd6};${cmd7};${cmd8};${cmd9};${cmd6}");
    }
  } else {
    system("${cmd1};${cmd3}");
    # move job at slave too
    my($err, $rc) = Util::Http->runshell($host, "${cmd4}");
  }

}


# returns Running | Not running | Died | Done / pid
# returns ("Running", $pid)
# return $ERROR
# $Process::ERROR
#
sub status {
  my($pro, $host, $id, $HIST, $file) = @_;
  my ($st, $pid);

  my $D = "tmp";
  $D = "tmphist" if ($HIST);

  my $PIDF = System->get_home() . "/DATA/${D}/${id}_pid";

  if ($file) {
    $PIDF = System->get_home() . "/DATA/${D}/$file";
  }

  my $hash = &get_pid($host, $id, $PIDF);

  if ($hash->{pid} =~ /\d+/ && !$hash->{end}) {

    # Given pid file name, the host is still need to check the real process 
    # on that host
    # Xuefeng Yao 10/21/04

    my $host2;

    if ($file) {
      $file =~ /(.+)_pid\.(.+)/;
      $host2 = $2;
    }

    my $trueHost = $host || $host2;

     my($a,$s) = Util->processTime($hash->{pid}, $trueHost);
     if (!$a) {
       my $hash2 = &get_pid($host, $id, $PIDF);

       if ($hash2->{pid} =~ /\d+/ && !$hash2->{end}) {  # no time and pid still there means that it died abruptly.
         $st = "Died";
         if ($trueHost) {
            Util::Http->deleteFile($trueHost, "${D}/${id}_pid", 10);
            unlink $PIDF;
         } else {
            unlink $PIDF;
         }
       } else {
         $st = "Done / $hash->{end}";
       }

     } else {
       $st = "Running";
     }
  } elsif ($hash->{end})  {
     $st = "Done / $hash->{end}";

  } else {
      $st = "Not running";
  }
  if (wantarray) {
    return ($st, $hash->{pid}, $hash);
  } else {
    return $st;
  }
}

# can background itself local/remote 
# default is running in the backgroud.
# Command->run($host or blank, "bin/fccheck 1 2 3", [0|1]);
# return 1

sub run {
  my($class, $host, $program, $run_method, $foreground, $to, $progress) = @_;
  my ($cpid);
  die "run missing!" if (!$run_method);
  my ($renv, $devices, $hosts, $notifs, $Config) = PDM::ConfigFile->read();

  my $host_ip = Util->name2ip($host);
  if (!$host || $host_ip eq $renv->{ipno} || $host eq $renv->{hostname} ) {
     if ($foreground) {
        &$run_method();
     } elsif (($cpid = fork()) == 0) {
        # comment the following line out since it will cause
        # revision check for 6310 failed  Xuefeng Yao July 2 2004
        #close STDIN; close STDOUT; close STDERR;
        &$run_method();
     } else {
        my $ix = index($program, " ");
        my $prg1 = $ix > 0 ? substr($program,0,$ix) : $program;
        print "OK $prg1 started.\n";
     }

 # } elsif (!$Config->hostByKey($host) ) {
 #    $ERROR = "Cannot find host $host\n";
 #    return undef;
  } else {  # REMOTE 
     $program .= "&" if (!$foreground);
     return Util::Http->runit($host, $program, $to, $progress);
  }
}

sub write {
  my($class, $report, $id) = @_;

   my $F    = System->get_home() . "/DATA/tmp/$id";
   open(O, ">$F");
   $Data::Dumper::Indent=1;
   print O Dumper($report);
   close(O);

   my $master = Util->findMaster();
   if ($master) {
      my $renv = System->get_renv();
      Util::Http->saveFile($master, "tmp/$id.$renv->{hostname}", Dumper($report));
   }
}

sub read {
  my($class, $host, $id, $HIST) = @_;
  my $VAR1;
  my $renv = System->get_renv();
  my $F = $HIST ? "/tmphist/${id}" : "/tmp/${id}";
#  my $F = "/tmp/${id}";
  my $host_ip = Util->name2ip($host);
  if (!$host || $host_ip eq $renv->{ipno} || $host eq $renv->{hostname}) {
      return Util->deserialize($F);
  } else {
#      my $data = Util::Http->readFile($host, $F, 10);
#      if ($data) {
#          eval $data;
#          return $VAR1;
#      }

# all files will be moved to master. so it will be read locally.
      $F = $F . "." . $host;
      return Util->deserialize($F);
  }
}

sub readOut {
  my($class, $host, $id, $HIST) = @_;

  my $D = $HIST ? "tmphist" : "tmp";

  if (!$host || $host eq "local") {
     my $F   = System->get_home() . "/DATA/${D}/${id}.out";
     open(O, $F); my @L = <O>; close(O);

     my $F2  = System->get_home() . "/DATA/${D}/${id}local.out";
     open(O, $F2); my @L2 = <O>; close(O);

     return join("", (@L, @L2));

  } else {
     my($ERR, $text) = Util::Http->readFile($host, "${D}/${id}.out", 10);
     my $F2  = System->get_home() . "/DATA/${D}/${id}${host}.out";
     open(O, $F2); my @L2 = <O>; close(O);

     return $text . join("", @L2);
  }
}

sub trace {
  my($class, $host, $id, $HIST) = @_;

  my $D = $HIST ? "tmphist" : "tmp";

  if (!$host || $host eq "local") {
     my $F   = System->get_home() . "/DATA/${D}/${id}_trace";
     open(O, $F); my @L = <O>; close(O);

     my $F2  = System->get_home() . "/DATA/${D}/${id}local_trace";
     open(O, $F2); my @L2 = <O>; close(O);

     return join("", (@L, @L2));

  } else {
     my($ERR, $text) = Util::Http->readFile($host, "${D}/${id}_trace", 10);
     my $F2  = System->get_home() . "/DATA/${D}/${id}${host}_trace";
     open(O, $F2); my @L2 = <O>; close(O);

     return $text . join("", @L2);
  }
}


sub done {
  my($pro, $id, $skip_remote) = @_;

  my $PIDF   = System->get_home() . "/DATA/tmp/${id}_pid";

  my $date = Util->get_today();
  open(O, ">>$PIDF"); print O "end=$date\n"; close(O);

  my $master = Util->findMaster;
  if ($master && !$skip_remote) {
     my $renv = System->get_renv();
     Util::Http->appendFile($master, "tmp/${id}_pid.$renv->{hostname}", "end=$date\n");
  }
}

sub start {
  my($class, $id) = @_;

  my $F    = System->get_home() . "/DATA/tmp/$id";
  my $PIDF = System->get_home() . "/DATA/tmp/${id}_pid";
  unlink $F;
  my $start = Util->get_today();
  open(O, ">$PIDF"); print O "pid=$$\nstart=$start\n"; close(O);

  my $master = Util->findMaster;
  if ($master) {
     my $renv = System->get_renv();
     Util::Http->saveFile($master, "tmp/${id}_pid.$renv->{hostname}", "pid=$$\nstart=$start\n");
  }

}

sub startIf {
  my($class, $id) = @_;

  my $F    = System->get_home() . "/DATA/tmp/$id";
  my $PIDF = System->get_home() . "/DATA/tmp/${id}_pid";

  my $current;
  open(O, $PIDF); my @L = <O>; close(O);
  my $h = Process->parse(join("", @L));

  return 0 if ($h->{pid} && !$h->{end}); # still running, abort

  unlink $F;
  my $start = Util->get_today();
  open(O, ">$PIDF"); print O "pid=$$\nstart=$start\n"; close(O);
  chmod 0664, $PIDF;
  my $master = Util->findMaster;
  if ($master) {
     my $renv = System->get_renv();
     Util::Http->saveFile($master, "tmp/${id}_pid.$renv->{hostname}", "pid=$$");
     Util::Http->saveFile($master, "tmp/${id}_start.$renv->{hostname}", "pid=$$\nstart=$start\n");
  }
  return 1;
}

#
# 
sub age {
  my($class, $ID, $format) = @_;
  $format = "h" if (!$format);

  return Util->getFileAge(System->get_home() . "/DATA/tmp/$ID", $format);
}





sub email {
  my($class, $report, $title) = @_;

  my($renv, $devices, $hosts, $notifs, $Config) = PDM::ConfigFile->read();
  my $date = $report->{date} || Util->get_today();

  foreach my $notif (@$notifs) {  # Each notification.
     next if (!$notif->{email});
     my $admin = Util->rtrim($notif->{email});
     my $eventType = Util->rtrim($notif->{event});
     next if (($eventType ne "*") && index(lc($eventType),"upgradeevent") < 0 );
     my $content =<<EOF;
--- $title SUMMARY REPORT ---

Site    : $renv->{site_name} $renv->{site_city} 
Hostname: $renv->{hostname}
Date    : $date

--- TRACE ---

$report->{data}{trace}

EOF

     my $devs = $Config->devices();
     my $ppro = $report->{data}{report};

     my %IP;  # map devices by ipno
     foreach my $d (@$devs) {
        $IP{$d->{ipno}} = $d;
     }

     my $LB = Labels->read("Service::Upgrade");
     $content .= "\n\n";
     $content .= "\n--- SUMMARY ---";
     $content .= "\n";

     foreach my $pp (@{$ppro->devices()}) {
      my $inv_d = $IP{$pp->key()};
      my $ip   = $pp->key();
      my $name = $inv_d->{name};
      my $type = lc($pp->type());
      my $dev  = $Config->deviceByIP($ip);
      my ($label, $k, $sev, $info);
      $sev = 0;
      if ($type eq "sunos") {
        $label = $LB->{sp};
        $k     = "host:" . $renv->{hostname};
      } elsif ($dev) {
        $label = "$dev->{type}: $dev->{name}";
        $k     = "$dev->{type}:$dev->{key}";
      } elsif ($name) {
        $label = "$type:$name";
      } else {
        $label = "$type:UNKNOWN";
      }
      my $P = $pp->patches();
      my $pcnt = $#$P +1;
      my $t2 = $LB->{patches};
      $content .= "\n $label: $pcnt patch(es). \n";
      my $cnt;
      foreach my $patch (@$P) {
         my $pid = $patch->{patchID};
         my $syn = $patch->{synopsis};
	 my $err = $patch->{error};
         my $frus;
         foreach my $fru (@{$patch->frus()}) {
            $frus .= "$fru->{locator}, ";
         }
         if ($frus) {
            chop($frus); chop($frus);
         }
         my $err2 = "   Message: $err \n" if ($err);

         $content .= "   PatchID: $pid \n";
         my $t1 = $syn;
         $t1 .= " " if ($syn && $err2);
         $t1 .= $err2;
         $t1 .= "      Frus: $frus\n" if ($frus);
         $content .= "$t1 \n";
         $cnt++;
      }
     } 

     Mail->mail($admin, $renv->{GSV_ACRONYM}, $title, $content, 10);
  }
}


1;
