org.jclouds.tools.ant.util.SSHExecute Maven / Gradle / Ivy
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.jclouds.tools.ant.util;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
import org.apache.tools.ant.taskdefs.PumpStreamHandler;
import org.apache.tools.ant.taskdefs.optional.ssh.SSHUserInfo;
import org.apache.tools.ant.util.FileUtils;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
/**
* Executes a command on a remote machine via ssh.
*
*
* adapted from SSHBase and SSHExec ant tasks, and Execute from the ant 1.7.1 release.
*
* @author Adrian Cole
*
*/
public class SSHExecute {
private static final int RETRY_INTERVAL = 500;
/** units are milliseconds, default is 0=infinite */
private long maxwait = 0;
private ExecuteStreamHandler streamHandler;
private String host;
private SSHUserInfo userInfo;
private int port = 22;
private Project project;
private String knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts";
/**
* Creates a new execute object using PumpStreamHandler for stream handling.
*/
public SSHExecute() {
this(new PumpStreamHandler());
}
/**
* Creates a new ssh object.
*
* @param streamHandler
* the stream handler used to handle the input and output streams of the subprocess.
*/
public SSHExecute(ExecuteStreamHandler streamHandler) {
setStreamHandler(streamHandler);
userInfo = new SSHUserInfo();
}
/**
* Set the stream handler to use.
*
* @param streamHandler
* ExecuteStreamHandler.
*/
public void setStreamHandler(ExecuteStreamHandler streamHandler) {
this.streamHandler = streamHandler;
}
/**
* Setting this to true trusts hosts whose identity is unknown.
*
* @param yesOrNo
* if true trust the identity of unknown hosts.
*/
public void setTrust(boolean yesOrNo) {
userInfo.setTrust(yesOrNo);
}
/**
* Used for logging
*/
public void setProject(Project project) {
this.project = project;
}
/**
* Username known to remote host.
*
* @param username
* The new username value
*/
public void setUsername(String username) {
userInfo.setName(username);
}
/**
* Sets the password for the user.
*
* @param password
* The new password value
*/
public void setPassword(String password) {
userInfo.setPassword(password);
}
/**
* Sets the keyfile for the user.
*
* @param keyfile
* The new keyfile value
*/
public void setKeyfile(String keyfile) {
userInfo.setKeyfile(keyfile);
}
/**
* Sets the passphrase for the users key.
*
* @param passphrase
* The new passphrase value
*/
public void setPassphrase(String passphrase) {
userInfo.setPassphrase(passphrase);
}
/**
* Remote host, either DNS name or IP.
*
* @param host
* The new host value
*/
public void setHost(String host) {
this.host = host;
}
/**
* Changes the port used to connect to the remote host.
*
* @param port
* port number of remote host.
*/
public void setPort(int port) {
this.port = port;
}
/**
* The connection can be dropped after a specified number of milliseconds. This is sometimes
* useful when a connection may be flaky. Default is 0, which means "wait forever".
*
* @param timeout
* The new timeout value in seconds
*/
public void setTimeout(long timeout) {
maxwait = timeout;
}
/**
* Sets the path to the file that has the identities of all known hosts. This is used by SSH
* protocol to validate the identity of the host. The default is
* ${user.home}/.ssh/known_hosts.
*
* @param knownHosts
* a path to the known hosts file.
*/
public void setKnownhosts(String knownHosts) {
this.knownHosts = knownHosts;
}
/**
* Execute the command on the remote host.
*
* @param command
* - what to execute on the remote host.
*
* @return return code of the process.
* @throws BuildException
* bad parameter.
* @throws JSchException
* if there's an underlying problem exposed in SSH
* @throws IOException
* if there's a problem attaching streams.
* @throws TimeoutException
* if we exceeded our timeout
*/
public int execute(String command) throws BuildException, JSchException, IOException,
TimeoutException {
if (command == null) {
throw new BuildException("Command is required.");
}
if (host == null) {
throw new BuildException("Host is required.");
}
if (userInfo.getName() == null) {
throw new BuildException("Username is required.");
}
if (userInfo.getKeyfile() == null && userInfo.getPassword() == null) {
throw new BuildException("Password or Keyfile is required.");
}
Session session = null;
try {
session = openSession();
return executeCommand(session, command);
} finally {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
/**
* Open an ssh seession.
*
* @return the opened session
* @throws JSchException
* on error
*/
protected Session openSession() throws JSchException {
JSch jsch = new JSch();
if (null != userInfo.getKeyfile()) {
jsch.addIdentity(userInfo.getKeyfile());
}
if (!userInfo.getTrust() && knownHosts != null) {
project.log("Using known hosts: " + knownHosts, Project.MSG_DEBUG);
jsch.setKnownHosts(knownHosts);
}
Session session = jsch.getSession(userInfo.getName(), host, port);
session.setUserInfo(userInfo);
project.log("Connecting to " + host + ":" + port, Project.MSG_VERBOSE);
session.connect();
return session;
}
/**
*
* FIXME Comment this
*
* @param session
* @param cmd
* @return return code of the process.
* @throws JSchException
* if there's an underlying problem exposed in SSH
* @throws IOException
* if there's a problem attaching streams.
* @throws TimeoutException
* if we exceeded our timeout
*/
private int executeCommand(Session session, String cmd) throws JSchException, IOException,
TimeoutException {
final ChannelExec channel;
session.setTimeout((int) maxwait);
/* execute the command */
channel = (ChannelExec) session.openChannel("exec");
channel.setCommand(cmd);
attachStreams(channel);
project.log("executing command: " + cmd, Project.MSG_VERBOSE);
channel.connect();
try {
waitFor(channel);
} finally {
streamHandler.stop();
closeStreams(channel);
}
return channel.getExitStatus();
}
private void attachStreams(final ChannelExec channel) throws IOException {
streamHandler.setProcessInputStream(channel.getOutputStream());
streamHandler.setProcessOutputStream(channel.getInputStream());
streamHandler.setProcessErrorStream(channel.getErrStream());
streamHandler.start();
}
/**
* Close the streams belonging to the given Process.
*
* @param process
* the Process.
* @throws IOException
*/
public static void closeStreams(ChannelExec process) throws IOException {
FileUtils.close(process.getInputStream());
FileUtils.close(process.getOutputStream());
FileUtils.close(process.getErrStream());
}
/**
* @throws TimeoutException
*/
@SuppressWarnings("deprecation")
private void waitFor(final ChannelExec channel) throws TimeoutException {
// wait for it to finish
Thread thread = new Thread() {
public void run() {
while (!channel.isClosed()) {
try {
sleep(RETRY_INTERVAL);
} catch (InterruptedException e) {
// ignored
}
}
}
};
thread.start();
try {
thread.join(maxwait);
} catch (InterruptedException e) {
// ignored
}
if (thread.isAlive()) {
thread.destroy();
throw new TimeoutException("command still running");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy