# $Id: JVM.pm,v 1.17 2005/03/24 00:20:00 jkremer Exp $
# Copyright 2004, Sun Microsystems, Inc. All rights reserved.
package Java::JVM;

use LWP::UserAgent;
use System;
use Debug;
use strict;

sub new {
  my($class, $args) = @_;

  my $jenv = {
				  "java.jrex.port"          => 7766,
				  "java.jrex.idle_timeout"  => 360,
				  "java.jrex.start_timeout" => 60,
				  "java.jrex.debug"         => 0,
				  "java.jrex.user"          => "peer",
				  "java.jrex.pass"          => "peer",
				  "java.jrex.runId"         => "noaccess",
				  "java.jrex.class"  =>
				  "com.sun.netstorage.fm.storade.agent.jmx.JMXServer"
				 };

  my ($renv, $devs, $hosts, $notifs) = PDM::ConfigFile->read();
  if ($renv->{solution} && $renv->{solution} ne "N"){
	 $jenv->{'java.jrex.runId'} = "admin";
  }
  for my $prop ( keys %{$jenv} ) {
	 if ( $renv->{$prop} ){
		$jenv->{$prop} = $renv->{$prop};
	 }
  }
  for my $arg ( keys %$args ){
	 $jenv->{$arg} = $args->{$arg};
  }
  if (!$jenv->{'java.home'}){
	 my $JVM_FOUND = $class->searchJVM();
	 $jenv->{'java.home'} = $JVM_FOUND;
	 $renv->{'java.home'} = $JVM_FOUND;
  }

  eval {
	 my $ipcFile = System->get_home() . "/DATA/IPC_Access";
	 if ( -f "$ipcFile"){
		if (open(O, $ipcFile)){
		  my $line = <O>;
		  chop($line);
		  my $index = rindex($line, ":");
		  my $user  = substr($line, 0, $index);
		  my $pass  = substr($line, $index+1);
		  $jenv->{"java.jrex.user"} = $user;
		  $jenv->{"java.jrex.pass"} = $pass;
		  close(O);
		}
	 }
  };

  bless $jenv, "Java::JVM";
  return $jenv;
}

sub searchJVM {
  my ($class) = @_;
  my $home = System->get_home();

  my $JVM_CHECK_CLASS = "com.sun.netstorage.fm.storade.agent.JVMCheck";
  my $JVM_CHECK_CP    = "$home/lib/storade_device.jar";
  my $JAVA_LIST = ["/usr/j2se/bin/java",
						 "/bin/java",
						 "/usr/java/bin/java",
						 "/usr/java/jre/bin/java",
						 "/opt/SMICC_Java/usr/bin/java",
						 "/opt/se6130/java/usr/bin/java"];

  my $JVM_FOUND = undef;
  foreach my $JAVA_PATH (@$JAVA_LIST){
	 if (-x $JAVA_PATH){
		my $JVM_CHECK = `$JAVA_PATH -classpath $JVM_CHECK_CP $JVM_CHECK_CLASS`;
		if ("OK\n" eq $JVM_CHECK){
		  $JVM_FOUND = $JAVA_PATH;
		  last;
		}
	 }
  }
  return $JVM_FOUND;
}

sub getCredential {
  my($this) = @_;
  return ($this->{"java.jrex.user"}, $this->{"java.jrex.pass"});
}

sub getPort {
  my($this) = @_;
  return $this->{"java.jrex.port"};
}

sub getJavaPath {
  my($this) = @_;
  my $path = $this->{"java.path"};
  if (-x $path){
	 return $path;
  }

  my $jhome = $this->{"java.home"};
  if (-d $jhome){
	 $path = $jhome . "/bin/java";
	 if (-x $path){
		$this->{"java.path"} = $path;
		return $path;
	 }
	 $path = $jhome . "/jre/bin/java";
	 if (-x $path){
		$this->{"java.path"} = $path;
		return $path;
	 }
  }

  $path = $jhome;
  if (-x $path){
	 $this->{"java.path"} = $path;
	 return $path;
  }

  return "/bin/java";
}



