package sos.stacks.ganymed;


import com.trilead.ssh2.StreamGobbler;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;


import sos.spooler.Order;
import sos.spooler.Variable_set;

/**
 @author andreas.pueschel@sos-berlin.com
 @author ghassan.beydoun@sos-berlin.com
 * $Id: JobSchedulerSSHJob.java 3178 2008-01-04 13:49:31Z al $
 * see job documentation in the package jobdoc for details
 */

public class JobSchedulerSSHJob extends JobSchedulerSSHBaseJob {


  /** default command delimiter */
    final static String DEFAULT_COMMAND_DELIMITER = "%%";
  
    /** regular expression for delimiter of multiple commands specified as job or order parameter */
    protected String commandDelimiter           = DEFAULT_COMMAND_DELIMITER;

    
    /** array of commands that have been separated by the commandDelimiter */
    protected String[] commands                 = {};
    
    /** ignore errors reported by the exit status of commands */
    protected boolean ignoreError               = false;
    
    /** ignore signals terminating remote execution */
    protected boolean ignoreSignal              = false;

    /** ignore output to stderr */
    protected boolean ignoreStderr              = false;
    
    /** Simulate a shell instead of just passing commands **/
    protected boolean simulateShell             = false;
    
    /** Trigger for this string to know that the shell is waiting **/
    protected String promptTrigger = "";
    
    /** Inputstreams for stdout and stderr **/
    protected InputStream stdout;
    protected InputStream stderr;
  
    protected int x=200;
    protected int y=30;
  
  /** timestamp of the last text from stdin or stderr **/
    protected long lasttime =0;
    
    /** time to wait if anything more is coming from stdout or stderr when logging in **/
    protected long loginTimeout = 0;
    
    /** time to wait if anything more is coming from stdout or stderr when executing commands **/
    protected long commandTimeout = 0;
  
  /** Output from stdout and stderr **/
    protected StringBuffer stdoutOutput;
    protected StringBuffer stderrOutput;
    
    /** Line currently being displayed on the shell **/
    protected String currentLine = "";
    
    
    /**
     * Processing
     
     */
    
