package Util;
#<copyright>
# ----------------------------------------------------------
# Sun Proprietary/Confidential Code
# Copyright 2001, Sun Microsystems, Inc. All rights reserved.
# ----------------------------------------------------------
#</copyright>

#  $Name:  $ 
#  $Id: Util.pm,v 1.90 2002/10/31 19:53:02 ccadieux Exp $

use Error;
use Labels;
use System;
use Html;
use Util::Http;
use PDM::ConfigFile;
use Debug;
use Socket;
use strict 'vars';

#use Time::Timezone;

#
#  $util->new({data => $DATA,  renv => $renv });
#
# Usage: Util->get_today
#        Util->today
#        Util->include
#        Util->MTH
#        Util->rtrim
#        Util->ltrim
#        Util->rsubstr
#
use vars qw ($DATE_SIZE %MTH %MTH2 %SERIAL_MAP $Xmin_value  $STILL_THERE %ABB %RES);
use vars qw ($COPY_ERR $MASTER %LOG_EVENT $CK_TIME $WWNINFO %RUN_CACHE $ERR $LANG_LIST);

$DATE_SIZE = 17;

%MTH  = (Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6,
         Jul => 7, Aug => 8, Sep => 9, Oct =>10, Nov =>11, Dec =>12);

%MTH2  = (1 => 'Jan', 2 => 'Feb', 3 => 'Mar', 4 => 'Apr', 5 => 'May', 6 => 'Jun' ,
         7 => 'Jul', 8 => 'Aug', 9 => 'Sep', 10 => 'Oct', 11 => 'Nov', 12 => 'Dec');

%SERIAL_MAP = ();

%ABB        = ('t3' => 'Sun t3');

sub type2name {
  my($class, $cat) = @_ ;
  return $ABB{$cat} || uc($cat);
}

sub dump {
  my($class, $data, $file) = @_;

require Data::Dumper;

  if (open(O, ">$file")) {
     $Data::Dumper::Indent = 1;
     print O Data::Dumper::Dumper($data);
     close(O);
     return 1;
  } else {
     $ERR = $!;
     return undef;
  }
}

sub uucpLogin {
  my($class) = @_;
  my ($machine, $l);

  if (open(O,"/etc/uucp/Permissions")) {
     while ($l = <O>) {
        if ($l =~ /^LOGNAME=.* MYNAME=([^ ]+) /) {
          $machine = $1;
          last;
        }
     }
     close(O);
  }
  return $machine;
}

########################################################
# JULIAN FUNCTIONS
#

# ($julian, $dow, $wk_no, $mins) = Util->julian(2002, 1, 10);
# wk_no start on sunday
#
sub julian {
  my($class, $year, $month, $day) = @_;
  require Time::JulianDay;
  my $jd  = Time::JulianDay::julian_day($year, $month, $day);
  my $dow = Time::JulianDay::day_of_week($jd);
  my $wk  = int(($jd+1)/7);

  return wantarray ? ($jd, $dow, $wk) : $jd;
}

sub julianMins {
  my($class, $year, $month, $day, $hms) = @_;
  require Time::JulianDay;
  my $jd  = Time::JulianDay::julian_day($year, $month, $day);
  my $mins = $jd * 60 * 24;
  if ($hms) {
     $mins += (substr($hms,0,2) * 60) + substr($hms,3,2);
  }
  return $mins;
}

# my ($year,$month, $sunday_day) = Util->findSunday($julian_day);
#
sub findSunday {
  my($class, $ju) = @_;
  my $wk = int(($ju+1)/7);
  my($wy,$wm,$wd) = $class->fromJulian($wk*7 -1);
  return ($wy, $wm, $wd);
}


# my ($year,$month, $day) = Util->fromJulian($julian_day);
#
sub fromJulian {
  my($class, $jd) = @_;
  require Time::JulianDay;
  return Time::JulianDay::inverse_julian_day($jd);
}

##########################################################




sub languageList {
  my($class, $hash) = @_;
  my ($l,$list);
  return $LANG_LIST if ($LANG_LIST && !$hash);
  my $G = Labels->read();
  my $D = System->get_home() . "/Lang/System/Navigation";
  opendir(O, $D);
  my @D = readdir(O); closedir(O);
  my %H;
  foreach my $a (@D) {
     next if (substr($a,0,1) eq "." || $a eq "CVS");
     $H{$a} = $G->{$a};
     $list .= "$a=$G->{$a}|";
  }
  $H{en} = $G->{en};
  $list .= "en=$G->{en}";
  $LANG_LIST = $list;
  if ($hash) {
     return \%H;
  }
  return $list;
}



# turn a wwn into a dot delimited list of decimal numbers
# suitable for mib comparison in FC_v2

sub wwn2mib {
  my($class, $wwn) = @_;
  my($x, $out);
  for ($x=0; $x <= 14; $x+= 2) {
    $out .= (hex substr($wwn, $x, 2)) . ".";
  }
  chop($out) if ($out);
  return $out;

}

sub abb {
  my($class, $val) = @_;
  my $r = $class->readResource("System/abbreviations.txt", "type");
  if ($val) {
     if (exists($r->{$val})) {
       return $r->{$val};
     } else {
       return $val;
     }
  } else {
     return $r;
  }
}

#my($hostid, $version, $masterFlag, $perl_version, $model, hostname) = split(/\s*\|\s*/, $rc);

sub rasInfo {
  my($class, $ip) = @_;
  my %V;
  my $rc = Util::Http->getCommand($ip, "INFO1");
  return undef if (!$rc);

  my(@A) = split(/\s*\|\s*/, $rc);
  $V{hostid}         = $A[0];
  $V{version}        = $A[1];
  $V{master}         = $A[2];
  $V{perlVersion}    = $A[3];
  $V{solutionModel}  = $A[4];
  chomp($A[5]);
  $V{hostname}       = $A[5];
  $V{solution}       = $A[6];
  return \%V;
}



