mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-01 16:04:46 +00:00
Merge remote-tracking branch 'origin/GP-1816-dragonmacher-osgi-building-update--SQUASHED'
This commit is contained in:
commit
a9c928bb14
|
@ -18,16 +18,15 @@ package ghidra.app.plugin.core.osgi;
|
|||
import generic.jar.ResourceFile;
|
||||
|
||||
/**
|
||||
* An error produced during {@link GhidraBundle#build()} with a time stamp
|
||||
* An error produced during {@link GhidraBundle#build()} with a timestamp.
|
||||
*/
|
||||
public class BuildError {
|
||||
// the lastModified time of the source causing this error
|
||||
private final long lastModified;
|
||||
|
||||
private final StringBuilder message = new StringBuilder();
|
||||
|
||||
/**
|
||||
* Construct an object to record error message produced for {@code sourceFile}
|
||||
* Construct an object to record error message produced for {@code sourceFile}.
|
||||
* @param sourceFile the file causing this error
|
||||
*/
|
||||
public BuildError(ResourceFile sourceFile) {
|
||||
|
@ -35,15 +34,15 @@ public class BuildError {
|
|||
}
|
||||
|
||||
/**
|
||||
* Append {@code str} to the current error message
|
||||
*
|
||||
* @param str the string to append
|
||||
* Append the given string to the current error message.
|
||||
* @param s the string to append
|
||||
*/
|
||||
public void append(String str) {
|
||||
message.append(str);
|
||||
public void append(String s) {
|
||||
message.append(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* The error message.
|
||||
* @return the error message
|
||||
*/
|
||||
String getMessage() {
|
||||
|
@ -51,6 +50,7 @@ public class BuildError {
|
|||
}
|
||||
|
||||
/**
|
||||
* The last modified time of the source for this build error.
|
||||
* @return the last modified time of the source for this build error
|
||||
*/
|
||||
public long getLastModified() {
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
@ -46,19 +47,19 @@ import ghidra.util.task.TaskMonitor;
|
|||
* Hosts the embedded OSGi framework and manages {@link GhidraBundle}s.
|
||||
*
|
||||
* <br/><br/>
|
||||
* note: {@link GhidraBundle}, its implementations, and this class constitute
|
||||
* a bridge between OSGi's {@link Bundle} and Ghidra.
|
||||
* Note: {@link GhidraBundle}, its implementations, and this class constitute a bridge between
|
||||
* OSGi's {@link Bundle} and Ghidra.
|
||||
* <ul>
|
||||
* <li> unqualified, "bundle" will mean {@link GhidraBundle}
|
||||
* <li> use of OSGi types, including {@link Bundle} and {@link Framework}, should be package
|
||||
* scoped (not public)
|
||||
* <li> bundle lifecycle is simplified to "active"(same as OSGi "active" state)
|
||||
* and "inactive" (OSGi "uninstalled" state)
|
||||
* <li> use of OSGi types, including {@link Bundle} and {@link Framework}, should be package scoped
|
||||
* (not public)
|
||||
* <li> bundle lifecycle is simplified to "active"(same as OSGi "active" state) and "inactive"
|
||||
* (OSGi "uninstalled" state)
|
||||
* </ul>
|
||||
*/
|
||||
public class BundleHost {
|
||||
public static final String ACTIVATING_BUNDLE_ERROR_MSG = "activating bundle";
|
||||
protected static final boolean STDERR_DEBUGGING = false;
|
||||
private static final boolean STDERR_DEBUGGING = false;
|
||||
private static final String SAVE_STATE_TAG_FILE = "BundleHost_FILE";
|
||||
private static final String SAVE_STATE_TAG_ENABLE = "BundleHost_ENABLE";
|
||||
private static final String SAVE_STATE_TAG_ACTIVE = "BundleHost_ACTIVE";
|
||||
|
@ -66,22 +67,10 @@ public class BundleHost {
|
|||
|
||||
private final BundleMap bundleMap = new BundleMap();
|
||||
|
||||
BundleContext frameworkBundleContext;
|
||||
Framework felixFramework;
|
||||
private BundleContext frameworkBundleContext;
|
||||
private Framework felixFramework;
|
||||
|
||||
List<BundleHostListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/** constructor */
|
||||
public BundleHost() {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* stop the framework.
|
||||
*/
|
||||
public void dispose() {
|
||||
stopFramework();
|
||||
}
|
||||
private List<BundleHostListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* If a {@link GhidraBundle} hasn't already been added for {@bundleFile}, add it now as a
|
||||
|
@ -132,8 +121,8 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* Assuming there is currently a bundle managed with file {@code bundleFile},
|
||||
* return its {@link GhidraBundle}, otherwise show an error dialog and return {@code null}.
|
||||
* Assuming there is currently a bundle managed with file {@code bundleFile}, return its
|
||||
* {@link GhidraBundle}, otherwise show an error dialog and return {@code null}.
|
||||
*
|
||||
* @param bundleFile the bundleFile of the sought bundle
|
||||
* @return a {@link GhidraBundle} or {@code null}
|
||||
|
@ -149,8 +138,8 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* If there is currently a bundle managed with file {@code bundleFile},
|
||||
* return its {@link GhidraBundle}, otherwise return {@code null}.
|
||||
* If there is currently a bundle managed with file {@code bundleFile}, return its
|
||||
* {@link GhidraBundle}, otherwise return {@code null}.
|
||||
*
|
||||
* @param bundleFile the bundleFile of the sought bundle
|
||||
* @return a {@link GhidraBundle} or {@code null}
|
||||
|
@ -180,7 +169,7 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new GhidraBundle and add to the list of managed bundles
|
||||
* Create a new GhidraBundle and add to the list of managed bundles.
|
||||
*
|
||||
* @param bundleFile the bundle file
|
||||
* @param enabled if the new bundle should be enabled
|
||||
|
@ -282,7 +271,7 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
|
||||
Bundle installFromLoc(String bundleLocation) throws GhidraBundleException {
|
||||
private Bundle installFromLoc(String bundleLocation) throws GhidraBundleException {
|
||||
try {
|
||||
return frameworkBundleContext.installBundle(bundleLocation);
|
||||
}
|
||||
|
@ -291,17 +280,8 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
|
||||
Bundle installAsLoc(String bundleLocation, InputStream contents) throws GhidraBundleException {
|
||||
try {
|
||||
return frameworkBundleContext.installBundle(bundleLocation, contents);
|
||||
}
|
||||
catch (BundleException e) {
|
||||
throw new GhidraBundleException(bundleLocation, "installing as bundle location", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return all of the currently managed bundles
|
||||
* Return all of the currently managed bundles.
|
||||
*
|
||||
* @return all the bundles
|
||||
*/
|
||||
|
@ -310,7 +290,7 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* return the list of currently managed bundle files
|
||||
* Return the list of currently managed bundle files.
|
||||
*
|
||||
* @return all the bundle files
|
||||
*/
|
||||
|
@ -318,14 +298,6 @@ public class BundleHost {
|
|||
return bundleMap.getBundleFiles();
|
||||
}
|
||||
|
||||
void dumpLoadedBundles() {
|
||||
System.err.printf("=== Bundles ===\n");
|
||||
for (Bundle bundle : frameworkBundleContext.getBundles()) {
|
||||
System.err.printf("%s: %s: %s: %s\n", bundle.getBundleId(), bundle.getSymbolicName(),
|
||||
bundle.getState(), bundle.getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to resolve a list of BundleRequirements with active Bundle capabilities.
|
||||
*
|
||||
|
@ -370,7 +342,7 @@ public class BundleHost {
|
|||
return tmpRequirements.isEmpty();
|
||||
}
|
||||
|
||||
protected String buildExtraSystemPackages() {
|
||||
private String buildExtraSystemPackages() {
|
||||
Set<String> packages = new HashSet<>();
|
||||
OSGiUtils.getPackagesFromClasspath(packages);
|
||||
return packages.stream().collect(Collectors.joining(","));
|
||||
|
@ -387,11 +359,11 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* A directory for use by the OSGi framework as a cache
|
||||
* A directory for use by the OSGi framework as a cache.
|
||||
*
|
||||
* @return the directory
|
||||
*/
|
||||
protected static Path getCacheDir() {
|
||||
private static Path getCacheDir() {
|
||||
return BundleHost.getOsgiDir().resolve("felixcache");
|
||||
}
|
||||
|
||||
|
@ -401,16 +373,19 @@ public class BundleHost {
|
|||
return cacheDir.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
protected void createAndConfigureFramework() throws IOException {
|
||||
private void createAndConfigureFramework() throws IOException {
|
||||
Properties config = new Properties();
|
||||
|
||||
// allow multiple bundles w/ the same symbolic name -- location can distinguish
|
||||
config.setProperty(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_MULTIPLE);
|
||||
// use the default, inferred from environment
|
||||
// config.setProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES,"osgi.ee; osgi.ee=\"JavaSE\";version:List=\"...\"");
|
||||
|
||||
// compute and add everything in the class path. extra packages have lower precedence than imports,
|
||||
// so an Import-Package / @importpackage will override the "living off the land" default
|
||||
// use the default, inferred from environment
|
||||
// config.setProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES,
|
||||
// "osgi.ee; osgi.ee=\"JavaSE\";version:List=\"...\"");
|
||||
|
||||
// compute and add everything in the class path. extra packages have lower precedence than
|
||||
// imports, so an Import-Package / @importpackage will override the "living off the land"
|
||||
// default
|
||||
config.setProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, buildExtraSystemPackages());
|
||||
|
||||
// only clean on first startup, o/w keep our storage around
|
||||
|
@ -423,41 +398,43 @@ public class BundleHost {
|
|||
config.put(FelixConstants.LOG_LEVEL_PROP, "1");
|
||||
if (STDERR_DEBUGGING) {
|
||||
config.put(FelixConstants.LOG_LEVEL_PROP, "999");
|
||||
// config.put(FelixConstants.LOG_LOGGER_PROP, new org.apache.felix.framework.Logger() {...});
|
||||
// config.put(FelixConstants.LOG_LOGGER_PROP,
|
||||
// new org.apache.felix.framework.Logger() {...});
|
||||
}
|
||||
|
||||
FrameworkFactory factory = new FrameworkFactory();
|
||||
felixFramework = factory.newFramework(config);
|
||||
}
|
||||
|
||||
protected void addDebuggingListeners() {
|
||||
frameworkBundleContext.addFrameworkListener(new FrameworkListener() {
|
||||
@Override
|
||||
public void frameworkEvent(FrameworkEvent event) {
|
||||
System.err.printf("%s %s\n", event.getBundle(), event);
|
||||
private void addDebuggingListeners() {
|
||||
if (!STDERR_DEBUGGING) {
|
||||
return;
|
||||
}
|
||||
|
||||
frameworkBundleContext.addFrameworkListener(
|
||||
event -> {
|
||||
String msg = String.format("AA: %s %s\n", event.getBundle(), event);
|
||||
Msg.debug(this, msg);
|
||||
});
|
||||
|
||||
frameworkBundleContext.addServiceListener(event -> {
|
||||
|
||||
String type = "?";
|
||||
if (event.getType() == ServiceEvent.REGISTERED) {
|
||||
type = "registered";
|
||||
}
|
||||
});
|
||||
frameworkBundleContext.addServiceListener(new ServiceListener() {
|
||||
@Override
|
||||
public void serviceChanged(ServiceEvent event) {
|
||||
|
||||
String type = "?";
|
||||
if (event.getType() == ServiceEvent.REGISTERED) {
|
||||
type = "registered";
|
||||
}
|
||||
else if (event.getType() == ServiceEvent.UNREGISTERING) {
|
||||
type = "unregistering";
|
||||
}
|
||||
|
||||
System.err.printf("%s %s from %s\n", event.getSource(), type,
|
||||
event.getServiceReference().getBundle().getLocation());
|
||||
|
||||
else if (event.getType() == ServiceEvent.UNREGISTERING) {
|
||||
type = "unregistering";
|
||||
}
|
||||
|
||||
String msg = String.format("BB: %s %s from %s\n", event.getSource(), type,
|
||||
event.getServiceReference().getBundle().getLocation());
|
||||
Msg.debug(this, msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* start the framework
|
||||
* Start the framework.
|
||||
*
|
||||
* @throws OSGiException framework failures
|
||||
* @throws IOException filesystem setup
|
||||
|
@ -469,48 +446,53 @@ public class BundleHost {
|
|||
felixFramework.init();
|
||||
}
|
||||
catch (BundleException e) {
|
||||
throw new OSGiException("initializing felix OSGi framework", e);
|
||||
throw new OSGiException("Exception initializing felix OSGi framework", e);
|
||||
}
|
||||
|
||||
frameworkBundleContext = felixFramework.getBundleContext();
|
||||
|
||||
if (STDERR_DEBUGGING) {
|
||||
addDebuggingListeners();
|
||||
}
|
||||
addDebuggingListeners();
|
||||
|
||||
frameworkBundleContext
|
||||
.addBundleListener(new MyBundleListener(frameworkBundleContext.getBundle()));
|
||||
Bundle bundle = frameworkBundleContext.getBundle();
|
||||
frameworkBundleContext.addBundleListener(new MyBundleListener(bundle));
|
||||
|
||||
try {
|
||||
felixFramework.start();
|
||||
}
|
||||
catch (BundleException e) {
|
||||
throw new OSGiException("starting felix OSGi framework", e);
|
||||
throw new OSGiException("Exception starting felix OSGi framework", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* stop the OSGi framework synchronously
|
||||
* Stop the OSGi framework.
|
||||
*
|
||||
* <p>This may wait for up to 5 seconds for the framework to fully stop. If that timeout
|
||||
* passes an error will be logged.
|
||||
*/
|
||||
protected void stopFramework() {
|
||||
if (felixFramework != null) {
|
||||
try {
|
||||
felixFramework.stop();
|
||||
// any bundles that linger after a few seconds might be the source
|
||||
// of subtle problems, so wait for them to stop and report any problems.
|
||||
FrameworkEvent event = felixFramework.waitForStop(5000);
|
||||
if (event.getType() == FrameworkEvent.WAIT_TIMEDOUT) {
|
||||
Msg.error(this, "Stopping OSGi framework timed out after 5 seconds.");
|
||||
}
|
||||
felixFramework = null;
|
||||
}
|
||||
catch (BundleException | InterruptedException e) {
|
||||
Msg.error(this, "Failed to stop OSGi framework.");
|
||||
e.printStackTrace();
|
||||
public void stopFramework() {
|
||||
if (felixFramework == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
felixFramework.stop();
|
||||
|
||||
// any bundles that linger after a few seconds might be the source of subtle problems,
|
||||
// so wait for them to stop and report any problems.
|
||||
FrameworkEvent event = felixFramework.waitForStop(5000);
|
||||
if (event.getType() == FrameworkEvent.WAIT_TIMEDOUT) {
|
||||
Msg.error(this, "Stopping OSGi framework timed-out after 5 seconds.");
|
||||
}
|
||||
felixFramework = null;
|
||||
}
|
||||
catch (BundleException | InterruptedException e) {
|
||||
Msg.error(this, "Failed to stop OSGi framework.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the host framework.
|
||||
* @return the OSGi framework
|
||||
*/
|
||||
Framework getHostFramework() {
|
||||
|
@ -565,7 +547,8 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* Deactivate a bundle. Either an exception is thrown or the bundle will be in "UNINSTALLED" state.
|
||||
* Deactivate a bundle. Either an exception is thrown or the bundle will be in "UNINSTALLED"
|
||||
* state.
|
||||
*
|
||||
* @param bundle the bundle
|
||||
* @throws GhidraBundleException if there's a problem activating
|
||||
|
@ -575,7 +558,7 @@ public class BundleHost {
|
|||
return;
|
||||
}
|
||||
FrameworkWiring frameworkWiring = felixFramework.adapt(FrameworkWiring.class);
|
||||
LinkedList<Bundle> dependentBundles = new LinkedList<Bundle>(
|
||||
LinkedList<Bundle> dependentBundles = new LinkedList<>(
|
||||
frameworkWiring.getDependencyClosure(Collections.singleton(bundle)));
|
||||
while (!dependentBundles.isEmpty()) {
|
||||
Bundle dependentBundle = dependentBundles.pop();
|
||||
|
@ -596,7 +579,8 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* Deactivate a bundle. Either an exception is thrown or the bundle will be in "UNINSTALLED" state.
|
||||
* Deactivate a bundle. Either an exception is thrown or the bundle will be in "UNINSTALLED"
|
||||
* state.
|
||||
*
|
||||
* @param bundleLocation the bundle location identifier
|
||||
* @throws InterruptedException if the wait is interrupted
|
||||
|
@ -611,8 +595,8 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* Refreshes the specified bundles. This forces the update (replacement)
|
||||
* or removal of packages exported by the specified bundles.
|
||||
* Refreshes the specified bundles. This forces the update (replacement) or removal of packages
|
||||
* exported by the specified bundles.
|
||||
*
|
||||
* @param bundles the bundles to refresh
|
||||
* @see FrameworkWiring#refreshBundles
|
||||
|
@ -620,22 +604,19 @@ public class BundleHost {
|
|||
protected void refreshBundlesSynchronously(Collection<Bundle> bundles) {
|
||||
FrameworkWiring frameworkWiring = felixFramework.adapt(FrameworkWiring.class);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
frameworkWiring.refreshBundles(bundles, new FrameworkListener() {
|
||||
@Override
|
||||
public void frameworkEvent(FrameworkEvent event) {
|
||||
if (event.getType() == FrameworkEvent.ERROR) {
|
||||
Bundle bundle = event.getBundle();
|
||||
Msg.error(BundleHost.this,
|
||||
String.format("OSGi error refreshing bundle: %s", bundle));
|
||||
}
|
||||
latch.countDown();
|
||||
frameworkWiring.refreshBundles(bundles, event -> {
|
||||
if (event.getType() == FrameworkEvent.ERROR) {
|
||||
Bundle bundle = event.getBundle();
|
||||
Msg.error(BundleHost.this,
|
||||
String.format("OSGi error refreshing bundle: %s", bundle));
|
||||
}
|
||||
latch.countDown();
|
||||
});
|
||||
try {
|
||||
latch.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
Msg.error(this, "Exception waiting for bundles to refresh", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -649,8 +630,11 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
/**
|
||||
* Activate a set of bundles and any dependencies in topological order. This method doesn't rely on the
|
||||
* framework, and so will add non-active dependencies.
|
||||
* Activate a set of bundles and any dependencies in topological order. This method doesn't
|
||||
* rely on the framework, and so will add non-active dependencies.
|
||||
*
|
||||
* <p>To load bundles without loading inactive dependencies, call
|
||||
* {@link #activateInStages(Collection, TaskMonitor, PrintWriter)}.
|
||||
*
|
||||
* @param bundles bundles to activate
|
||||
* @param monitor a task monitor
|
||||
|
@ -671,9 +655,13 @@ public class BundleHost {
|
|||
activateSynchronously(bundle.getLocationIdentifier());
|
||||
}
|
||||
catch (GhidraBundleException e) {
|
||||
// TODO should we report failing bundles to the console as well so they get logged
|
||||
// in headless mode?
|
||||
fireBundleException(e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// write the error to the console or log file
|
||||
console.println("Unexpected error activating bundles: " + bundles);
|
||||
e.printStackTrace(console);
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
|
@ -682,7 +670,10 @@ public class BundleHost {
|
|||
|
||||
/**
|
||||
* Activate a set of bundles in dependency topological order by resolving against currently
|
||||
* active bundles in stages. No bundles outside those requested will be activated.
|
||||
* active bundles in stages. <b>No bundles outside those requested will be activated.</b>
|
||||
*
|
||||
* <p>To have inactive dependencies loaded, call
|
||||
* {@link #activateAll(Collection, TaskMonitor, PrintWriter)}.
|
||||
*
|
||||
* @param bundles bundles to activate
|
||||
* @param monitor a task monitor
|
||||
|
@ -702,11 +693,13 @@ public class BundleHost {
|
|||
requirementMap.put(bundle, requirements);
|
||||
}
|
||||
catch (GhidraBundleException e) {
|
||||
// TODO should we report failing bundles to the console as well so they get logged
|
||||
// in headless mode?
|
||||
fireBundleException(e);
|
||||
}
|
||||
}
|
||||
List<GhidraBundle> bundlesRemaining = new ArrayList<>(requirementMap.keySet());
|
||||
|
||||
List<GhidraBundle> bundlesRemaining = new ArrayList<>(requirementMap.keySet());
|
||||
monitor.setMaximum(bundlesRemaining.size());
|
||||
while (!bundlesRemaining.isEmpty() && !monitor.isCancelled()) {
|
||||
List<GhidraBundle> resolvableBundles = bundlesRemaining.stream()
|
||||
|
@ -731,9 +724,13 @@ public class BundleHost {
|
|||
activateSynchronously(bundle.getLocationIdentifier());
|
||||
}
|
||||
catch (GhidraBundleException e) {
|
||||
// TODO should we report failing bundles to the console as well so they get logged
|
||||
// in headless mode?
|
||||
fireBundleException(e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// write the error to the console or log file
|
||||
console.println("Unexpected error activating bundles: " + bundles);
|
||||
e.printStackTrace(console);
|
||||
}
|
||||
monitor.incrementProgress(1);
|
||||
|
@ -813,7 +810,7 @@ public class BundleHost {
|
|||
*
|
||||
* <p>Bundles that had been active are reactivated.
|
||||
*
|
||||
* <p>note: This is done once on startup after system bundles have been added.
|
||||
* <p>Note: This is done once on startup after system bundles have been added.
|
||||
*
|
||||
* @param saveState the state object
|
||||
* @param tool the tool
|
||||
|
@ -863,7 +860,7 @@ public class BundleHost {
|
|||
}
|
||||
|
||||
if (!bundlesToActivate.isEmpty()) {
|
||||
TaskLauncher.launchNonModal("restoring bundle state",
|
||||
TaskLauncher.launchNonModal("Restoring bundle state",
|
||||
(monitor) -> activateInStages(bundlesToActivate, monitor, new NullPrintWriter()));
|
||||
}
|
||||
}
|
||||
|
@ -896,35 +893,43 @@ public class BundleHost {
|
|||
saveState.putBooleans(SAVE_STATE_TAG_SYSTEM, bundleIsSystem);
|
||||
}
|
||||
|
||||
private static class Dependency {
|
||||
// exists only to be distinguished by id
|
||||
//=================================================================================================
|
||||
// Inner Classes
|
||||
//=================================================================================================
|
||||
|
||||
private static class BundleEdge {
|
||||
// edge type for dependency graph
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to build a dependency graph from bundles where capabilities map to requirements.
|
||||
* Utility class to build a dependency graph from bundles where capabilities map to
|
||||
* requirements.
|
||||
*/
|
||||
private class BundleDependencyGraph extends DirectedMultigraph<GhidraBundle, Dependency> {
|
||||
final Map<GhidraBundle, List<BundleCapability>> capabilityMap = new HashMap<>();
|
||||
final List<GhidraBundle> availableBundles;
|
||||
final TaskMonitor monitor;
|
||||
private class BundleDependencyGraph extends DirectedMultigraph<GhidraBundle, BundleEdge> {
|
||||
|
||||
BundleDependencyGraph(Collection<GhidraBundle> startingBundles, TaskMonitor monitor) {
|
||||
private final List<GhidraBundle> availableBundles = new ArrayList<>();
|
||||
private final Map<GhidraBundle, List<BundleCapability>> capabilityMap = new HashMap<>();
|
||||
private final TaskMonitor monitor;
|
||||
|
||||
BundleDependencyGraph(Collection<GhidraBundle> activatingBundles, TaskMonitor monitor) {
|
||||
super(null, null, false);
|
||||
this.monitor = monitor;
|
||||
|
||||
// maintain a list of bundles available for resolution, starting with all of the enabled bundles
|
||||
this.availableBundles = new ArrayList<>();
|
||||
// maintain a list of bundles available for resolution, starting with all of the
|
||||
// enabled bundles
|
||||
for (GhidraBundle bundle : getGhidraBundles()) {
|
||||
if (bundle.isEnabled()) {
|
||||
addToAvailable(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
// An edge A->B indicates that the capabilities of A resolve some requirement(s) of B
|
||||
|
||||
// "front" accumulates bundles and links to bundles already in the graph that they provide capabilities for.
|
||||
// e.g. if front[A]=[B,...] then A->B, B is already in the graph, and we will add A next iteration.
|
||||
// "front" accumulates bundles and links to bundles already in the graph that they
|
||||
// provide capabilities for. e.g., if front[A]=[B,...] then A->B, B is already in the
|
||||
// graph, and we will add A next iteration.
|
||||
Map<GhidraBundle, Set<GhidraBundle>> front = new HashMap<>();
|
||||
for (GhidraBundle bundle : startingBundles) {
|
||||
for (GhidraBundle bundle : activatingBundles) {
|
||||
front.put(bundle, null);
|
||||
}
|
||||
|
||||
|
@ -939,46 +944,45 @@ public class BundleHost {
|
|||
handleBackEdges(newFront);
|
||||
front = newFront;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Iterable<GhidraBundle> inTopologicalOrder() {
|
||||
return () -> new TopologicalOrderIterator<>(this);
|
||||
}
|
||||
|
||||
void handleBackEdges(Map<GhidraBundle, Set<GhidraBundle>> newFront) {
|
||||
Iterator<Entry<GhidraBundle, Set<GhidraBundle>>> newFrontIter =
|
||||
private void handleBackEdges(Map<GhidraBundle, Set<GhidraBundle>> newFront) {
|
||||
Iterator<Entry<GhidraBundle, Set<GhidraBundle>>> it =
|
||||
newFront.entrySet().iterator();
|
||||
while (newFrontIter.hasNext() && !monitor.isCancelled()) {
|
||||
Entry<GhidraBundle, Set<GhidraBundle>> entry = newFrontIter.next();
|
||||
while (it.hasNext() && !monitor.isCancelled()) {
|
||||
Entry<GhidraBundle, Set<GhidraBundle>> entry = it.next();
|
||||
GhidraBundle source = entry.getKey();
|
||||
if (containsVertex(source)) {
|
||||
for (GhidraBundle destination : entry.getValue()) {
|
||||
if (source != destination) {
|
||||
addEdge(source, destination, new Dependency());
|
||||
addEdge(source, destination, new BundleEdge());
|
||||
}
|
||||
}
|
||||
newFrontIter.remove();
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addFront(Map<GhidraBundle, Set<GhidraBundle>> front) {
|
||||
for (Entry<GhidraBundle, Set<GhidraBundle>> e : front.entrySet()) {
|
||||
GhidraBundle source = e.getKey();
|
||||
private void addFront(Map<GhidraBundle, Set<GhidraBundle>> front) {
|
||||
for (Entry<GhidraBundle, Set<GhidraBundle>> entry : front.entrySet()) {
|
||||
GhidraBundle source = entry.getKey();
|
||||
if (addToAvailable(source)) {
|
||||
addVertex(source);
|
||||
Set<GhidraBundle> destinations = e.getValue();
|
||||
if (destinations != null) {
|
||||
for (GhidraBundle destination : destinations) {
|
||||
addEdge(source, destination, new Dependency());
|
||||
Set<GhidraBundle> dependents = entry.getValue();
|
||||
if (dependents != null) {
|
||||
for (GhidraBundle destination : dependents) {
|
||||
addEdge(source, destination, new BundleEdge());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean addToAvailable(GhidraBundle bundle) {
|
||||
private boolean addToAvailable(GhidraBundle bundle) {
|
||||
try {
|
||||
capabilityMap.put(bundle, bundle.getAllCapabilities());
|
||||
availableBundles.add(bundle);
|
||||
|
@ -990,33 +994,38 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
|
||||
// populate newFront with edges depBundle -> bundle,
|
||||
// where depBundle has a capability that resolves a requirement of bundle
|
||||
void resolve(GhidraBundle bundle, Map<GhidraBundle, Set<GhidraBundle>> newFront) {
|
||||
// Populate newFront with edges supplierBundle -> dependentBundle, where supplierBundle has
|
||||
// a capability that resolves a requirement of dependentBundle. Items added to newFront are
|
||||
// already in the graph. These items will be added if the given bundle to resolve
|
||||
// becomes a dependent on this added supplier.
|
||||
private void resolve(GhidraBundle toResolve,
|
||||
Map<GhidraBundle, Set<GhidraBundle>> newFront) {
|
||||
List<BundleRequirement> requirements;
|
||||
try {
|
||||
requirements = new ArrayList<>(bundle.getAllRequirements());
|
||||
if (requirements.isEmpty()) {
|
||||
return;
|
||||
List<BundleRequirement> bundleRequirements = toResolve.getAllRequirements();
|
||||
if (bundleRequirements.isEmpty()) {
|
||||
return; // no dependencies to resolve
|
||||
}
|
||||
requirements = new ArrayList<>(bundleRequirements);
|
||||
}
|
||||
catch (GhidraBundleException e) {
|
||||
fireBundleException(e);
|
||||
removeVertex(bundle);
|
||||
removeVertex(toResolve);
|
||||
return;
|
||||
}
|
||||
|
||||
for (GhidraBundle depBundle : availableBundles) {
|
||||
for (BundleCapability capability : capabilityMap.get(depBundle)) {
|
||||
for (GhidraBundle supplierBundle : availableBundles) {
|
||||
for (BundleCapability capability : capabilityMap.get(supplierBundle)) {
|
||||
if (monitor.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
Iterator<BundleRequirement> reqIter = requirements.iterator();
|
||||
while (reqIter.hasNext()) {
|
||||
BundleRequirement req = reqIter.next();
|
||||
Iterator<BundleRequirement> it = requirements.iterator();
|
||||
while (it.hasNext()) {
|
||||
BundleRequirement req = it.next();
|
||||
if (req.matches(capability)) {
|
||||
newFront.computeIfAbsent(depBundle, b -> new HashSet<>()).add(bundle);
|
||||
reqIter.remove();
|
||||
newFront.computeIfAbsent(supplierBundle, b -> new HashSet<>())
|
||||
.add(toResolve);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
if (requirements.isEmpty()) {
|
||||
|
@ -1024,13 +1033,15 @@ public class BundleHost {
|
|||
}
|
||||
}
|
||||
}
|
||||
// if requirements remain, some will be resolved by system
|
||||
// and others will generate helpful errors for the user during activation
|
||||
|
||||
// If requirements remain, some will be resolved by system and others will generate
|
||||
// helpful errors for the user during activation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@code BundleListener} that notifies {@link BundleHostListener}s of bundle activation changes
|
||||
* The {@code BundleListener} that notifies {@link BundleHostListener}s of bundle activation
|
||||
* changes.
|
||||
*/
|
||||
private class MyBundleListener implements BundleListener {
|
||||
private final Bundle systemBundle;
|
||||
|
@ -1050,8 +1061,10 @@ public class BundleHost {
|
|||
if (STDERR_DEBUGGING) {
|
||||
String symbolicName = osgiBundle.getSymbolicName();
|
||||
String locationIdentifier = osgiBundle.getLocation();
|
||||
System.err.printf("%s %s from %s\n", OSGiUtils.getEventTypeString(event),
|
||||
symbolicName, locationIdentifier);
|
||||
String message =
|
||||
String.format("CC: %s %s from %s\n", OSGiUtils.getEventTypeString(event),
|
||||
symbolicName, locationIdentifier);
|
||||
Msg.debug(this, message);
|
||||
}
|
||||
GhidraBundle bundle;
|
||||
switch (event.getType()) {
|
||||
|
|
|
@ -45,7 +45,7 @@ public class BundleMap {
|
|||
bundlesByLocation.put(bundle.getLocationIdentifier(), bundle);
|
||||
}
|
||||
finally {
|
||||
lock.writeLock().unlock();
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +197,7 @@ public class BundleMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the currently mapped bundles.
|
||||
* @return the currently mapped bundles
|
||||
*/
|
||||
public Collection<GhidraBundle> getGhidraBundles() {
|
||||
|
@ -210,6 +211,7 @@ public class BundleMap {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the currently mapped bundle files.
|
||||
* @return the currently mapped bundle files
|
||||
*/
|
||||
public Collection<ResourceFile> getBundleFiles() {
|
||||
|
|
|
@ -45,7 +45,7 @@ import resources.Icons;
|
|||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* component for managing OSGi bundle status
|
||||
* Component for managing OSGi bundle status
|
||||
*/
|
||||
public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
|
||||
|
@ -289,7 +289,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
files.stream().map(ResourceFile::new).collect(Collectors.toUnmodifiableList());
|
||||
Collection<GhidraBundle> bundles = bundleHost.add(resourceFiles, true, false);
|
||||
|
||||
TaskLauncher.launchNonModal("activating new bundles", (monitor) -> {
|
||||
TaskLauncher.launchNonModal("Activating new bundles", (monitor) -> {
|
||||
bundleHost.activateAll(bundles, monitor,
|
||||
getTool().getService(ConsoleService.class).getStdErr());
|
||||
});
|
||||
|
@ -339,9 +339,6 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup this component
|
||||
*/
|
||||
public void dispose() {
|
||||
filterPanel.dispose();
|
||||
}
|
||||
|
@ -351,9 +348,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
/**
|
||||
* This is for testing only! during normal execution, statuses are only added through BundleHostListener bundle(s) added events.
|
||||
* This is for testing only! during normal execution, statuses are only added through
|
||||
* BundleHostListener bundle(s) added events.
|
||||
*
|
||||
* <p>each new bundle will be enabled and writable
|
||||
* <p>Each new bundle will be enabled and writable
|
||||
*
|
||||
* @param bundleFiles the files to use
|
||||
*/
|
||||
|
@ -363,6 +361,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
// Inner Classes
|
||||
//=================================================================================================
|
||||
|
||||
private final class RemoveBundlesTask extends Task {
|
||||
private final DeactivateAndDisableBundlesTask deactivateBundlesTask;
|
||||
private final List<BundleStatus> statuses;
|
||||
|
@ -378,7 +380,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
deactivateBundlesTask.run(monitor);
|
||||
monitor.checkCanceled();
|
||||
// partition bundles into system (bundles.get(true)) and non-system (bundles.get(false)).
|
||||
// partition bundles into system (bundles.get(true)) / non-system (bundles.get(false))
|
||||
Map<Boolean, List<GhidraBundle>> bundles = statuses.stream()
|
||||
.map(bs -> bundleHost.getExistingGhidraBundle(bs.getFile()))
|
||||
.collect(Collectors.partitioningBy(GhidraBundle::isSystemBundle));
|
||||
|
@ -483,8 +485,8 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
/*
|
||||
* Activating/deactivating a single bundle doesn't require resolving dependents,
|
||||
* so this task is slightly different from the others.
|
||||
* Activating/deactivating a single bundle doesn't require resolving dependents, so this task
|
||||
* is slightly different from the others.
|
||||
*/
|
||||
private class ActivateDeactivateBundleTask extends Task {
|
||||
private final BundleStatus status;
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
|
@ -32,8 +32,10 @@ import generic.jar.ResourceFile;
|
|||
import generic.util.Path;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.table.column.*;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.table.column.AbstractGColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
|
||||
/**
|
||||
* Model for {@link BundleStatus} objects.
|
||||
|
@ -73,10 +75,10 @@ public class BundleStatusTableModel
|
|||
}
|
||||
|
||||
private BundleStatus getStatus(GhidraBundle bundle) {
|
||||
return getStatusFromLoc(bundle.getLocationIdentifier());
|
||||
return getStatusFromLocation(bundle.getLocationIdentifier());
|
||||
}
|
||||
|
||||
private BundleStatus getStatusFromLoc(String bundleLoc) {
|
||||
private BundleStatus getStatusFromLocation(String bundleLoc) {
|
||||
return bundleLocToStatusMap.get(bundleLoc);
|
||||
}
|
||||
|
||||
|
@ -254,7 +256,7 @@ public class BundleStatusTableModel
|
|||
// wrap the assigned comparator to detect if the order changes
|
||||
|
||||
AtomicBoolean changed = new AtomicBoolean(false);
|
||||
Comparator<BundleStatus> wrapper = new Comparator<BundleStatus>() {
|
||||
Comparator<BundleStatus> wrapper = new Comparator<>() {
|
||||
Comparator<BundleStatus> comparator = sortingContext.getComparator();
|
||||
|
||||
@Override
|
||||
|
@ -367,7 +369,7 @@ public class BundleStatusTableModel
|
|||
@Override
|
||||
public void bundleException(GhidraBundleException exception) {
|
||||
Swing.runLater(() -> {
|
||||
BundleStatus status = getStatusFromLoc(exception.getBundleLocation());
|
||||
BundleStatus status = getStatusFromLocation(exception.getBundleLocation());
|
||||
if (status != null) {
|
||||
status.setSummary(exception.getMessage());
|
||||
int rowIndex = getRowIndex(status);
|
||||
|
|
|
@ -30,8 +30,20 @@ import generic.jar.ResourceFile;
|
|||
*/
|
||||
public abstract class GhidraBundle {
|
||||
|
||||
protected final ResourceFile file;
|
||||
/**
|
||||
* A {@link GhidraBundle} can be
|
||||
* <ul>
|
||||
* <li>a Bndtools .bnd script</li>
|
||||
* <li>an OSGi bundle .jar file</li>
|
||||
* <li>a directory of Java source</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
enum Type {
|
||||
BND_SCRIPT, JAR, SOURCE_DIR, INVALID
|
||||
}
|
||||
|
||||
protected final ResourceFile bundleFile; // can be a dir or a jar file
|
||||
protected final BundleHost bundleHost;
|
||||
protected boolean enabled;
|
||||
protected boolean systemBundle;
|
||||
|
@ -39,29 +51,29 @@ public abstract class GhidraBundle {
|
|||
GhidraBundle(BundleHost bundleHost, ResourceFile bundleFile, boolean enabled,
|
||||
boolean systemBundle) {
|
||||
this.bundleHost = bundleHost;
|
||||
this.file = bundleFile;
|
||||
this.bundleFile = bundleFile;
|
||||
this.enabled = enabled;
|
||||
this.systemBundle = systemBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* clean build artifacts generated during build of this bundle
|
||||
* Clean build artifacts generated during build of this bundle.
|
||||
*
|
||||
* @return true if anything was done
|
||||
*/
|
||||
abstract boolean clean();
|
||||
|
||||
/**
|
||||
* build OSGi bundle if possible
|
||||
* Build OSGi bundle if needed and if possible.
|
||||
*
|
||||
* @param writer console for build messages to user
|
||||
* @return true if build happened, false if already built
|
||||
* @return true if build happened, false if already built or could not build
|
||||
* @throws Exception if the build cannot complete
|
||||
*/
|
||||
public abstract boolean build(PrintWriter writer) throws Exception;
|
||||
|
||||
/**
|
||||
* same as {@link #build(PrintWriter)} with writer = {@link System#err}.
|
||||
* Same as {@link #build(PrintWriter)} with writer = {@link System#err}.
|
||||
*
|
||||
* @return true if build happened, false if already built
|
||||
* @throws Exception if the build cannot complete
|
||||
|
@ -74,28 +86,43 @@ public abstract class GhidraBundle {
|
|||
* Return the location identifier of the bundle that this GhidraBundle represents.
|
||||
*
|
||||
* <p>The location identifier is used by the framework, e.g. it is passed to
|
||||
* {@link org.osgi.framework.BundleContext#installBundle} when the bundle is
|
||||
* first installed.
|
||||
* {@link org.osgi.framework.BundleContext#installBundle} when the bundle is first installed.
|
||||
*
|
||||
* <p>Although the bundle location is a URI, outside of interactions with the framework,
|
||||
* the bundle location should remain opaque.
|
||||
* <p>Although the bundle location is a URI, outside of interactions with the framework, the
|
||||
* bundle location should remain opaque.
|
||||
*
|
||||
* @return location identifier of this bundle
|
||||
*/
|
||||
public abstract String getLocationIdentifier();
|
||||
|
||||
/**
|
||||
* Returns all bundle requirements.
|
||||
*
|
||||
* @return the requirements
|
||||
* @throws GhidraBundleException if there is an exception parsing / loading bundle requirements
|
||||
*/
|
||||
public abstract List<BundleRequirement> getAllRequirements() throws GhidraBundleException;
|
||||
|
||||
/**
|
||||
* Returns all bundle capabilities.
|
||||
*
|
||||
* @return the capabilities
|
||||
* @throws GhidraBundleException if there is an exception parsing / loading bundle capabilities
|
||||
*/
|
||||
public abstract List<BundleCapability> getAllCapabilities() throws GhidraBundleException;
|
||||
|
||||
/**
|
||||
* @return the file where this bundle is loaded from
|
||||
* The file where this bundle is loaded from.
|
||||
*
|
||||
* @return the file from where this bundle is loaded
|
||||
*/
|
||||
public ResourceFile getFile() {
|
||||
return file;
|
||||
return bundleFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if this bundle is enabled.
|
||||
*
|
||||
* @return true if this bundle is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
|
@ -103,7 +130,7 @@ public abstract class GhidraBundle {
|
|||
}
|
||||
|
||||
/**
|
||||
* set the enablement flag for this bundle.
|
||||
* Set the enablement flag for this bundle.
|
||||
*
|
||||
* <p>If a bundle is enabled its contents will be scanned, e.g. for scripts.
|
||||
*
|
||||
|
@ -123,7 +150,7 @@ public abstract class GhidraBundle {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the type of a GhidraBundle from its file.
|
||||
* Get the type of {@link GhidraBundle} from its file.
|
||||
*
|
||||
* @param file a bundle file
|
||||
* @return the type
|
||||
|
@ -163,8 +190,8 @@ public abstract class GhidraBundle {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the OSGi bundle represented by this GhidraBundle or null if it isn't in
|
||||
* the "installed" state.
|
||||
* Get the OSGi bundle represented by this GhidraBundle or null if it isn't in the "installed"
|
||||
* state.
|
||||
*
|
||||
* @return a Bundle or null
|
||||
*/
|
||||
|
@ -173,6 +200,8 @@ public abstract class GhidraBundle {
|
|||
}
|
||||
|
||||
/**
|
||||
* True if this bundle is active.
|
||||
*
|
||||
* @return true if this bundle is active
|
||||
*/
|
||||
public boolean isActive() {
|
||||
|
@ -180,17 +209,8 @@ public abstract class GhidraBundle {
|
|||
return (bundle != null) && bundle.getState() == Bundle.ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A GhidraBundle can be
|
||||
* <ul>
|
||||
* <li>a Bndtools .bnd script</li>
|
||||
* <li>an OSGi bundle .jar file</li>
|
||||
* <li>a directory of Java source</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
enum Type {
|
||||
BND_SCRIPT, JAR, SOURCE_DIR, INVALID
|
||||
@Override
|
||||
public String toString() {
|
||||
return getOSGiBundle().getSymbolicName();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public class GhidraJarBundle extends GhidraBundle {
|
|||
}
|
||||
|
||||
protected ManifestParser createManifestParser() throws GhidraBundleException {
|
||||
try (Jar jar = new Jar(file.getFile(true))) {
|
||||
try (Jar jar = new Jar(bundleFile.getFile(true))) {
|
||||
Manifest manifest = jar.getManifest();
|
||||
if (manifest == null) {
|
||||
throw new GhidraBundleException(bundleLocation, "jar bundle with no manifest");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -281,5 +281,4 @@ public class OSGiUtils {
|
|||
Msg.error(OSGiUtils.class, "Error while collecting packages from jar", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1166,7 +1166,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
class RefreshingBundleHostListener implements BundleHostListener {
|
||||
private class RefreshingBundleHostListener implements BundleHostListener {
|
||||
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
|
@ -1179,8 +1179,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||
GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
|
||||
ResourceFile sourceDirectory = sourceBundle.getFile();
|
||||
if (summary == null) {
|
||||
// a null summary means the build didn't change anything,
|
||||
// so use any errors from the last build
|
||||
// a null summary means the build didn't change anything, so use any errors from
|
||||
// the last build
|
||||
for (ResourceFile sourceFile : sourceBundle.getAllErrors().keySet()) {
|
||||
if (sourceFile.getParentFile().equals(sourceDirectory)) {
|
||||
ScriptInfo scriptInfo = infoManager.getScriptInfo(sourceFile);
|
||||
|
|
|
@ -83,7 +83,7 @@ public class GhidraScriptUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* initialize state of GhidraScriptUtil with user, system paths, and optional extra system paths.
|
||||
* Initialize state of GhidraScriptUtil with user, system, and optional extra system paths.
|
||||
*
|
||||
* @param aBundleHost the host to use
|
||||
* @param extraSystemPaths additional system paths for this run, can be null
|
||||
|
@ -106,7 +106,7 @@ public class GhidraScriptUtil {
|
|||
*/
|
||||
public static void dispose() {
|
||||
if (bundleHost != null) {
|
||||
bundleHost.dispose();
|
||||
bundleHost.stopFramework();
|
||||
bundleHost = null;
|
||||
}
|
||||
providers = null;
|
||||
|
|
|
@ -121,8 +121,8 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||
throw new ClassNotFoundException(
|
||||
"Failed to find source bundle containing script: " + sourceFile.toString());
|
||||
}
|
||||
bundleHost.activateAll(Collections.singletonList(bundle), TaskMonitor.DUMMY, writer);
|
||||
|
||||
bundleHost.activateAll(Collections.singletonList(bundle), TaskMonitor.DUMMY, writer);
|
||||
String classname = bundle.classNameForScript(sourceFile);
|
||||
Class<?> clazz = bundle.getOSGiBundle().loadClass(classname); // throws ClassNotFoundException
|
||||
return clazz;
|
||||
|
|
|
@ -87,7 +87,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
|
||||
@After
|
||||
public void tearDown() {
|
||||
bundleHost.dispose();
|
||||
bundleHost.stopFramework();
|
||||
capturingBundleHostListener = null;
|
||||
bundleHost = null;
|
||||
|
||||
|
|
|
@ -435,11 +435,11 @@ public class BundleStatusManagerTest extends AbstractGhidraScriptMgrPluginTest {
|
|||
}
|
||||
|
||||
void awaitActivation() throws InterruptedException {
|
||||
assertTrue(activationLatch.await(5000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(activationLatch.await(30000, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
void awaitDisablement() throws InterruptedException {
|
||||
assertTrue(disablementLatch.await(5000, TimeUnit.MILLISECONDS));
|
||||
assertTrue(disablementLatch.await(30000, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.script;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.apache.logging.log4j.*;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
|
@ -150,7 +151,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
// remove all class files from the user script bin dir
|
||||
File userScriptsBinDir =
|
||||
GhidraSourceBundle.getBindirFromScriptFile(new ResourceFile(newScriptFile)).toFile();
|
||||
getBinDirFromScriptFile(new ResourceFile(newScriptFile)).toFile();
|
||||
File[] userScriptBinDirFiles;
|
||||
if (userScriptsBinDir.exists()) {
|
||||
userScriptBinDirFiles = userScriptsBinDir.listFiles(classFileFilter);
|
||||
|
@ -195,7 +196,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
|
||||
// verify that the generated class file is placed in the default scripting home/bin
|
||||
File userScriptsBinDir =
|
||||
GhidraSourceBundle.getBindirFromScriptFile(systemScriptFile).toFile();
|
||||
getBinDirFromScriptFile(systemScriptFile).toFile();
|
||||
String className = scriptName.replace(".java", ".class");
|
||||
File expectedClassFile = new File(userScriptsBinDir, className);
|
||||
|
||||
|
@ -232,7 +233,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
waitForScriptCompletion(scriptID, 20000);
|
||||
|
||||
// verify a bin dir was created and that the class file is in it
|
||||
File binDir = GhidraSourceBundle.getBindirFromScriptFile(newScriptFile).toFile();
|
||||
File binDir = getBinDirFromScriptFile(newScriptFile).toFile();
|
||||
assertTrue("bin output dir not created", binDir.exists());
|
||||
|
||||
File scriptClassFile = new File(binDir, rawScriptName + ".class");
|
||||
|
@ -493,4 +494,11 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
assertContainsText("The field of the script still has state--the script was not recreated",
|
||||
"*2*", output);
|
||||
}
|
||||
|
||||
private Path getBinDirFromScriptFile(ResourceFile sourceFile) {
|
||||
ResourceFile tmpSourceDir = sourceFile.getParentFile();
|
||||
String tmpSymbolicName = GhidraSourceBundle.sourceDirHash(tmpSourceDir);
|
||||
return GhidraSourceBundle.getCompiledBundlesDir().resolve(tmpSymbolicName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,14 +23,12 @@ import java.io.*;
|
|||
*
|
||||
*
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "10.2") // This is not used
|
||||
public class JavaCompiler {
|
||||
|
||||
private IOThread cmdOut;
|
||||
private IOThread cmdErr;
|
||||
|
||||
/**
|
||||
* Compile a java file.
|
||||
*/
|
||||
public void compile(File javaFile) {
|
||||
String name = javaFile.getName();
|
||||
String className = name.substring(0, name.indexOf(".")) + ".class";
|
||||
|
@ -39,7 +36,7 @@ public class JavaCompiler {
|
|||
File parent = javaFile.getParentFile();
|
||||
String parentPath = parent.getAbsolutePath();
|
||||
int pos = parentPath.lastIndexOf("ghidra");
|
||||
String destPath = parentPath.substring(0, pos-1);
|
||||
String destPath = parentPath.substring(0, pos - 1);
|
||||
|
||||
javaFile.deleteOnExit();
|
||||
|
||||
|
@ -49,10 +46,10 @@ public class JavaCompiler {
|
|||
String classpath = System.getProperty("java.class.path");
|
||||
String javaLoc = System.getProperty("java.home");
|
||||
if (javaLoc.endsWith("jre")) {
|
||||
javaLoc = javaLoc.substring(0, javaLoc.indexOf("jre")-1);
|
||||
javaLoc = javaLoc.substring(0, javaLoc.indexOf("jre") - 1);
|
||||
}
|
||||
String argV[] = new String[6];
|
||||
argV[0] = javaLoc + File.separator + "bin" + File.separator +"javac";
|
||||
argV[0] = javaLoc + File.separator + "bin" + File.separator + "javac";
|
||||
argV[1] = "-classpath";
|
||||
argV[2] = classpath;
|
||||
argV[3] = "-d";
|
||||
|
@ -60,13 +57,13 @@ public class JavaCompiler {
|
|||
argV[5] = javaFile.getAbsolutePath();
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(argV);
|
||||
for (int i=0; i<argV.length; i++) {
|
||||
System.out.print(argV[i] + " ");
|
||||
for (String element : argV) {
|
||||
System.out.print(element + " ");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
InputStream stderrStream = p.getErrorStream();
|
||||
InputStream stdinStream = p.getInputStream();
|
||||
InputStream stdinStream = p.getInputStream();
|
||||
|
||||
setupIO(stdinStream, stderrStream);
|
||||
p.waitFor();
|
||||
|
@ -74,19 +71,14 @@ public class JavaCompiler {
|
|||
cmdOut.join();
|
||||
cmdErr.join();
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up threads to read from stdin and stderr and output that
|
||||
* to stdout.
|
||||
* @param stdin
|
||||
* @param stderr
|
||||
*/
|
||||
private void setupIO (InputStream stdin, InputStream stderr){
|
||||
private void setupIO(InputStream stdin, InputStream stderr) {
|
||||
cmdOut = new IOThread(stdin); //
|
||||
cmdErr = new IOThread(stderr);
|
||||
cmdOut.start();
|
||||
|
@ -100,19 +92,19 @@ public class JavaCompiler {
|
|||
private class IOThread extends Thread {
|
||||
private BufferedReader shellOutput;
|
||||
|
||||
public IOThread (InputStream input) {
|
||||
shellOutput = new BufferedReader(new InputStreamReader(input));
|
||||
public IOThread(InputStream input) {
|
||||
shellOutput = new BufferedReader(new InputStreamReader(input));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
public void run() {
|
||||
String line = null;
|
||||
try {
|
||||
while ((line = shellOutput.readLine()) != null) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ import java.io.PrintWriter;
|
|||
|
||||
public class NullPrintWriter extends PrintWriter {
|
||||
|
||||
public static PrintWriter dummyIfNull(PrintWriter pw) {
|
||||
return pw == null ? new NullPrintWriter() : pw;
|
||||
}
|
||||
|
||||
public NullPrintWriter() {
|
||||
super(new NullWriter());
|
||||
}
|
||||
|
|
|
@ -20,11 +20,9 @@ import java.net.InetAddress;
|
|||
import java.net.UnknownHostException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
public class FileLocker {
|
||||
|
||||
|
@ -91,28 +89,16 @@ public class FileLocker {
|
|||
|
||||
Properties properties = new Properties();
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(lockFile);
|
||||
try (InputStream is = new FileInputStream(lockFile)) {
|
||||
properties.load(is);
|
||||
return properties;
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
// should never happen
|
||||
// should not happen
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// we tried!
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -178,25 +164,13 @@ public class FileLocker {
|
|||
|
||||
private boolean storeProperties(Properties properties) {
|
||||
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = new FileOutputStream(lockFile);
|
||||
try (OutputStream os = new FileOutputStream(lockFile)) {
|
||||
properties.store(os, "Ghidra Lock File");
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// don't care; we tried
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLockOwner() {
|
||||
|
@ -212,7 +186,7 @@ public class FileLocker {
|
|||
for (String key : PROPERTY_KEYS) {
|
||||
String originalProperty = createdLockProperties.getProperty(key);
|
||||
String currentProperty = currentLockProperties.getProperty(key);
|
||||
if (!SystemUtilities.isEqual(originalProperty, currentProperty)) {
|
||||
if (!Objects.equals(originalProperty, currentProperty)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,12 +15,12 @@
|
|||
*/
|
||||
package ghidra.framework.project;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import generic.util.FileLocker;
|
||||
import generic.util.LockFactory;
|
||||
import ghidra.framework.model.ProjectLocator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* A simple delegate for creating and using locks in Ghidra.
|
||||
*/
|
||||
|
@ -29,7 +28,7 @@ class ProjectLock {
|
|||
|
||||
private final File lockFile;
|
||||
|
||||
FileLocker locker;
|
||||
private FileLocker locker;
|
||||
|
||||
public ProjectLock(ProjectLocator projectLocator) {
|
||||
this.lockFile = projectLocator.getProjectLockFile();
|
||||
|
|
|
@ -880,7 +880,8 @@ public final class FileUtilities {
|
|||
*
|
||||
* @param f1 the parent file
|
||||
* @param f2 the child file
|
||||
* @return the portion of the second file that trails the full path of the first file.
|
||||
* @return the portion of the second file that trails the full path of the first file; null as
|
||||
* described above
|
||||
* @throws IOException if there is an error canonicalizing the path
|
||||
*/
|
||||
public static String relativizePath(File f1, File f2) throws IOException {
|
||||
|
@ -903,17 +904,18 @@ public final class FileUtilities {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the relative path string of one resource file in another. If
|
||||
* no path can be constructed or the files are the same, then null is returned.
|
||||
* Return the relative path string of one resource file in another. If no path can be
|
||||
* constructed or the files are the same, then null is returned.
|
||||
*
|
||||
* Note: unlike {@link #relativizePath(File, File)}, this function does not resolve symbolic links.
|
||||
* Note: unlike {@link #relativizePath(File, File)}, this function does not resolve symbolic
|
||||
* links.
|
||||
*
|
||||
* <P>For example, given, in this order, two files with these paths
|
||||
* <code>/a/b</code> and <code>/a/b/c</code>, this method will return 'c'.
|
||||
*
|
||||
* @param f1 the parent resource file
|
||||
* @param f2 the child resource file
|
||||
* @return the relative path of {@code f2} in {@code f1}
|
||||
* @return the relative path of {@code f2} in {@code f1}; null if f1 is not a parent of f2
|
||||
*/
|
||||
public static String relativizePath(ResourceFile f1, ResourceFile f2) {
|
||||
StringBuilder sb = new StringBuilder(f2.getName());
|
||||
|
|
Loading…
Reference in a new issue