Save This Page
Home » j2ssh-0.2.9-src » com.sshtools.daemon.session » [javadoc | source]
    1   /*
    2    *  SSHTools - Java SSH2 API
    3    *
    4    *  Copyright (C) 2002-2003 Lee David Painter and Contributors.
    5    *
    6    *  Contributions made by:
    7    *
    8    *  Brett Smith
    9    *  Richard Pernavas
   10    *  Erwin Bolwidt
   11    *
   12    *  This program is free software; you can redistribute it and/or
   13    *  modify it under the terms of the GNU General Public License
   14    *  as published by the Free Software Foundation; either version 2
   15    *  of the License, or (at your option) any later version.
   16    *
   17    *  This program is distributed in the hope that it will be useful,
   18    *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   19    *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20    *  GNU General Public License for more details.
   21    *
   22    *  You should have received a copy of the GNU General Public License
   23    *  along with this program; if not, write to the Free Software
   24    *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   25    */
   26   package com.sshtools.daemon.session;
   27   
   28   import com.sshtools.daemon.configuration;
   29   import com.sshtools.daemon.platform;
   30   import com.sshtools.daemon.scp;
   31   import com.sshtools.daemon.subsystem;
   32   
   33   import com.sshtools.j2ssh;
   34   import com.sshtools.j2ssh.agent;
   35   import com.sshtools.j2ssh.configuration;
   36   import com.sshtools.j2ssh.connection;
   37   import com.sshtools.j2ssh.io;
   38   import com.sshtools.j2ssh.util;
   39   
   40   import org.apache.commons.logging;
   41   
   42   import java.io;
   43   
   44   import java.util;
   45   
   46   
   47   /**
   48    *
   49    *
   50    * @author $author$
   51    * @version $Revision: 1.16 $
   52    */
   53   public class SessionChannelServer extends IOChannel {
   54       private static Log log = LogFactory.getLog(SessionChannelServer.class);
   55   
   56       /**  */
   57       public final static String SESSION_CHANNEL_TYPE = "session";
   58       private static Map allowedSubsystems = new HashMap();
   59       private Map environment = new HashMap();
   60       private NativeProcessProvider processInstance;
   61       private SubsystemServer subsystemInstance;
   62       private Thread thread;
   63       private IOStreamConnector ios;
   64       private ChannelOutputStream stderrOut;
   65       private InputStream stderrIn;
   66       private ProcessMonitorThread processMonitor;
   67       private PseudoTerminalWrapper pty;
   68       private SshAgentForwardingListener agent;
   69       private ServerConfiguration config;
   70   
   71       /**
   72    * Creates a new SessionChannelServer object.
   73    *
   74    * @throws ConfigurationException
   75    */
   76       public SessionChannelServer() throws ConfigurationException {
   77           super();
   78   
   79           // Load the allowed subsystems from the server configuration
   80           config = (ServerConfiguration) ConfigurationLoader.getConfiguration(ServerConfiguration.class);
   81           allowedSubsystems.putAll(config.getSubsystems());
   82       }
   83   
   84       private void bindStderrInputStream(InputStream stderrIn) {
   85           this.stderrIn = stderrIn;
   86           ios = new IOStreamConnector(stderrIn, stderrOut);
   87       }
   88   
   89       /**
   90    *
   91    *
   92    * @param cols
   93    * @param rows
   94    * @param width
   95    * @param height
   96    */
   97       protected void onChangeTerminalDimensions(int cols, int rows, int width,
   98           int height) {
   99       }
  100   
  101       /**
  102    *
  103    *
  104    * @throws IOException
  105    */
  106       protected void onChannelClose() throws IOException {
  107           // Remove our reference to the agent
  108           if (agent != null) {
  109               agent.removeReference(this);
  110           }
  111   
  112           if (processInstance != null) {
  113               if (processInstance.stillActive()) {
  114                   processInstance.kill();
  115               }
  116           }
  117   
  118           if (subsystemInstance != null) {
  119               subsystemInstance.stop();
  120           }
  121   
  122           // If we have a process monitor then get the exit code
  123           // and send before we close the channel
  124           if (processMonitor != null) {
  125               StartStopState state = processMonitor.getStartStopState();
  126   
  127               try {
  128                   state.waitForState(StartStopState.STOPPED);
  129               } catch (InterruptedException ex) {
  130                   throw new IOException("The process monitor was interrupted");
  131               }
  132           }
  133       }
  134   
  135       /**
  136    *
  137    *
  138    * @throws IOException
  139    */
  140       protected void onChannelEOF() throws IOException {
  141       }
  142   
  143       /**
  144    *
  145    *
  146    * @param data
  147    *
  148    * @throws IOException
  149    */
  150       protected void onChannelExtData(byte[] data) throws IOException {
  151           // Do something with the data
  152       }
  153   
  154       /**
  155    *
  156    *
  157    * @throws InvalidChannelException
  158    */
  159       protected void onChannelOpen() throws InvalidChannelException {
  160           stderrOut = new ChannelOutputStream(this,
  161                   new Integer(SshMsgChannelExtendedData.SSH_EXTENDED_DATA_STDERR));
  162       }
  163   
  164       /**
  165    *
  166    *
  167    * @param command
  168    *
  169    * @return
  170    *
  171    * @throws IOException
  172    */
  173       protected boolean onExecuteCommand(String command)
  174           throws IOException {
  175           log.debug("Executing command " + command);
  176   
  177           // Hack for now
  178           if (command.startsWith("scp ")) {
  179               if (processInstance == null) {
  180                   processInstance = new ScpServer();
  181               }
  182           }
  183   
  184           // Create an instance of the native process provider if we n
  185           if (processInstance == null) {
  186               processInstance = NativeProcessProvider.newInstance();
  187           }
  188   
  189           if (processInstance == null) {
  190               log.debug("Failed to create process");
  191   
  192               return false;
  193           }
  194   
  195           boolean result = processInstance.createProcess(command, environment);
  196   
  197           if (result) {
  198               if (pty != null) {
  199                   // Bind the streams to the pseudo terminal wrapper
  200                   pty.bindMasterOutputStream(getOutputStream());
  201                   pty.bindMasterInputStream(getInputStream());
  202                   pty.bindSlaveInputStream(processInstance.getInputStream());
  203                   pty.bindSlaveOutputStream(processInstance.getOutputStream());
  204   
  205                   // Initialize the terminal
  206                   pty.initialize();
  207   
  208                   // Bind the master output stream of the pty to the session
  209                   bindInputStream(pty.getMasterInputStream());
  210   
  211                   // Bind the processes stderr
  212                   bindStderrInputStream(processInstance.getStderrInputStream());
  213               } else {
  214                   // Just bind the process streams to the session
  215                   bindInputStream(processInstance.getInputStream());
  216                   bindOutputStream(processInstance.getOutputStream());
  217                   bindStderrInputStream(processInstance.getStderrInputStream());
  218               }
  219           }
  220   
  221           return result;
  222       }
  223   
  224       /**
  225    *
  226    *
  227    * @param term
  228    * @param cols
  229    * @param rows
  230    * @param width
  231    * @param height
  232    * @param modes
  233    *
  234    * @return
  235    */
  236       protected boolean onRequestPseudoTerminal(String term, int cols, int rows,
  237           int width, int height, String modes) {
  238           try {
  239               // Create an instance of the native process provider
  240               processInstance = NativeProcessProvider.newInstance();
  241   
  242               if (processInstance.supportsPseudoTerminal(term)) {
  243                   return processInstance.allocatePseudoTerminal(term, cols, rows,
  244                       width, height, modes);
  245               } else {
  246                   pty = new PseudoTerminalWrapper(term, cols, rows, width,
  247                           height, modes);
  248   
  249                   return true;
  250               }
  251           } catch (IOException ioe) {
  252               log.warn("Failed to allocate pseudo terminal " + term, ioe);
  253   
  254               return false;
  255           }
  256       }
  257   
  258       /**
  259    *
  260    *
  261    * @param name
  262    * @param value
  263    */
  264       protected void onSetEnvironmentVariable(String name, String value) {
  265           environment.put(name, value);
  266       }
  267   
  268       /**
  269    *
  270    *
  271    * @return
  272    *
  273    * @throws IOException
  274    */
  275       protected boolean onStartShell() throws IOException {
  276           String shell = config.getTerminalProvider();
  277   
  278           if (processInstance == null) {
  279               processInstance = NativeProcessProvider.newInstance();
  280           }
  281   
  282           if ((shell != null) && !shell.trim().equals("")) {
  283               int idx = shell.indexOf("%DEFAULT_TERMINAL%");
  284   
  285               if (idx > -1) {
  286                   shell = ((idx > 0) ? shell.substring(0, idx) : "") +
  287                       processInstance.getDefaultTerminalProvider() +
  288                       (((idx + 18) < shell.length()) ? shell.substring(idx + 18)
  289                                                      : "");
  290               }
  291           } else {
  292               shell = processInstance.getDefaultTerminalProvider();
  293           }
  294   
  295           return onExecuteCommand(shell);
  296       }
  297   
  298       /**
  299    *
  300    *
  301    * @param subsystem
  302    *
  303    * @return
  304    */
  305       protected boolean onStartSubsystem(String subsystem) {
  306           boolean result = false;
  307   
  308           try {
  309               if (!allowedSubsystems.containsKey(subsystem)) {
  310                   log.error(subsystem + " Subsystem is not available");
  311   
  312                   return false;
  313               }
  314   
  315               AllowedSubsystem obj = (AllowedSubsystem) allowedSubsystems.get(subsystem);
  316   
  317               if (obj.getType().equals("class")) {
  318                   // Create the class implementation and start the subsystem
  319                   Class cls = Class.forName(obj.getProvider());
  320                   subsystemInstance = (SubsystemServer) cls.newInstance();
  321                   subsystemInstance.setSession(this);
  322                   bindInputStream(subsystemInstance.getInputStream());
  323                   bindOutputStream(subsystemInstance.getOutputStream());
  324   
  325                   return true;
  326               } else {
  327                   // Determine the subsystem provider
  328                   String provider = obj.getProvider();
  329                   File f = new File(provider);
  330   
  331                   if (!f.exists()) {
  332                       provider = ConfigurationLoader.getHomeDirectory() + "bin" +
  333                           File.separator + provider;
  334                       f = new File(provider);
  335   
  336                       if (!f.exists()) {
  337                           log.error("Failed to locate subsystem provider " +
  338                               obj.getProvider());
  339   
  340                           return false;
  341                       }
  342                   }
  343   
  344                   return onExecuteCommand(provider);
  345               }
  346           } catch (Exception e) {
  347               log.error("Failed to start subsystem " + subsystem, e);
  348           }
  349   
  350           return false;
  351       }
  352   
  353       /**
  354    *
  355    *
  356    * @return
  357    */
  358       public byte[] getChannelOpenData() {
  359           return null;
  360       }
  361   
  362       /**
  363    *
  364    *
  365    * @return
  366    */
  367       public byte[] getChannelConfirmationData() {
  368           return null;
  369       }
  370   
  371       /**
  372    *
  373    *
  374    * @return
  375    */
  376       protected int getMinimumWindowSpace() {
  377           return 1024;
  378       }
  379   
  380       /**
  381    *
  382    *
  383    * @return
  384    */
  385       protected int getMaximumWindowSpace() {
  386           return 32648;
  387       }
  388   
  389       /**
  390    *
  391    *
  392    * @return
  393    */
  394       protected int getMaximumPacketSize() {
  395           return 32648;
  396       }
  397   
  398       /**
  399    *
  400    *
  401    * @return
  402    */
  403       public String getChannelType() {
  404           return SESSION_CHANNEL_TYPE;
  405       }
  406   
  407       /**
  408    *
  409    *
  410    * @param requestType
  411    * @param wantReply
  412    * @param requestData
  413    *
  414    * @throws IOException
  415    */
  416       protected void onChannelRequest(String requestType, boolean wantReply,
  417           byte[] requestData) throws IOException {
  418           log.debug("Channel Request received: " + requestType);
  419   
  420           boolean success = false;
  421   
  422           if (requestType.equals("shell")) {
  423               success = onStartShell();
  424   
  425               if (success) {
  426                   if (wantReply) {
  427                       connection.sendChannelRequestSuccess(this);
  428                   }
  429   
  430                   processInstance.start();
  431                   processMonitor = new ProcessMonitorThread(processInstance);
  432               } else if (wantReply) {
  433                   connection.sendChannelRequestFailure(this);
  434               }
  435           }
  436   
  437           if (requestType.equals("env")) {
  438               ByteArrayReader bar = new ByteArrayReader(requestData);
  439               String name = bar.readString();
  440               String value = bar.readString();
  441               onSetEnvironmentVariable(name, value);
  442   
  443               if (wantReply) {
  444                   connection.sendChannelRequestSuccess(this);
  445               }
  446           }
  447   
  448           if (requestType.equals("exec")) {
  449               ByteArrayReader bar = new ByteArrayReader(requestData);
  450               String command = bar.readString();
  451               success = onExecuteCommand(command);
  452   
  453               if (success) {
  454                   if (wantReply) {
  455                       connection.sendChannelRequestSuccess(this);
  456                   }
  457   
  458                   processInstance.start();
  459                   processMonitor = new ProcessMonitorThread(processInstance);
  460               } else if (wantReply) {
  461                   connection.sendChannelRequestFailure(this);
  462               }
  463           }
  464   
  465           if (requestType.equals("subsystem")) {
  466               ByteArrayReader bar = new ByteArrayReader(requestData);
  467               String subsystem = bar.readString();
  468               success = onStartSubsystem(subsystem);
  469   
  470               if (success) {
  471                   if (wantReply) {
  472                       connection.sendChannelRequestSuccess(this);
  473                   }
  474   
  475                   if (processInstance != null) {
  476                       processInstance.start();
  477                       processMonitor = new ProcessMonitorThread(processInstance);
  478                   } else if (subsystemInstance != null) {
  479                       subsystemInstance.start();
  480                       processMonitor = new ProcessMonitorThread(subsystemInstance);
  481                   }
  482               } else if (wantReply) {
  483                   connection.sendChannelRequestFailure(this);
  484               }
  485           }
  486   
  487           if (requestType.equals("pty-req")) {
  488               ByteArrayReader bar = new ByteArrayReader(requestData);
  489               String term = bar.readString();
  490               int cols = (int) bar.readInt();
  491               int rows = (int) bar.readInt();
  492               int width = (int) bar.readInt();
  493               int height = (int) bar.readInt();
  494               String modes = bar.readString();
  495               success = onRequestPseudoTerminal(term, cols, rows, width, height,
  496                       modes);
  497   
  498               if (wantReply && success) {
  499                   connection.sendChannelRequestSuccess(this);
  500               } else if (wantReply) {
  501                   connection.sendChannelRequestFailure(this);
  502               }
  503           }
  504   
  505           if (requestType.equals("window-change")) {
  506               ByteArrayReader bar = new ByteArrayReader(requestData);
  507               int cols = (int) bar.readInt();
  508               int rows = (int) bar.readInt();
  509               int width = (int) bar.readInt();
  510               int height = (int) bar.readInt();
  511               onChangeTerminalDimensions(cols, rows, width, height);
  512   
  513               if (wantReply && success) {
  514                   connection.sendChannelRequestSuccess(this);
  515               } else if (wantReply) {
  516                   connection.sendChannelRequestFailure(this);
  517               }
  518           }
  519   
  520           if (requestType.equals("auth-agent-req")) {
  521               try {
  522                   SshThread thread = SshThread.getCurrentThread();
  523   
  524                   // Get an agent instance
  525                   agent = SshAgentForwardingListener.getInstance(thread.getSessionIdString(),
  526                           connection);
  527   
  528                   // Inform the agent we want to track this reference
  529                   agent.addReference(this);
  530   
  531                   // Set the environment so processes can find the agent
  532                   environment.put("SSH_AGENT_AUTH", agent.getConfiguration());
  533   
  534                   // Set a thread property so other services within this server can find it
  535                   thread.setProperty("sshtools.agent", agent.getConfiguration());
  536   
  537                   if (wantReply) {
  538                       connection.sendChannelRequestSuccess(this);
  539                   }
  540               } catch (Exception ex) {
  541                   if (wantReply) {
  542                       connection.sendChannelRequestFailure(this);
  543                   }
  544               }
  545           }
  546       }
  547   
  548       class ProcessMonitorThread extends Thread {
  549           private NativeProcessProvider process;
  550           private SubsystemServer subsystem;
  551           private StartStopState state;
  552   
  553           public ProcessMonitorThread(NativeProcessProvider process) {
  554               this.process = process;
  555               state = new StartStopState(StartStopState.STARTED);
  556               start();
  557           }
  558   
  559           public ProcessMonitorThread(SubsystemServer subsystem) {
  560               state = subsystem.getState();
  561           }
  562   
  563           public StartStopState getStartStopState() {
  564               return state;
  565           }
  566   
  567           public void run() {
  568               try {
  569                   log.info("Monitor waiting for process exit code");
  570   
  571                   int exitcode = process.waitForExitCode();
  572   
  573                   if (exitcode == 9999999) {
  574                       log.error("Process monitor failed to retrieve exit code");
  575                   } else {
  576                       log.debug("Process exit code is " +
  577                           String.valueOf(exitcode));
  578                       process.getInputStream().close();
  579                       process.getOutputStream().close();
  580                       process.getStderrInputStream().close();
  581   
  582                       ByteArrayWriter baw = new ByteArrayWriter();
  583                       baw.writeInt(exitcode);
  584   
  585                       // Send the exit request
  586                       if (connection.isConnected() &&
  587                               SessionChannelServer.this.isOpen()) {
  588                           connection.sendChannelRequest(SessionChannelServer.this,
  589                               "exit-status", false, baw.toByteArray());
  590                       }
  591   
  592                       // Stop the monitor
  593                       state.setValue(StartStopState.STOPPED);
  594   
  595                       // Close the session
  596                       SessionChannelServer.this.close();
  597                   }
  598               } catch (IOException ioe) {
  599                   log.error("Failed to kill process", ioe);
  600               }
  601           }
  602       }
  603   }

Save This Page
Home » j2ssh-0.2.9-src » com.sshtools.daemon.session » [javadoc | source]