001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019package org.crsh.ssh.term;
020
021import org.crsh.console.jline.Terminal;
022import org.crsh.console.jline.console.ConsoleReader;
023import org.apache.sshd.server.Environment;
024import org.crsh.console.jline.JLineProcessor;
025import org.crsh.shell.Shell;
026import org.crsh.util.Utils;
027
028import java.io.IOException;
029import java.io.InputStream;
030import java.io.OutputStream;
031import java.io.PrintStream;
032import java.nio.charset.Charset;
033import java.security.Principal;
034import java.util.concurrent.atomic.AtomicBoolean;
035import java.util.logging.Level;
036import java.util.logging.Logger;
037
038public class CRaSHCommand extends AbstractCommand implements Runnable, Terminal {
039
040  /** . */
041  protected static final Logger log = Logger.getLogger(CRaSHCommand.class.getName());
042
043  /** . */
044  private final CRaSHCommandFactory factory;
045
046  /** . */
047  private Thread thread;
048
049  /** . */
050  private String encoding;
051
052  public CRaSHCommand(CRaSHCommandFactory factory) {
053    this.factory = factory;
054  }
055
056  /** . */
057  private SSHContext context;
058
059  /** . */
060  private JLineProcessor console;
061
062  public void start(Environment env) throws IOException {
063    context = new SSHContext(env);
064    encoding = context.encoding != null ? context.encoding.name() : factory.encoding.name();
065    thread = new Thread(this, "CRaSH");
066
067    //
068    thread.start();
069  }
070
071  public SSHContext getContext() {
072    return context;
073  }
074
075  public void destroy() {
076    Utils.close(console);
077    thread.interrupt();
078  }
079
080  public void run() {
081    final AtomicBoolean exited = new AtomicBoolean(false);
082    try {
083      final String userName = session.getAttribute(SSHLifeCycle.USERNAME);
084      Principal user = new Principal() {
085        public String getName() {
086          return userName;
087        }
088      };
089      Shell shell = factory.shellFactory.create(user);
090      ConsoleReader reader = new ConsoleReader(in, out, this) {
091        @Override
092        public void shutdown() {
093          exited.set(true);
094          callback.onExit(0);
095          super.shutdown();
096        }
097      };
098      JLineProcessor processor = new JLineProcessor(true, shell, reader, new PrintStream(out, false, encoding), "\r\n");
099      processor.run();
100    } catch (java.io.InterruptedIOException e) {
101      // Expected behavior because of the onExit callback in the shutdown above
102      // clear interrupted status on purpose
103      Thread.interrupted();
104    } catch (Exception e) {
105      log.log(Level.WARNING, "Error during execution", e);
106    } finally {
107      // Make sure we call it
108      if (!exited.get()) {
109        callback.onExit(0);
110      }
111    }
112  }
113
114  //
115
116  @Override
117  public String getOutputEncoding() {
118    return encoding;
119  }
120
121  @Override
122  public void init() throws Exception {
123  }
124
125  @Override
126  public void restore() throws Exception {
127  }
128
129  @Override
130  public void reset() throws Exception {
131  }
132
133  @Override
134  public boolean isSupported() {
135    return true;
136  }
137
138  @Override
139  public int getWidth() {
140    return context.getWidth();
141  }
142
143  @Override
144  public int getHeight() {
145    return context.getHeight();
146  }
147
148  @Override
149  public boolean isAnsiSupported() {
150    return true;
151  }
152
153  @Override
154  public OutputStream wrapOutIfNeeded(OutputStream out) {
155    return out;
156  }
157
158  @Override
159  public InputStream wrapInIfNeeded(InputStream in) throws IOException {
160    return in;
161  }
162
163  @Override
164  public boolean hasWeirdWrap() {
165    return false;
166  }
167
168  @Override
169  public boolean isEchoEnabled() {
170    return false;
171  }
172
173  @Override
174  public void setEchoEnabled(boolean enabled) {
175  }
176}
177
178