org.jboss.byteman.agent.Main Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of byteman Show documentation
Show all versions of byteman Show documentation
The byteman jar merges the byteman-agent jar contents with those of the
byteman-jigsaw and byteman-layer jars as a mutli-release jar. The contents
of the latter two jars are installed under META-INF/versions/9 ensuring
that they are only linked when Byteman is deployed on a JDK9+ JVM
/*
* JBoss, Home of Professional Open Source
* Copyright 2008-10 Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
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.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarFile;
/**
* agent class supplied at JVM startup to install byteman package bytecode transformer
*/
public class Main {
public static boolean firstTime = true;
public final static String BYTEMAN_PREFIX = "org.jboss.byteman.";
public final static String BYTEMAN_AGENT_LOADED = "org.jboss.byteman.agent.loaded";
public static void premain(String args, Instrumentation inst)
throws Exception
{
// guard against the agent being loaded twice
synchronized (Main.class) {
if (firstTime) {
firstTime = false;
System.setProperty(BYTEMAN_AGENT_LOADED, Boolean.TRUE.toString());
} else {
throw new Exception("Main : attempting to load Byteman agent more than once");
}
}
boolean installPolicy = false;
if (args != null) {
// args are supplied separated by ',' characters
String[] argsArray = args.split(",");
// we accept extra jar files to be added to the boot/sys classpaths
// script files to be scanned for rules
// listener flag which implies use of a retransformer
for (String arg : argsArray) {
if (arg.startsWith(BOOT_PREFIX)) {
bootJarPaths.add(arg.substring(BOOT_PREFIX.length(), arg.length()));
} else if (arg.startsWith(SYS_PREFIX)) {
sysJarPaths.add(arg.substring(SYS_PREFIX.length(), arg.length()));
} else if (arg.startsWith(ADDRESS_PREFIX)) {
hostname = arg.substring(ADDRESS_PREFIX.length(), arg.length());
if (managerClassName == null) {
managerClassName=MANAGER_NAME;
}
} 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 if (managerClassName == null) {
managerClassName=MANAGER_NAME;
}
} catch (Exception e) {
System.err.println("Invalid port specified [" + arg + "]. Cause: " + e);
}
} else if (arg.startsWith(SCRIPT_PREFIX)) {
scriptPaths.add(arg.substring(SCRIPT_PREFIX.length(), arg.length()));
} else if (arg.startsWith(RESOURCE_SCRIPT_PREFIX)) {
resourcescriptPaths.add(arg.substring(RESOURCE_SCRIPT_PREFIX.length(), arg.length()));
} else if (arg.startsWith(LISTENER_PREFIX)) {
// listener:true is an alias for manager:o.j.b.a.TransformListener
// listener:false means no manager (yes, not even TransformListener)
String value = arg.substring(LISTENER_PREFIX.length(), arg.length());
if (Boolean.parseBoolean(value)) {
managerClassName = MANAGER_NAME;
} else {
managerClassName = null;
}
} 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());
if (Boolean.parseBoolean(value)) {
managerClassName = MANAGER_NAME;
} else {
managerClassName = null;
}
} else if (arg.startsWith(PROP_PREFIX)) {
// this can be used to set byteman properties
String prop = arg.substring(PROP_PREFIX.length(), arg.length());
String value="";
if (prop.startsWith(BYTEMAN_PREFIX)) {
int index = prop.indexOf('=');
if (index > 0) {
// need to split off the value
if (index == prop.length() - 1)
{
// value is empty so just drop the =
prop = prop.substring(0, index);
} else {
value = prop.substring(index + 1);
prop = prop.substring(0, index);
}
}
System.out.println("Setting " + prop + "=" + value);
System.setProperty(prop, value);
} else {
System.err.println("Invalid property : " + prop);
}
} 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());
if (managerClassName.length() == 0) {
managerClassName = null;
}
} else if (arg.startsWith(MODULE_PREFIX)) {
// this can be used to set byteman properties
String mod = arg.substring(MODULE_PREFIX.length(), arg.length());
String moduleArgs="";
int index = mod.indexOf('=');
if (index > 0) {
// need to split off the value
if (index == mod.length() - 1)
{
// value is empty so just drop the =
mod = mod.substring(0, index);
} else {
moduleArgs = mod.substring(index + 1);
mod = mod.substring(0, index);
}
}
moduleSystemName = mod;
moduleSystemArgs = moduleArgs;
} else {
System.err.println("org.jboss.byteman.agent.Main:\n" +
" illegal agent argument : " + arg + "\n" +
" valid arguments are boot:, sys:, script:, resourcescript:,"
+ "prop:, address:, port:, modules:, "
+ "policy:, manager: or listener:");
}
}
}
// add any boot jars to the boot class path
for (String bootJarPath : bootJarPaths) {
try {
JarFile jarfile = new JarFile(new File(bootJarPath));
inst.appendToBootstrapClassLoaderSearch(jarfile);
} catch (IOException ioe) {
System.err.println("org.jboss.byteman.agent.Main: unable to open boot jar file : " + bootJarPath);
throw ioe;
}
}
// add any sys jars to the system class path
for (String sysJarPath : sysJarPaths) {
try {
JarFile jarfile = new JarFile(new File(sysJarPath));
inst.appendToSystemClassLoaderSearch(jarfile);
} catch (IOException ioe) {
System.err.println("org.jboss.byteman.agent.Main: unable to open system jar file : " + sysJarPath);
throw ioe;
}
}
// create a socket so we can be sure it is loaded before the transformer gets created. otherwise
// we seem to hit a deadlock when trying to instrument socket
Socket dummy = new Socket();
// look up rules in any script files
for (String scriptPath : scriptPaths) {
FileInputStream fis = null;
try {
fis = new FileInputStream(scriptPath);
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
String ruleScript = new String(bytes);
scripts.add(ruleScript);
} catch (IOException ioe) {
System.err.println("org.jboss.byteman.agent.Main: unable to read rule script file : " + scriptPath);
throw ioe;
} finally {
if (fis != null)
fis.close();
}
}
// look up rules in any resource script files
for (String scriptPath : resourcescriptPaths) {
try {
InputStream is = ClassLoader.getSystemResourceAsStream(scriptPath);
if (is == null) {
throw new Exception("org.jboss.byteman.agent.Main: could not read rule script resource file : " + scriptPath);
}
byte[] bytes = new byte[is.available()];
is.read(bytes);
String ruleScript = new String(bytes);
scripts.add(ruleScript);
// merge the resource and file script paths into one list
scriptPaths.add(scriptPath);
} catch (IOException ioe) {
System.err.println("org.jboss.byteman.agent.Main: error reading rule script resource file : " + scriptPath);
throw ioe;
}
}
// install an instance of Transformer to instrument the bytecode
// n.b. this is done with boxing gloves on using explicit class loading and method invocation
// via reflection for a GOOD reason. This class (Main) gets loaded by the System class loader.
// If we refer to Transformer by name then it also gets loaded via the System class loader.
// But if we want to transform a bootstrap class we need Transformer (et al) to be visible
// from the bootstrap class loader. That will not happen until after this method has called
// inst.appendToBootstrapClassLoaderSearch (see above) to add the byteman jar to the path.
// Directly referring to Transformer will give us two versions of Transformer et al. Not only
// does that cause us class mismatch problem it also means that a new done here will not install
// the new instance in the static field of the one loaded in the bootstrap loader. If instead we
// use boxing gloves then the byteman code will get loaded in the bootstrap loader and its constructor
// will be called.
//
// Of course, if the user does not supply boot:byteman.jar as a -javaagent option then class references
// resolve against the system loader and injection into bootstrap classes fails. But that's still ok
// because the byteman classes are still only found in one place.
ClassLoader loader = ClassLoader.getSystemClassLoader();
if (moduleSystemName == null) {
moduleSystemName = "org.jboss.byteman.modules.NonModuleSystem";
}
Class/*ModuleSystem*/> moduleSystemInteraceClazz = loader.loadClass(MODULE_SYSTEM_NAME);
Class/*ModuleSystem*/> moduleSystemImplClazz = loader.loadClass(moduleSystemName);
final Object/*ModuleSystem*/ moduleSystem = moduleSystemImplClazz.newInstance();
final Method/*String->void*/ moduleSystemInit = moduleSystemInteraceClazz.getMethod("initialize", String.class);
moduleSystemInit.invoke(moduleSystem, moduleSystemArgs);
boolean isRedefine = inst.isRedefineClassesSupported();
Class/**/ transformerClazz;
ClassFileTransformer transformer;
if (managerClassName != null && isRedefine) {
transformerClazz = loader.loadClass(RETRANSFORMER_NAME);
//transformer = new Retransformer(inst, moduleSystem, scriptPaths, scripts, true);
Constructor/**/ constructor = transformerClazz.getConstructor(Instrumentation.class,moduleSystemInteraceClazz, List.class, List.class, boolean.class);
transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, moduleSystem, scriptPaths, scripts, isRedefine});
} else {
transformerClazz = loader.loadClass(TRANSFORMER_NAME);
//transformer = new Transformer(inst, moduleSystem, scriptPaths, scripts, isRedefine);
Constructor/**/ constructor = transformerClazz.getConstructor(Instrumentation.class, moduleSystemInteraceClazz, List.class, List.class, boolean.class);
transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, moduleSystem, scriptPaths, scripts, isRedefine});
}
inst.addTransformer(transformer, true);
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) {
Method method = transformerClazz.getMethod("installPolicy");
method.invoke(transformer);
}
if (isRedefine) {
Method method;
method = transformerClazz.getMethod("installBootScripts");
method.invoke(transformer);
//transformer.installBootScripts();
}
}
public static void agentmain(String args, Instrumentation inst) throws Exception
{
premain(args, inst);
}
/**
* prefix used to specify port argument for agent
*/
private static final String PORT_PREFIX = "port:";
/**
* 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
*/
private static final String BOOT_PREFIX = "boot:";
/**
* prefix used to specify system jar argument for agent
*/
private static final String SYS_PREFIX = "sys:";
/**
* prefix used to request installation of an access-all-areas security
* policy at install time for agent code
*/
private static final String POLICY_PREFIX = "policy:";
/**
* prefix used to specify file script argument for agent
*/
private static final String SCRIPT_PREFIX = "script:";
/**
* prefix used to specify resource script argument for agent
*/
private static final String RESOURCE_SCRIPT_PREFIX = "resourcescript:";
/**
* prefix used to specify transformer type argument for agent
*/
private static final String LISTENER_PREFIX = "listener:";
/**
* for backwards compatibiltiy
*/
private static final String REDEFINE_PREFIX = "redefine:";
/**
* prefix used to specify system properties to be set before starting the agent
*/
private static final String PROP_PREFIX = "prop:";
/**
* prefix used to specify the manager class
*/
private static final String MANAGER_PREFIX = "manager:";
/**
* prefix used to specify the module system class
*/
private static final String MODULE_PREFIX = "modules:";
/**
* name of basic transformer class.
*/
private static final String TRANSFORMER_NAME = "org.jboss.byteman.agent.Transformer";
/**
* name of retransformer class.
*/
private static final String RETRANSFORMER_NAME = "org.jboss.byteman.agent.Retransformer";
/**
* name of default manager class.
*/
private static final String MANAGER_NAME = "org.jboss.byteman.agent.TransformListener";
/**
* name of module system interface.
*/
private static final String MODULE_SYSTEM_NAME = "org.jboss.byteman.modules.ModuleSystem";
/**
* list of paths to extra bootstrap jars supplied on command line
*/
private static List bootJarPaths = new ArrayList();
/**
* list of paths to extra system jars supplied on command line
*/
private static List sysJarPaths = new ArrayList();
/**
* list of paths to script files supplied on command line
*/
private static List scriptPaths = new ArrayList();
/**
* list of paths to resource script files supplied on command line
*/
private static List resourcescriptPaths = new ArrayList();
/**
* list of scripts read from script files
*/
private static List scripts = new ArrayList();
/**
* 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;
/**
* The name of the module system implementation class, supplied on the
* command line (optional argument)
*/
private static String moduleSystemName = null;
/**
* The arguments to the module system implementation class, supplied on the
* command line (optional argument)
*/
private static String moduleSystemArgs = "";
}