in geode-logging/src/main/java/org/apache/geode/logging/internal/OSProcess.java [112:337]
private static native int bgexecInternal(String[] cmdarray, String workdir, String logfile,
boolean inheritLogfile) throws IOException;
/**
* Starts execution of the specified command and arguments in a separate detached process in the
* specified working directory writing output to the specified log file.
* <p>
* If there is a security manager, its <code>checkExec</code> method is called with the first
* component of the array <code>cmdarray</code> as its argument. This may result in a security
* exception.
* <p>
* Given an array of strings <code>cmdarray</code>, representing the tokens of a command line,
* this method creates a new process in which to execute the specified command.
*
* @param cmdarray array containing the command to call and its arguments.
* @param workdir the current directory of the created process; null causes working directory to
* default to the current directory.
* @param logfile the file the created process will write stdout and stderr to; null causes a
* default log file name to be used.
* @param inheritLogfile can be set to false if the child process is willing to create its own log
* file. Setting to false can help on Windows because it keeps the child process from
* inheriting handles from the parent.
* @param env any extra environment variables as key,value map; these will be in addition to those
* inherited from the parent process and will overwrite same keys
* @return the process id of the created process; -1 on failure
* @exception SecurityException if the current thread cannot create a subprocess.
* @see java.lang.SecurityException
* @see java.lang.SecurityManager#checkExec(java.lang.String)
*/
public static int bgexec(String[] cmdarray, File workdir, File logfile, boolean inheritLogfile,
Map<String, String> env) throws IOException {
String commandShell =
System.getProperty(GeodeGlossary.GEMFIRE_PREFIX + "commandShell", "bash");
if (cmdarray.length == 0) {
throw new java.lang.IndexOutOfBoundsException();
}
boolean isWindows = false;
String os = System.getProperty("os.name");
if (os != null) {
if (os.indexOf("Windows") != -1) {
isWindows = true;
}
}
for (int i = 0; i < cmdarray.length; i++) {
if (cmdarray[i] == null) {
throw new NullPointerException();
}
if (isWindows) {
if (i == 0) {
// do the following before quotes get added.
File cmd = new File(cmdarray[0]);
if (!cmd.exists()) {
cmd = new File(cmdarray[0] + ".exe");
if (cmd.exists()) {
cmdarray[0] = cmd.getPath();
}
}
}
String s = cmdarray[i];
if (i != 0) {
if (s.length() == 0) {
cmdarray[i] = "\"\""; // fix for bug 22207
} else if ((s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0)) {
String unquotedS = s;
if (s.indexOf('\"') != -1) {
// Note that Windows provides no way to embed a double
// quote in a double quoted string so need to remove
// any internal quotes and let the outer quotes
// preserve the whitespace.
StringBuilder b = new StringBuilder(s);
int quoteIdx = s.lastIndexOf('\"');
while (quoteIdx != -1) {
b.deleteCharAt(quoteIdx);
quoteIdx = s.lastIndexOf('\"', quoteIdx - 1);
}
unquotedS = b.toString();
}
// It has whitespace and its not quoted
cmdarray[i] = '"' + unquotedS + '"';
}
}
}
}
File cmd = new File(cmdarray[0]);
if (!cmd.exists()) {
throw new IOException(String.format("the executable %s does not exist",
cmd.getPath()));
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExec(cmdarray[0]);
}
if (workdir != null && !workdir.isDirectory()) {
String curDir = new File("").getAbsolutePath();
System.out.println(
String.format("WARNING: %s is not a directory. Defaulting to current directory %s.",
workdir, curDir));
workdir = null;
}
if (workdir == null) {
workdir = new File("").getAbsoluteFile();
}
if (logfile == null) {
logfile = File.createTempFile("bgexec", ".log", workdir);
}
if (!logfile.isAbsolute()) {
// put it in the working directory
logfile = new File(workdir, logfile.getPath());
}
// fix for bug 24575
if (logfile.exists()) {
// it already exists so make sure its a file and can be written
if (!logfile.isFile()) {
throw new IOException(String.format("The log file %s was not a normal file.",
logfile.getPath()));
}
if (!logfile.canWrite()) {
throw new IOException(String.format("Need write access for the log file %s.",
logfile.getPath()));
}
} else {
try {
logfile.createNewFile();
} catch (IOException io) {
throw new IOException(String.format("Could not create log file %s because: %s.",
logfile.getPath(), io.getMessage()));
}
}
String trace = System.getProperty("org.apache.geode.logging.internal.OSProcess.trace");
if (trace != null && trace.length() > 0) {
for (int i = 0; i < cmdarray.length; i++) {
System.out.println("cmdarray[" + i + "] = " + cmdarray[i]);
}
System.out.println("workdir=" + workdir.getPath());
System.out.println("logfile=" + logfile.getPath());
}
int result = 0;
StringBuilder sb = new StringBuilder();
Vector<String> cmdVec = new Vector<>();
// Add shell code to spawn a process silently
if (isWindows) {
cmdVec.add("cmd.exe");
cmdVec.add("/c");
sb.append("start /b \"\" ");
} else {
// to address issue with users that don't have bash shell installed
if (commandShell.equals("bash")) {
cmdVec.add("bash");
cmdVec.add("--norc");
cmdVec.add("-c");
} else {
cmdVec.add(commandShell);
}
}
// Add the actual command
for (int i = 0; i < cmdarray.length; i++) {
if (i != 0) {
sb.append(" ");
}
if (cmdarray[i].length() != 0 && cmdarray[i].charAt(0) == '\"') {
// The token has already been quoted, see bug 40835
sb.append(cmdarray[i]);
} else {
sb.append("\"");
sb.append(cmdarray[i]);
sb.append("\"");
}
}
// Add the IO redirction code, this prevents hangs and IO blocking
sb.append(" >> ");
sb.append(logfile.getPath());
sb.append(" 2>&1");
if (isWindows) {
sb.append(" <NUL");
} else {
sb.append(" </dev/null &");
}
cmdVec.add(sb.toString());
String[] cmdStrings = cmdVec.toArray(new String[0]);
if (trace != null && trace.length() > 0) {
for (int i = 0; i < cmdStrings.length; i++) {
System.out.println("cmdStrings[" + i + "] = " + cmdStrings[i]);
}
System.out.println("workdir=" + workdir.getPath());
System.out.println("logfile=" + logfile.getPath());
}
final ProcessBuilder procBuilder = new ProcessBuilder(cmdStrings);
if (env != null && env.size() > 0) {
// adjust the environment variables inheriting from parent
procBuilder.environment().putAll(env);
}
procBuilder.directory(workdir);
final Process process = procBuilder.start();
try {
process.getInputStream().close();
} catch (IOException ignore) {
}
try {
process.getOutputStream().close();
} catch (IOException ignore) {
}
try {
process.getErrorStream().close();
} catch (IOException ignore) {
}
try {
// short count = 1000;
boolean processIsStillRunning = true;
while (processIsStillRunning) {
Thread.sleep(10);
try {
process.exitValue();
processIsStillRunning = false;
} catch (IllegalThreadStateException itse) {
// Ignore this, we are polling the exitStatus
// instead of using the blocking Process#waitFor()
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
return result; // Always 0 for pureJava
}