/* * Copyright 2010 netling project <http://netling.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file may incorporate work covered by the following copyright and * permission notice: * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.netling.ssh.connection.channel.direct; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.Map; import java.util.concurrent.TimeUnit; import org.netling.io.StreamCopier; import org.netling.ssh.common.Buffer; import org.netling.ssh.common.SSHException; import org.netling.ssh.common.SSHPacket; import org.netling.ssh.common.SSHRuntimeException; import org.netling.ssh.connection.Connection; import org.netling.ssh.connection.ConnectionException; import org.netling.ssh.connection.channel.ChannelInputStream; import org.netling.ssh.transport.TransportException; /** {@link Session} implementation. */ public class SessionChannel extends AbstractDirectChannel implements Session, Session.Command, Session.Shell, Session.Subsystem { private final ChannelInputStream err = new ChannelInputStream(this, trans, lwin); private Integer exitStatus; private Signal exitSignal; private Boolean wasCoreDumped; private String exitErrMsg; private Boolean canDoFlowControl; private boolean usedUp; public SessionChannel(Connection conn) { super(conn, "session"); } @Override public void allocateDefaultPTY() throws ConnectionException, TransportException { allocatePTY("vt100", 80, 24, 0, 0, Collections.<PTYMode, Integer>emptyMap()); } @Override public void allocatePTY(String term, int cols, int rows, int width, int height, Map<PTYMode, Integer> modes) throws ConnectionException, TransportException { sendChannelRequest( "pty-req", true, new Buffer.PlainBuffer() .putString(term) .putInt(cols) .putInt(rows) .putInt(width) .putInt(height) .putBytes(PTYMode.encode(modes)) ).await(conn.getTimeout(), TimeUnit.SECONDS); } @Override public Boolean canDoFlowControl() { return canDoFlowControl; } @Override public void changeWindowDimensions(int cols, int rows, int width, int height) throws TransportException { sendChannelRequest( "pty-req", false, new Buffer.PlainBuffer() .putInt(cols) .putInt(rows) .putInt(width) .putInt(height) ); } @Override public Command exec(String command) throws ConnectionException, TransportException { checkReuse(); log.info("Will request to exec `{}`", command); sendChannelRequest("exec", true, new Buffer.PlainBuffer().putString(command)) .await(conn.getTimeout(), TimeUnit.SECONDS); usedUp = true; return this; } @Override public String getErrorAsString() throws IOException { return StreamCopier.copyStreamToString(err); } @Override public InputStream getErrorStream() { return err; } @Override public String getExitErrorMessage() { return exitErrMsg; } @Override public Signal getExitSignal() { return exitSignal; } @Override public Integer getExitStatus() { return exitStatus; } @Override public String getOutputAsString() throws IOException { return StreamCopier.copyStreamToString(getInputStream()); } @Override public void handleRequest(String req, SSHPacket buf) throws ConnectionException, TransportException { if ("xon-xoff".equals(req)) canDoFlowControl = buf.readBoolean(); else if ("exit-status".equals(req)) exitStatus = buf.readInt(); else if ("exit-signal".equals(req)) { exitSignal = Signal.fromString(buf.readString()); wasCoreDumped = buf.readBoolean(); // core dumped exitErrMsg = buf.readString(); sendClose(); } else super.handleRequest(req, buf); } @Override public void reqX11Forwarding(String authProto, String authCookie, int screen) throws ConnectionException, TransportException { sendChannelRequest( "x11-req", true, new Buffer.PlainBuffer() .putBoolean(false) .putString(authProto) .putString(authCookie) .putInt(screen) ).await(conn.getTimeout(), TimeUnit.SECONDS); } @Override public void setEnvVar(String name, String value) throws ConnectionException, TransportException { sendChannelRequest("env", true, new Buffer.PlainBuffer().putString(name).putString(value)) .await(conn.getTimeout(), TimeUnit.SECONDS); } @Override public void signal(Signal sig) throws TransportException { sendChannelRequest("signal", false, new Buffer.PlainBuffer().putString(sig.toString())); } @Override public Shell startShell() throws ConnectionException, TransportException { checkReuse(); sendChannelRequest("shell", true, null).await(conn.getTimeout(), TimeUnit.SECONDS); usedUp = true; return this; } @Override public Subsystem startSubsystem(String name) throws ConnectionException, TransportException { checkReuse(); log.info("Will request `{}` subsystem", name); sendChannelRequest("subsystem", true, new Buffer.PlainBuffer().putString(name)) .await(conn.getTimeout(), TimeUnit.SECONDS); usedUp = true; return this; } @Override public Boolean getExitWasCoreDumped() { return wasCoreDumped; } @Override protected void closeAllStreams() { org.netling.io.Util.closeQuietly(err); super.closeAllStreams(); } @Override protected void eofInputStreams() { err.eof(); // also close the stderr stream super.eofInputStreams(); } @Override protected void gotExtendedData(int dataTypeCode, SSHPacket buf) throws ConnectionException, TransportException { if (dataTypeCode == 1) receiveInto(err, buf); else super.gotExtendedData(dataTypeCode, buf); } public void notifyError(SSHException error) { err.notifyError(error); super.notifyError(error); } private void checkReuse() { if (usedUp) throw new SSHRuntimeException("This session channel is used up"); } }