package com.sun.launcher.core;

import java.io.*;
import java.util.*;
import com.sun.wizards.core.*;
import com.sun.install.products.*;
import com.sun.launcher.*;
import com.sun.launcher.tasks.*;

public class LaunchServerObject implements ServerObject, Serializable
{
  private transient WizardState wizardState = null;

  public LaunchServerObject()
  {
  }

  public void setWizardState(WizardState wizardState)
  {
    this.wizardState = wizardState;
  }


  /**
    *  Calls method to eject CD (still works if CDdeviceRaw is null)
    */
  public void EjectCD()
  {
    String CDdeviceRaw=(String)wizardState.getData(LaunchConstants.CDdeviceRaw);
    MountCDTask.EjectCD(CDdeviceRaw); 
  }


  /**
    *  runBPgetfile
    *
    *  Uses bpgetfile to determine location of boot.
    *  
    *  Output should be in form:
    *       servername  serverIP  path
    *
    */
  public static String runBPgetfile()
  {
    String cmd = "/usr/sbin/bpgetfile -retries 1";
    int result = 0;

    /*
     * Run bpgetfile. Output should be in form:
     *  servername  serverIP  path
     */
    RunCmd r = new RunCmd(cmd, true);
    result = r.getResult();
    String output =  r.getAllOutput();
    if (Def.DEBUG) {
      System.out.println("runBPgetfile: bpgetfile output= " + output +
                    ", Return code =" + result);
    }
    return(output);
  }


  /**
    *  doAutoNetMount
    *
    *  Try to automount image from net 
    *  Uses bpgetfile to determine location
    *
    */
  public Boolean doAutoNetMount(String imagedir)
  {
    int blankPosition = 0;
    int lastBlankPosition = 0;
    String serverName = null;
    String pathName = null;

    /*
     * Get saved output from bpgetfile. Output should be in form:
     *       servername  serverIP  path
     */
    String output = (String)wizardState.getData(LaunchConstants.bpOutput);
    if ((output == null) || (output.trim()).equals("")) {
      return(new Boolean(false));
    }

    /*
     * Parse the line. Get server name
     */
    blankPosition = output.indexOf(' ');
    if (blankPosition > 0)  {
      serverName = output.substring(0, blankPosition);
    } else {
      return(new Boolean(false));
    }

    /*
     * Now get the install path 
     */
    lastBlankPosition = output.lastIndexOf(' ');
    if (blankPosition != lastBlankPosition)  {
      pathName = (output.substring(lastBlankPosition+1)).trim();
    } else {
      return(new Boolean(false));
    }

    /*
     * bpgetfile gives path down to Solaris_8/Tools/Boot. Need to pop up
     * four levels to find net image, if it's there.
     */
    String parentPath = "/net/" + serverName + pathName + "/../../../..";

    /*
     * See if we can see parent of server install directory
     */
    String imagePath = parentPath + "/" + imagedir;
    File imagePathFile = new File(imagePath);
    if (imagePathFile.exists() && imagePathFile.isDirectory()) {
      if (Def.DEBUG) {
        System.out.println("LaunchServer: set launchLocation to "+imagePath);
      }
      wizardState.setData(LaunchConstants.launchLocation, imagePath);
      return(new Boolean(true));
    } else {
      if (Def.DEBUG) {
        System.out.println("LaunchServer: couldn't see"+ imagePath);
      }
      return(new Boolean(false));
    }

  }

  /*
   * for miniroot only, tries to mount the image from the location
   * taken from /.netmnt file
   */
  public Boolean doNetMntMount(String imagedir)
  {
    String dotNetMnt = "/.netmnt";
    String mountdir = "/mnt";
    String netmntLine = null;
    boolean status = false;

    /*
     * See if /.netmnt exists (in miniroot). 
     * If so, see if IMAGEDIR is out there
     */
    File netmnt = new File(dotNetMnt);
    if (netmnt.exists() && netmnt.isFile()) {
      if (Def.DEBUG) System.out.println("LaunchServer doNetMntMount: netmnt exists");
     
      /*
       * Get contents of .netmnt and try to mount ../IMAGEDIR
       */
      netmntLine = readLineofFile(dotNetMnt);
      if (netmntLine != null) {
        File mntDir = new File(mountdir);
        if (! mntDir.exists()) {
          if ( ! mntDir.mkdir()) {
            System.out.println("LaunchServer: Unable to mkdir: " + 
                                mntDir.toString());
          }
        } 

        /*
         * Now that we have a /mnt directory, try to mount the image
         */
        unMount(mntDir.toString());
        status =  mountOnParent(netmntLine, imagedir, mntDir.toString());
        if (status) {
          if (Def.DEBUG) {
            System.out.println(" LaunchServer: mountOnParent success" );
          }
          wizardState.setData(LaunchConstants.launchLocation, mountdir);
        } else {
          if (Def.DEBUG) {
            System.out.println(" LaunchServer: mountOnParent failed" );
          }
        }
      }
    } else {
      status = false;
    }
    return(new Boolean(status));

  }


