blob: 334475d94177fc5b8e33c1960ad8d5e543407fb4 [file] [log] [blame]
/*
* 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.apache.chaos.remote
import com.jcraft.jsch.ChannelExec
import com.jcraft.jsch.ChannelShell
import com.jcraft.jsch.Session
import groovy.util.logging.Commons
import org.apache.tools.ant.util.KeepAliveOutputStream
import org.apache.tools.ant.util.TeeOutputStream
/**
* Send lists strings of commands
*/
@Commons
class SshCommands {
private final Session session
private volatile boolean stop = false
private volatile Thread thread
long commandsTimeout = 10000
SshCommands(Session session) {
this.session = session
}
private synchronized void begin() {
thread = Thread.currentThread()
Thread.start("ssh command waker") {
sleep(commandsTimeout);
stop()
}
}
private synchronized end() {
thread = null
stop = false
}
public synchronized void stop() {
if (!stop) {
stop = true;
if (thread) {
thread.interrupt()
}
}
}
/**
* Execute a list of commands, return the exit status. Output goes to the
* output stream
* @param args , one per line -all converted to strings first. If the list
* element is itself a collection, it is flattened then joined with spaces. This
* can be used to build up more complex commands.
* @return the result of the execution -the last response from the command
*/
List exec(List args) {
assert session && session.connected
List<String> commandSet = args.collect() {
(it instanceof Collection) ? (it.flatten().join(" ")) : it.toString()
}
String commandLine = commandSet.join("\n")
log.info("commands: $commandLine")
ChannelShell channel = null
try {
begin()
channel = (ChannelShell) session.openChannel("shell")
//connect triggers a linkup and the streaming
channel.connect();
assert channel.connected
channel.pty = true
OutputStream toServer = channel.outputStream
Reader fromServer = new BufferedReader(
new InputStreamReader(channel.inputStream, "UTF-8"));
toServer.write(commandLine.bytes)
toServer.flush()
StringBuilder builder = new StringBuilder()
String line
while (!channel.closed && !stop) {
line = fromServer.readLine()
StringBuilder append = builder.append(line)
log.debug(line);
}
int status = channel.exitStatus
if (Thread.interrupted()) {
log.debug("Interrupted while waiting for the end of the SSH stream")
throw new InterruptedIOException()
}
return [status, builder.toString()]
} finally {
end()
boolean interrupted = Thread.interrupted()
channel?.disconnect()
}
}
/**
* Execute a single of commands, return the exit status. Output goes to the
* output stream
* @param args -a list that is flattened then joined with spaces. This
* can be used to build up more complex commands.
* @return the result of the execution -the response from the command
*/
List command(String arg) {
return command([arg])
}
List command(List args) {
assert session && session.connected
session.timeout = commandsTimeout
String commandLine = args.join(" ")
log.info("commands: $commandLine")
ChannelExec channel = null
ByteArrayOutputStream out = new ByteArrayOutputStream();
TeeOutputStream tee =
new TeeOutputStream(out,
KeepAliveOutputStream.wrapSystemOut());
try {
begin()
channel = (ChannelExec) session.openChannel("exec")
channel.setCommand(commandLine);
channel.setOutputStream(tee);
channel.setExtOutputStream(tee);
channel.pty = true
//connect triggers a linkup and the streaming
channel.connect();
assert channel.connected
Thread thread = Thread.start() {
while (!channel.isClosed()) {
if (thread == null) {
return;
}
try {
sleep(500);
} catch (Exception e) {
log.debug("In channel thread $e", e)
// ignored
}
}
}
thread.join(commandsTimeout);
if (thread.isAlive()) {
// ran out of time
thread = null;
throw new IOException("Timeout executing command " + commandLine);
}
String textResult = out.toString("UTF-8")
int status = channel.exitStatus
if (Thread.interrupted()) {
log.debug("Interrupted while waiting for the end of the SSH stream")
throw new InterruptedIOException()
}
return [status, textResult]
} finally {
end()
channel?.disconnect()
}
}
/* List exec(List args, int timeout) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
int rv = exec(outputStream, args, timeout)
String outstr = outputStream.toString("UTF-8")
[rv, outstr]
}*/
}