1 package org
.argeo
.slc
.jsch
;
3 import java
.io
.BufferedReader
;
4 import java
.io
.BufferedWriter
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
7 import java
.io
.InputStreamReader
;
8 import java
.io
.OutputStreamWriter
;
9 import java
.util
.ArrayList
;
10 import java
.util
.HashMap
;
11 import java
.util
.Hashtable
;
12 import java
.util
.List
;
15 import org
.apache
.commons
.io
.IOUtils
;
16 import org
.apache
.commons
.logging
.Log
;
17 import org
.apache
.commons
.logging
.LogFactory
;
18 import org
.argeo
.slc
.SlcException
;
19 import org
.argeo
.slc
.core
.execution
.tasks
.SystemCall
;
20 import org
.springframework
.beans
.factory
.InitializingBean
;
21 import org
.springframework
.core
.io
.Resource
;
22 import org
.springframework
.util
.StringUtils
;
24 import com
.jcraft
.jsch
.Channel
;
25 import com
.jcraft
.jsch
.ChannelExec
;
26 import com
.jcraft
.jsch
.ChannelShell
;
27 import com
.jcraft
.jsch
.Session
;
29 public class RemoteExec
extends AbstractJschTask
implements InitializingBean
{
30 private final static Log log
= LogFactory
.getLog(RemoteExec
.class);
32 private Boolean failOnBadExitStatus
= true;
34 private List
<String
> commands
= new ArrayList
<String
>();
35 private String command
;
36 private SystemCall systemCall
;
37 private List
<SystemCall
> systemCalls
= new ArrayList
<SystemCall
>();
38 private Resource script
;
39 private Boolean xForwarding
= false;
40 private Boolean agentForwarding
= false;
41 private Boolean forceShell
= false;
42 private Map
<String
, String
> env
= new HashMap
<String
, String
>();
44 public void run(Session session
) {
45 List
<String
> commandsToUse
= new ArrayList
<String
>(commands
);
46 String commandToUse
= command
;
47 // convert system calls
48 if (systemCall
!= null) {
50 throw new SlcException("Cannot specify command AND systemCall");
51 commandToUse
= convertSystemCall(systemCall
);
54 if (systemCalls
.size() != 0) {
55 if (commandsToUse
.size() != 0)
56 throw new SlcException(
57 "Cannot specify commands AND systemCalls");
58 for (SystemCall systemCall
: systemCalls
)
59 commandsToUse
.add(convertSystemCall(systemCall
));
63 if (commandsToUse
.size() != 0)
64 throw new SlcException("Cannot specify commands and script");
65 BufferedReader reader
= null;
67 reader
= new BufferedReader(new InputStreamReader(script
70 while ((line
= reader
.readLine()) != null) {
71 if (!StringUtils
.hasText(line
))
73 commandsToUse
.add(line
);
75 } catch (IOException e
) {
76 throw new SlcException("Cannot read script " + script
, e
);
78 IOUtils
.closeQuietly(reader
);
83 commandsToUse
.add(commandToUse
);
88 if (commandToUse
!= null) {
89 if (commandsToUse
.size() != 0)
90 throw new SlcException(
91 "Specify either a single command or a list of commands.");
92 remoteExec(session
, commandToUse
);
94 if (commandsToUse
.size() == 0)
95 throw new SlcException(
96 "Neither a single command or a list of commands has been specified.");
98 remoteExec(session
, commandsToUse
);
102 protected String
convertSystemCall(SystemCall systemCall
) {
103 // TODO: prepend environment variables
104 // TODO: deal with exec dir
105 return systemCall
.asCommand();
108 protected void remoteExec(Session session
, final List
<String
> commands
) {
110 final ChannelShell channel
= (ChannelShell
) session
111 .openChannel("shell");
112 channel
.setInputStream(null);
113 channel
.setXForwarding(xForwarding
);
114 channel
.setAgentForwarding(agentForwarding
);
115 channel
.setEnv(new Hashtable
<String
, String
>(env
));
118 * // Choose the pty-type "vt102".
119 * ((ChannelShell)channel).setPtyType("vt102");
122 final BufferedWriter writer
= new BufferedWriter(
123 new OutputStreamWriter(channel
.getOutputStream()));
127 // write commands to shell
128 Thread writerThread
= new Thread("Shell writer " + getSshTarget()) {
132 for (String line
: commands
) {
133 if (!StringUtils
.hasText(line
))
138 writer
.append("exit");
141 // channel.disconnect();
142 } catch (IOException e
) {
143 throw new SlcException("Cannot write to shell on "
144 + getSshTarget(), e
);
146 IOUtils
.closeQuietly(writer
);
150 writerThread
.start();
153 checkExitStatus(channel
);
154 channel
.disconnect();
156 } catch (Exception e
) {
157 throw new SlcException("Cannot use SSH shell on " + getSshTarget(),
163 protected void remoteExec(Session session
, String command
) {
165 final ChannelExec channel
= (ChannelExec
) session
166 .openChannel("exec");
167 channel
.setCommand(command
);
169 channel
.setInputStream(null);
170 channel
.setXForwarding(xForwarding
);
171 channel
.setAgentForwarding(agentForwarding
);
172 channel
.setEnv(new Hashtable
<String
, String
>(env
));
173 channel
.setErrStream(null);
178 if (log
.isDebugEnabled())
179 log
.debug("Run '" + command
+ "' on " + getSshTarget() + "...");
182 checkExitStatus(channel
);
183 channel
.disconnect();
184 } catch (Exception e
) {
185 throw new SlcException("Cannot execute remotely '" + command
186 + "' on " + getSshTarget(), e
);
190 protected void readStdErr(final ChannelExec channel
) {
191 new Thread("stderr " + getSshTarget()) {
193 BufferedReader stdErr
= null;
195 InputStream in
= channel
.getErrStream();
196 stdErr
= new BufferedReader(new InputStreamReader(in
));
198 while ((line
= stdErr
.readLine()) != null) {
199 if (!line
.trim().equals(""))
202 } catch (IOException e
) {
203 if (log
.isDebugEnabled())
204 log
.error("Cannot read stderr from " + getSshTarget(),
207 IOUtils
.closeQuietly(stdErr
);
213 protected void readStdOut(Channel channel
) {
214 BufferedReader stdOut
= null;
216 InputStream in
= channel
.getInputStream();
217 stdOut
= new BufferedReader(new InputStreamReader(in
));
219 while ((line
= stdOut
.readLine()) != null) {
220 if (!line
.trim().equals(""))
223 } catch (IOException e
) {
224 if (log
.isDebugEnabled())
225 log
.error("Cannot read stdout from " + getSshTarget(), e
);
227 IOUtils
.closeQuietly(stdOut
);
231 protected void checkExitStatus(Channel channel
) {
232 if (channel
.isClosed()) {
233 int exitStatus
= channel
.getExitStatus();
234 if (exitStatus
== 0) {
235 if (log
.isTraceEnabled())
236 log
.trace("Remote execution exit status: " + exitStatus
);
238 String msg
= "Remote execution failed with " + " exit status: "
240 if (failOnBadExitStatus
)
241 throw new SlcException(msg
);
249 public void afterPropertiesSet() throws Exception
{
250 // TODO Auto-generated method stub
254 public void setCommand(String command
) {
255 this.command
= command
;
258 public void setCommands(List
<String
> commands
) {
259 this.commands
= commands
;
262 public void setFailOnBadExitStatus(Boolean failOnBadExitStatus
) {
263 this.failOnBadExitStatus
= failOnBadExitStatus
;
266 public void setSystemCall(SystemCall systemCall
) {
267 this.systemCall
= systemCall
;
270 public void setSystemCalls(List
<SystemCall
> systemCalls
) {
271 this.systemCalls
= systemCalls
;
274 public void setScript(Resource script
) {
275 this.script
= script
;
278 public void setxForwarding(Boolean xForwarding
) {
279 this.xForwarding
= xForwarding
;
282 public void setAgentForwarding(Boolean agentForwarding
) {
283 this.agentForwarding
= agentForwarding
;
286 public void setEnv(Map
<String
, String
> env
) {
290 public void setForceShell(Boolean forceShell
) {
291 this.forceShell
= forceShell
;
294 public List
<String
> getCommands() {