#  $x = Util->readResource("System/abbreviations.txt", "type");
#  print $x->{'a5k.long'}

sub readResource {
  my($class, $file, $sep, $two) = @_;
  my (%V, $l);
  return $RES{$file} if (exists($RES{$file}));
  $sep = "type" if (!$sep);
  if (!open(OO, System->get_home() . "/$file")) {
     return undef;
  }
  my $curr ;
  while ($l = <OO>)  {
    next if (substr($l,0,1) eq "#");
    next if (index($l, "=") < 0);
    chop($l);
    my(@a) = split(/\s*=\s*/, $l);
    if ($a[0] eq $sep) {
       $curr = $a[1];
    }
    if ($two) {
      $V{$curr}{$a[0]} = $a[1];
    } else {
      $V{"$curr.$a[0]"} = $a[1];
    }
  }
  close(OO);
  my $p = \%V;
  $RES{$file} = $p;
  return $p;
  
}


# return 1: go ahead
# return 2: gave up, passwd $wait seconds
# return 3: cannot open pidfile;
#
sub waitForPID {
  my($class, $pidfile, $wait) = @_;
  my ($pid, $w);
  $wait = 10 * 60 if (!$wait);
  $w = 0;
  if (open(PP, $pidfile)) {
     $pid = <PP>; close(PP);
  } else {
     return 3;
  }
  while (-f $pidfile) {        # wait up to 10 minute if rasagent is running.
   my ($atime, $secs) = Util->processTime($pid);
   if (!$atime) {
      unlink $pidfile;
      last;
   }
   sleep 10;
   $w += 10;
   return 2 if ($w > $wait);
  }
  return 1;
}

sub serialize {
  my($class, $file, $data) = @_;
  my($file0) = System->get_home() . "/DATA/$file";
  $ERR = undef;

require Data::Dumper;

  if (open(O, ">$file0")) {
     $Data::Dumper::Indent = 1;
     print O Data::Dumper::Dumper($data);
     close(O);
     return 1;
  } else {
     $ERR = $!;
     return undef;
  }
}


sub ipPrefix {
  my($class,$ip) = @_;
  my($a1,$a2,$a3,$a4) = split(/\./, $ip);
  if ($a3 || $a1 || $a2) {
    return "$a1.$a2.$a3";
  } else {
    return undef;
  }
}

sub deserialize {
  my($class, $file, $use_cache) = @_;
  if ($use_cache) {
    return $SERIAL_MAP{$file} if ($SERIAL_MAP{$file});
  }
  $ERR = undef;

  my($file0) = System->get_home() . "/DATA/$file";
  my($VAR1);
  if (open(O, $file0)) {
    my(@a) = <O>;
    close(O);
    eval "@a";
    $SERIAL_MAP{$file} = $VAR1 if ($use_cache);
    return $VAR1;

  } else {
     $ERR = $!;
     return undef;
  }
}

#
#   Util->cleanUpDir("/DATA/Proc", 2); # remove files that are 2 days old
#                                        in the directories under Prod.
sub cleanUpDir {
  my($class, $D, $days) = @_;

  $D = System->get_home() . "/$D";

  opendir(O, $D);
  my @dirs  = readdir(O); close(O);
  foreach my $host (@dirs) {
     next if (substr($host,0,1) eq ".");
     next if (!-d "$D/$host");
     my $D1 = "$D/$host";
     if (opendir(O, $D1)) {
        my @dirs2  = readdir(O); close(O);
        foreach my $f (sort @dirs2) {
           next if (substr($f,0,1)  eq ".");
           my $age = Util->getFileAge("$D1/$f",  "d");
           unlink "$D1/$f" if ($age > $days);
        }
     }
  }
}


#  $moveToHistory("topo/file1", "topohist", "file2", "2001-01-01", 10);

sub copyToHistory {
  my($class, $file, $dir, $histfile, $date,  $max) = @_;
  return $class->_toHistory($file, $dir, $histfile, $date, $max, 1);
}

sub moveToHistory {
  my($class, $file, $dir, $histfile, $date, $max) = @_;
  return $class->_toHistory($file, $dir, $histfile, $date, $max, 0);
}

sub _toHistory {
  my($class, $file, $dir, $histfile, $date,  $max, $copy) = @_;
  my($l);
  $ERR = undef;
  $max = 10 if (!$max);
  my $D = System->get_home() . "/DATA";

  my $F1 = "$D/$file";
  $date =~ s/ /_/g;

  my $ix = rindex($file, "/");
  my $F20 = $histfile;
  my $F2 = "$D/$dir/" . $F20 . "." . $date;

  opendir(O, "$D/$dir");
  my @h = readdir(O);
  closedir(O);
  my $cnt;
  my @SAVE;
  foreach my $file (sort @h) {
    if (substr($file ,0, length($F20)) eq $F20) {
       push(@SAVE, $file);
    }
  }
  if ($max < $#SAVE +1) {
     my $x;
     for ($x=0; $x <= $#SAVE - $max; $x++) {
       unlink "$D/$dir/$SAVE[$x]";
     }
  }
  if (open(R, $F1)) {
    if (open(W, ">$F2")) {
       while ($l = <R>) {
         print W $l;
       } 
       close(R);  close(W);
       unlink $F1 if (!$copy);
       return 1;
    } else {
      $ERR = "Cannot write $F2: $!";
      return undef;
    }

  } else {
    $ERR = "Cannot read $F1: $!";
    return undef;
  }
}


sub escapeValue {
  my($class, $data) = @_;

  $data =~ s/&/&amp;/sg;
  $data =~ s/</&lt;/sg;
  $data =~ s/>/&gt;/sg;
  $data =~ s/"/&quot;/sg;

  return($data);
}

# $hours = getFileAge(file, "h");

