View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.slc.core.execution.tasks;
17  
18  import java.io.File;
19  import java.io.FileOutputStream;
20  import java.io.FileWriter;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.io.PipedInputStream;
25  import java.io.PipedOutputStream;
26  import java.io.Writer;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.UUID;
33  
34  import javax.security.auth.callback.CallbackHandler;
35  
36  import org.apache.commons.exec.CommandLine;
37  import org.apache.commons.exec.DefaultExecutor;
38  import org.apache.commons.exec.ExecuteException;
39  import org.apache.commons.exec.ExecuteResultHandler;
40  import org.apache.commons.exec.ExecuteStreamHandler;
41  import org.apache.commons.exec.ExecuteWatchdog;
42  import org.apache.commons.exec.Executor;
43  import org.apache.commons.exec.LogOutputStream;
44  import org.apache.commons.exec.PumpStreamHandler;
45  import org.apache.commons.exec.ShutdownHookProcessDestroyer;
46  import org.apache.commons.io.FileUtils;
47  import org.apache.commons.io.IOUtils;
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  import org.argeo.slc.SlcException;
51  import org.argeo.slc.UnsupportedException;
52  import org.argeo.slc.core.execution.ExecutionResources;
53  import org.argeo.slc.core.test.SimpleResultPart;
54  import org.argeo.slc.test.TestResult;
55  import org.argeo.slc.test.TestStatus;
56  import org.springframework.core.io.Resource;
57  
58  /** Execute an OS specific system call. */
59  public class SystemCall implements Runnable {
60  	public final static String LOG_STDOUT = "System.out";
61  
62  	private final Log log = LogFactory.getLog(getClass());
63  
64  	private String execDir;
65  
66  	private String cmd = null;
67  	private List<Object> command = null;
68  
69  	private Executor executor = new DefaultExecutor();
70  	private Boolean synchronous = true;
71  
72  	private String stdErrLogLevel = "ERROR";
73  	private String stdOutLogLevel = "INFO";
74  
75  	private Resource stdOutFile = null;
76  	private Resource stdErrFile = null;
77  
78  	private Resource stdInFile = null;
79  	/**
80  	 * If no {@link #stdInFile} provided, writing to this stream will write to
81  	 * the stdin of the process.
82  	 */
83  	private OutputStream stdInSink = null;
84  
85  	private Boolean redirectStdOut = false;
86  
87  	private List<SystemCallOutputListener> outputListeners = Collections
88  			.synchronizedList(new ArrayList<SystemCallOutputListener>());
89  
90  	private Map<String, List<Object>> osCommands = new HashMap<String, List<Object>>();
91  	private Map<String, String> osCmds = new HashMap<String, String>();
92  	private Map<String, String> environmentVariables = new HashMap<String, String>();
93  
94  	private Boolean logCommand = false;
95  	private Boolean redirectStreams = true;
96  	private Boolean exceptionOnFailed = true;
97  	private Boolean mergeEnvironmentVariables = true;
98  
99  //	private Authentication authentication;
100 
101 	private String osConsole = null;
102 	private String generateScript = null;
103 
104 	/** 24 hours */
105 	private Long watchdogTimeout = 24 * 60 * 60 * 1000l;
106 
107 	private TestResult testResult;
108 
109 	private ExecutionResources executionResources;
110 
111 	/** Sudo the command, as root if empty or as user if not. */
112 	private String sudo = null;
113 	// TODO make it more secure and robust, test only once
114 	private final String sudoPrompt = UUID.randomUUID().toString();
115 	private String askPassProgram = "/usr/libexec/openssh/ssh-askpass";
116 	@SuppressWarnings("unused")
117 	private boolean firstLine = true;
118 	@SuppressWarnings("unused")
119 	private CallbackHandler callbackHandler;
120 	/** Chroot to the this path (must not be empty) */
121 	private String chroot = null;
122 
123 	// Current
124 	/** Current watchdog, null if process is completed */
125 	ExecuteWatchdog currentWatchdog = null;
126 
127 	/** Empty constructor */
128 	public SystemCall() {
129 
130 	}
131 
132 	/**
133 	 * Constructor based on the provided command list.
134 	 * 
135 	 * @param command
136 	 *            the command list
137 	 */
138 	public SystemCall(List<Object> command) {
139 		this.command = command;
140 	}
141 
142 	/**
143 	 * Constructor based on the provided command.
144 	 * 
145 	 * @param cmd
146 	 *            the command. If the provided string contains no space a
147 	 *            command list is initialized with the argument as first
148 	 *            component (useful for chained construction)
149 	 */
150 	public SystemCall(String cmd) {
151 		if (cmd.indexOf(' ') < 0) {
152 			command = new ArrayList<Object>();
153 			command.add(cmd);
154 		} else {
155 			this.cmd = cmd;
156 		}
157 	}
158 
159 	/** Executes the system call. */
160 	public void run() {
161 //		authentication = SecurityContextHolder.getContext().getAuthentication();
162 
163 		// Manage streams
164 		Writer stdOutWriter = null;
165 		OutputStream stdOutputStream = null;
166 		Writer stdErrWriter = null;
167 		InputStream stdInStream = null;
168 		if (stdOutFile != null)
169 			if (redirectStdOut)
170 				stdOutputStream = createOutputStream(stdOutFile);
171 			else
172 				stdOutWriter = createWriter(stdOutFile, true);
173 
174 		if (stdErrFile != null) {
175 			stdErrWriter = createWriter(stdErrFile, true);
176 		} else {
177 			if (stdOutFile != null && !redirectStdOut)
178 				stdErrWriter = createWriter(stdOutFile, true);
179 		}
180 
181 		try {
182 			if (stdInFile != null)
183 				stdInStream = stdInFile.getInputStream();
184 			else {
185 				stdInStream = new PipedInputStream();
186 				stdInSink = new PipedOutputStream(
187 						(PipedInputStream) stdInStream);
188 			}
189 		} catch (IOException e2) {
190 			throw new SlcException("Cannot open a stream for " + stdInFile, e2);
191 		}
192 
193 		if (log.isTraceEnabled()) {
194 			log.debug("os.name=" + System.getProperty("os.name"));
195 			log.debug("os.arch=" + System.getProperty("os.arch"));
196 			log.debug("os.version=" + System.getProperty("os.version"));
197 		}
198 
199 		// Execution directory
200 		File dir = new File(getExecDirToUse());
201 		// if (!dir.exists())
202 		// dir.mkdirs();
203 
204 		// Watchdog to check for lost processes
205 		Executor executorToUse;
206 		if (executor != null)
207 			executorToUse = executor;
208 		else
209 			executorToUse = new DefaultExecutor();
210 		executorToUse.setWatchdog(createWatchdog());
211 
212 		if (redirectStreams) {
213 			// Redirect standard streams
214 			executorToUse.setStreamHandler(createExecuteStreamHandler(
215 					stdOutWriter, stdOutputStream, stdErrWriter, stdInStream));
216 		} else {
217 			// Dummy stream handler (otherwise pump is used)
218 			executorToUse.setStreamHandler(new DummyexecuteStreamHandler());
219 		}
220 
221 		executorToUse.setProcessDestroyer(new ShutdownHookProcessDestroyer());
222 		executorToUse.setWorkingDirectory(dir);
223 
224 		// Command line to use
225 		final CommandLine commandLine = createCommandLine();
226 		if (logCommand)
227 			log.info("Execute command:\n" + commandLine
228 					+ "\n in working directory: \n" + dir + "\n");
229 
230 		// Env variables
231 		Map<String, String> environmentVariablesToUse = null;
232 		environmentVariablesToUse = new HashMap<String, String>();
233 		if (mergeEnvironmentVariables)
234 			environmentVariablesToUse.putAll(System.getenv());
235 		if (environmentVariables.size() > 0)
236 			environmentVariablesToUse.putAll(environmentVariables);
237 
238 		// Execute
239 		ExecuteResultHandler executeResultHandler = createExecuteResultHandler(commandLine);
240 
241 		//
242 		// THE EXECUTION PROPER
243 		//
244 		try {
245 			if (synchronous)
246 				try {
247 					int exitValue = executorToUse.execute(commandLine,
248 							environmentVariablesToUse);
249 					executeResultHandler.onProcessComplete(exitValue);
250 				} catch (ExecuteException e1) {
251 					if (e1.getExitValue() == Executor.INVALID_EXITVALUE) {
252 						Thread.currentThread().interrupt();
253 						return;
254 					}
255 					// Sleep 1s in order to make sure error logs are flushed
256 					Thread.sleep(1000);
257 					executeResultHandler.onProcessFailed(e1);
258 				}
259 			else
260 				executorToUse.execute(commandLine, environmentVariablesToUse,
261 						executeResultHandler);
262 		} catch (SlcException e) {
263 			throw e;
264 		} catch (Exception e) {
265 			throw new SlcException("Could not execute command " + commandLine,
266 					e);
267 		} finally {
268 			IOUtils.closeQuietly(stdOutWriter);
269 			IOUtils.closeQuietly(stdErrWriter);
270 			IOUtils.closeQuietly(stdInStream);
271 			IOUtils.closeQuietly(stdInSink);
272 		}
273 
274 	}
275 
276 	public synchronized String function() {
277 		final StringBuffer buf = new StringBuffer("");
278 		SystemCallOutputListener tempOutputListener = new SystemCallOutputListener() {
279 			private Long lineCount = 0l;
280 
281 			public void newLine(SystemCall systemCall, String line,
282 					Boolean isError) {
283 				if (!isError) {
284 					if (lineCount != 0l)
285 						buf.append('\n');
286 					buf.append(line);
287 					lineCount++;
288 				}
289 			}
290 		};
291 		addOutputListener(tempOutputListener);
292 		run();
293 		removeOutputListener(tempOutputListener);
294 		return buf.toString();
295 	}
296 
297 	public String asCommand() {
298 		return createCommandLine().toString();
299 	}
300 
301 	@Override
302 	public String toString() {
303 		return asCommand();
304 	}
305 
306 	/**
307 	 * Build a command line based on the properties. Can be overridden by
308 	 * specific command wrappers.
309 	 */
310 	protected CommandLine createCommandLine() {
311 		// Check if an OS specific command overrides
312 		String osName = System.getProperty("os.name");
313 		List<Object> commandToUse = null;
314 		if (osCommands.containsKey(osName))
315 			commandToUse = osCommands.get(osName);
316 		else
317 			commandToUse = command;
318 		String cmdToUse = null;
319 		if (osCmds.containsKey(osName))
320 			cmdToUse = osCmds.get(osName);
321 		else
322 			cmdToUse = cmd;
323 
324 		CommandLine commandLine = null;
325 
326 		// Which command definition to use
327 		if (commandToUse == null && cmdToUse == null)
328 			throw new SlcException("Please specify a command.");
329 		else if (commandToUse != null && cmdToUse != null)
330 			throw new SlcException(
331 					"Specify the command either as a line or as a list.");
332 		else if (cmdToUse != null) {
333 			if (chroot != null && !chroot.trim().equals(""))
334 				cmdToUse = "chroot \"" + chroot + "\" " + cmdToUse;
335 			if (sudo != null) {
336 				environmentVariables.put("SUDO_ASKPASS", askPassProgram);
337 				if (!sudo.trim().equals(""))
338 					cmdToUse = "sudo -p " + sudoPrompt + " -u " + sudo + " "
339 							+ cmdToUse;
340 				else
341 					cmdToUse = "sudo -p " + sudoPrompt + " " + cmdToUse;
342 			}
343 
344 			// GENERATE COMMAND LINE
345 			commandLine = CommandLine.parse(cmdToUse);
346 		} else if (commandToUse != null) {
347 			if (commandToUse.size() == 0)
348 				throw new SlcException("Command line is empty.");
349 
350 			if (chroot != null && sudo != null) {
351 				commandToUse.add(0, "chroot");
352 				commandToUse.add(1, chroot);
353 			}
354 
355 			if (sudo != null) {
356 				environmentVariables.put("SUDO_ASKPASS", askPassProgram);
357 				commandToUse.add(0, "sudo");
358 				commandToUse.add(1, "-p");
359 				commandToUse.add(2, sudoPrompt);
360 				if (!sudo.trim().equals("")) {
361 					commandToUse.add(3, "-u");
362 					commandToUse.add(4, sudo);
363 				}
364 			}
365 
366 			// GENERATE COMMAND LINE
367 			commandLine = new CommandLine(commandToUse.get(0).toString());
368 
369 			for (int i = 1; i < commandToUse.size(); i++) {
370 				if (log.isTraceEnabled())
371 					log.debug(commandToUse.get(i));
372 				commandLine.addArgument(commandToUse.get(i).toString());
373 			}
374 		} else {
375 			// all cases covered previously
376 			throw new UnsupportedException();
377 		}
378 
379 		if (generateScript != null) {
380 			File scriptFile = new File(getExecDirToUse() + File.separator
381 					+ generateScript);
382 			try {
383 				FileUtils.writeStringToFile(scriptFile,
384 						(osConsole != null ? osConsole + " " : "")
385 								+ commandLine.toString());
386 			} catch (IOException e) {
387 				throw new SlcException("Could not generate script "
388 						+ scriptFile, e);
389 			}
390 			commandLine = new CommandLine(scriptFile);
391 		} else {
392 			if (osConsole != null)
393 				commandLine = CommandLine.parse(osConsole + " "
394 						+ commandLine.toString());
395 		}
396 
397 		return commandLine;
398 	}
399 
400 	/**
401 	 * Creates a {@link PumpStreamHandler} which redirects streams to the custom
402 	 * logging mechanism.
403 	 */
404 	protected ExecuteStreamHandler createExecuteStreamHandler(
405 			final Writer stdOutWriter, final OutputStream stdOutputStream,
406 			final Writer stdErrWriter, final InputStream stdInStream) {
407 
408 		// Log writers
409 		OutputStream stdout = stdOutputStream != null ? stdOutputStream
410 				: new LogOutputStream() {
411 					protected void processLine(String line, int level) {
412 						// if (firstLine) {
413 						// if (sudo != null && callbackHandler != null
414 						// && line.startsWith(sudoPrompt)) {
415 						// try {
416 						// PasswordCallback pc = new PasswordCallback(
417 						// "sudo password", false);
418 						// Callback[] cbs = { pc };
419 						// callbackHandler.handle(cbs);
420 						// char[] pwd = pc.getPassword();
421 						// char[] arr = Arrays.copyOf(pwd,
422 						// pwd.length + 1);
423 						// arr[arr.length - 1] = '\n';
424 						// IOUtils.write(arr, stdInSink);
425 						// stdInSink.flush();
426 						// } catch (Exception e) {
427 						// throw new SlcException(
428 						// "Cannot retrieve sudo password", e);
429 						// }
430 						// }
431 						// firstLine = false;
432 						// }
433 
434 						if (line != null && !line.trim().equals(""))
435 							logStdOut(line);
436 
437 						if (stdOutWriter != null)
438 							appendLineToFile(stdOutWriter, line);
439 					}
440 				};
441 
442 		OutputStream stderr = new LogOutputStream() {
443 			protected void processLine(String line, int level) {
444 				if (line != null && !line.trim().equals(""))
445 					logStdErr(line);
446 				if (stdErrWriter != null)
447 					appendLineToFile(stdErrWriter, line);
448 			}
449 		};
450 
451 		PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdout,
452 				stderr, stdInStream) {
453 
454 			@Override
455 			public void stop() throws IOException {
456 				// prevents the method to block when joining stdin
457 				if (stdInSink != null)
458 					IOUtils.closeQuietly(stdInSink);
459 
460 				super.stop();
461 			}
462 		};
463 		return pumpStreamHandler;
464 	}
465 
466 	/** Creates the default {@link ExecuteResultHandler}. */
467 	protected ExecuteResultHandler createExecuteResultHandler(
468 			final CommandLine commandLine) {
469 		return new ExecuteResultHandler() {
470 
471 			public void onProcessComplete(int exitValue) {
472 				String msg = "System call '" + commandLine
473 						+ "' properly completed.";
474 				if (log.isTraceEnabled())
475 					log.trace(msg);
476 				if (testResult != null) {
477 					forwardPath(testResult);
478 					testResult.addResultPart(new SimpleResultPart(
479 							TestStatus.PASSED, msg));
480 				}
481 				releaseWatchdog();
482 			}
483 
484 			public void onProcessFailed(ExecuteException e) {
485 
486 				String msg = "System call '" + commandLine + "' failed.";
487 				if (testResult != null) {
488 					forwardPath(testResult);
489 					testResult.addResultPart(new SimpleResultPart(
490 							TestStatus.ERROR, msg, e));
491 				} else {
492 					if (exceptionOnFailed)
493 						throw new SlcException(msg, e);
494 					else
495 						log.error(msg, e);
496 				}
497 				releaseWatchdog();
498 			}
499 		};
500 	}
501 
502 	@Deprecated
503 	protected void forwardPath(TestResult testResult) {
504 		// TODO: allocate a TreeSPath
505 	}
506 
507 	/**
508 	 * Shortcut method getting the execDir to use
509 	 */
510 	protected String getExecDirToUse() {
511 		try {
512 			if (execDir != null) {
513 				return execDir;
514 			}
515 			return System.getProperty("user.dir");
516 		} catch (Exception e) {
517 			throw new SlcException("Cannot find exec dir", e);
518 		}
519 	}
520 
521 	protected void logStdOut(String line) {
522 		for (SystemCallOutputListener outputListener : outputListeners)
523 			outputListener.newLine(this, line, false);
524 		log(stdOutLogLevel, line);
525 	}
526 
527 	protected void logStdErr(String line) {
528 		for (SystemCallOutputListener outputListener : outputListeners)
529 			outputListener.newLine(this, line, true);
530 		log(stdErrLogLevel, line);
531 	}
532 
533 	/** Log from the underlying streams. */
534 	protected void log(String logLevel, String line) {
535 		// TODO optimize
536 //		if (SecurityContextHolder.getContext().getAuthentication() == null) {
537 //			SecurityContextHolder.getContext()
538 //					.setAuthentication(authentication);
539 //		}
540 
541 		if ("ERROR".equals(logLevel))
542 			log.error(line);
543 		else if ("WARN".equals(logLevel))
544 			log.warn(line);
545 		else if ("INFO".equals(logLevel))
546 			log.info(line);
547 		else if ("DEBUG".equals(logLevel))
548 			log.debug(line);
549 		else if ("TRACE".equals(logLevel))
550 			log.trace(line);
551 		else if (LOG_STDOUT.equals(logLevel))
552 			System.out.println(line);
553 		else if ("System.err".equals(logLevel))
554 			System.err.println(line);
555 		else
556 			throw new SlcException("Unknown log level " + logLevel);
557 	}
558 
559 	/** Append line to a log file. */
560 	protected void appendLineToFile(Writer writer, String line) {
561 		try {
562 			writer.append(line).append('\n');
563 		} catch (IOException e) {
564 			log.error("Cannot write to log file", e);
565 		}
566 	}
567 
568 	/** Creates the writer for the output/err files. */
569 	protected Writer createWriter(Resource target, Boolean append) {
570 		FileWriter writer = null;
571 		try {
572 
573 			final File file;
574 			if (executionResources != null)
575 				file = new File(executionResources.getAsOsPath(target, true));
576 			else
577 				file = target.getFile();
578 			writer = new FileWriter(file, append);
579 		} catch (IOException e) {
580 			log.error("Cannot get file for " + target, e);
581 			IOUtils.closeQuietly(writer);
582 		}
583 		return writer;
584 	}
585 
586 	/** Creates an outputstream for the output/err files. */
587 	protected OutputStream createOutputStream(Resource target) {
588 		FileOutputStream out = null;
589 		try {
590 
591 			final File file;
592 			if (executionResources != null)
593 				file = new File(executionResources.getAsOsPath(target, true));
594 			else
595 				file = target.getFile();
596 			out = new FileOutputStream(file, false);
597 		} catch (IOException e) {
598 			log.error("Cannot get file for " + target, e);
599 			IOUtils.closeQuietly(out);
600 		}
601 		return out;
602 	}
603 
604 	/** Append the argument (for chaining) */
605 	public SystemCall arg(String arg) {
606 		if (command == null)
607 			command = new ArrayList<Object>();
608 		command.add(arg);
609 		return this;
610 	}
611 
612 	/** Append the argument (for chaining) */
613 	public SystemCall arg(String arg, String value) {
614 		if (command == null)
615 			command = new ArrayList<Object>();
616 		command.add(arg);
617 		command.add(value);
618 		return this;
619 	}
620 
621 	// CONTROL
622 	public synchronized Boolean isRunning() {
623 		return currentWatchdog != null;
624 	}
625 
626 	private synchronized ExecuteWatchdog createWatchdog() {
627 //		if (currentWatchdog != null)
628 //			throw new SlcException("A process is already being monitored");
629 		currentWatchdog = new ExecuteWatchdog(watchdogTimeout);
630 		return currentWatchdog;
631 	}
632 
633 	private synchronized void releaseWatchdog() {
634 		currentWatchdog = null;
635 	}
636 
637 	public synchronized void kill() {
638 		if (currentWatchdog != null)
639 			currentWatchdog.destroyProcess();
640 	}
641 
642 	/** */
643 	public void setCmd(String command) {
644 		this.cmd = command;
645 	}
646 
647 	public void setCommand(List<Object> command) {
648 		this.command = command;
649 	}
650 
651 	public void setExecDir(String execdir) {
652 		this.execDir = execdir;
653 	}
654 
655 	public void setStdErrLogLevel(String stdErrLogLevel) {
656 		this.stdErrLogLevel = stdErrLogLevel;
657 	}
658 
659 	public void setStdOutLogLevel(String stdOutLogLevel) {
660 		this.stdOutLogLevel = stdOutLogLevel;
661 	}
662 
663 	public void setSynchronous(Boolean synchronous) {
664 		this.synchronous = synchronous;
665 	}
666 
667 	public void setOsCommands(Map<String, List<Object>> osCommands) {
668 		this.osCommands = osCommands;
669 	}
670 
671 	public void setOsCmds(Map<String, String> osCmds) {
672 		this.osCmds = osCmds;
673 	}
674 
675 	public void setEnvironmentVariables(Map<String, String> environmentVariables) {
676 		this.environmentVariables = environmentVariables;
677 	}
678 
679 	public Map<String, String> getEnvironmentVariables() {
680 		return environmentVariables;
681 	}
682 
683 	public void setWatchdogTimeout(Long watchdogTimeout) {
684 		this.watchdogTimeout = watchdogTimeout;
685 	}
686 
687 	public void setStdOutFile(Resource stdOutFile) {
688 		this.stdOutFile = stdOutFile;
689 	}
690 
691 	public void setStdErrFile(Resource stdErrFile) {
692 		this.stdErrFile = stdErrFile;
693 	}
694 
695 	public void setStdInFile(Resource stdInFile) {
696 		this.stdInFile = stdInFile;
697 	}
698 
699 	public void setTestResult(TestResult testResult) {
700 		this.testResult = testResult;
701 	}
702 
703 	public void setLogCommand(Boolean logCommand) {
704 		this.logCommand = logCommand;
705 	}
706 
707 	public void setRedirectStreams(Boolean redirectStreams) {
708 		this.redirectStreams = redirectStreams;
709 	}
710 
711 	public void setExceptionOnFailed(Boolean exceptionOnFailed) {
712 		this.exceptionOnFailed = exceptionOnFailed;
713 	}
714 
715 	public void setMergeEnvironmentVariables(Boolean mergeEnvironmentVariables) {
716 		this.mergeEnvironmentVariables = mergeEnvironmentVariables;
717 	}
718 
719 	public void setOsConsole(String osConsole) {
720 		this.osConsole = osConsole;
721 	}
722 
723 	public void setGenerateScript(String generateScript) {
724 		this.generateScript = generateScript;
725 	}
726 
727 	public void setExecutionResources(ExecutionResources executionResources) {
728 		this.executionResources = executionResources;
729 	}
730 
731 	public void setRedirectStdOut(Boolean redirectStdOut) {
732 		this.redirectStdOut = redirectStdOut;
733 	}
734 
735 	public void addOutputListener(SystemCallOutputListener outputListener) {
736 		outputListeners.add(outputListener);
737 	}
738 
739 	public void removeOutputListener(SystemCallOutputListener outputListener) {
740 		outputListeners.remove(outputListener);
741 	}
742 
743 	public void setOutputListeners(
744 			List<SystemCallOutputListener> outputListeners) {
745 		this.outputListeners = outputListeners;
746 	}
747 
748 	public void setExecutor(Executor executor) {
749 		this.executor = executor;
750 	}
751 
752 	public void setSudo(String sudo) {
753 		this.sudo = sudo;
754 	}
755 
756 	public void setCallbackHandler(CallbackHandler callbackHandler) {
757 		this.callbackHandler = callbackHandler;
758 	}
759 
760 	public void setChroot(String chroot) {
761 		this.chroot = chroot;
762 	}
763 
764 	private class DummyexecuteStreamHandler implements ExecuteStreamHandler {
765 
766 		public void setProcessErrorStream(InputStream is) throws IOException {
767 		}
768 
769 		public void setProcessInputStream(OutputStream os) throws IOException {
770 		}
771 
772 		public void setProcessOutputStream(InputStream is) throws IOException {
773 		}
774 
775 		public void start() throws IOException {
776 		}
777 
778 		public void stop() {
779 		}
780 
781 	}
782 }