  /*
   * mountPathFromUser
   *
   *   Return mount status as integer and sets 
   *        LaunchConstants.launchLocation if successful
   */
  public Integer mountPathFromUser(String serverpath)
  {
    int status = LauncherResources.MOUNT_FAILED;
    if (serverpath != null) {
      if (serverpath.startsWith(File.separator)) {
        File pathFile = new File(serverpath);
        /*
         * try 3 times to allow automount
         */
        int maxattempts = 2;
        int i = 0;
        while ( !pathFile.exists() && (i++ < maxattempts) ) {
          if (Def.DEBUG) {
            System.out.println(i+". file doesn't exist::" + pathFile);
          }
          try {
            Thread.sleep(2000);
          } catch (InterruptedException ex) {
            System.out.println("mountPathFromUser: sleep: "+ex);
          }
        }

        if ( pathFile.exists()) {
          if (Def.DEBUG) {
             System.out.println("mountPathFromUser: Setting launch "
                                 + "location to " + serverpath);
          }
          wizardState.setData(LaunchConstants.launchLocation, serverpath);
          status = LauncherResources.MOUNT_SUCCESS;
        } else {
            status = LauncherResources.ABSPATH_DOESNOT_EXIST;
        }
      } else {
        /*
         * do nfs mount
         */
        status = mountServer(serverpath);
      }
    }
    return(new Integer(status));

  }


  /*
   *  mountServer
   *
   *   Returns int mount status, codes for LauncherResources are
   *       also give error messages
   */
  public int mountServer(String serverpath)
  {
    String mountdir = "/mnt";
    int status = LauncherResources.MOUNT_FAILED;

    /*
     * Make sure we have a /mnt
     */
    if (serverpath != null) {
      File mntDir = new File(mountdir);
      if (! mntDir.exists()) {
        if ( ! mntDir.mkdir()) {
          System.out.println("LaunchServer: Unable to mkdir: " + 
                              mntDir.toString());
        }
      }

      /*
       * Now that we have a /mnt directory, try to mount the image
       * If successful, set launchLocation to /mnt.
       */
      unMount(mntDir.toString());

     /*
      * Parse serverpath for errors here. We do this here because 
      * if we try to mount a hostname that's not responding, it
      * takes forever.
      */
      status = parsePath(serverpath);
      if (status == LauncherResources.PARSE_SUCCESS) {
        boolean mountStatus =  mountImage(serverpath, mntDir.toString());

        if (mountStatus) {
          if (Def.DEBUG) {
            System.out.println(" LaunchServer: mountImage success" );
          }
          wizardState.setData(LaunchConstants.launchLocation, mountdir);
          status = LauncherResources.MOUNT_SUCCESS;
        } else {
          /*
           *  Mount failed. Maybe not exported??
           */
          status = LauncherResources.MAYBE_NOT_EXPORTED;

          if (Def.DEBUG) {
            System.out.println(" LaunchServer: mountImage failed: " + status);
          }
        }
      }
    }
    return(status);

  }


