mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
672 lines
17 KiB
Java
672 lines
17 KiB
Java
/*
|
|
* @(#) $(JCGO)/goclsp/vm/java/lang/VMProcess.java --
|
|
* VM specific implementation of Java "Process".
|
|
**
|
|
* Project: JCGO (http://www.ivmaisoft.com/jcgo/)
|
|
* Copyright (C) 2001-2009 Ivan Maidanski <ivmai@ivmaisoft.com>
|
|
* All rights reserved.
|
|
**
|
|
* Class specification origin: GNU Classpath v0.93 vm/reference
|
|
*/
|
|
|
|
/*
|
|
* This is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
**
|
|
* This software is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License (GPL) for more details.
|
|
**
|
|
* Linking this library statically or dynamically with other modules is
|
|
* making a combined work based on this library. Thus, the terms and
|
|
* conditions of the GNU General Public License cover the whole
|
|
* combination.
|
|
**
|
|
* As a special exception, the copyright holders of this library give you
|
|
* permission to link this library with independent modules to produce an
|
|
* executable, regardless of the license terms of these independent
|
|
* modules, and to copy and distribute the resulting executable under
|
|
* terms of your choice, provided that you also meet, for each linked
|
|
* independent module, the terms and conditions of the license of that
|
|
* module. An independent module is a module which is not derived from
|
|
* or based on this library. If you modify this library, you may extend
|
|
* this exception to your version of the library, but you are not
|
|
* obligated to do so. If you do not wish to do so, delete this
|
|
* exception statement from your version.
|
|
*/
|
|
|
|
package java.lang;
|
|
|
|
import gnu.classpath.SystemProperties;
|
|
|
|
import gnu.java.nio.FileChannelImpl;
|
|
import gnu.java.nio.VMAccessorGnuJavaNio;
|
|
import gnu.java.nio.VMChannel;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.VMAccessorJavaIo;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
final class VMProcess extends Process /* hard-coded class name */
|
|
{
|
|
|
|
private static final int STATE_INITIAL = 0;
|
|
|
|
private static final int STATE_RUNNING = 1;
|
|
|
|
private static final int STATE_TERMINATED = 2;
|
|
|
|
private static int threadNum;
|
|
|
|
private static final String[] PROPAGATE_ENVS_LIST =
|
|
{
|
|
"LD_LIBRARY_PATH", "TMPDIR", "HOME", "LOGNAME", "LC_CTYPE", "TZ",
|
|
"COMSPEC", "TMP", "TEMP"
|
|
};
|
|
|
|
private static final boolean IS_PROG_SEARCH_NEEDED =
|
|
isProgSearchNeeded0() != 0;
|
|
|
|
private static final int IS_CMD_SPACE_DELIMITED = isCmdSpaceDelimited0();
|
|
|
|
private static final String VALID_PROG_EXTS_LIST = getValidProgExtsList();
|
|
|
|
private static final boolean preventBlocking = VMRuntime.preventIOBlocking();
|
|
|
|
volatile int state;
|
|
|
|
long pid;
|
|
|
|
OutputStream stdinStream;
|
|
|
|
InputStream stdoutStream;
|
|
|
|
InputStream stderrStream;
|
|
|
|
int exitValue;
|
|
|
|
private VMProcess() {}
|
|
|
|
private VMProcess(final String[] cmd, final String[] env, final File dir,
|
|
final boolean redirect)
|
|
throws IOException
|
|
{
|
|
final Throwable[] throwableArr = new Throwable[1];
|
|
ThreadGroup tg = Thread.currentThread().getThreadGroup();
|
|
try
|
|
{
|
|
for (ThreadGroup tgn = tg; tgn != null; tgn = tg.getParent())
|
|
tg = tgn;
|
|
}
|
|
catch (SecurityException e) {}
|
|
Thread processThread =
|
|
new Thread(tg, "Process reaper-".concat(String.valueOf(nextThreadNum())))
|
|
{
|
|
|
|
public void run()
|
|
{
|
|
synchronized (VMProcess.this)
|
|
{
|
|
try
|
|
{
|
|
nativeSpawn(cmd, env, dir, redirect);
|
|
state = STATE_RUNNING;
|
|
}
|
|
catch (ThreadDeath e)
|
|
{
|
|
state = STATE_TERMINATED;
|
|
throw e;
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
state = STATE_TERMINATED;
|
|
throwableArr[0] = e;
|
|
}
|
|
VMProcess.this.notifyAll();
|
|
}
|
|
waitForProcess();
|
|
}
|
|
};
|
|
processThread.setDaemon(true);
|
|
try
|
|
{
|
|
processThread.start();
|
|
}
|
|
catch (OutOfMemoryError e)
|
|
{
|
|
processThread.run();
|
|
}
|
|
synchronized (this)
|
|
{
|
|
while (state == STATE_INITIAL)
|
|
{
|
|
try
|
|
{
|
|
wait();
|
|
}
|
|
catch (InterruptedException e) {}
|
|
}
|
|
}
|
|
Throwable throwable = throwableArr[0];
|
|
if (throwable != null)
|
|
{
|
|
throwable.fillInStackTrace();
|
|
if (throwable instanceof IOException)
|
|
throw (IOException) throwable;
|
|
if (throwable instanceof RuntimeException)
|
|
throw (RuntimeException) throwable;
|
|
if (throwable instanceof Error)
|
|
throw (Error) throwable;
|
|
throw (Error) (new InternalError("VMProcess")).initCause(throwable);
|
|
}
|
|
}
|
|
|
|
void setProcessInfo(OutputStream stdin, InputStream stdout,
|
|
InputStream stderr, long pid)
|
|
{
|
|
stdinStream = stdin != null ? stdin :
|
|
new OutputStream()
|
|
{
|
|
|
|
public void write(int b)
|
|
throws IOException
|
|
{
|
|
throw new IOException("process input stream is not redirected");
|
|
}
|
|
};
|
|
InputStream nulIn = stdout != null && stderr != null ? null :
|
|
new InputStream()
|
|
{
|
|
|
|
public int read()
|
|
throws IOException
|
|
{
|
|
return -1;
|
|
}
|
|
};
|
|
stdoutStream = stdout != null ? stdout : nulIn;
|
|
stderrStream = stderr != null ? stderr : nulIn;
|
|
this.pid = pid;
|
|
}
|
|
|
|
static Process exec(String[] cmd, String[] env, File dir)
|
|
throws IOException
|
|
{
|
|
return new VMProcess(cmd, env, dir, false);
|
|
}
|
|
|
|
static Process exec(List cmdList, Map envMap, File dir, boolean redirect)
|
|
throws IOException
|
|
{
|
|
String[] cmd = new String[cmdList.size()];
|
|
Iterator iter = cmdList.iterator();
|
|
for (int i = 0; iter.hasNext(); i++)
|
|
cmd[i] = (String) iter.next();
|
|
String[] env = new String[envMap.size()];
|
|
iter = envMap.entrySet().iterator();
|
|
for (int i = 0; iter.hasNext(); i++)
|
|
{
|
|
Map.Entry entry = (Map.Entry) iter.next();
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append((String) entry.getKey());
|
|
sb.append('=');
|
|
sb.append((String) entry.getValue());
|
|
env[i] = sb.toString();
|
|
}
|
|
return new VMProcess(cmd, env, dir, redirect);
|
|
}
|
|
|
|
public OutputStream getOutputStream()
|
|
{
|
|
return stdinStream;
|
|
}
|
|
|
|
public InputStream getInputStream()
|
|
{
|
|
return stdoutStream;
|
|
}
|
|
|
|
public InputStream getErrorStream()
|
|
{
|
|
return stderrStream;
|
|
}
|
|
|
|
public synchronized int waitFor()
|
|
throws InterruptedException
|
|
{
|
|
while (state != STATE_TERMINATED)
|
|
wait();
|
|
return exitValue;
|
|
}
|
|
|
|
public synchronized int exitValue()
|
|
{
|
|
if (state != STATE_TERMINATED)
|
|
throw new IllegalThreadStateException();
|
|
return exitValue;
|
|
}
|
|
|
|
public synchronized void destroy()
|
|
{
|
|
if (state != STATE_TERMINATED)
|
|
{
|
|
int res = nativeKill(pid);
|
|
if (res > 0)
|
|
{
|
|
if (exitValue == 0)
|
|
exitValue = res;
|
|
state = STATE_TERMINATED;
|
|
notifyAll();
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
try
|
|
{
|
|
wait();
|
|
}
|
|
catch (InterruptedException e) {}
|
|
} while (state != STATE_TERMINATED);
|
|
}
|
|
}
|
|
}
|
|
|
|
final void nativeSpawn(String[] cmd, String[] env, File dir,
|
|
boolean redirect)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
if (cmd == null)
|
|
throw new NullPointerException();
|
|
int res = cmd.length;
|
|
if (res == 0)
|
|
throw new IndexOutOfBoundsException();
|
|
do
|
|
{
|
|
if (cmd[--res] == null)
|
|
throw new NullPointerException();
|
|
} while (res > 0);
|
|
String envZBlock = "";
|
|
if (env != null)
|
|
{
|
|
env = addMissingEnvVar(env, "PATH", ".");
|
|
for (int i = 0; i < PROPAGATE_ENVS_LIST.length; i++)
|
|
env = addMissingEnvVar(env, PROPAGATE_ENVS_LIST[i], null);
|
|
envZBlock = convertEnv(env);
|
|
}
|
|
String dirpath = ".";
|
|
boolean changeDir = false;
|
|
if (dir != null)
|
|
{
|
|
dirpath = VMAccessorJavaIo.normVolumeColonVMFile(dir.getPath());
|
|
if (!dirpath.equals("."))
|
|
{
|
|
VMAccessorGnuJavaNio.checkIOResCodeVMChannel(
|
|
checkPermissions0(dirpath, 1));
|
|
changeDir = true;
|
|
}
|
|
}
|
|
String progName;
|
|
String cmdZBlock;
|
|
if (IS_CMD_SPACE_DELIMITED < 0)
|
|
{
|
|
progName = convertProgName(cmd[0], true);
|
|
cmdZBlock = convertCmd(null, cmd, true);
|
|
}
|
|
else
|
|
{
|
|
cmdZBlock = convertCmd(convertProgName(cmd[0], changeDir), cmd,
|
|
IS_CMD_SPACE_DELIMITED != 0);
|
|
progName = "";
|
|
}
|
|
int bufLen = getSpawnWorkBufSize0(cmdZBlock, envZBlock, cmd.length,
|
|
env != null ? env.length : 0);
|
|
byte[] workBuf = new byte[bufLen >= 0 ? bufLen : -1 >>> 1];
|
|
VMChannel inCh = new VMChannel();
|
|
VMChannel outCh = new VMChannel();
|
|
VMChannel errCh = redirect ? null : new VMChannel();
|
|
int[] fdsArr = { -1, -1, -1 };
|
|
long[] pidArr = new long[1];
|
|
boolean retrying = false;
|
|
do
|
|
{
|
|
res = nativeSpawn0(progName, cmdZBlock, envZBlock, dirpath, fdsArr, pidArr,
|
|
workBuf, bufLen, redirect ? 1 : 0);
|
|
if (retrying || !VMAccessorGnuJavaNio.isIORetryNeededOnceVMChannel(res))
|
|
break;
|
|
retrying = true;
|
|
} while (true);
|
|
VMAccessorGnuJavaNio.checkIOResCodeVMChannel(res);
|
|
if (fdsArr[2] == -1)
|
|
errCh = null;
|
|
try
|
|
{
|
|
if (fdsArr[0] != -1)
|
|
VMAccessorGnuJavaNio.setNativeFileFDVMChannel(inCh, fdsArr[0],
|
|
FileChannelImpl.WRITE);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
if (fdsArr[1] != -1)
|
|
VMAccessorGnuJavaNio.setNativeFileFDVMChannel(outCh, fdsArr[1],
|
|
FileChannelImpl.READ);
|
|
}
|
|
finally
|
|
{
|
|
if (errCh != null)
|
|
VMAccessorGnuJavaNio.setNativeFileFDVMChannel(errCh, fdsArr[2],
|
|
FileChannelImpl.READ);
|
|
}
|
|
}
|
|
setProcessInfo(fdsArr[0] != -1 ? createOutputStream(inCh) : null,
|
|
fdsArr[1] != -1 ? createInputStream(outCh) : null,
|
|
errCh != null ? createInputStream(errCh) : null, pidArr[0]);
|
|
}
|
|
|
|
final void waitForProcess()
|
|
{ /* used by VM classes only */
|
|
int nohang = 0;
|
|
if (preventBlocking)
|
|
{
|
|
Thread.yield();
|
|
nohang = 1;
|
|
}
|
|
int res;
|
|
do
|
|
{
|
|
res = nativeWaitFor0(pid, nohang);
|
|
if (state == STATE_TERMINATED)
|
|
return;
|
|
if (res != -1)
|
|
break;
|
|
try
|
|
{
|
|
Thread.sleep(1L);
|
|
}
|
|
catch (InterruptedException e) {}
|
|
} while (true);
|
|
exitValue = res;
|
|
synchronized (this)
|
|
{
|
|
if (state != STATE_TERMINATED)
|
|
{
|
|
state = STATE_TERMINATED;
|
|
notifyAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
static final void pipe(VMChannel chIn, VMChannel chOut)
|
|
throws IOException
|
|
{ /* used by VM classes only */
|
|
int[] fdsArr = { -1, -1 };
|
|
int res;
|
|
boolean retrying = false;
|
|
do
|
|
{
|
|
res = pipe0(fdsArr);
|
|
if (retrying || !VMAccessorGnuJavaNio.isIORetryNeededOnceVMChannel(res))
|
|
break;
|
|
retrying = true;
|
|
} while (true);
|
|
VMAccessorGnuJavaNio.checkIOResCodeVMChannel(res);
|
|
try
|
|
{
|
|
VMAccessorGnuJavaNio.setNativeFileFDVMChannel(chIn, fdsArr[0],
|
|
FileChannelImpl.READ);
|
|
}
|
|
finally
|
|
{
|
|
VMAccessorGnuJavaNio.setNativeFileFDVMChannel(chOut, fdsArr[1],
|
|
FileChannelImpl.WRITE);
|
|
}
|
|
}
|
|
|
|
private static synchronized int nextThreadNum()
|
|
{
|
|
return ++threadNum;
|
|
}
|
|
|
|
private static String[] addMissingEnvVar(String[] env, String name,
|
|
String defValue)
|
|
{
|
|
String value = VMAccessorJavaIo.getenvPlatformVMFile(name);
|
|
if (value == null && (value = defValue) == null)
|
|
return env;
|
|
int len = env.length;
|
|
String nameAssign = name.concat("=");
|
|
String str;
|
|
for (int i = 0; i < len; i++)
|
|
if ((str = env[i]) != null && str.startsWith(nameAssign))
|
|
return env;
|
|
String[] newEnv = new String[len + 1];
|
|
VMSystem.arraycopy(env, 0, newEnv, 0, len);
|
|
newEnv[len] = nameAssign.concat(value);
|
|
return newEnv;
|
|
}
|
|
|
|
private static String convertEnv(String[] env)
|
|
{
|
|
int len = env.length;
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
String envStr = env[i];
|
|
if (envStr == null)
|
|
throw new NullPointerException();
|
|
if (envStr.length() > 0)
|
|
{
|
|
int pos = envStr.indexOf('\0', 0);
|
|
sb.append(pos >= 0 ? envStr.substring(0, pos) : envStr).append('\0');
|
|
}
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
private static String getValidProgExtsList()
|
|
{
|
|
String validDotExts = VMSystem.getenv("PATHEXT");
|
|
return validDotExts != null || (validDotExts =
|
|
getValidProgExtsList0()) != null ? validDotExts : "";
|
|
}
|
|
|
|
private static String convertProgName(String progName, boolean changeDir)
|
|
throws IOException
|
|
{
|
|
int pos = progName.indexOf('\0', 0);
|
|
if (pos >= 0)
|
|
progName = progName.substring(0, pos);
|
|
String validDotExts = VALID_PROG_EXTS_LIST;
|
|
boolean isSearchNeeded = IS_PROG_SEARCH_NEEDED;
|
|
if (progName.length() == 0 ||
|
|
(validDotExts.length() == 0 && !isSearchNeeded))
|
|
return (new File(progName)).getPath();
|
|
String name = (new File(progName)).getName();
|
|
String searchList = isSearchNeeded && progName.equals(name) ?
|
|
VMSystem.getenv("PATH") : null;
|
|
char pathSep =
|
|
SystemProperties.getProperty("path.separator", ":").charAt(0);
|
|
if (searchList == null)
|
|
searchList = "";
|
|
if (name.lastIndexOf('.') >= 0)
|
|
validDotExts = "";
|
|
pos = searchList.length();
|
|
if (pos > 0 && searchList.charAt(pos - 1) != pathSep &&
|
|
(searchList.charAt(pos - 1) != '.' ||
|
|
(pos != 1 && searchList.charAt(pos - 2) != pathSep)))
|
|
searchList = searchList.concat(String.valueOf(pathSep)).concat(".");
|
|
if ((pos = validDotExts.length()) > 0 && validDotExts.charAt(pos - 1) !=
|
|
pathSep && validDotExts.charAt(0) != pathSep)
|
|
validDotExts = validDotExts.concat(String.valueOf(pathSep));
|
|
pos = 0;
|
|
String prefix;
|
|
File file;
|
|
String path = null;
|
|
do
|
|
{
|
|
int next = searchList.indexOf(pathSep, pos);
|
|
if (next < 0)
|
|
next = searchList.length();
|
|
prefix = searchList.substring(pos, next);
|
|
if (pos == next && searchList.length() > 0)
|
|
prefix = ".";
|
|
int pos2 = 0;
|
|
do
|
|
{
|
|
int next2 = validDotExts.indexOf(pathSep, pos2);
|
|
if (next2 < 0)
|
|
next2 = validDotExts.length();
|
|
name = progName;
|
|
if (pos2 < next2)
|
|
{
|
|
String ext = validDotExts.substring(pos2, next2);
|
|
if (ext.charAt(0) != '.')
|
|
ext = ".".concat(ext);
|
|
name = progName.concat(ext);
|
|
}
|
|
file = prefix.length() > 0 ? new File(prefix, name) : new File(name);
|
|
if (checkPermissions0(file.getPath(), 0) >= 0)
|
|
{
|
|
next = searchList.length();
|
|
path = file.getPath();
|
|
break;
|
|
}
|
|
pos2 = next2 + 1;
|
|
} while (validDotExts.length() > pos2);
|
|
pos = next + 1;
|
|
} while (searchList.length() > pos);
|
|
if (path == null)
|
|
throw new IOException("executable file not found: ".concat(progName));
|
|
if (changeDir && isSearchNeeded && !file.isAbsolute())
|
|
{
|
|
if (prefix.equals("."))
|
|
file = new File(name);
|
|
path = file.getAbsoluteFile().getPath();
|
|
}
|
|
return path;
|
|
}
|
|
|
|
private static String convertCmd(String progName, String[] cmd,
|
|
boolean isSpaceDelimited)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
String cmdStr = progName;
|
|
int i = 0;
|
|
if (isSpaceDelimited)
|
|
{
|
|
do
|
|
{
|
|
if (cmdStr != null)
|
|
{
|
|
if (cmdStr.length() > 0 && cmdStr.indexOf(' ') < 0 &&
|
|
cmdStr.indexOf('\t') < 0 && cmdStr.indexOf('\n') < 0 &&
|
|
cmdStr.indexOf('\r') < 0 && cmdStr.indexOf('"') < 0)
|
|
sb.append(cmdStr);
|
|
else
|
|
{
|
|
sb.append('"');
|
|
do
|
|
{
|
|
int j = cmdStr.indexOf('"');
|
|
int k = cmdStr.indexOf('\\');
|
|
if (k >= 0 && (j < 0 || k < j))
|
|
j = k;
|
|
if (j < 0)
|
|
break;
|
|
sb.append(cmdStr.substring(0, j));
|
|
if (j == k)
|
|
{
|
|
int len = cmdStr.length();
|
|
do
|
|
{
|
|
j++;
|
|
} while (j < len && cmdStr.charAt(j) == '\\');
|
|
sb.append(cmdStr.substring(k, j));
|
|
if (j >= len || cmdStr.charAt(j) == '"')
|
|
sb.append(cmdStr.substring(k, j));
|
|
j--;
|
|
}
|
|
else sb.append('\\').append('"');
|
|
cmdStr = cmdStr.substring(j + 1);
|
|
} while (true);
|
|
sb.append(cmdStr).append('"');
|
|
}
|
|
}
|
|
if (++i >= cmd.length)
|
|
break;
|
|
if (cmdStr != null)
|
|
sb.append(' ');
|
|
cmdStr = cmd[i];
|
|
int pos = cmdStr.indexOf('\0', 0);
|
|
if (pos >= 0)
|
|
cmdStr = cmdStr.substring(0, pos);
|
|
} while (true);
|
|
sb.append('\0');
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (cmdStr != null)
|
|
{
|
|
if (cmdStr.length() == 0 || cmdStr.charAt(0) == ' ')
|
|
sb.append(' ');
|
|
sb.append(cmdStr).append('\0');
|
|
}
|
|
if (++i >= cmd.length)
|
|
break;
|
|
cmdStr = cmd[i];
|
|
int pos = cmdStr.indexOf('\0', 0);
|
|
if (pos >= 0)
|
|
cmdStr = cmdStr.substring(0, pos);
|
|
} while (true);
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
private static InputStream createInputStream(VMChannel ch)
|
|
{
|
|
return VMAccessorJavaIo.newFileInputStream(
|
|
VMAccessorGnuJavaNio.newFileChannelImpl(ch, FileChannelImpl.READ));
|
|
}
|
|
|
|
private static OutputStream createOutputStream(VMChannel ch)
|
|
{
|
|
return VMAccessorJavaIo.newFileOutputStream(
|
|
VMAccessorGnuJavaNio.newFileChannelImpl(ch, FileChannelImpl.WRITE));
|
|
}
|
|
|
|
private static native String getValidProgExtsList0();
|
|
|
|
private static native int isProgSearchNeeded0();
|
|
|
|
private static native int isCmdSpaceDelimited0();
|
|
|
|
private static native int checkPermissions0(String path, int isDirCheck);
|
|
|
|
private static native int getSpawnWorkBufSize0(String cmdZBlock,
|
|
String envZBlock, int cmdArrLen, int envArrLen);
|
|
|
|
private static native int nativeSpawn0(String progName, String cmdZBlock,
|
|
String envZBlock, String dirpath, int[] fdsArr, long[] pidArr,
|
|
byte[] workBuf, int bufLen, int redirect);
|
|
|
|
private static native int nativeWaitFor0(long pid,
|
|
int nohang); /* blocking syscall */
|
|
|
|
private static native int nativeKill(long pid);
|
|
|
|
private static native int pipe0(int[] fdsArr);
|
|
}
|