sub getClasspath {
  my($this) = @_;
  my $SA_HOME   = $this->getHomeDir();
  my $SA_LIB    = "$SA_HOME/lib";
  my $CLASSPATH = "$SA_LIB";

  if (-f "/opt/wsi/lib/wbem.jar"){
	 	$CLASSPATH .= ":/opt/wsi/lib/wbem.jar";
  }

  opendir(O, $SA_LIB);
  my @L = readdir(O);
  closedir(O);
  foreach my $fname (@L) {
	 if ($fname =~ /.jar$/){
		$CLASSPATH .= ":$SA_LIB/$fname";
	 }
  }
  return $CLASSPATH;
}


sub getHomeDir {
  my($this) = @_;
  my $sahome = $this->{"storade.home"};
  if (!$sahome){
	 my $BASEDIR = "/opt";
	 my $PKG     = "SUNWstade";
	 my $pkgerr  = system("pkginfo -q $PKG");
	 if (!$pkgerr){
		$BASEDIR = `pkgparam $PKG BASEDIR`;
		chomp($BASEDIR);
	 }
	 $sahome = "$BASEDIR/$PKG";
	 $this->{"storade.home"} = $sahome;
  }
  return $sahome;
}


# return 1 if JVM is running 0 if not.
sub isRunning {
  my($this) = @_;
  my $url = "http://localhost:" . $this->getPort() . "/jvm/ping";
  my ($user, $pass) = $this->getCredential();
  my $req = HTTP::Request->new(GET => $url);
  $req->authorization_basic($user, $pass);
  my $ua = new LWP::UserAgent;
  my $res = $ua->request($req);
  if ($res->is_success) {
	 return 1;
  }
  return 0;
}

# stop JVM if it is running
sub stop {
  my($this) = @_;
  my $ua = LWP::UserAgent->new;

  if ($this->isRunning()){
	 my $url = "http://localhost:" . $this->getPort() . "/jvm/die";
	 my ($user, $pass) = $this->getCredential();
	 my $req = HTTP::Request->new(GET => $url);
	 $req->authorization_basic($user, $pass);
	 my $res = $ua->request($req);
	 if ($res->is_success) {
		return 1;
	 }
  }
  return 0;
}

# Startup Server JVM
# return 0 if started otherwise error number.
sub start {
  my($this) = @_;

  my $JCLASS  = $this->{'java.jrex.class'};
  my $JCMD    = $this->getJavaPath();
  $JCMD .= " -DSUNWstade=" . $this->getHomeDir();
  $JCMD .= " -cp " . $this->getClasspath();
  $JCMD .= " " . $JCLASS;

  if ($this->{'java.jrex.idle_timeout'}){
	 $JCMD .= " -i " . $this->{'java.jrex.idle_timeout'};
  }

  my $LOGFILE =  $this->getHomeDir() . "/log/agent_jvm_log";
  open (LOG, ">$LOGFILE");
  print LOG "JVM started with command: $JCMD\n";
  close (LOG);
  my $rc;
  my $runId = $this->{'java.jrex.runId'};

  if (($<) || ($runId eq "root")){
	  $rc = system("$JCMD >>$LOGFILE </dev/null 2>&1 &");
  }
  else { # run as noaccess if UID is root
	 my @runpw = getpwnam($runId);
	 chown ($runpw[2], $runpw[3], $LOGFILE);
    my $runUserId = $runpw[2];
	 Debug->print3("Running JVM as $runId = $runUserId");
	 my $pid = fork();
	 if ( $pid == 0 ){
		$> = $runUserId;
		$< = $runUserId;
		$rc = system("$JCMD >>$LOGFILE </dev/null 2>&1 &");
		exit $rc;
    }
  }
  if ($rc != 0){
    Debug->err(ERROR => "JVM start failed.", $rc);
	 return $rc;
  }

  my $maxTries = 20;
  my $try = 0;
  for ($try = 0; $try < $maxTries; $try++){
	 sleep(1);
	 if ($this->isRunning()){
		return 0;
	 }
	 sleep(1);
  }

  return -1; #timed out
}

# run the JVM in the forground.
# warning call will hang until JVM exits.
sub run {
   my($this) = @_;
	my $JCLASS  = $this->{"java.jrex.class"};
	my $JCMD    = $this->getJavaPath();
	$JCMD .= " -DSUNWstade=" . $this->getHomeDir();
	$JCMD .= " -cp " . $this->getClasspath();
	$JCMD .= " " . $JCLASS;
	my $rc = system("$JCMD");
}

1;
