// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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.
//
// Automatically generated by addcopyright.py at 04/03/2012
package com.cloud.utils.script;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.script.OutputInterpreter.TimedOutLogger;
public class Script implements Callable<String> {
private static final Logger s_logger = Logger.getLogger(Script.class);
private final Logger _logger;
public static final String ERR_EXECUTE = "execute.error";
public static final String ERR_TIMEOUT = "timeout";
private int _defaultTimeout = 3600 * 1000; /* 1 hour */
private volatile boolean _isTimeOut = false;
private boolean _passwordCommand = false;
private static final ScheduledExecutorService s_executors = Executors.newScheduledThreadPool(10, new NamedThreadFactory("Script"));
String _workDir;
ArrayList<String> _command;
long _timeout;
Process _process;
Thread _thread;
ScriptBuilder _builder;
public Script(String command, long timeout, Logger logger) {
_command = new ArrayList<String>();
_command.add(command);
_timeout = timeout;
if (_timeout == 0) {
/* always using default timeout 1 hour to avoid thread hang */
_timeout = _defaultTimeout;
}
_process = null;
_logger = logger != null ? logger : s_logger;
}
protected Script(ScriptBuilder builder) {
this(builder._command, builder._timeout, builder._logger);
}
public Script(boolean runWithSudo, String command, long timeout, Logger logger) {
this(command, timeout, logger);
if (runWithSudo) {
_command.add(0, "sudo");
}
}
public Script(String command, Logger logger) {
this(command, 0, logger);
}
public Script(String command) {
this(command, 0, s_logger);
}
public Script(String command, long timeout) {
this(command, timeout, s_logger);
}
public void add(String... params) {
for (String param : params) {
_command.add(param);
}
}
public void add(String param) {
_command.add(param);
}
public Script set(String name, String value) {
_command.add(name);
_command.add(value);
return this;
}
public void setWorkDir(String workDir) {
_workDir = workDir;
}
protected String buildCommandLine(String[] command) {
StringBuilder builder = new StringBuilder();
boolean obscureParam = false;
for (int i = 0; i < command.length; i++) {
String cmd = command[i];
if (obscureParam) {
builder.append("******").append(" ");
obscureParam = false;
} else {
builder.append(command[i]).append(" ");
}
if ("-y".equals(cmd) || "-z".equals(cmd)) {
obscureParam = true;
_passwordCommand = true;
}
}
return builder.toString();
}
protected String buildCommandLine(List<String> command) {
StringBuilder builder = new StringBuilder();
boolean obscureParam = false;
for (String cmd : command) {
if (obscureParam) {
builder.append("******").append(" ");
obscureParam = false;
} else {
builder.append(cmd).append(" ");
}
if ("-y".equals(cmd) || "-z".equals(cmd)) {
obscureParam = true;
_passwordCommand = true;
}
}
return builder.toString();
}
public String execute() {
return execute(new OutputInterpreter.OutputLogger(_logger));
}
@Override
public String toString() {
String[] command = _command.toArray(new String[_command.size()]);
return buildCommandLine(command);
}
public String execute(OutputInterpreter interpreter) {
String[] command = _command.toArray(new String[_command.size()]);
if (_logger.isDebugEnabled()) {
_logger.debug("Executing: " + buildCommandLine(command));
}
try {
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
if(_workDir != null)
pb.directory(new File(_workDir));
_process = pb.start();
if (_process == null) {
_logger.warn("Unable to execute: " + buildCommandLine(command));
return "Unable to execute the command: " + command[0];
}
BufferedReader ir = new BufferedReader(new InputStreamReader(_process.getInputStream()));
_thread = Thread.currentThread();
ScheduledFuture<String> future = null;
if (_timeout > 0) {
future = s_executors.schedule(this, _timeout, TimeUnit.MILLISECONDS);
}
Task task = null;
if (interpreter.drain()) {
task = new Task(interpreter, ir);
s_executors.execute(task);
}
while (true) {
try {
if (_process.waitFor() == 0) {
_logger.debug("Execution is successful.");
return interpreter.drain() ? task.getResult() : interpreter.interpret(ir);
} else {
break;
}
} catch (InterruptedException e) {
if (!_isTimeOut) {
/* This is not timeout, we are interrupted by others, continue */
_logger.debug("We are interrupted but it's not a timeout, just continue");
continue;
}
TimedOutLogger log = new TimedOutLogger(_process);
Task timedoutTask = new Task(log, ir);
timedoutTask.run();
if (!_passwordCommand) {
_logger.warn("Timed out: " + buildCommandLine(command) + ". Output is: " + timedoutTask.getResult());
} else {
_logger.warn("Timed out: " + buildCommandLine(command));
}
return ERR_TIMEOUT;
} finally {
if (future != null) {
future.cancel(false);
}
Thread.interrupted();
}
}
_logger.debug("Exit value is " + _process.exitValue());
BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128);
String error = interpreter.processError(reader);
if (_logger.isDebugEnabled()) {
_logger.debug(error);
}
return error;
} catch (SecurityException ex) {
_logger.warn("Security Exception....not running as root?", ex);
StringWriter writer = new StringWriter();
ex.printStackTrace(new PrintWriter(writer));
return writer.toString();
} catch (Exception ex) {
_logger.warn("Exception: " + buildCommandLine(command), ex);
StringWriter writer = new StringWriter();
ex.printStackTrace(new PrintWriter(writer));
return writer.toString();
} finally {
if (_process != null) {
try {
_process.getErrorStream().close();
} catch (IOException ex) {
}
try {
_process.getOutputStream().close();
} catch (IOException ex) {
}
try {
_process.getInputStream().close();
} catch (IOException ex) {
}
_process.destroy();
}
}
}
@Override
public String call() {
try {
_logger.trace("Checking exit value of process");
_process.exitValue();
_logger.trace("Script ran within the alloted time");
} catch (IllegalThreadStateException e) {
_logger.warn("Interrupting script.");
_isTimeOut = true;
_thread.interrupt();
}
return null;
}
public static class Task implements Runnable {
OutputInterpreter interpreter;
BufferedReader reader;
String result;
boolean done;
public Task(OutputInterpreter interpreter, BufferedReader reader) {
this.interpreter = interpreter;
this.reader = reader;
this.result = null;
}
public void run() {
done = false;
try {
result = interpreter.interpret(reader);
} catch (IOException ex) {
StringWriter writer = new StringWriter();
ex.printStackTrace(new PrintWriter(writer));
result = writer.toString();
} catch (Exception ex) {
StringWriter writer = new StringWriter();
ex.printStackTrace(new PrintWriter(writer));
result = writer.toString();
} finally {
synchronized (this) {
done = true;
notifyAll();
}
try {
reader.close();
} catch (IOException ex) {
}
;
}
}
public synchronized String getResult() throws InterruptedException {
if (!done) {
wait();
}
return result;
}
}
public static String findScript(String path, String script) {
s_logger.debug("Looking for " + script + " in the classpath");
path = path.replace("/", File.separator);
URL url = ClassLoader.getSystemResource(script);
s_logger.debug("System resource: " + url);
File file = null;
if (url != null) {
file = new File(url.getFile());
return file.getAbsolutePath();
}
if (path.endsWith(File.separator)) {
path = path.substring(0, path.lastIndexOf(File.separator));
}
if (path.startsWith(File.separator)) {
// Path given was absolute so we assume the caller knows what they want.
file = new File(path + File.separator + script);
return file.exists() ? file.getAbsolutePath() : null;
}
s_logger.debug("Looking for " + script);
String search = null;
for (int i = 0; i < 3; i++) {
if (i == 0) {
String cp = Script.class.getResource(Script.class.getSimpleName() + ".class").toExternalForm();
int begin = cp.indexOf(File.separator);
// work around with the inconsistency of java classpath and file separator on Windows 7
if (begin < 0)
begin = cp.indexOf('/');
int endBang = cp.lastIndexOf("!");
int end = cp.lastIndexOf(File.separator, endBang);
if (end < 0)
end = cp.lastIndexOf('/', endBang);
if(end < 0)
cp = cp.substring(begin);
else
cp = cp.substring(begin, end);
s_logger.debug("Current binaries reside at " + cp);
search = cp;
} else if (i == 1) {
s_logger.debug("Searching in environment.properties");
try {
final File propsFile = PropertiesUtil.findConfigFile("environment.properties");
if (propsFile == null) {
s_logger.debug("environment.properties could not be opened");
} else {
final FileInputStream finputstream = new FileInputStream(propsFile);
final Properties props = new Properties();
props.load(finputstream);
finputstream.close();
search = props.getProperty("paths.script");
}
} catch (IOException e) {
s_logger.debug("environment.properties could not be opened");
continue;
}
s_logger.debug("environment.properties says scripts should be in " + search);
} else {
s_logger.debug("Searching in the current directory");
search = ".";
}
search += File.separatorChar + path + File.separator;
do {
search = search.substring(0, search.lastIndexOf(File.separator));
file = new File(search + File.separator + script);
s_logger.debug("Looking for " + script + " in " + file.getAbsolutePath());
} while (!file.exists() && search.lastIndexOf(File.separator) != -1);
if (file.exists()) {
return file.getAbsolutePath();
}
}
s_logger.warn("Unable to find script " + script);
return null;
}
public static String runSimpleBashScript(String command) {
Script s = new Script("/bin/bash");
s.add("-c");
s.add(command);
OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
if (s.execute(parser) != null)
return null;
String result = parser.getLine();
if (result == null || result.trim().isEmpty())
return null;
else
return result.trim();
}
public static String runSimpleBashScript(String command, int timeout) {
Script s = new Script("/bin/bash", timeout);
s.add("-c");
s.add(command);
OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
if (s.execute(parser) != null)
return null;
String result = parser.getLine();
if (result == null || result.trim().isEmpty())
return null;
else
return result.trim();
}
public static void main(String[] args) {
String path = findScript(".", "try.sh");
Script script = new Script(path, 5000, s_logger);
script.execute();
System.exit(1);
}
}