sub getFileAge {
  my($class, $file, $format ) = @_;
  my($old_time , $age, $age_mins, $age_secs, $mins);

  my(@stats) = stat($file);
  $old_time = $stats[9];
  $age = time - $old_time; # seconds

  if (!$format || ($format eq "m") ) {
    return int($age/60);

  } elsif ($format eq "s") {
    return $age;

  } elsif ($format eq "d") {
    return int($age/60/60/24);

  } elsif ($format eq "h") {
    return int($age/60/60);

  } elsif ($format eq "ms") {
    return (int($age / 60) , $age % 60);

  } elsif ($format eq "hm") {
    $mins = int($age / 60);
    return (int($mins / 60), $mins % 60);

  } elsif ($format eq "hms") {
    $mins = int($age / 60);
    return (int($mins / 60), $mins % 60, $age % 60);
  }
}


sub last_message {
  my($class, $D) = @_;
  my $l;
  return undef if (!-r $D);

  if (open(O, $D)) {
    seek(O, -4000, 2);
    my $ltime;
    while ($l = <O>) {
       if ($l =~ /^(\d\d\/\d\d\/\d\d \d\d:\d\d:\d\d)/) { # 06/14/01 13:43:28
         $ltime = $1;
       }
    }
    close(O);
    return undef if (!$ltime);
    my $today = Util->get_today(); # YYYY-MM-DD HH:MM:SS
    if (substr($ltime,0,2) eq substr($today,5,2) &&
        substr($ltime,3,2) eq substr($today,8,2) ) {  # same day
        my $st_min = substr($ltime,9,2) * 60 + substr($ltime,12,2);
        my $min    = substr($today,11,2) * 60 + substr($today,14,2);
        return ($min - $st_min, $ltime);
    }
  }
  return undef;
}




sub shortHostname {
  my($class, $host) = @_;

  if ($host !~ /^\d+\.\d+/) {
     my $ii = index($host, ".");
     $host = substr($host, 0, $ii) if ($ii > 0);
  }
  return $host;
}

