throw new SlcException("Cannot determine canonical path for "
+ path, e);
}
- if (log.isDebugEnabled())
+ if (log.isTraceEnabled())
log.debug("OSGi local resource: " + file + " from " + resource);
return file;
}
if (sshTarget.getSession() != null) {
Session session = sshTarget.getSession();
if (session.isConnected()) {
- if (log.isDebugEnabled())
- log.debug("Using cached sesison to " + getSshTarget()
+ if (log.isTraceEnabled())
+ log.debug("Using cached session to " + getSshTarget()
+ " via SSH");
return session;
}
package org.argeo.slc.jsch;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
import java.util.List;
+import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.slc.SlcException;
import org.argeo.slc.core.execution.tasks.SystemCall;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+import org.springframework.util.StringUtils;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Session;
-public class RemoteExec extends AbstractJschTask {
+public class RemoteExec extends AbstractJschTask implements InitializingBean {
private final static Log log = LogFactory.getLog(RemoteExec.class);
private Boolean failOnBadExitStatus = true;
private String command;
private SystemCall systemCall;
private List<SystemCall> systemCalls = new ArrayList<SystemCall>();
+ private Resource script;
+ private Boolean xForwarding = false;
+ private Boolean agentForwarding = false;
+ private Boolean forceShell = false;
+ private Map<String, String> env = new HashMap<String, String>();
public void run(Session session) {
+ List<String> commandsToUse = new ArrayList<String>(commands);
+ String commandToUse = command;
// convert system calls
if (systemCall != null) {
if (command != null)
throw new SlcException("Cannot specify command AND systemCall");
- command = convertSystemCall(systemCall);
+ commandToUse = convertSystemCall(systemCall);
}
if (systemCalls.size() != 0) {
- if (commands.size() != 0)
+ if (commandsToUse.size() != 0)
throw new SlcException(
"Cannot specify commands AND systemCalls");
for (SystemCall systemCall : systemCalls)
- commands.add(convertSystemCall(systemCall));
+ commandsToUse.add(convertSystemCall(systemCall));
+ }
+
+ if (script != null) {
+ if (commandsToUse.size() != 0)
+ throw new SlcException("Cannot specify commands and script");
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(script
+ .getInputStream()));
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ if (!StringUtils.hasText(line))
+ continue;
+ commandsToUse.add(line);
+ }
+ } catch (IOException e) {
+ throw new SlcException("Cannot read script " + script, e);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+ if (forceShell) {
+ commandsToUse.add(commandToUse);
+ commandToUse = null;
}
// execute command(s)
- if (command != null) {
- if (commands.size() != 0)
+ if (commandToUse != null) {
+ if (commandsToUse.size() != 0)
throw new SlcException(
"Specify either a single command or a list of commands.");
- remoteExec(session, command);
+ remoteExec(session, commandToUse);
} else {
- if (commands.size() == 0)
+ if (commandsToUse.size() == 0)
throw new SlcException(
"Neither a single command or a list of commands has been specified.");
- for (String cmd : commands) {
- remoteExec(session, cmd);
- }
+ remoteExec(session, commandsToUse);
}
}
protected String convertSystemCall(SystemCall systemCall) {
- // TODO: prepend environemnt variables
+ // TODO: prepend environment variables
// TODO: deal with exec dir
return systemCall.asCommand();
}
- protected void remoteExec(Session session, String command) {
- BufferedReader execIn = null;
+ protected void remoteExec(Session session, final List<String> commands) {
try {
- Channel channel = session.openChannel("exec");
- ((ChannelExec) channel).setCommand(command);
+ final ChannelShell channel = (ChannelShell) session
+ .openChannel("shell");
+ channel.setInputStream(null);
+ channel.setXForwarding(xForwarding);
+ channel.setAgentForwarding(agentForwarding);
+ channel.setEnv(new Hashtable<String, String>(env));
- // X Forwarding
- // channel.setXForwarding(true);
+ /*
+ * // Choose the pty-type "vt102".
+ * ((ChannelShell)channel).setPtyType("vt102");
+ */
+ // Writer thread
+ final BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(channel.getOutputStream()));
- // channel.setInputStream(System.in);
- channel.setInputStream(null);
+ channel.connect();
- ((ChannelExec) channel).setErrStream(System.err);
+ // write commands to shell
+ Thread writerThread = new Thread("Shell writer " + getSshTarget()) {
+ @Override
+ public void run() {
+ try {
+ for (String line : commands) {
+ if (!StringUtils.hasText(line))
+ continue;
+ writer.write(line);
+ writer.newLine();
+ }
+ writer.append("exit");
+ writer.newLine();
+ writer.flush();
+ // channel.disconnect();
+ } catch (IOException e) {
+ throw new SlcException("Cannot write to shell on "
+ + getSshTarget(), e);
+ } finally {
+ IOUtils.closeQuietly(writer);
+ }
+ }
+ };
+ writerThread.start();
- InputStream in = channel.getInputStream();
+ readStdOut(channel);
+ checkExitStatus(channel);
+ channel.disconnect();
- if (log.isDebugEnabled())
- log.debug("Run '" + command + "' on " + getSshTarget() + "...");
+ } catch (Exception e) {
+ throw new SlcException("Cannot use SSH shell on " + getSshTarget(),
+ e);
+ }
- channel.connect();
+ }
- // byte[] tmp = new byte[1024];
- while (true) {
- execIn = new BufferedReader(new InputStreamReader(in));
- String line = null;
- while ((line = execIn.readLine()) != null) {
- if (!line.trim().equals(""))
- log.info(line);
- }
+ protected void remoteExec(Session session, String command) {
+ try {
+ final ChannelExec channel = (ChannelExec) session
+ .openChannel("exec");
+ channel.setCommand(command);
- if (channel.isClosed()) {
- int exitStatus = channel.getExitStatus();
- if (exitStatus == 0) {
- if (log.isTraceEnabled())
- log.trace("Remote execution exit status: "
- + exitStatus);
- } else {
- String msg = "Remote execution failed with "
- + " exit status: " + exitStatus;
- if (failOnBadExitStatus)
- throw new SlcException(msg);
- else
- log.error(msg);
- }
+ channel.setInputStream(null);
+ channel.setXForwarding(xForwarding);
+ channel.setAgentForwarding(agentForwarding);
+ channel.setEnv(new Hashtable<String, String>(env));
+ channel.setErrStream(null);
- break;
- }
- try {
- Thread.sleep(1000);
- } catch (Exception ee) {
- }
- }
+ // Standard Error
+ readStdErr(channel);
+
+ if (log.isDebugEnabled())
+ log.debug("Run '" + command + "' on " + getSshTarget() + "...");
+ channel.connect();
+ readStdOut(channel);
+ checkExitStatus(channel);
channel.disconnect();
} catch (Exception e) {
throw new SlcException("Cannot execute remotely '" + command
+ "' on " + getSshTarget(), e);
+ }
+ }
+
+ protected void readStdErr(final ChannelExec channel) {
+ new Thread("stderr " + getSshTarget()) {
+ public void run() {
+ BufferedReader stdErr = null;
+ try {
+ InputStream in = channel.getErrStream();
+ stdErr = new BufferedReader(new InputStreamReader(in));
+ String line = null;
+ while ((line = stdErr.readLine()) != null) {
+ if (!line.trim().equals(""))
+ log.error(line);
+ }
+ } catch (IOException e) {
+ if (log.isDebugEnabled())
+ log.error("Cannot read stderr from " + getSshTarget(),
+ e);
+ } finally {
+ IOUtils.closeQuietly(stdErr);
+ }
+ }
+ }.start();
+ }
+
+ protected void readStdOut(Channel channel) {
+ BufferedReader stdOut = null;
+ try {
+ InputStream in = channel.getInputStream();
+ stdOut = new BufferedReader(new InputStreamReader(in));
+ String line = null;
+ while ((line = stdOut.readLine()) != null) {
+ if (!line.trim().equals(""))
+ log.info(line);
+ }
+ } catch (IOException e) {
+ if (log.isDebugEnabled())
+ log.error("Cannot read stdout from " + getSshTarget(), e);
} finally {
- IOUtils.closeQuietly(execIn);
+ IOUtils.closeQuietly(stdOut);
+ }
+ }
+
+ protected void checkExitStatus(Channel channel) {
+ if (channel.isClosed()) {
+ int exitStatus = channel.getExitStatus();
+ if (exitStatus == 0) {
+ if (log.isTraceEnabled())
+ log.trace("Remote execution exit status: " + exitStatus);
+ } else {
+ String msg = "Remote execution failed with " + " exit status: "
+ + exitStatus;
+ if (failOnBadExitStatus)
+ throw new SlcException(msg);
+ else
+ log.error(msg);
+ }
}
+
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ // TODO Auto-generated method stub
+
}
public void setCommand(String command) {
this.systemCalls = systemCalls;
}
+ public void setScript(Resource script) {
+ this.script = script;
+ }
+
+ public void setxForwarding(Boolean xForwarding) {
+ this.xForwarding = xForwarding;
+ }
+
+ public void setAgentForwarding(Boolean agentForwarding) {
+ this.agentForwarding = agentForwarding;
+ }
+
+ public void setEnv(Map<String, String> env) {
+ this.env = env;
+ }
+
+ public void setForceShell(Boolean forceShell) {
+ this.forceShell = forceShell;
+ }
+
+ public List<String> getCommands() {
+ return commands;
+ }
+
}
package org.argeo.slc.jsch;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
-import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
}
if (localResource != null) {
- try {
- File lFile = localResource.getFile();
- uploadFile(session, lFile, remotePath);
- } catch (IOException e) {
- OutputStream out = null;
- InputStream in = null;
- File tempFile = null;
- try {
- tempFile = File.createTempFile(getClass().getSimpleName()
- + "-" + localResource.getFilename(), null);
- out = FileUtils.openOutputStream(tempFile);
- in = localResource.getInputStream();
- IOUtils.copy(in, out);
- uploadFile(session, tempFile, remotePath);
- } catch (IOException e1) {
- throw new SlcException("Can neither interpret resource "
- + localResource
- + " as file, nor create a temporary file", e1);
- } finally {
- IOUtils.closeQuietly(in);
- IOUtils.closeQuietly(out);
- FileUtils.deleteQuietly(tempFile);
- }
- }
+ uploadResource(session, localResource, remoteDir);
}
}
return false;
}
- protected void uploadFile(Session session, File localFile, String remoteFile) {
- InputStream in = null;
+ protected void uploadFile(Session session, File file, String remoteFile) {
+ try {
+ uploadFile(session, new FileInputStream(file), file.length(), file
+ .getPath(), file.toString(), remoteFile);
+ } catch (FileNotFoundException e) {
+ throw new SlcException("Cannot upload " + file, e);
+ }
+ }
+
+ protected void uploadResource(Session session, Resource resource,
+ String remoteFile) {
+ try {
+ File lFile = resource.getFile();
+ uploadFile(session, lFile, remotePath);
+ } catch (IOException e) {
+ // no underlying file found
+ // load the resource in memory before transferring it
+ InputStream in = null;
+ try {
+ in = resource.getInputStream();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ IOUtils.copy(in, out);
+ byte[] arr = out.toByteArray();
+ ByteArrayInputStream content = new ByteArrayInputStream(arr);
+ uploadFile(session, content, arr.length, resource.getURL()
+ .getPath(), resource.toString(), remotePath);
+ arr = null;
+ } catch (IOException e1) {
+ throw new SlcException("Can neither interpret resource "
+ + localResource
+ + " as file, nor create a temporary file", e1);
+ } finally {
+ IOUtils.closeQuietly(in);
+ // no need to close byte arrays streams
+ }
+ }
+ }
+
+ protected void uploadFile(Session session, InputStream in, long size,
+ String path, String sourceDesc, String remoteFile) {
OutputStream channelOut;
InputStream channelIn;
try {
// send "C0644 filesize filename", where filename should not include
// '/'
- long filesize = localFile.length();
+ long filesize = size;
command = "C0644 " + filesize + " ";
- int index = localFile.getPath().lastIndexOf('/');
+ int index = path.lastIndexOf('/');
if (index > 0) {
- command += localFile.getPath().substring(index + 1);
+ command += path.substring(index + 1);
} else {
- command += localFile.getPath();
+ command += path;
}
command += "\n";
checkAck(channelIn);
if (log.isTraceEnabled())
- log.debug("Start copy of " + localFile + " to " + remoteFile
+ log.debug("Start copy of " + sourceDesc + " to " + remoteFile
+ " on " + getSshTarget() + "...");
final long oneMB = 1024l;// in KB
final long tenMB = 10 * oneMB;// in KB
// send a content of lfile
- in = new FileInputStream(localFile);
byte[] buf = new byte[1024];
long cycleCount = 0;
+ long nbrOfBytes = 0;
while (true) {
int len = in.read(buf, 0, buf.length);
if (len <= 0)
break;
channelOut.write(buf, 0, len); // out.flush();
+ nbrOfBytes = nbrOfBytes + len;
if (((cycleCount % oneMB) == 0) && cycleCount != 0)// each 1 MB
System.out.print('#');
if (((cycleCount % (tenMB)) == 0) && cycleCount != 0)// each 10
channelOut.flush();
checkAck(channelIn);
- if (log.isTraceEnabled())
- log.debug((cycleCount) + " KB sent to server. ("
- + (cycleCount / oneMB + " MB)"));
-
if (log.isDebugEnabled())
- log.debug("Finished copy to " + remoteFile + " on "
- + getSshTarget() + " from " + localFile);
+ log.debug("Transferred to " + remoteFile + " ("
+ + sizeDesc(nbrOfBytes) + ") on " + getSshTarget()
+ + " from " + sourceDesc);
IOUtils.closeQuietly(channelOut);
channel.disconnect();
} catch (Exception e) {
- throw new SlcException("Cannot copy " + localFile + " to "
- + remoteFile, e);
+ throw new SlcException("Cannot copy " + path + " to " + remoteFile,
+ e);
} finally {
IOUtils.closeQuietly(in);
}
}
+ protected String sizeDesc(Long nbrOfBytes) {
+ if (nbrOfBytes < 1024)
+ return nbrOfBytes + " B";
+ else if (nbrOfBytes < 1024 * 1024)
+ return (nbrOfBytes / 1024) + " KB";
+ else
+ return nbrOfBytes / (1024 * 1024) + " MB";
+ }
+
public void setLocalResource(Resource localFile) {
this.localResource = localFile;
}
dir = targetBase + '/' + relPath.substring(0, lastIndexSubDir);
else
dir = targetBase;
+
if (!subDirs.contains(dir)) {
- RemoteExec remoteExec = new RemoteExec();
- remoteExec.setCommand("mkdir -p " + dir);
subDirs.add(dir);
- multiTasks.getTasks().add(remoteExec);
}
// Copy resource
// TODO: set permissions
}
+ RemoteExec remoteExec = new RemoteExec();
+ for (String dir : subDirs) {
+ remoteExec.getCommands().add("mkdir -p " + dir);
+ }
+ multiTasks.getTasks().add(0, remoteExec);
+
multiTasks.setSshTarget(getSshTarget());
multiTasks.run(session);
}
--- /dev/null
+package org.argeo.slc.jsch;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.springframework.core.io.Resource;
+import org.springframework.util.StringUtils;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.Session;
+
+public class SshShell extends AbstractJschTask {
+ private final static Log log = LogFactory.getLog(SshShell.class);
+ private Resource input;
+
+ @Override
+ void run(Session session) {
+ try {
+ final Channel channel = session.openChannel("shell");
+
+ // Enable agent-forwarding.
+ // ((ChannelShell)channel).setAgentForwarding(true);
+
+ // channel.setInputStream(System.in);
+ // channel.setInputStream(input.getInputStream());
+ /*
+ * // a hack for MS-DOS prompt on Windows.
+ * channel.setInputStream(new FilterInputStream(System.in){ public
+ * int read(byte[] b, int off, int len)throws IOException{ return
+ * in.read(b, off, (len>1024?1024:len)); } });
+ */
+
+ // channel.setOutputStream(System.out);
+
+ /*
+ * // Choose the pty-type "vt102".
+ * ((ChannelShell)channel).setPtyType("vt102");
+ */
+
+ /*
+ * // Set environment variable "LANG" as "ja_JP.eucJP".
+ * ((ChannelShell)channel).setEnv("LANG", "ja_JP.eucJP");
+ */
+
+ // Writer thread
+ final BufferedWriter writer = new BufferedWriter(
+ new OutputStreamWriter(channel.getOutputStream()));
+
+ // channel.connect();
+ channel.connect(3 * 1000);
+
+ // while (!channel.isConnected())
+ // try {
+ // Thread.sleep(500);
+ // } catch (InterruptedException e1) {
+ // // silent
+ // }
+
+ Thread writerThread = new Thread("Shell writer " + getSshTarget()) {
+
+ @Override
+ public void run() {
+
+ if (log.isDebugEnabled())
+ log.debug("Start writing to shell");
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(input
+ .getInputStream()));
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ if (!StringUtils.hasText(line))
+ continue;
+ writer.write(line);
+ writer.newLine();
+ }
+ writer.append("exit");
+ writer.newLine();
+ writer.flush();
+ // channel.disconnect();
+ } catch (IOException e) {
+ throw new SlcException("Cannot write to shell on "
+ + getSshTarget(), e);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+ };
+ writerThread.start();
+
+ BufferedReader execIn = null;
+ try {
+ execIn = new BufferedReader(new InputStreamReader(channel
+ .getInputStream()));
+ String line = null;
+ while ((line = execIn.readLine()) != null) {
+ if (!line.trim().equals(""))
+ log.info(line);
+ }
+ } catch (Exception e) {
+ throw new SlcException("Cannot read from shell on "
+ + getSshTarget(), e);
+ } finally {
+ IOUtils.closeQuietly(execIn);
+ }
+
+ } catch (Exception e) {
+ throw new SlcException("Cannot use SSH shell on " + getSshTarget(),
+ e);
+ }
+ }
+
+ public void setInput(Resource input) {
+ this.input = input;
+ }
+
+}
}
public String toString() {
- return "ssh:" + getUser() + "@" + getHost() + ":" + getPort();
+ return getUser() + "@" + getHost() + ":" + getPort();
}
public Session getSession() {