Skip to content

Commit

Permalink
BYTEMAN-280 Enable a 'manager' component to be configured, that can m…
Browse files Browse the repository at this point in the history
…ake use of the retransfer as appropriate to dynamically install/remove scripts. Includes further changes suggested by Andrew.
  • Loading branch information
objectiser committed Apr 14, 2015
1 parent 9f7d449 commit 003cee5
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 54 deletions.
68 changes: 40 additions & 28 deletions agent/src/main/java/org/jboss/byteman/agent/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@
package org.jboss.byteman.agent;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.jar.JarFile;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.net.Socket;
import java.util.jar.JarFile;

/**
* agent class supplied at JVM startup to install byteman package bytecode transformer
Expand All @@ -55,7 +55,6 @@ public static void premain(String args, Instrumentation inst)
throw new Exception("Main : attempting to load Byteman agent more than once");
}
}
boolean allowRedefine = false;
boolean installPolicy = false;

if (args != null) {
Expand All @@ -71,17 +70,12 @@ public static void premain(String args, Instrumentation inst)
sysJarPaths.add(arg.substring(SYS_PREFIX.length(), arg.length()));
} else if (arg.startsWith(ADDRESS_PREFIX)) {
hostname = arg.substring(ADDRESS_PREFIX.length(), arg.length());
// setting host name forces listener on
allowRedefine = true;
} else if (arg.startsWith(PORT_PREFIX)) {
try {
port = Integer.valueOf(arg.substring(PORT_PREFIX.length(), arg.length()));
if (port <= 0) {
System.err.println("Invalid port specified [" + port + "]");
port = null;
} else {
// setting port forces listener on
allowRedefine = true;
}
} catch (Exception e) {
System.err.println("Invalid port specified [" + arg + "]. Cause: " + e);
Expand All @@ -92,18 +86,14 @@ public static void premain(String args, Instrumentation inst)
resourcescriptPaths.add(arg.substring(RESOURCE_SCRIPT_PREFIX.length(), arg.length()));
} else if (arg.startsWith(LISTENER_PREFIX)) {
String value = arg.substring(LISTENER_PREFIX.length(), arg.length());
allowRedefine = Boolean.parseBoolean(value);
// clearing listener when port or host is set should be flagged
if (!allowRedefine && (hostname != null || port != null)) {
System.err.println("listener disabled with host/port set");
if (Boolean.parseBoolean(value)) {
managerClassName = TransformListener.class.getName();
}
} else if (arg.startsWith(REDEFINE_PREFIX)) {
// this is only for backwards compatibility -- it is the same as listener
String value = arg.substring(REDEFINE_PREFIX.length(), arg.length());
allowRedefine = Boolean.parseBoolean(value);
// clearing listener when port or host is set should be flagged
if (!allowRedefine && (hostname != null || port != null)) {
System.err.println("listener disabled with host/port set");
if (Boolean.parseBoolean(value)) {
managerClassName = TransformListener.class.getName();
}
} else if (arg.startsWith(PROP_PREFIX)) {
// this can be used to set byteman properties
Expand All @@ -130,6 +120,8 @@ public static void premain(String args, Instrumentation inst)
} else if (arg.startsWith(POLICY_PREFIX)) {
String value = arg.substring(POLICY_PREFIX.length(), arg.length());
installPolicy = Boolean.parseBoolean(value);
} else if (arg.startsWith(MANAGER_PREFIX)) {
managerClassName = arg.substring(MANAGER_PREFIX.length(), arg.length());
} else {
System.err.println("org.jboss.byteman.agent.Main:\n" +
" illegal agent argument : " + arg + "\n" +
Expand Down Expand Up @@ -224,7 +216,7 @@ public static void premain(String args, Instrumentation inst)
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class transformerClazz;

if (allowRedefine && isRedefine) {
if (managerClassName != null && isRedefine) {
transformerClazz = loader.loadClass("org.jboss.byteman.agent.Retransformer");
//transformer = new Retransformer(inst, scriptPaths, scripts, true);
Constructor constructor = transformerClazz.getConstructor(Instrumentation.class, List.class, List.class, boolean.class);
Expand All @@ -237,10 +229,17 @@ public static void premain(String args, Instrumentation inst)
}

inst.addTransformer(transformer, true);

if (allowRedefine && isRedefine) {
Method method = transformerClazz.getMethod("addTransformListener", String.class, Integer.class);
method.invoke(transformer, hostname, port);

if (managerClassName != null && isRedefine) {
Class managerClazz = loader.loadClass(managerClassName);

try {
Method method = managerClazz.getMethod("initialize", transformerClazz, String.class, Integer.class);
method.invoke(null, transformer, hostname, port);
} catch (NoSuchMethodException e) {
Method method = managerClazz.getMethod("initialize", transformerClazz);
method.invoke(null, transformer);
}
}

if (installPolicy) {
Expand Down Expand Up @@ -270,7 +269,7 @@ public static void agentmain(String args, Instrumentation inst) throws Exception
* prefix used to specify bind address argument for agent
*/
private static final String ADDRESS_PREFIX = "address:";