sub getInfo {
  my($class, $file, $section) = @_;

  if (open(O, System->get_home() . "/System/$file")) {
    my @L = <O>; close(O);
    my($in, $out, $l);

    foreach $l (@L) {
      if ($l =~ /\[$section\]/) {
         $in = 1;
      } elsif ($in) {
         last if ($l =~ /^\[/);
         $out .= $l if ($l ne "\n");
      }
    }
    if (!$out) {
      my $ix = index($section, ":");
      if ($ix > 0) {
        my $sec = substr($section, 0, $ix);
        foreach $l (@L) {
          if ($l =~ /\[$sec\]/) {
             $in = 1;
          } elsif ($in) {
             last if ($l =~ /^\[/);
             $out .= $l if ($l ne "\n");
          }
        }
      }
    }
    return $out;
  } else {
    return "$file:$section not found";
  }
}

#
# get all WWNs from 'Found Fibre Channel device(s):
# from a remote host
# comma separated list
#
sub getFCWWNs { 
  my($class, $host) = @_;
  if ($host eq System->hostname()) {
     return get_lux_wwn();
  } else {
     my $data = Util::Http->getCommand($host, "Util::lux_wwn&HTTP=1",  110);
     return $data;
  }
}

sub get_lux_wwn {
  my($q) = @_;
  my($in, $list);
  my($err, $o) = Util->run_command("/usr/sbin/luxadm probe","text",100);
  foreach my $l (@$o) {
    if ($l =~ /Found Fibre Channel/) {
       $in = 1;
    } elsif ($in) {
       if ($l =~ /Node WWN:(\w+)/) {
         $list .= "$1,";
       }
    }
  }
  if ($q->{HTTP}) {
     print $list;
  } else {
     return $list;
  }
}


sub removePort {
  my($class, $ip) = @_;

  my($x) = index($ip, ":");
  if ($x > 0) {
    return substr($ip, 0, $x);
  } else {
    return $ip;
  }
}

#
#  return 1 if the device is yours to monitor

sub isMineToMonitor {
  my($class, $dev) = @_;

  my($renv)  = System->get_renv();
  my $h1     = $dev->{host} || $renv->{hostname};

  return ($h1 eq $renv->{hostname});
}

#
# readFile in htdocs 
sub readInfo {
  my($class, $f) = @_;
  my(@l);
  if (open(O,System->get_home() . "/htdocs/$f.html") ) {
    @l = <O>; close(O);
    return "@l";
  } else {
    return "Error: $f: $!";
  }
}


#########################################################################
#    RUN COMMAND
#    timeout:30 secs by default
#    output: (Err, text)
#    return : how many processes cound not be killed
#########################################################################


sub killCommand {
  my($class, $command, $timeo) = @_;

  my(@procs) = `/usr/bin/ps -ef`;   # kill hanging processes.
  my(@kills, $pid) ;

  foreach my $proc (sort @procs) {
     my(@b) = split(/\s+/, Util->ltrim($proc));
     if ($proc =~ /$command/ && $b[2] eq "$$") { # found and we are parent
         $pid = $b[1];
         last;
     }
  }
  if ($pid) {
     push(@kills, $pid);
     my($list) = ",$pid,"; # keep track of children

     foreach my $proc (sort @procs) {
       my(@b) = split(/\s+/, Util->ltrim($proc));
       if (index($list, ",$b[2],") >= 0) { 
          if (index($list, ",$b[1],") < 0) {
            $list .= "$b[1],";
            push(@kills, $b[1]) ;
          }
        }
     }
  }

  if ($#kills >= 0) {
     Debug->print2("Killing $command processes '@kills' after $timeo secs time-out ");
     kill 9, @kills;
     sleep(20);
     my(@procs) = `/usr/bin/ps -ef`;   # kill hanging processes.
     my $still= 0;
     foreach my $proc (sort @procs) {
       my(@b) = split(/\s+/, Util->ltrim($proc));
       if ($proc =~ /$command/ && $b[2] eq "$$") { # found and we are parent
           $still++;
       }
     }
     return $still;
  }
  return 0;
}



sub run_bg {
  my($util, $command, $to) = @_;
  my($pid, $command0);
  my($to2) = $to;

  my($sp)  = index($command," ");
  $command0 = ($sp > 0)? substr($command,0,$sp) : $command;
  
  if (($pid = fork()) == 0) { # child
     unlink "/tmp/run_bg_$$";
     exec "$command > /tmp/run_gb_$$ 2>&1";
     exit 0;
  } elsif ($pid > 0) {
     while ($to > 0) {
         sleep 2;
         last if (!-f "/proc/$pid/psinfo");
         $to -= 2;
     }
     if ($to == 0) {
        Util->killCommand($command0, $to2);
     }
  }
}


#
#   run_command("luxadm probe -p", "probe", 30, { cache => 0}); # cache is on by default
#  { cache => 0} same as 
#  { data  => 1}

sub run_command {
  my($util, $command, $file, $timeo, $arg) = @_;
  my(@out, $command0, $stderr);
  my $org_command = $command;
  my $cache = 1;
  $cache = $arg->{cache} if ($arg && defined($arg->{cache}));
  $STILL_THERE = 0;
  my $data = $arg->{data};
  $cache = 0 if (exists($arg->{data}));

  if ($cache) {
    my $run = $RUN_CACHE{$org_command};
    if ($run) {
      return ($run->[0], $run->[1], $run->[2]);
    }
  }
  $timeo = 40 if (!$timeo);
  my($f, $l);
  my($sp)  = index($command," ");
  $command0 = ($sp > 0)? substr($command,0,$sp) : $command;

  if (System->get_testMode) {
    $f = System->get_home() . "/Test/$file";
  } else {
    return ("Cannot run $command", []) if (!-x $command0);
    if ($command !~ /2\>&/) {
       $command .= " 2>/tmp/run_command.$$";
    }
    if ($data) {
      $f = "|$command";
    } else {
      $f = "$command|";
    }
  }
  Debug->print3("run_command($timeo): $f");
  Debug->snapshot("=============================\nRUN_COMMAND: $f, timeout=$timeo");
  my($err);
  eval {
    local $SIG{ALRM} = sub { die "alarm\n" };
    alarm $timeo;
    if (open(RUN, $f)) {
       if ($data) {
         print RUN $data;
         close(RUN);

       } else {
         while ($l = <RUN>) {
           chop($l);
           push(@out, $l);
         }
         close(RUN);
       }
    } else {
       $err = "Cannot open $f: $!";
    }
  };
  alarm 0;
  if (open(O1, "/tmp/run_command.$$")) {
    while ($l = <O1>) {
      $stderr .= $l;
    }
    close(O1);
    unlink "/tmp/run_command.$$";
  }
  if ($@) {
    if ($@ eq "alarm\n") {
       $STILL_THERE = Util->killCommand($command0, $timeo);
       $RUN_CACHE{$org_command} = ["Timeout", []];
       return ("Timeout",[]);
    } else {
       $RUN_CACHE{$org_command} = [$@, []];
       return ($@,       []);
    }
  } elsif ($err)  {
    $RUN_CACHE{$org_command} = [$err, []];
    return ($err,        []);
  } else {
    Debug->snapshot1(join("\n", @out));
    if ($cache) {
      $RUN_CACHE{$org_command} = [undef, \@out, $stderr];
    }
    return (undef, \@out, $stderr);
  }
}

sub findProcesses {
  my($class, $pid) = @_;

  my(@procs) = `/usr/bin/ps -ef`;   # kill hanging processes.

  my $list = ",$pid,";
  my $parent;
  my (@P);
  my $again = 0;

  while ($again < 2) {
    foreach my $proc (@procs) {
       my(@b) = split(/\s+/, $proc);
       if ($b[2] == $pid) {
         $parent = $b[2];

       } elsif (index($list, ",$b[3],") >= 0) {
         if (index($list, ",$b[2],") < 0) {
           push(@P, $b[2]);
           $list .= "$b[2],";
         }
       }
    }
    $again++;
  }

  return ($parent, \@P);
}



sub file2object {
  my($class, $file) = @_;

  open(O, $file);
  my(@l) = <O>; close(O);
  my($obj) = eval substr("@l", 8);
  return $obj;
}


sub makeUrl {
  my($class, $ip, $url) = @_;
  $url =~ s/ /+/g;

  my $proc = (substr($url,0,3) eq "?GO") ? $Http::WEBPROC  : "/";
  if ($ip =~ /:/) {
     return "http://$ip$proc$url";
  } else {
     my $p = System->get_rasport();
     if (!$p) {
        Debug->err(TEXT => "rasport not defined");
        $p = "7654";
     }
     return "http://$ip:$p$proc$url";
  }
}



sub nnl {
  my($class, $s) = @_;
  $s =~ s/\n/ || /g; 
  return $s;
}

sub new {
  my($class , $f) = @_;
  
  $f->{today} = &today;
  bless($f, $class);
  return $f;
}

sub MTH {
  my($this, $m) = @_;
  return $MTH{$m} || $m;
}

sub MTH2NAME {
  my($this, $m) = @_;
  return $MTH2{$m+0} || $m;
}

sub include {
  my($this, $opts, $val) = @_;

  if (index($opts, $val) >= 0) {
    return 1;
  } else {
    return 0;
  }
}

sub today {
  my($class, $format) = @_;
  my($this, @now, $today);

  @now = localtime; $now[4]++; $now[5] += 1900;
  if ($format eq "YMDH") {
    $today = sprintf("%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
                   $now[5], $now[4], $now[3], $now[2], $now[1], $now[0]);
  } else {
    $today = sprintf("%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
                   $now[4], $now[3], $now[2], $now[1], $now[0]);
  }
  $today;
}


sub read_and_truncate {
 my($util, $file) = @_;
 my($l, $out);

  if (open(O2,$file)) {
     while ($l = <O2>) {
        $out .= $l;
     }
     close(O2);
     unlink($file);
  }
  $out;
}

sub get_sum {
 my($util) = @_;
 my($s1, $s2, $l);
 chdir System->get_home();
 open(O,"/usr/bin/sum bin/http lib/*pm lib/Agent/*|");

 while ($l = <O>) {
   chop($l);
   my @a = split(/\s+/, $l);
   $s1 += $a[0];
   $s2 += $a[1];
 }
 close(O);
 return "$s1-$s2\n";
}

sub testIp {
  my($util, $name, $timeout) = @_;
  my($i, $ix);

  if (($ix = index($name, ":")) > 0) {
     $name = substr($name, 0, $ix);
  }
  $timeout = 6 if (!$timeout);
  my $ping = "/usr/sbin/ping";
  $i = gethostbyname($name);
  if ($i) {
    open(O1, "$ping $name $timeout|");
    my($ans) = <O1>; close(O1);
    if ($ans =~ /is alive/) {
      return inet_ntoa($i);
    } else {
      return undef;
    }
  } else {
    return undef;
  }
}

sub ping {
  my($class, $ip, $to) = @_;
  my($ix);
  if (($ix = index($ip, ":")) > 0) {
     $ip = substr($ip, 0, $ix);
  }
  $to = 10 if (!$to);
  my $ping = `/usr/sbin/ping $ip $to`;
  if ($ping =~ /is alive/) {
     return 1;
  }
  return undef;
}



#  name2ip() : returns ipno of name or localipno if there is no name

sub name2ip {
 my($util, $name, $timeout) = @_;
 my($out, $p, $i);

 if ($name) {
   my $ix = index($name, ":");
   $name = substr($name, 0, $ix) if ($ix > 0);

   return $name if ($name =~ /^\d+\.\d+\.\d+\./) ;
 } else {
   $name = System->hostname();
 }
 $i = gethostbyname($name);
 if ($i) {
   return inet_ntoa($i);
 } else {
   return undef;
 }

}

sub get_date {
  my($this, $time) = @_;

  my(@date) = localtime($time);
  $date[4]++; $date[5] += 1900;

  sprintf("%2.2d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
    $date[5], $date[4],$date[3], $date[2], $date[1], $date[0]);
}

# YYYY-MM-DD HH:MM:SS
sub get_today {
  my($this) = @_;

  my(@date) = localtime(time);
  $date[4]++; $date[5] += 1900;

  sprintf("%2.2d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
    $date[5], $date[4],$date[3], $date[2], $date[1], $date[0]);
}

sub get_file_created {
  my($this, $file) = @_;
  return undef if (!-f $file);
  my(@stats) = stat($file);
  my(@date) = localtime($stats[9]);

  $date[4]++; $date[5]+= 1900;        
  sprintf("%2.2d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
    $date[5], $date[4],$date[3], $date[2], $date[1], $date[0]);
}


  
#    'C' code :
#
#    if ($isdst > 0) { 
#      sprintf($currZone, "%d|", altzone);
#      sprintf(tmpStr, "%s", tzname[1]);
#    } else {
#      sprintf(currZone, "%d|", timezone);
#      sprintf(tmpStr, "%s", tzname[0]);
#    }
#    if (strlen(currZone) + strlen(tmpStr) > 30)
#      sprintf(currZone, "0|GMT");
#    else {
#      ctr = 0;
#      while (tmpStr[ctr] != NULL) {
#        if (tmpStr[ctr] == '&')
#          strcat(currZone, "&amp;");
#        else if (tmpStr[ctr] == '<')
#          strcat(currZone, "&lt;");
#        else if (tmpStr[ctr] == '>')
#          strcat(currZone, "&gt;");
#        else if (tmpStr[ctr] == '"')
#          strcat(currZone, "&quot;");
#        else if (tmpStr[ctr] == '\'')
#          strcat(currZone, "&#039;");
#        else
#          strncat(currZone, &tmpStr[ctr], 1);
#        ctr = ctr + 1;
#     }
#   }



sub ltrim {
  my($this, $s) = @_;

  $s =~ /^(\s*)/;
  if ($1) {
    substr($s,length($1));
  } else {
    $s;
  }
}

sub rtrim {
  my($this, $s) = @_;

  $s =~ /(\s*)$/;
  if ($1) {
    substr($s,0,0 - length($1));
  } else {
    $s;
  }
}

sub trim {
  my($this, $s) = @_;

  return ($this->rtrim($this->ltrim($s)));
}


sub rsubstr {
  my($util, $l, $off, $len) = @_;
  my($a, $b, $la);

  $a  = substr($l, $off, $len);
  $b  = substr($l, $off);
  $la = length($a);

  if ($off >= length($l) ) {
    return "";
  } elsif (length($b) < $la) {
    return $b;
  } elsif ($la > 0) {
    return rtrim($a);
  } else {
     return "";
  }
}

##############################################
#          LAST RUN QUERY
##############################################

# returns yesterday if it's been one day since last_run, else return '';


sub clear_dir {
 my($util, $dir) = @_;

 opendir(O, $dir);
 my(@a) = readdir(O); closedir(O);
 my($cnt) = 0;
 foreach my $file (@a) {
    next if (substr($file,0,1) eq ".");
    unlink "$dir/$file";
    $cnt++;
 }
 return $cnt;
}

#
# returns TRUE every $freq minutes, FALSE on the first time.
# use negative freq to set the timer.
#
$Xmin_value = 0;

sub set_Xmins {
 my($util, $file, $freq) = @_;
 my($now) = time;
 $util->string2file($now, $file);
}

sub is_Xmins {
 my($util, $file, $freq) = @_;

 my($now) = time;

 my($lastrun);
 $lastrun  = $util->file2string($file, {silent=>1});
 $Xmin_value = 0;
 if (!$lastrun) {   # if new, save today to start
    $util->string2file($now, $file);
    return undef;

 } else {
   $Xmin_value = ($now - $lastrun) / 60;
#   $freq = $util->{renv}{frequency};
   if ( ($now - $lastrun) - ($freq * 60) >= -5 ) {
      $util->string2file($now, $file);
      return ($now - $lastrun);
   }
 }
 return undef;
}


sub fileSize {
  my($util, $file)  = @_;
  return (stat($file))[7];
}
 

$COPY_ERR = "";
# options: {bigger => 1}  # requires that old be bigger than new
#                         # to copy
# options: {bigger => 50} # within 50 bytes
sub copy {
  my($util, $old, $new, $options) = @_;
  my($l);
  my $bigger = $options->{bigger};

  if ($bigger) {
     my($osize) = (stat($old))[7];
     my($nsize) = (stat($new))[7];
     if ($bigger == 1) {
       if ($osize < $nsize) {
          $COPY_ERR = "Error: '$old:$osize' is smaller that '$new:$nsize'";
          return undef;
       }
     } else {
       if ($osize < $nsize && ($nsize - $osize > $bigger) ) {
          $COPY_ERR = "Error: '$old:$osize' is smaller that '$new:$nsize'";
          return undef;
       }
     }
  }
  if (open(O,$old) && open(W, ">$new")) {
    while ($l = <O>) {
      print W $l;
    }
    close(O); close(W);
    return 1;
  } else {
    $COPY_ERR = "Error copying $old -> $new: $!";
    return undef;
  }
}

sub as_array {
  my($agent, $s) = @_;

  my(@a) = split(/\n/, $s);
  return \@a;
}

sub readHtdocs {
  my($class, $f)  = @_;
  return Html->readHtdocs($f);
}


sub readf {
  my($agent, $f, $off) = @_;
  my($o, $l);
  if (open(O, $f)) {
     while ($l = <O>) {
       $o .= $off . $l;
     }
     return $o;
  } else {
     return undef;
  }
}

sub writef {
  my($agent, $f, $data) = @_;
  
  if (open(O,">$f")) {
    print O $data;
    close(O);
    return undef;
  } else {
    return $!;
  }
}

$MASTER = undef;

sub clearMaster {
  my($class) = @_;
  $MASTER = undef;
  unlink System->get_home() . "/DATA/MASTER";
}

sub setMaster {
  my($class, $master) = @_;
  open(O, ">" . System->get_home() . "/DATA/MASTER");
  print O $master;
  $MASTER = $master;
  close(O);
}

sub findMaster {
  my($class) = @_;
  if (defined($MASTER) ) {
     return $MASTER;
  }
  $MASTER = $class->read_system_field("MASTER") || 0;
  return $MASTER;
}

# value=\"ras:agent\@$h->{hostname}:$port$Http::WEBPROC?GO=$from\"

sub select_hosts {
  my($class, $from) = @_;
  my($renv, $devs, $hosts,$notifs) = PDM::ConfigFile->read();
  my($master) = Util->findMaster;
  my($port) = System->get_rasport();
  my($out) ;
  if ($master)  { # you are a slave
    return "<b>[ <a href=http://$master:$port$Http::WEBPROC?GO=$from>Back to Master</a> ]";
  } else {
    return "" if ($#$hosts < 0) ;
    $out .= "<table border=1 cellspacing=0 cellpadding=0><tr><td><b><input type=hidden name=GO value=Util::select_hosts1>
    <select name=hosts><option value=\"[GO=$from]\">[Select Slave Host]";

    foreach my $h (@$hosts) {
       my $hno = $h->{ipno} || $h->{hostname};
       $out .= "<option value=\"$hno:$port$Http::WEBPROC?GO=$from\">$h->{hostname}</option>";
    }
    $out .= "</select><b><input type=submit name=ACTION value=GO></table>";
    return $out;
  }
 
}

sub redirect {
  my($class,$url, $time) = @_;
  $time = 0 if (!$time);
  return "<meta http-equiv=Refresh content=\"$time;URL=$Http::WEBPROC?GO=$url\">";
}

sub select_hosts1 {
  my($q) = @_;
  if (substr($q->{hosts},0,1) ne "[") {
     print "<meta http-equiv=Refresh content=\"0;URL=http://$q->{hosts}\">";
  } else {
     my($ix) = index($q->{hosts}, "GO=");
     my($sub) = substr($q->{hosts}, $ix+3, -1);
     if (defined(&$sub)) {
        &$sub;
     }
  }
}
  


sub read_system_field {
  my($class, $file) = @_;
  my($l);
  if (open(O,System->get_home() . "/DATA/$file")) {
    $l = <O>;
    close(O);
    chop($l) if (substr($l,-1) eq "\n");
    return $l;
  } else {
    return undef;
  }
}

sub file2string {
  my($util, $file, $arg) = @_;
  my($l);
  if (open(O, System->get_home() . "/DATA/$file")) {
     $l = <O>;
     close(O);
     return $l;
  }
  return undef;
}

sub string2file {
   my($util, $s, $file) = @_;

   if (open(O,">" . System->get_home() . "/DATA/$file")) {
      print O $s;
      close O;
      return 1;
   } else {
      return undef;
   }
}

#########################################################################
#    HTTP GET
#########################################################################

sub encodeAll {
  my($class, $s) = @_;

  $s =~ s/(.)/sprintf("%%%2.2x",ord($1))/eg;
  $s;
}

sub encode {
  my($class, $s) = @_;

  $s =~ s/(\W)/sprintf("%%%2.2x",ord($1))/eg;
  $s;
}

sub decode {
  my($class, $s) = @_;
  $s =~ s/\+/ /g;
  $s =~ s/%([0-9|A-Fa-f]{2})/pack('C',hex($1))/eg;
  $s;
}



%LOG_EVENT = ();


sub pprint {
  my($class, @args) = @_;
  print indentXML($class, @args);
}

#
#  pprint $string, $string2...;
sub indentXML {
    my($class, @args) = @_;
    my($o);
    foreach (@args) {
        s/></>\n</g;

        my $depth = -1;
        foreach $_ (split /\n/) {
            my ($up, $down) = (0, 0);

            if (/<.*>.*<\/.*>/) {  # both start and end tag
                $up = $down = 1;
            }
            if (/<\?xml /) {       # special tags with no indent
                $up = $down = 1;
            }
            elsif (/<\/.*/) {      # single end tag
                $down = 1;
            }
            elsif (/.*\/>/) {      # start and end tag as one, eg. '<VALUE/>'
                $up = $down = 1;
            }
            elsif (/<.*/) {        # single start tag
                $up = 1;
            }
            else {                 # simple text
                $up = $down = 1;
            }
            $depth++ if $up;
            $o .= "  " x ($depth) . $_ . "\n";
            $depth-- if $down;
        }
    }
    return $o;
}

sub wOpen {
  my($util) = @_;

  print <<EOF;
<script>
  var w=window.open("","Log","scrollbars,resizable,width=300,height=200");
  w.moveTo(10,10);
  w.focus();
  var d=w.document;
  d.clear();
  d.write('<head><title>Agent Log</title></head><body bgcolor=white><p>');
</script>
EOF
}

sub wPrint {
  my($util, $x) = @_;
  print "<script>d.write('$x');</script>";
}
sub wClose {
  my($util, $x) = @_;
  print "<script>d.write('<center><form><input type=submit value=Close onclick=window.close()></form>');</script>";
}


# arg = {select => 0}
# default is no select option

sub makeSelect {
  my($class, $name, $l, $val, $arg) = @_;
  return Html->makeSelect($name, $l, $val, $arg);
}


####################################
#
#          SNMP STUFF
#
####################################


sub snmpType {
  my($class, $ip, $host) = @_;
  my $rc;
  if ($host) {
     my $url = Util->makeUrl($host, "?GO=Util::snmpWWN_&ip=$ip&HTTP=1&type=type");
     $rc = Util::Http->get($url, 20);
  } else {
     $rc = &snmpWWN_({ip => $ip, type => 'type'});
  }
  return $rc;
}



# host is optional, works remotely

sub snmpWWN {
  my($class, $ip, $host) = @_;

  if ($host) {
     my $url = Util->makeUrl($host, "?GO=Util::snmpWWN_&ip=$ip&HTTP=1");
     my $rc = Util::Http->get($url, 10);
     return $rc;
  } else {
     return &snmpWWN_({ip => $ip});
  }
}

sub snmpWWN_ {
  my($q) = @_;

  my $ip = $q->{ip};

  $ENV{LD_LIBRARY_PATH} = System->get_home() . "/snmp/lib";

  my($com) = System->get_home() . "/snmp/bin/snmpget";

  if ($q->{type} eq "type") {
     my($rc) ;
     open(O, "$com -t 20 -r 0 $ip public 1.1.0 2>/dev/null|");
     my $l = <O>;
     close(O);
     $l = lc($l);
     if ($l =~ /t3/) {
        $rc = "storage.t3";

     } elsif ($l =~ /brocade/) {
        $rc = "switch.brocade";

     } elsif ($l =~ /switch/) {
        $rc = "switch.switch";
     } 
     if ($q->{HTTP}) {
       print $rc;
     } else {
       return $rc;
     }
  }
  my($M_WWN1) = ".iso.3.6.1.4.1.42.2.28.2.2.5.2.1.10.0.0";
  my($M_WWN2) = ".iso.3.6.1.4.1.42.2.28.2.2.5.2.1.10.1.0";

   if ($q->{type} eq "t3keyXXX") {
     my $key;
     my $s1 = ".iso.3.6.1.4.1.42.2.28.2.2.3.2.1.6.0.9";
     my $s2 = ".iso.3.6.1.4.1.42.2.28.2.2.3.2.1.7.1.14";
     my $s3 = ".iso.3.6.1.4.1.42.2.28.2.2.3.2.1.9.0.14";
     foreach my $s ($s1, $s2, $s3) {
       open(O, "$com -t 20 -r 0 $ip public $s 2>/dev/null|"); 
       my $l = <O>; close(O);
       chop($l);
       my(@a) = split(/\s*=[\s\"]*/, $l);
       $l = $a[1];
       last if (!$l);
       my $xx = index($l, "\" hex: ");
       $l = substr($l,0,$xx) if ($xx > 0);
       chop($l) if (substr($l,-1) eq "\"");
       $l = Util->rtrim(lc($l));
       $key .= $l . ".";
     }
     my($wwn1, $wwn2);
     if ($key) {
       chop($key) if ($key);
       $wwn1 = &swwn($com, $ip, $M_WWN1);
       $wwn2 = &swwn($com, $ip, $M_WWN2);
       $wwn1 = substr($wwn1,1,-1) if (substr($wwn1,0,1) eq "\"");
       $wwn2 = substr($wwn2,1,-1) if (substr($wwn2,0,1) eq "\"");
     }

     if ($q->{HTTP}) {
       print "$key|$wwn1|$wwn2";
     } else {
       return "$key|$wwn1|$wwn2";
     }
  }

  my ($mib, $rc);
  if ($q->{type} eq "t3wwn2") {
     $rc = &swwn($com, $ip, $M_WWN2);

  } elsif ($q->{type} eq "t3") {
     $rc = &swwn($com, $ip, $M_WWN1);

  } else {
     $rc = &swwn($com, $ip, ".iso.3.6.1.3.42.2.1.1.2.0"); # "2.2.1.6.1");
  }
  if ($q->{HTTP}) {
     print $rc;
  } else {
    return $rc;
  }
}


sub swwn {
  my($com, $ip, $mib) = @_;

  open(O, "$com -t 20 -r 0 $ip public $mib 2>/dev/null|");
  my $l = <O>;
  close(O);
  my(@a) = split(/=/, $l);
  $l = $a[1];
  $l = lc($l);
  $l =~ s/hex: //;
  $l = Util->trim($l);
  return "" if ($l eq "\"\"");
  @a = split(/[\: ]/, $l);
  my($out) = "";
  foreach my $x (@a) {
     my $x0 = lc($x);
     if (length($x0) == 1) {
          $x0 = "0$x0";
     }
     $out .= $x0;
  }
  return $out;
}




#
# $a = Util->findMethods("Agent","Agent", "isSelectable");
# $a->[0] = A5K=1
# $a->[1] = A35K=1

sub findMethods {
  my($class, $dir, $pat, $method) = @_;
  my(@a, @V, $l);

  opendir(O, System->get_home() . "/lib/$dir");
  @a = readdir(O); closedir(O);

  foreach my $f (@a) {
     next if (substr($f,0,1) eq ".");
     next if (substr($f,-3) ne ".pm");
     next if ($pat && $f !~ /$pat/) ;
     open(O, System->get_home() . "/lib/$dir/$f");
     my($cnt) = 0;
     while (defined($l = <O>) && ($cnt++ < 100)) {
          if ($l =~ /^sub $method (.*)/) {
             my($val) = eval $1;
             push(@V, substr($f,0,-3) . "=$val");
             last;
          }
     }
     close(O);
  }
  return \@V;
}

  
#  $XREF = Util->getXref();

sub getXref {
  my($class) = @_;

  my($f) = System->get_home() . "/DATA/CACHE/device_access";
  my($XREF);

  if (open(O, $f)) {
    my(@a) = <O>; close(O);
    eval "\$XREF " . substr("@a",5);
  } else {
    Debug->print2("Error: $!");
  }
  return $XREF;
}

  
sub get_remoteDataPath {
  my($q) = @_;
  my($lux)  = System->find_luxadm;
  my($wwn) = $q->{wwn};

  my($TO) = 30;

  my($err,$com) = Util->run_command("$lux display $wwn 2>&1", "luxadm.txt" , $TO);
  if ($err) {
     print "ERR";
  } elsif ($#$com > 5) {
     print "OK";
  } else {
     print "BAD";
  }
  print "\n";
}

sub get_checkRasCrons {
  my($q) = @_;
  print Util->checkRasCrons();
}

# # of seconds in mtime and how long the process was running
#  ($atime, $secs) = Util->processTime(412, ['ccadieux']);
#
sub processTime {
  my($class, $pid, $host) = @_;
  $pid =~ s/\n//g;
  my($renv)     = System->get_renv();
  $host = $renv->{hostname} if (!$host);

  if ($renv->{hostname} eq $host) {
     my $age = (stat("/proc/$pid/psinfo"))[9];
     return ($age, time - $age);
  } else {
     my $data = Util::Http->getPID($host,$pid, 10);
     if ($data =~ /atime=(\d+), secs=(\d+)/) {
        return ($1, $2);
     } else {
        return undef;
     }
  }
}

  


sub checkRasCrons {
  my($q) = @_;
  my($out, @l);
  my($ps)   =  System->find_command("ps");
  my($grep) =  System->find_command("grep");
  my($renv) = System->get_renv();
  my($acronym) = $renv->{GSV_ACRONYM};
  my($package) = $renv->{GSV_PACKAGE};

  open(O, "$grep rasagent /var/spool/cron/crontabs/root|");
  @l = <O>; close(O);
  my $cron = "@l";

  open(O, "$ps -ef | $grep \"/cron\$\" | $grep  -v grep|");
  @l = <O>; close(O);
  $ps = "@l";

  if ($cron  && $cron !~ /^\s*\#/) {
     $out .= "<b><font color=green>- OK: The $acronym cron exist.</font></b>";
  } else {
     $out .= "<b><font color=red>- ERROR: $acronym cron is missing:</font></b><br>
      Verify the installation, possibly run 'ras_install' again.
      A cron entry for the $package Agent should be present for $acronym to run properly.";
  }
  $out .= "<p>";
  my($testMode) = "<h2>*** Test Mode On ***</h2>" if (System->get_testMode());
  if ($ps) {
     $out .= "<b><font color=green>- OK: The cron daemon is running.</font></b>";
  } else {
     $out .= "<b><font color=red>- ERROR: The cron daemon is NOT running:</font></b>
      <br> The cron daemon should always be running, consult your system administrator.";
  }
  return $out;
}

#
#  desc : content of email
#  name : attachment name
#  content: attachment content
#

sub email_html {
  my($class, $from, $subject, $to, $desc, @contents) = @_;
  my($x, $out);

  $out =<<EOF;
From: $from
Subject: $subject
To: $to
MIME-Version: 1.0
Content-Type: MULTIPART/mixed; BOUNDARY=Bale_of_Turtles_123_000

--Bale_of_Turtles_123_000
Content-Type: TEXT/plain; charset=us-ascii

$desc

EOF

  for ($x=0; $x <= $#contents; $x+=3) {
    my $name    = $contents[$x];
    my $content = $contents[$x+1];
    my $mime    = $contents[$x+2] || 'plain';

    $out .=<<EOF;
--Bale_of_Turtles_123_000
Content-Type: TEXT/$mime; name="$name"; charset=us-ascii; x-unix-mode=0600
Content-Description: $name

$content

EOF
  }
  $out .= "--Bale_of_Turtles_123_000--\n";

  return $out;
}


1;
