]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/tasks/SystemCall.java
Add security
[gpl/argeo-slc.git] / runtime / org.argeo.slc.core / src / main / java / org / argeo / slc / core / execution / tasks / SystemCall.java
1 package org.argeo.slc.core.execution.tasks;
2
3 import java.io.File;
4 import java.io.FileWriter;
5 import java.io.IOException;
6 import java.io.Writer;
7 import java.util.HashMap;
8 import java.util.List;
9 import java.util.Map;
10
11 import org.apache.commons.exec.CommandLine;
12 import org.apache.commons.exec.DefaultExecutor;
13 import org.apache.commons.exec.ExecuteException;
14 import org.apache.commons.exec.ExecuteResultHandler;
15 import org.apache.commons.exec.ExecuteWatchdog;
16 import org.apache.commons.exec.Executor;
17 import org.apache.commons.exec.LogOutputStream;
18 import org.apache.commons.exec.PumpStreamHandler;
19 import org.apache.commons.exec.ShutdownHookProcessDestroyer;
20 import org.apache.commons.io.IOUtils;
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.argeo.slc.SlcException;
24 import org.argeo.slc.core.structure.tree.TreeSPath;
25 import org.argeo.slc.core.structure.tree.TreeSRelatedHelper;
26 import org.argeo.slc.core.test.SimpleResultPart;
27 import org.argeo.slc.structure.StructureAware;
28 import org.argeo.slc.test.TestResult;
29 import org.argeo.slc.test.TestStatus;
30 import org.springframework.core.io.Resource;
31
32 /** Execute and OS system call. */
33 public class SystemCall extends TreeSRelatedHelper implements Runnable,
34 StructureAware<TreeSPath> {
35 private final Log log = LogFactory.getLog(getClass());
36
37 private String execDir;
38
39 private String cmd = null;
40 private List<Object> command = null;
41
42 private Boolean synchronous = true;
43
44 private String stdErrLogLevel = "ERROR";
45 private String stdOutLogLevel = "INFO";
46
47 private Resource stdOutFile = null;
48 private Resource stdErrFile = null;
49
50 private Map<String, List<Object>> osCommands = new HashMap<String, List<Object>>();
51 private Map<String, String> osCmds = new HashMap<String, String>();
52 private Map<String, String> environmentVariables = new HashMap<String, String>();
53
54 private Long watchdogTimeout = 24 * 60 * 60 * 1000l;
55
56 private TestResult testResult;
57
58 public SystemCall() {
59
60 }
61
62 public SystemCall(List<Object> command) {
63 super();
64 this.command = command;
65 }
66
67 public void run() {
68 // Log writers
69 final Writer stdOutWriter;
70 final Writer stdErrWriter;
71 if (stdOutFile != null) {
72 stdOutWriter = createWriter(stdOutFile);
73 } else
74 stdOutWriter = null;
75 if (stdErrFile != null) {
76 stdErrWriter = createWriter(stdErrFile);
77 } else {
78 if (stdOutFile != null) {
79 stdErrWriter = createWriter(stdOutFile);
80 } else
81 stdErrWriter = null;
82 }
83
84 try {
85 if (log.isTraceEnabled()) {
86 log.debug("os.name=" + System.getProperty("os.name"));
87 log.debug("os.arch=" + System.getProperty("os.arch"));
88 log.debug("os.version=" + System.getProperty("os.version"));
89 }
90
91 // Execution directory
92 File dir = null;
93 if (execDir != null) {
94 // Replace '/' by local file separator, for portability
95 execDir.replace('/', File.separatorChar);
96 dir = new File(execDir).getCanonicalFile();
97 }
98
99 // Check if an OS specific command overrides
100 String osName = System.getProperty("os.name");
101 List<Object> commandToUse = null;
102 if (osCommands.containsKey(osName))
103 commandToUse = osCommands.get(osName);
104 else
105 commandToUse = command;
106 String cmdToUse = null;
107 if (osCmds.containsKey(osName))
108 cmdToUse = osCmds.get(osName);
109 else
110 cmdToUse = cmd;
111
112 // Prepare executor
113 if (dir == null)
114 dir = new File(getUsedDir(dir));
115 if (!dir.exists())
116 dir.mkdirs();
117
118 Executor executor = new DefaultExecutor();
119 executor.setWatchdog(new ExecuteWatchdog(watchdogTimeout));
120
121 PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(
122 new LogOutputStream() {
123 protected void processLine(String line, int level) {
124 log(stdOutLogLevel, line);
125 if (stdOutWriter != null)
126 appendLineToFile(stdOutWriter, line);
127 }
128 }, new LogOutputStream() {
129 protected void processLine(String line, int level) {
130 log(stdErrLogLevel, line);
131 if (stdErrWriter != null)
132 appendLineToFile(stdErrWriter, line);
133 }
134 }, null);
135 executor.setStreamHandler(pumpStreamHandler);
136 executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());
137 executor.setWorkingDirectory(dir);
138 final CommandLine commandLine;
139
140 // Which command definition to use
141 if (commandToUse == null && cmdToUse == null)
142 throw new SlcException("Please specify a command.");
143 else if (commandToUse != null && cmdToUse != null)
144 throw new SlcException(
145 "Specify the command either as a line or as a list.");
146 else if (cmdToUse != null) {
147 if (log.isTraceEnabled())
148 log.trace("Execute '" + cmdToUse + "' in "
149 + getUsedDir(dir));
150
151 commandLine = CommandLine.parse(cmdToUse);
152 } else if (commandToUse != null) {
153 if (log.isTraceEnabled())
154 log.trace("Execute '" + commandToUse + "' in "
155 + getUsedDir(dir));
156 if (commandToUse.size() == 0)
157 throw new SlcException("Command line is empty.");
158
159 commandLine = new CommandLine(commandToUse.get(0).toString());
160 for (int i = 1; i < commandToUse.size(); i++)
161 commandLine.addArgument(commandToUse.get(i).toString());
162 } else {
163 // all cases covered previously
164 throw new UnsupportedOperationException();
165 }
166
167 // Env variables
168 Map<String, String> environmentVariablesToUse = environmentVariables
169 .size() > 0 ? environmentVariables : null;
170
171 // Execute
172 ExecuteResultHandler executeResultHandler = new ExecuteResultHandler() {
173
174 public void onProcessComplete(int exitValue) {
175 if (log.isDebugEnabled())
176 log.debug("Process " + commandLine
177 + " properly completed.");
178 if (testResult != null) {
179 forwardPath(testResult, null);
180 testResult.addResultPart(new SimpleResultPart(
181 TestStatus.PASSED, "Process " + commandLine
182 + " properly completed."));
183 }
184 }
185
186 public void onProcessFailed(ExecuteException e) {
187 if (testResult != null) {
188 forwardPath(testResult, null);
189 testResult.addResultPart(new SimpleResultPart(
190 TestStatus.ERROR, "Process " + commandLine
191 + " failed.", e));
192 } else {
193 throw new SlcException("Process " + commandLine
194 + " failed.", e);
195 }
196 }
197 };
198
199 if (synchronous)
200 try {
201 int exitValue = executor.execute(commandLine,
202 environmentVariablesToUse);
203 executeResultHandler.onProcessComplete(exitValue);
204 } catch (ExecuteException e1) {
205 executeResultHandler.onProcessFailed(e1);
206 }
207 else
208 executor.execute(commandLine, environmentVariablesToUse,
209 executeResultHandler);
210 } catch (Exception e) {
211 throw new SlcException("Could not execute command " + cmd, e);
212 } finally {
213 IOUtils.closeQuietly(stdOutWriter);
214 IOUtils.closeQuietly(stdErrWriter);
215 }
216
217 }
218
219 /**
220 * Shortcut method returning the current exec dir if the specified one is
221 * null.
222 */
223 private String getUsedDir(File dir) {
224 if (dir == null)
225 return System.getProperty("user.dir");
226 else
227 return dir.getPath();
228 }
229
230 protected void log(String logLevel, String line) {
231 if ("ERROR".equals(logLevel))
232 log.error(line);
233 else if ("WARN".equals(logLevel))
234 log.warn(line);
235 else if ("INFO".equals(logLevel))
236 log.info(line);
237 else if ("DEBUG".equals(logLevel))
238 log.debug(line);
239 else if ("TRACE".equals(logLevel))
240 log.trace(line);
241 else
242 throw new SlcException("Unknown log level " + logLevel);
243 }
244
245 protected void appendLineToFile(Writer writer, String line) {
246 try {
247 writer.append(line).append('\n');
248 } catch (IOException e) {
249 log.error("Cannot write to log file", e);
250 }
251 }
252
253 protected Writer createWriter(Resource target) {
254 FileWriter writer = null;
255 try {
256 File file = target.getFile();
257 writer = new FileWriter(file, true);
258 } catch (IOException e) {
259 log.error("Cannot create log file " + target, e);
260 IOUtils.closeQuietly(writer);
261 }
262 return writer;
263 }
264
265 public void setCmd(String command) {
266 this.cmd = command;
267 }
268
269 public void setExecDir(String execdir) {
270 this.execDir = execdir;
271 }
272
273 public void setStdErrLogLevel(String stdErrLogLevel) {
274 this.stdErrLogLevel = stdErrLogLevel;
275 }
276
277 public void setStdOutLogLevel(String stdOutLogLevel) {
278 this.stdOutLogLevel = stdOutLogLevel;
279 }
280
281 public void setSynchronous(Boolean synchronous) {
282 this.synchronous = synchronous;
283 }
284
285 public void setCommand(List<Object> command) {
286 this.command = command;
287 }
288
289 public void setOsCommands(Map<String, List<Object>> osCommands) {
290 this.osCommands = osCommands;
291 }
292
293 public void setOsCmds(Map<String, String> osCmds) {
294 this.osCmds = osCmds;
295 }
296
297 public void setEnvironmentVariables(Map<String, String> environmentVariables) {
298 this.environmentVariables = environmentVariables;
299 }
300
301 public void setWatchdogTimeout(Long watchdogTimeout) {
302 this.watchdogTimeout = watchdogTimeout;
303 }
304
305 public void setStdOutFile(Resource stdOutFile) {
306 this.stdOutFile = stdOutFile;
307 }
308
309 public void setStdErrFile(Resource stdErrFile) {
310 this.stdErrFile = stdErrFile;
311 }
312
313 public void setTestResult(TestResult testResult) {
314 this.testResult = testResult;
315 }
316
317 }