]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.support.simple/src/main/java/org/argeo/slc/jsch/RemoteExec.java
Improve SSH support
[gpl/argeo-slc.git] / runtime / org.argeo.slc.support.simple / src / main / java / org / argeo / slc / jsch / RemoteExec.java
1 package org.argeo.slc.jsch;
2
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;
13 import java.util.Map;
14
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;
23
24 import com.jcraft.jsch.Channel;
25 import com.jcraft.jsch.ChannelExec;
26 import com.jcraft.jsch.ChannelShell;
27 import com.jcraft.jsch.Session;
28
29 public class RemoteExec extends AbstractJschTask implements InitializingBean {
30 private final static Log log = LogFactory.getLog(RemoteExec.class);
31
32 private Boolean failOnBadExitStatus = true;
33
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>();
43
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) {
49 if (command != null)
50 throw new SlcException("Cannot specify command AND systemCall");
51 commandToUse = convertSystemCall(systemCall);
52 }
53
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));
60 }
61
62 if (script != null) {
63 if (commandsToUse.size() != 0)
64 throw new SlcException("Cannot specify commands and script");
65 BufferedReader reader = null;
66 try {
67 reader = new BufferedReader(new InputStreamReader(script
68 .getInputStream()));
69 String line = null;
70 while ((line = reader.readLine()) != null) {
71 if (!StringUtils.hasText(line))
72 continue;
73 commandsToUse.add(line);
74 }
75 } catch (IOException e) {
76 throw new SlcException("Cannot read script " + script, e);
77 } finally {
78 IOUtils.closeQuietly(reader);
79 }
80 }
81
82 if (forceShell) {
83 commandsToUse.add(commandToUse);
84 commandToUse = null;
85 }
86
87 // execute command(s)
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);
93 } else {
94 if (commandsToUse.size() == 0)
95 throw new SlcException(
96 "Neither a single command or a list of commands has been specified.");
97
98 remoteExec(session, commandsToUse);
99 }
100 }
101
102 protected String convertSystemCall(SystemCall systemCall) {
103 // TODO: prepend environment variables
104 // TODO: deal with exec dir
105 return systemCall.asCommand();
106 }
107
108 protected void remoteExec(Session session, final List<String> commands) {
109 try {
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));
116
117 /*
118 * // Choose the pty-type "vt102".
119 * ((ChannelShell)channel).setPtyType("vt102");
120 */
121 // Writer thread
122 final BufferedWriter writer = new BufferedWriter(
123 new OutputStreamWriter(channel.getOutputStream()));
124
125 channel.connect();
126
127 // write commands to shell
128 Thread writerThread = new Thread("Shell writer " + getSshTarget()) {
129 @Override
130 public void run() {
131 try {
132 for (String line : commands) {
133 if (!StringUtils.hasText(line))
134 continue;
135 writer.write(line);
136 writer.newLine();
137 }
138 writer.append("exit");
139 writer.newLine();
140 writer.flush();
141 // channel.disconnect();
142 } catch (IOException e) {
143 throw new SlcException("Cannot write to shell on "
144 + getSshTarget(), e);
145 } finally {
146 IOUtils.closeQuietly(writer);
147 }
148 }
149 };
150 writerThread.start();
151
152 readStdOut(channel);
153 checkExitStatus(channel);
154 channel.disconnect();
155
156 } catch (Exception e) {
157 throw new SlcException("Cannot use SSH shell on " + getSshTarget(),
158 e);
159 }
160
161 }
162
163 protected void remoteExec(Session session, String command) {
164 try {
165 final ChannelExec channel = (ChannelExec) session
166 .openChannel("exec");
167 channel.setCommand(command);
168
169 channel.setInputStream(null);
170 channel.setXForwarding(xForwarding);
171 channel.setAgentForwarding(agentForwarding);
172 channel.setEnv(new Hashtable<String, String>(env));
173 channel.setErrStream(null);
174
175 // Standard Error
176 readStdErr(channel);
177
178 if (log.isDebugEnabled())
179 log.debug("Run '" + command + "' on " + getSshTarget() + "...");
180 channel.connect();
181 readStdOut(channel);
182 checkExitStatus(channel);
183 channel.disconnect();
184 } catch (Exception e) {
185 throw new SlcException("Cannot execute remotely '" + command
186 + "' on " + getSshTarget(), e);
187 }
188 }
189
190 protected void readStdErr(final ChannelExec channel) {
191 new Thread("stderr " + getSshTarget()) {
192 public void run() {
193 BufferedReader stdErr = null;
194 try {
195 InputStream in = channel.getErrStream();
196 stdErr = new BufferedReader(new InputStreamReader(in));
197 String line = null;
198 while ((line = stdErr.readLine()) != null) {
199 if (!line.trim().equals(""))
200 log.error(line);
201 }
202 } catch (IOException e) {
203 if (log.isDebugEnabled())
204 log.error("Cannot read stderr from " + getSshTarget(),
205 e);
206 } finally {
207 IOUtils.closeQuietly(stdErr);
208 }
209 }
210 }.start();
211 }
212
213 protected void readStdOut(Channel channel) {
214 BufferedReader stdOut = null;
215 try {
216 InputStream in = channel.getInputStream();
217 stdOut = new BufferedReader(new InputStreamReader(in));
218 String line = null;
219 while ((line = stdOut.readLine()) != null) {
220 if (!line.trim().equals(""))
221 log.info(line);
222 }
223 } catch (IOException e) {
224 if (log.isDebugEnabled())
225 log.error("Cannot read stdout from " + getSshTarget(), e);
226 } finally {
227 IOUtils.closeQuietly(stdOut);
228 }
229 }
230
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);
237 } else {
238 String msg = "Remote execution failed with " + " exit status: "
239 + exitStatus;
240 if (failOnBadExitStatus)
241 throw new SlcException(msg);
242 else
243 log.error(msg);
244 }
245 }
246
247 }
248
249 public void afterPropertiesSet() throws Exception {
250 // TODO Auto-generated method stub
251
252 }
253
254 public void setCommand(String command) {
255 this.command = command;
256 }
257
258 public void setCommands(List<String> commands) {
259 this.commands = commands;
260 }
261
262 public void setFailOnBadExitStatus(Boolean failOnBadExitStatus) {
263 this.failOnBadExitStatus = failOnBadExitStatus;
264 }
265
266 public void setSystemCall(SystemCall systemCall) {
267 this.systemCall = systemCall;
268 }
269
270 public void setSystemCalls(List<SystemCall> systemCalls) {
271 this.systemCalls = systemCalls;
272 }
273
274 public void setScript(Resource script) {
275 this.script = script;
276 }
277
278 public void setxForwarding(Boolean xForwarding) {
279 this.xForwarding = xForwarding;
280 }
281
282 public void setAgentForwarding(Boolean agentForwarding) {
283 this.agentForwarding = agentForwarding;
284 }
285
286 public void setEnv(Map<String, String> env) {
287 this.env = env;
288 }
289
290 public void setForceShell(Boolean forceShell) {
291 this.forceShell = forceShell;
292 }
293
294 public List<String> getCommands() {
295 return commands;
296 }
297
298 }