/**
* prefix used to specify boot jar argument for agent
*/
Expand Down Expand Up @@ -316,6 +315,12 @@ public static void agentmain(String args, Instrumentation inst) throws Exception

private static final String PROP_PREFIX = "prop:";

/**
* prefix used to specify the manager class
*/

private static final String MANAGER_PREFIX = "manager:";

/**
* list of paths to extra bootstrap jars supplied on command line
*/
Expand All @@ -340,14 +345,21 @@ public static void agentmain(String args, Instrumentation inst) throws Exception
* list of scripts read from script files
*/
private static List<String> scripts = new ArrayList<String>();

/**
* The hostname to bind the listener to, supplied on the command line (optional argument)
*/
private static String hostname = null;

/**
* The port that the listener will listen to, supplied on the command line (optional argument)
*/
private static Integer port = null;

/**
* The name of the manager class responsible for loading/unloading scripts, supplied on the
* command line (optional argument)
*/
private static String managerClassName = null;

}
25 changes: 12 additions & 13 deletions agent/src/main/java/org/jboss/byteman/agent/Retransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@
*/
package org.jboss.byteman.agent;

import java.io.PrintWriter;
import java.lang.instrument.Instrumentation;
import java.util.*;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import java.io.PrintWriter;

/**
* byte code transformer used to introduce byteman events into JBoss code
Expand Down Expand Up @@ -146,7 +150,7 @@ public void installScript(List<String> scriptTexts, List<String> scriptNames, Pr
}
}
}

// now we can safely purge keys for all deleted scripts

for (RuleScript ruleScript : toBeRemoved) {
Expand Down Expand Up @@ -203,11 +207,6 @@ protected void listScripts(PrintWriter out) throws Exception
}
}

public void addTransformListener(String hostname, Integer port)
{
TransformListener.initialize(this, hostname, port);
}

public void removeScripts(List<String> scriptTexts, PrintWriter out) throws Exception
{
List<RuleScript> toBeRemoved;
Expand Down Expand Up @@ -243,7 +242,7 @@ public void removeScripts(List<String> scriptTexts, PrintWriter out) throws Exce
out.println("ERROR No rule scripts to remove");
return;
}

for (RuleScript ruleScript : toBeRemoved) {
if (scriptRepository.removeScript(ruleScript) != ruleScript) {
out.println("ERROR remove failed to find script " + ruleScript.getName());
Expand Down Expand Up @@ -328,10 +327,10 @@ public void appendJarFile(PrintWriter out, JarFile jarfile, boolean isBoot) thro
/**
* Returns jars that this retransformer was asked to
* {@link #appendJarFile(PrintWriter, JarFile, boolean) add} to the boot classloader.
*
*
* Note that the returned set will not include those jars that were added to the
* instrumentor object at startup via the -javaagent command line argument.
*
*
* @return set of jar pathnames for all jars loaded in the boot classloader
*/
public Set<String> getLoadedBootJars() {
Expand All @@ -341,10 +340,10 @@ public Set<String> getLoadedBootJars() {
/**
* Returns jars that this retransformer was asked to
* {@link #appendJarFile(PrintWriter, JarFile, boolean) add} to the system classloader.
*
*
* Note that the returned set will not include those jars that were added to the
* instrumentor object at startup via the -javaagent command line argument.
*
*
* @return set of jar pathnames for all jars loaded in the system classloader
*/
public Set<String> getLoadedSystemJars() {
Expand Down
38 changes: 25 additions & 13 deletions agent/src/main/java/org/jboss/byteman/agent/TransformListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,26 @@
*/
package org.jboss.byteman.agent;

import org.jboss.byteman.agent.Retransformer;
import org.jboss.byteman.rule.Rule;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetSocketAddress;
import java.io.*;
import java.util.List;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarFile;

import org.jboss.byteman.rule.Rule;

/**
* a socket based listener class which reads scripts from stdin and installs them in the current runtime
*/
Expand All @@ -52,7 +58,12 @@ public class TransformListener extends Thread
private TransformListener(Retransformer retransformer)
{
this.retransformer = retransformer;
setDaemon(true);
setDaemon(true);
}

public static synchronized boolean initialize(Retransformer retransformer)
{
return (initialize(retransformer, null, null));
}

public static synchronized boolean initialize(Retransformer retransformer, String hostname, Integer port)
Expand Down Expand Up @@ -118,10 +129,11 @@ public static synchronized boolean terminate()
}
}

@Override
public void run()
{
// we don't want to see any triggers in the listener thread

Rule.disableTriggersInternal();

while (true) {
Expand Down Expand Up @@ -387,7 +399,7 @@ private void listSystemProperties(BufferedReader in, PrintWriter out) throws Exc
if (Boolean.parseBoolean(sysProps.getProperty(Transformer.SYSPROPS_STRICT_MODE, "true"))) {
strictMode = true;
}

for (Map.Entry<Object, Object> entry : sysProps.entrySet()) {
String name = entry.getKey().toString();
if (!strictMode || name.startsWith("org.jboss.byteman.")) {
Expand All @@ -398,14 +410,14 @@ private void listSystemProperties(BufferedReader in, PrintWriter out) throws Exc
out.println("OK");
out.flush();
}

private void setSystemProperties(BufferedReader in, PrintWriter out) throws Exception
{
boolean strictMode = false;
if (Boolean.parseBoolean(System.getProperty(Transformer.SYSPROPS_STRICT_MODE, "true"))) {
strictMode = true;
}

final String endMarker = "ENDSETSYSPROPS";
String line = in.readLine().trim();
while (line != null && !line.equals(endMarker)) {
Expand All @@ -417,7 +429,7 @@ private void setSystemProperties(BufferedReader in, PrintWriter out) throws Exce
String name = nameValuePair[0];
String value = nameValuePair[1];
if (strictMode && !name.startsWith("org.jboss.byteman.")) {
throw new Exception("strict mode is enabled, cannot set non-byteman system property");
throw new Exception("strict mode is enabled, cannot set non-byteman system property");
}
if (name.equals(Transformer.SYSPROPS_STRICT_MODE) && !value.equals("true")) {
// nice try
Expand All @@ -427,7 +439,7 @@ private void setSystemProperties(BufferedReader in, PrintWriter out) throws Exce
// everything looks good and we are allowed to set the system property now
if (value.length() > 0) {
// "some.sys.prop=" means the client wants to delete the system property
System.setProperty(name, value);
System.setProperty(name, value);
out.append("Set system property [" + name + "] to value [" + value + "]\n");
} else {
System.clearProperty(name);
Expand Down

0 comments on commit 003cee5

Please sign in to comment.