  /**
   *  parsePath
   *
   *    Parses server:/pathname for errors 
   */
  public int parsePath(String serverpath)
  {
    if (serverpath.length() == 0) {
      return(LauncherResources.EMPTY_SERVERPATH);
    }

    int colon_pos = serverpath.indexOf(':');
    if (colon_pos == -1) {
      return(LauncherResources.COLON_NOT_FOUND);
    }
  
    if (serverpath.startsWith(":")) {
      return(LauncherResources.MISSING_SERVER_NAME);
    }
  
    if (serverpath.startsWith(File.separator)) {
      return(LauncherResources.MISSING_SERVER_NAME);
    }
  
    if (serverpath.startsWith(File.separator)) {
      return(LauncherResources.MISSING_SERVER_NAME);
    }
 
    /*
     * Ensure there is a path and that it is absolute 
     */
    if (serverpath.length() == (serverpath.indexOf(':') + 1)) {
      return(LauncherResources.SERVER_NO_PATH);
    }

    String pastColon = String.valueOf(serverpath.charAt(colon_pos+1));
    if ( ! pastColon.equals(File.separator)) {
      return(LauncherResources.PATH_NOT_ABSOLUTE);
    }

    /*
     * ping hostname (3 second t/o) to see if it exists
     */
    String hostname = serverpath.substring(0,colon_pos); 
    String cmd = "/usr/sbin/ping " + hostname + " 3 ";
    int result=0;
    RunCmd r = new RunCmd(cmd, true);
    result = r.getResult();
    String output =  r.getAllOutput();
    if (Def.DEBUG) {
      System.out.println("\n==>LaunchServerObj parsePath cmd:\n" + cmd );
      System.out.println("parsePath: output= " + output +
                    ", Return code =" + result);
    }

    /*
     * if ping not successful, parse its output for reason
     */
    if (result != 0) {
      if ( output.indexOf("no answer") != -1 ) {
        return(LauncherResources.SERVER_DOWN);
      } else if ( output.indexOf("unknown") != -1 ) {
        return(LauncherResources.SERVER_UNKNOWN);
      } else return(LauncherResources.MOUNT_FAILED);
    }

    /*
     * Nothing glaring, keep going
     */
    return(LauncherResources.PARSE_SUCCESS);

  }



  /**
    *  Attemps to nfs mount a CD image on the parent of passed in directory
    */
  public boolean mountOnParent(String childDir, String imagedir, String mntpt)
  {
    String parentDir = (new File(childDir)).getParent();
    String fullImageDir = parentDir + "/" + imagedir;
    String cmd = "/sbin/mount -F nfs -m -o hard,ro,retry=2 " + fullImageDir + " " + mntpt;

    int result=0;
    RunCmd r = new RunCmd(cmd, true);
    result = r.getResult();
    String output =  r.getAllOutput();
    if (Def.DEBUG) {
      System.out.println ("\n==>LaunchServerObj mountOnParent cmd:\n" + cmd );
      System.out.println("mountOnParent: output= " + output +
                    ", Return code =" + result);
    }
    if (result == 0) {
        return (true);
    } else {
        return (false);
    }
  }


  /**
    *  Attemps to nfs mount a CD image 
    */
  public boolean mountImage(String serverPath, String mntpt)
  {
    String cmd = "/sbin/mount -F nfs -o ro,hard,retry=2 " + serverPath + " " + mntpt;

    int result=0;
    RunCmd r = new RunCmd(cmd, true);
    result = r.getResult();
    String output =  r.getAllOutput();
    if (Def.DEBUG) {
      System.out.println ("\n==>LaunchServerObj mountImage cmd:\n" + cmd );
      System.out.println("mountImage: output= " + output +
                    ", Return code =" + result);
    }
    if (result == 0) {
        return (true);
    } else {
        return (false);
    }
  }


  /**
    *  Reads first line of file and returns it as string
    */
  public static String readLineofFile(String filepathname)
  {
    boolean done = false;
    BufferedReader bfrReader;
    FileInputStream inputStream;
    String contents = null;

    if (Def.DEBUG) System.out.println("readFile: file="+filepathname);
    File passedFile= new File(filepathname);
    try {
        inputStream = new FileInputStream(passedFile);
        bfrReader = new BufferedReader(new InputStreamReader(inputStream));
    } catch(IOException e) {
        System.out.println("Error opening file " +
                           passedFile.getPath() +
                           ".");
        return(null);
    }
    while (!done) {
      try {
        String line = bfrReader.readLine();
        if (line==null) {
          done = true;
        } else {
          contents = line;
        }
      } catch (IOException ex) {
        done = true;
        System.err.println("Error reading file " + passedFile.getPath() + ".");
      }
    }
    try {
        inputStream.close();
    } catch(IOException e) {
    }

    if (Def.DEBUG) {
      System.out.println("readLineofFile: file=" + filepathname + 
                           "\n     contents=" + contents);
    }
    return contents;
  }


