X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=runtime%2Forg.argeo.slc.support.simple%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fslc%2Fjsch%2FRemoteExec.java;h=814e080481219641027d74f96df82648cf8f0eab;hb=f3d820826d86548e0f6aa948066896f117ebcbf2;hp=094b77fc183e0118723ce03686dbf22a59176d6e;hpb=7cb867319d0f8b1b0af3e7eb3f9833b091281320;p=gpl%2Fargeo-slc.git diff --git a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/RemoteExec.java b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/RemoteExec.java index 094b77fc1..814e08048 100644 --- a/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/RemoteExec.java +++ b/runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/RemoteExec.java @@ -1,23 +1,43 @@ +/* + * Copyright (C) 2010 Mathieu Baudier + * + * 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. + */ + package org.argeo.slc.jsch; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; 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 java.util.StringTokenizer; 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.ExecutionResources; 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; @@ -26,7 +46,7 @@ import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.ChannelShell; import com.jcraft.jsch.Session; -public class RemoteExec extends AbstractJschTask implements InitializingBean { +public class RemoteExec extends AbstractJschTask { private final static Log log = LogFactory.getLog(RemoteExec.class); private Boolean failOnBadExitStatus = true; @@ -40,6 +60,27 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { private Boolean agentForwarding = false; private Boolean forceShell = false; private Map env = new HashMap(); + private Resource stdIn = null; + private Resource stdOut = null; + private ExecutionResources executionResources; + + private String user; + + /** + * If set, stdout is written to it as a list of lines. Cleared before each + * run. + */ + private List stdOutLines = null; + private Boolean logEvenIfStdOutLines = false; + private Boolean quiet = false; + + public RemoteExec() { + } + + public RemoteExec(SshTarget sshTarget, String cmd) { + setSshTarget(sshTarget); + setCommand(cmd); + } public void run(Session session) { List commandsToUse = new ArrayList(commands); @@ -60,6 +101,7 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { } if (script != null) { + // TODO: simply pass the script as a string command if (commandsToUse.size() != 0) throw new SlcException("Cannot specify commands and script"); BufferedReader reader = null; @@ -80,10 +122,40 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { } if (forceShell) { - commandsToUse.add(commandToUse); + // for the time being do not interpret both \n and ; + // priority to \n + // until we know how to parse ; within "" + if (commandToUse.indexOf('\n') >= 0) { + StringTokenizer st = new StringTokenizer(commandToUse, "\n"); + while (st.hasMoreTokens()) { + String cmd = st.nextToken(); + commandsToUse.add(cmd); + } + } else if (commandToUse.indexOf(';') >= 0) { + StringTokenizer st = new StringTokenizer(commandToUse, ";"); + while (st.hasMoreTokens()) { + String cmd = st.nextToken(); + commandsToUse.add(cmd); + } + } else { + commandsToUse.add(commandToUse); + } commandToUse = null; } + // run as user + if (user != null) { + if (commandsToUse.size() > 0) { + commandsToUse.add(0, "su - " + user); + commandsToUse.add("exit"); + } else { + if (command.indexOf('\"') >= 0) + throw new SlcException( + "Don't know how to su a command with \", use shell instead."); + commandToUse = "su - " + user + " -c \"" + command + "\""; + } + } + // execute command(s) if (commandToUse != null) { if (commandsToUse.size() != 0) @@ -95,7 +167,8 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { throw new SlcException( "Neither a single command or a list of commands has been specified."); - remoteExec(session, commandsToUse); + remoteExec(session, commandsToUse, script != null ? "script " + + script.getFilename() : commandsToUse.size() + " commands"); } } @@ -105,7 +178,8 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { return systemCall.asCommand(); } - protected void remoteExec(Session session, final List commands) { + protected void remoteExec(Session session, final List commands, + String description) { try { final ChannelShell channel = (ChannelShell) session .openChannel("shell"); @@ -122,6 +196,9 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { final BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(channel.getOutputStream())); + if (log.isDebugEnabled()) + log.debug("Run " + description + " on " + getSshTarget() + + "..."); channel.connect(); // write commands to shell @@ -178,6 +255,25 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { if (log.isDebugEnabled()) log.debug("Run '" + command + "' on " + getSshTarget() + "..."); channel.connect(); + + if (stdIn != null) { + Thread stdInThread = new Thread("Stdin " + getSshTarget()) { + @Override + public void run() { + OutputStream out = null; + try { + out = channel.getOutputStream(); + IOUtils.copy(stdIn.getInputStream(), out); + } catch (IOException e) { + throw new SlcException("Cannot write stdin on " + + getSshTarget(), e); + } finally { + IOUtils.closeQuietly(out); + } + } + }; + stdInThread.start(); + } readStdOut(channel); checkExitStatus(channel); channel.disconnect(); @@ -211,20 +307,40 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { } 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); + if (stdOut != null) { + OutputStream localStdOut = createOutputStream(stdOut); + try { + IOUtils.copy(channel.getInputStream(), localStdOut); + } catch (IOException e) { + throw new SlcException("Cannot redirect stdout", e); + } finally { + IOUtils.closeQuietly(localStdOut); + } + } else { + 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("")) { + + if (stdOutLines != null) { + stdOutLines.add(line); + if (logEvenIfStdOutLines && !quiet) + log.info(line); + } else { + if (!quiet) + log.info(line); + } + } + } + } catch (IOException e) { + if (log.isDebugEnabled()) + log.error("Cannot read stdout from " + getSshTarget(), e); + } finally { + IOUtils.closeQuietly(stdOut); } - } catch (IOException e) { - if (log.isDebugEnabled()) - log.error("Cannot read stdout from " + getSshTarget(), e); - } finally { - IOUtils.closeQuietly(stdOut); } } @@ -246,9 +362,21 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { } - public void afterPropertiesSet() throws Exception { - // TODO Auto-generated method stub + protected OutputStream createOutputStream(Resource target) { + FileOutputStream out = null; + try { + final File file; + if (executionResources != null) + file = new File(executionResources.getAsOsPath(target, true)); + else + file = target.getFile(); + out = new FileOutputStream(file, false); + } catch (IOException e) { + log.error("Cannot get file for " + target, e); + IOUtils.closeQuietly(out); + } + return out; } public void setCommand(String command) { @@ -295,4 +423,32 @@ public class RemoteExec extends AbstractJschTask implements InitializingBean { return commands; } + public void setStdOutLines(List stdOutLines) { + this.stdOutLines = stdOutLines; + } + + public void setLogEvenIfStdOutLines(Boolean logEvenIfStdOutLines) { + this.logEvenIfStdOutLines = logEvenIfStdOutLines; + } + + public void setQuiet(Boolean quiet) { + this.quiet = quiet; + } + + public void setStdIn(Resource stdIn) { + this.stdIn = stdIn; + } + + public void setStdOut(Resource stdOut) { + this.stdOut = stdOut; + } + + public void setExecutionResources(ExecutionResources executionResources) { + this.executionResources = executionResources; + } + + public void setUser(String user) { + this.user = user; + } + }