    public boolean spooler_process() {
      stdoutOutput = new StringBuffer();
      stderrOutput = new StringBuffer();
      currentLine = "";
      simulateShell=false;
      promptTrigger = "";
      loginTimeout = 0;
      commandTimeout = 0;
      
        Order order = null;
        Variable_set params = null;
        
        try {
            try // to fetch parameters, order parameters have precedence to job parameters
                params = spooler_task.params();
                
                if (spooler_task.job().order_queue() != null) {
                    order = spooler_task.order();
                    if order.params() != null)
                      params.merge(order.params());
                }
                
                // get basic authentication parameters
                this.getBaseParameters();
                
               if (params.value("command_delimiter"!= null && params.value("command_delimiter").length() 0) {
                   this.setCommandDelimiter(params.value("command_delimiter"));
                   spooler_log.info(".. parameter [command_delimiter]: " this.getCommandDelimiter());
               else {
                   this.setCommandDelimiter(DEFAULT_COMMAND_DELIMITER);
               }

               if (params.value("command"!= null && params.value("command").length() 0) {
                   this.setCommands(params.value("command").split(this.getCommandDelimiter()));
                   spooler_log.info(".. parameter [command]: " + params.value("command"));
               else {
                   throw new Exception("no command has been specified for parameter [command]");
               }
           
               
               if (params.value("ignore_error"!= null && params.value("ignore_error").length() 0) {
                   if (params.value("ignore_error").equalsIgnoreCase("true"|| params.value("ignore_error").equalsIgnoreCase("yes"|| params.value("ignore_error").equals("1")) {
                       this.setIgnoreError(true);
                   else {
                       this.setIgnoreError(false);
                   }
                   spooler_log.info(".. parameter [ignore_error]: " this.isIgnoreError());
               else {
                   this.setIgnoreError(false);
               }
               
               if (params.value("ignore_signal"!= null && params.value("ignore_signal").length() 0) {
                   if (params.value("ignore_signal").equalsIgnoreCase("true"|| params.value("ignore_signal").equalsIgnoreCase("yes"|| params.value("ignore_signal").equals("1")) {
                       this.setIgnoreSignal(true);
                   else {
                       this.setIgnoreSignal(false);
                   }
                   spooler_log.info(".. parameter [ignore_signal]: " this.isIgnoreSignal());
               else {
                   this.setIgnoreSignal(false);
               }
               
               if (params.value("ignore_stderr"!= null && params.value("ignore_stderr").length() 0) {
                   if (params.value("ignore_stderr").equalsIgnoreCase("true"|| params.value("ignore_stderr").equalsIgnoreCase("yes"|| params.value("ignore_stderr").equals("1")) {
                       this.setIgnoreStderr(true);
                   else {
                       this.setIgnoreStderr(false);
                   }
                   spooler_log.info(".. parameter [ignore_stderr]: " this.isIgnoreStderr());
               else {
                   this.setIgnoreStderr(false);
               }
               
               if (params.value("simulate_shell"!= null && params.value("simulate_shell").length() 0) {
                   if (params.value("simulate_shell").equalsIgnoreCase("true"|| params.value("simulate_shell").equalsIgnoreCase("yes"|| params.value("simulate_shell").equals("1")) {
                       simulateShell= true;
                   else {
                     simulateShell= false;
                   }
                   spooler_log.info(".. parameter [simulate_shell]: " + simulateShell);
               else {
                 simulateShell= false;
               }
               
               if (params.value("simulate_shell_prompt_trigger"!= null && params.value("simulate_shell_prompt_trigger").length() 0) {
                   this.setPromptTrigger(params.value("simulate_shell_prompt_trigger"));
                   spooler_log.info(".. parameter [simulate_shell_prompt_trigger]: " this.getPromptTrigger());
               else {
                   this.setPromptTrigger("");
               }
               
               if (params.value("simulate_shell_login_timeout"!= null && params.value("simulate_shell_login_timeout").length() 0) {
                   this.loginTimeout=Long.parseLong(params.value("simulate_shell_login_timeout"));
                   spooler_log.info(".. parameter [simulate_shell_login_timeout]: " this.loginTimeout);
               else {
                   loginTimeout=0;
               }
               
               if (params.value("simulate_shell_inactivity_timeout"!= null && params.value("simulate_shell_inactivity_timeout").length() 0) {
                   this.commandTimeout=Long.parseLong(params.value("simulate_shell_inactivity_timeout"));
                   spooler_log.info(".. parameter [simulate_shell_inactivity_timeout]: " this.commandTimeout);
               else {
                   commandTimeout=0;
               }
               
               if (simulateShell && getPromptTrigger().length()==&& (loginTimeout==|| commandTimeout==0)){
                 throw new Exception("if simulate_shell=true then either simulate_shell_prompt_trigger or "+
                     "simulate_shell_login_timeout and simulate_shell_inactivity_timeout need to br set.");
               }
               
           catch (Exception e) {
               throw new Exception("error occurred processing parameters: " + e.getMessage());
           }
           RemoteConsumer stdoutConsumer=null;
            RemoteConsumer stderrConsumer=null;
        
            try // to connect, authenticate and execute commands
                this.getBaseAuthentication();
                OutputStream stdin; 
          OutputStreamWriter stdinWriter=null;
          
                if (simulateShell){
                  this.setSshSession(this.getSshConnection().openSession());
                  spooler_log.debug3("Requesting PTY...");
                  this.getSshSession().requestDumbPTY();
                  spooler_log.debug3("Starting shell...");
                  this.getSshSession().startShell();
                  stdout = getSshSession().getStdout();
                  stderr = getSshSession().getStderr();
                  stdoutConsumer = new RemoteConsumer(stdoutOutput, true, stdout);
                  stderrConsumer = new RemoteConsumer(stderrOutput, false, stderr);
                  stdoutConsumer.start();
                  stderrConsumer.start();
                  stdin = getSshSession().getStdin();
              stdinWriter = new OutputStreamWriter(stdin);
              spooler_log.debug3("Waiting for login prompt...");
              boolean loggedIn=false;
              while (!loggedIn){
                if (lasttime>0){
                  long now = System.currentTimeMillis();
                  if (loginTimeout>&& lasttime+loginTimeout<now){// kommt nichts mehr
                    loggedIn = true;
                  }
                  if (promptTrigger.length()>&& currentLine.indexOf(promptTrigger)!=-1){
                    spooler_log.debug3("Found login prompt "+promptTrigger);
                    loggedIn = true;
                    currentLine="";
                  }
                }
              }
                }
                
                // execute commands
                for (int i=0; i<this.getCommands().length; i++) {
                    try {
                        Integer exitStatus = null;
                        String exitSignal = null;
                        
                        spooler_log.info("executing remote command: " this.getCommands()[i]);
                        if (simulateShell){
                          stdinWriter.write(this.getCommands()[i]+"\n");
                          stdinWriter.flush();
                          boolean prompt = false;
                          while (!prompt){                        
                          long now = System.currentTimeMillis();
                          if (loginTimeout>&& lasttime+loginTimeout<now){// kommt nichts mehr
                            prompt = true;
                          }
                          if (promptTrigger.length()>&& currentLine.indexOf(promptTrigger)!=-1){
                            spooler_log.debug3("Found prompt "+promptTrigger);
                            prompt = true;
                          }                                               
                      }
                          currentLine="";
                          spooler_log.info("output to stdout for remote command: " this.getCommands()[i]);
                    spooler_log.info(stdoutOutput.toString());
                    stdoutOutput = new StringBuffer();
                        }else {
                          this.setSshSession(this.getSshConnection().openSession());
                          this.getSshSession().execCommand(this.getCommands()[i]);
                          
                          spooler_log.info("output to stdout for remote command: " this.getCommands()[i]);
                          stdout = new StreamGobbler(this.getSshSession().getStdout());
                          stderr = new StreamGobbler(this.getSshSession().getStderr());
                          BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout));
                          while (true) {
                            String line = stdoutReader.readLine();
                            if (line == nullbreak;
                            spooler_log.info(line);
                          }
                          
                          spooler_log.info("output to stderr for remote command: " this.getCommands()[i]);
                          // Beide StreamGobbler müssen hintereinander instanziiert werden
                          //InputStream stderr = new StreamGobbler(this.getSshSession().getStderr());
                          BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr));
                          stderrOutput = new StringBuffer();
                          while (true) {
                            String line = stderrReader.readLine();
                            if (line == nullbreak;
                            spooler_log.info(line);
                            stderrOutput.appendline + "\n");
                          }
                        }
                        if (stderrOutput != null && stderrOutput.length() 0) {
                            if (this.isIgnoreStderr()) {
                                spooler_log.info("output to stderr is ignored: " + stderrOutput);
                            else {
                                throw new Exception("remote execution reports error: " + stderrOutput);
                            }
                        }
                        

                        try {
                            exitStatus = this.getSshSession().getExitStatus();
                        catch (Exception e) {
                            spooler_log.info("could not retrieve exit status, possibly not supported by remote ssh server");
                        }

                        if (exitStatus != null) {
                            if (!exitStatus.equals(new Integer(0))) {
                                if (this.isIgnoreError()) {
                                    spooler_log.info("exit status is ignored: " + exitStatus);
                                else {
                                    throw new Exception("remote command terminated with exit status: " + exitStatus);
                                }
                            }
                        }

                        
                        try {
                            exitSignal = this.getSshSession().getExitSignal();
                        catch (Exception e) {
                            spooler_log.info("could not retrieve exit signal, possibly not supported by remote ssh server");
                        }
                        
                        if (exitSignal != null) {
                            if (exitSignal.length() 0) {
                                if (this.isIgnoreSignal()) {
                                    spooler_log.info("exit signal is ignored: " + exitSignal);
                                else {
                                    throw new Exception("remote command terminated with exit signal: " + exitSignal);
                                }
                            }
                        }

                    catch (Exception e) {
                        throw new Exception(e.getMessage());
                    finally {
                        if (this.getSshSession() != nulltry this.getSshSession().close()this.setSshSession(null)catch (Exception ex) {} // gracefully ignore this error
                    }
                }
                
            catch (Exception e) {
                throw new Exception("error occurred processing ssh command: " + e.getMessage());                
            finally {
              if (stderrConsumer!=nullstderrConsumer.end();
              if (stdoutConsumer!=nullstdoutConsumer.end();
                if (this.getSshConnection() != nulltry this.getSshConnection().close()this.setSshConnection(null)catch (Exception ex) {} // gracefully ignore this error
            }
            
            // return value for classic and order driven processing
            return (spooler_task.job().order_queue() != null);

        catch (Exception e) {
        spooler_log.warn(e.getMessage());
        return false;
        }
    }

    
    /**
     @return Returns the commands.
     */
    private String[] getCommands() {
        return commands;
    }


    /**
     @param commands The commands to set.
     */
    private void setCommands(String[] commands) {
        this.commands = commands;
    }


    /**
     @return Returns the commandDelimiter.
     */
    private String getCommandDelimiter() {
        return commandDelimiter;
    }


    /**
     @param commandDelimiter The commandDelimiter to set.
     */
    private void setCommandDelimiter(String commandDelimiter) {
        this.commandDelimiter = commandDelimiter;
    }


    /**
     @return Returns the ignoreError.
     */
    public boolean isIgnoreError() {
        return ignoreError;
    }


    /**
     @param ignoreError The ignoreError to set.
     */
    public void setIgnoreError(boolean ignoreError) {
        this.ignoreError = ignoreError;
    }


    /**
     @return Returns the ignoreSignal.
     */
    public boolean isIgnoreSignal() {
        return ignoreSignal;
    }


    /**
     @param ignoreSignal The ignoreSignal to set.
     */
    public void setIgnoreSignal(boolean ignoreSignal) {
        this.ignoreSignal = ignoreSignal;
    }


    /**
     @return Returns the ignoreStderr.
     */
    public boolean isIgnoreStderr() {
        return ignoreStderr;
    }


    /**
     @param ignoreStderr The ignoreStderr to set.
     */
    public void setIgnoreStderr(boolean ignoreStderr) {
        this.ignoreStderr = ignoreStderr;
    }

    /**
   * This thread consumes output from the remote server puts it into
   * fields of the main class
   */
  class RemoteConsumer extends Thread
  {
    private StringBuffer sbuf;
    private boolean writeCurrentline = false;
    private InputStream stream;
    boolean end = false;
    
    private RemoteConsumer(StringBuffer buffer, boolean writeCurr, InputStream str){
      this.sbuf = buffer;
      this.writeCurrentline = true;
      this.stream = str;
    }

    private void addText(byte[] data, int len)
    {
      lasttime = System.currentTimeMillis();      
      String outstring = new String(data).substring(0,len);      
      sbuf.append(outstring);
      if (writeCurrentline){        
        int newlineIndex = outstring.indexOf("\n");
        if (newlineIndex>-1){
          String stringAfterNewline = outstring.substring(newlineIndex);
          currentLine = stringAfterNewline;
        else currentLine += outstring;
      }
    }

    public void run()
    {
      byte[] buff = new byte[64];

      try
      {
        while (!end)
        {
          buff = new byte[8];
          int len = stream.read(buff);
          if (len == -1)
            return;
          addText(buff, len);
        }
      }
      catch (Exception e)
      {
      }
    }
    
    public synchronized void end() {
      end = true;
    }
  }

  /**
   @return Returns the promptTrigger.
   */
  public String getPromptTrigger() {
    return promptTrigger;
  }


  /**
   @param promptTrigger The promptTrigger to set.
   */
  public void setPromptTrigger(String promptTrigger) {
    this.promptTrigger = promptTrigger;
  }
}
Java2html