  /**
    *  Execs unmount command 
    */
  public boolean unMount(String mntpt)
  {
    String cmd = "/sbin/umount " + mntpt;

    int result=0;
    RunCmd r = new RunCmd(cmd, true);
    result = r.getResult();
    String output =  r.getAllOutput();
    if (Def.DEBUG) {
      System.out.println ("\n==> LaunchServerObj unMount cmd: " + cmd );
      System.out.println("unMount: output= " + output +
                    ", Return code =" + result);
    }
    if (result == 0) {
        return (true);
    } else {
        return (false);
    }
  }



  /**
    *  Execs command to restart Volume Manager 
    */
  public void startVold()
  {
    int result=0;
    RunCmd r = new RunCmd("/etc/init.d/volmgt start", false);
    result = r.getResult();
    if (Def.DEBUG) System.out.println("startVold:  Return code =" + result);
  }

  
  /**
    *  If the Volume Manager is running, stop it. Does not return
    *  until vold process is gone.
    */
  public void killVold()
  {
    Boolean vold= (Boolean) wizardState.getData(LaunchConstants.usingVold);
    if (vold.booleanValue()) return;


    if ( isVoldAlive(wizardState) ) {
      /*
       * there were some problems getting vold to stop using the
       * volmgt stop command. A decision was made to use vold, but the
       * problems were never resolved as of the switch to vold.
       */
      if (Def.DEBUG) System.out.println("killVold: Vold is alive");
      RunCmd r = new RunCmd("/etc/init.d/volmgt stop", true);
      //r = new RunCmd("/usr/bin/pkill -9 -x -u 0 vold", false);
      if (Def.DEBUG)System.out.println("volmgt stop output: "+r.getAllOutput());

      /*
       * Check again
       */
      while ( isVoldAlive(wizardState) ) {

      }
      if (Def.DEBUG) System.out.println("killVold: Vold is stopped ");
      wizardState.setData(LaunchConstants.voldStopped, Boolean.TRUE);
    } else {
      if (Def.DEBUG) System.out.println("killVold: Vold is not alive");
    }

    return;
   }


  /**
    *  Check to see if Volume Manger is running
    *  getVold is a shell script to perform the volume manager process
    *   status checking.
    */
   public static boolean isVoldAlive(WizardState state) 
   { 
     RunCmd r = new RunCmd("/bin/sh " + "bin/getVold", true);
     if ( r.getResult() == 0 ) {
       if ( r.getOutput().compareTo("") != 0 ) {
         return (true);
       } else {
         return (false);
       }
     } else  {
       return (false);
     }
   }


  /**
    * Returns a hashtable containing the contents of the 
    * launchfile.
    */
  public static Hashtable readLaunchfile(String filepathname)
  {
    Hashtable myhash = new Hashtable();
    myhash = InitTask.readLaunchFile(filepathname);
    return(myhash);
  } 


  /**
    * Updates the checkpoint file. String should 
    * correspond to the CD just completed.
    */
  public static void updateCheckpoint(String value)
  {
    InitTask.updateCheckpoint(value);
  } 


  /**
    * Creates dir designated by path
    */
  public static void createDir(String dirpath)
  {
    File dirFile = new File(dirpath);
    dirFile.mkdirs();
  } 

  /**
    * Removes dir designated by path
    */
  public static void removeDir(String dirpath)
  {
    File dirFile = new File(dirpath);
    if (dirFile.exists()) dirFile.delete();
  } 

  /*
   * doesFileExist
   *
   *   Return true if file exists, false otherwise
   */
  public Boolean doesFileExist(String path)
  {
    File theFile = new File(path);
    if (theFile.exists()) return (Boolean.TRUE);
    else return(Boolean.FALSE);
  }

  /**
    * Utility function to write to a log.
    *
    * @param logFile Path to the desired log file.
    * @param output The string to write to the log.
    */
  public static void writeToLog(String logFile, String output)
  {
    if ( logFile != null)
      {
        try
          {
            FileOutputStream log = new FileOutputStream(logFile, true);
            DataOutputStream outstrm = new DataOutputStream(log);
            outstrm.writeBytes( output + "\n" );
            outstrm.close();
          }
        catch (IOException ex)
          {
            System.out.println("writeToLog IOException: " + ex);
            if (Def.DEBUG)
              {
                System.out.println("writeToLog logFile=" + logFile);
              }
          }
      }
    else
      {
        if (Def.DEBUG) System.out.println(output);
      }
  }


  /**
    * Get the runtime classes required by this ServerObject.
    */
  public void addRuntimeResources(Vector resourceVector)
  {
    resourceVector.addElement(new String[] {null, "com.sun.launcher.core.LaunchServerObject"});
  }

}

