aboutsummaryrefslogtreecommitdiff
path: root/nailgun_launcher
diff options
context:
space:
mode:
authorJan Christopher Vogt <oss.nsp@cvogt.org>2016-10-14 00:52:10 -0400
committerGitHub <noreply@github.com>2016-10-14 00:52:10 -0400
commit358a189db9842705d5c885a4c315635cd36fdc14 (patch)
tree7533ac2d08c686278256117a14ad64419d708274 /nailgun_launcher
parent86a4f0970c0e55ac072648691df8c159de50e969 (diff)
parentdc01feea46cef8c075d41b1bcbb11f54d3f7fad1 (diff)
downloadcbt-358a189db9842705d5c885a4c315635cd36fdc14.tar.gz
cbt-358a189db9842705d5c885a4c315635cd36fdc14.tar.bz2
cbt-358a189db9842705d5c885a4c315635cd36fdc14.zip
Merge pull request #276 from cvogt/fix-cross-classloader-security-manager
Fix exit code trapping and out/err redirection with and without nailgun
Diffstat (limited to 'nailgun_launcher')
-rw-r--r--nailgun_launcher/NailgunLauncher.java26
-rw-r--r--nailgun_launcher/Stage0Lib.java10
-rw-r--r--nailgun_launcher/ThreadLocalOutputStream.java30
-rw-r--r--nailgun_launcher/TrapSecurityManager.java52
-rw-r--r--nailgun_launcher/TrappedExitCode.java8
5 files changed, 101 insertions, 25 deletions
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index 5a70312..b1daf2a 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -23,13 +23,6 @@ public class NailgunLauncher{
public final static SecurityManager initialSecurityManager
= System.getSecurityManager();
- public final static ThreadLocal<Boolean> trapExitCode =
- new ThreadLocal<Boolean>() {
- @Override protected Boolean initialValue() {
- return false;
- }
- };
-
public static String TARGET = System.getenv("TARGET");
private static String NAILGUN = "nailgun_launcher/";
private static String STAGE1 = "stage1/";
@@ -66,7 +59,24 @@ public class NailgunLauncher{
installProxySettings();
String[] diff = args[0].split("\\.");
long start = _start - (Long.parseLong(diff[0]) * 1000L) - Long.parseLong(diff[1]);
-
+
+ // if nailgun didn't install it's threadLocal stdout/err replacements, install CBT's.
+ // this hack allows to later swap out System.out/err while still affecting things like
+ // scala.Console, which captured them at startup
+ try{
+ System.out.getClass().getDeclaredField("streams"); // nailgun ThreadLocalPrintStream
+ assert(System.out.getClass().getName() == "com.martiansoftware.nailgun.ThreadLocalPrintStream");
+ } catch( NoSuchFieldException e ){
+ System.setOut( new PrintStream(new ThreadLocalOutputStream(System.out)) );
+ }
+ try{
+ System.err.getClass().getDeclaredField("streams"); // nailgun ThreadLocalPrintStream
+ assert(System.out.getClass().getName() == "com.martiansoftware.nailgun.ThreadLocalPrintStream");
+ } catch( NoSuchFieldException e ){
+ System.setErr( new PrintStream(new ThreadLocalOutputStream(System.err)) );
+ }
+ // ---------------------
+
_assert(System.getenv("CBT_HOME") != null, "environment variable CBT_HOME not defined");
String CBT_HOME = System.getenv("CBT_HOME");
String cache = CBT_HOME + "/cache/";
diff --git a/nailgun_launcher/Stage0Lib.java b/nailgun_launcher/Stage0Lib.java
index 11a9aab..452bdae 100644
--- a/nailgun_launcher/Stage0Lib.java
+++ b/nailgun_launcher/Stage0Lib.java
@@ -20,21 +20,21 @@ public class Stage0Lib{
}
public static int runMain(String cls, String[] args, ClassLoader cl) throws Throwable{
- Boolean trapExitCodeBefore = NailgunLauncher.trapExitCode.get();
+ Boolean trapExitCodeBefore = TrapSecurityManager.trapExitCode().get();
try{
- NailgunLauncher.trapExitCode.set(true);
+ TrapSecurityManager.trapExitCode().set(true);
cl.loadClass(cls)
.getMethod("main", String[].class)
.invoke( null, (Object) args);
return 0;
}catch( InvocationTargetException exception ){
Throwable cause = exception.getCause();
- if(cause instanceof TrappedExitCode){
- return ((TrappedExitCode) cause).exitCode;
+ if(TrapSecurityManager.isTrappedExit(cause)){
+ return TrapSecurityManager.exitCode(cause);
}
throw exception;
} finally {
- NailgunLauncher.trapExitCode.set(trapExitCodeBefore);
+ TrapSecurityManager.trapExitCode().set(trapExitCodeBefore);
}
}
diff --git a/nailgun_launcher/ThreadLocalOutputStream.java b/nailgun_launcher/ThreadLocalOutputStream.java
new file mode 100644
index 0000000..c12b775
--- /dev/null
+++ b/nailgun_launcher/ThreadLocalOutputStream.java
@@ -0,0 +1,30 @@
+package cbt;
+import java.io.*;
+
+public class ThreadLocalOutputStream extends OutputStream{
+ final public ThreadLocal<OutputStream> threadLocal;
+ final private OutputStream initialValue;
+
+ public ThreadLocalOutputStream( OutputStream initialValue ){
+ this.initialValue = initialValue;
+ threadLocal = new ThreadLocal<OutputStream>() {
+ @Override protected OutputStream initialValue() {
+ return ThreadLocalOutputStream.this.initialValue;
+ }
+ };
+ }
+
+ public OutputStream get(){
+ return threadLocal.get();
+ }
+
+ public void set( OutputStream outputStream ){
+ threadLocal.set( outputStream );
+ }
+
+ public void write( int b ) throws IOException{
+ // after implementing this I realized NailgunLauncher uses the same hack,
+ // so probably this is not a problem performance
+ get().write(b);
+ }
+}
diff --git a/nailgun_launcher/TrapSecurityManager.java b/nailgun_launcher/TrapSecurityManager.java
index fada878..1626787 100644
--- a/nailgun_launcher/TrapSecurityManager.java
+++ b/nailgun_launcher/TrapSecurityManager.java
@@ -9,6 +9,33 @@ would be Nailgun's if running on Nailgun. If we do not delegate to Nailgun, it s
could in some cases kill the server process
*/
public class TrapSecurityManager extends ProxySecurityManager{
+ public static ThreadLocal<Boolean> trapExitCode(){
+ // storing the flag in the installed security manager
+ // instead of e.g. a static member is necessary because
+ // we run multiple versions of CBT with multiple TrapSecurityManager classes
+ // but we need to affect the installed one
+ SecurityManager sm = System.getSecurityManager();
+ if(sm instanceof TrapSecurityManager){
+ return ((TrapSecurityManager) sm)._trapExitCode;
+ } else {
+ try{
+ @SuppressWarnings("unchecked")
+ ThreadLocal<Boolean> res =
+ (ThreadLocal<Boolean>) sm.getClass().getMethod("trapExitCode").invoke(null);
+ return res;
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private final ThreadLocal<Boolean> _trapExitCode =
+ new ThreadLocal<Boolean>() {
+ @Override protected Boolean initialValue() {
+ return false;
+ }
+ };
+
public TrapSecurityManager(){
super(NailgunLauncher.initialSecurityManager);
}
@@ -19,21 +46,38 @@ public class TrapSecurityManager extends ProxySecurityManager{
Calling .super leads to ClassNotFound exteption for a lambda.
Calling to the previous SecurityManager leads to a stack overflow
*/
- if(!NailgunLauncher.trapExitCode.get()){
+ if(!TrapSecurityManager.trapExitCode().get()){
super.checkPermission(permission);
}
}
public void checkPermission( Permission permission, Object context ){
/* Does this methods need to be overidden? */
- if(!NailgunLauncher.trapExitCode.get()){
+ if(!TrapSecurityManager.trapExitCode().get()){
super.checkPermission(permission, context);
}
}
+
+ private static final String prefix = "[TrappedExit] ";
+
@Override
public void checkExit( int status ){
- if(NailgunLauncher.trapExitCode.get()){
- throw new TrappedExitCode(status);
+ if(TrapSecurityManager.trapExitCode().get()){
+ // using a RuntimeException and a prefix here instead of a custom
+ // exception type because this is thrown by the installed TrapSecurityManager
+ // but other versions of cbt need to be able to catch it, that do not have access
+ // to that version of the TrapSecurityManager class
+ throw new RuntimeException(prefix+status);
}
super.checkExit(status);
}
+
+ public static boolean isTrappedExit( Throwable t ){
+ return t instanceof RuntimeException && t.getMessage().startsWith(prefix);
+ }
+
+ public static int exitCode( Throwable t ){
+ assert(isTrappedExit(t));
+ return Integer.parseInt( t.getMessage().substring(prefix.length()) );
+ }
+
}
diff --git a/nailgun_launcher/TrappedExitCode.java b/nailgun_launcher/TrappedExitCode.java
deleted file mode 100644
index 154db27..0000000
--- a/nailgun_launcher/TrappedExitCode.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package cbt;
-import java.security.*;
-public class TrappedExitCode extends SecurityException{
- public int exitCode;
- public TrappedExitCode(int exitCode){
- this.exitCode = exitCode;
- }
-}