start connecting OSGi to bundlestatusmanager

- minimize access to BundleHost instance (JavaScriptProvider is still bad)
- BundlePath
  - restrict construction to osgi package
  - remove "editable" attribute and fix incorrect reference
  - remove references in GhidraScriptUtil
    - BundlePath attribute choice came down only to system or user
    - user filtering on "enabled" were redundant
- move compilation from JavaScriptProvider to BundheHost
This commit is contained in:
Jason P. Leasure 2020-03-30 13:02:27 -04:00
parent 36f4a219d9
commit f39d55cca1
14 changed files with 352 additions and 282 deletions

View File

@ -39,9 +39,9 @@ import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.BreadthFirstIterator;
import generic.jar.ResourceFile;
import generic.util.Path;
import ghidra.app.plugin.core.script.osgi.*;
import ghidra.app.script.*;
import ghidra.app.script.osgi.BundleHost;
import ghidra.app.services.ConsoleService;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.ComponentProviderAdapter;
@ -97,9 +97,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
};
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin) {
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
super(plugin.getTool(), "Script Manager", plugin.getName());
this.plugin = plugin;
this.bundleHost=bundleHost;
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
setIcon(ResourceManager.loadImage("images/play.png"));
@ -161,8 +162,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
void renameScript() {
ResourceFile script = getSelectedScript();
ResourceFile directory = script.getParentFile();
Path path = GhidraScriptUtil.getScriptPath(directory);
if (path == null || path.isReadOnly()) {
if (!bundleStatusProvider.getModel().isWriteable(directory)) {
Msg.showWarn(getClass(), getComponent(), getName(),
"Unable to rename scripts in '" + directory + "'.");
return;
@ -297,8 +297,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
ResourceFile directory = script.getParentFile();
Path path = GhidraScriptUtil.getScriptPath(directory);
if (path == null || path.isReadOnly()) {
if (!bundleStatusProvider.getModel().isWriteable(directory)) {
Msg.showWarn(getClass(), getComponent(), getName(),
"Unable to delete scripts in '" + directory + "'.");
return;
@ -339,9 +338,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
}
public List<BundlePath> getScriptDirectories() {
public List<ResourceFile> getScriptDirectories() {
return bundleStatusProvider.getModel().getPaths().stream().filter(
BundlePath::isDirectory).collect(Collectors.toList());
ResourceFile::isDirectory).collect(Collectors.toList());
}
public void enableScriptDirectory(ResourceFile scriptDir) {
@ -396,9 +395,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
void runScript(String scriptName, TaskListener listener) {
List<BundlePath> dirPaths = bundleStatusProvider.getModel().getPaths();
for (Path dir : dirPaths) {
ResourceFile scriptSource = new ResourceFile(dir.getPath(), scriptName);
List<ResourceFile> dirPaths = bundleStatusProvider.getModel().getPaths();
for (ResourceFile dir : dirPaths) {
ResourceFile scriptSource = new ResourceFile(dir, scriptName);
if (scriptSource.exists()) {
runScript(scriptSource, listener);
return;
@ -536,11 +535,11 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
private void updateAvailableScriptFilesForAllPaths() {
List<ResourceFile> scriptsToRemove = tableModel.getScripts();
List<ResourceFile> scriptAccumulator = new ArrayList<>();
List<BundlePath> bundlePaths = bundleStatusProvider.getModel().getPaths();
for (BundlePath bundlePath : bundlePaths) {
List<ResourceFile> bundlePaths = bundleStatusProvider.getModel().getPaths();
for (ResourceFile bundlePath : bundlePaths) {
if (bundlePath.isDirectory()) {
updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator,
bundlePath.getPath());
bundlePath);
}
}
@ -731,8 +730,33 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
return true;
}
private void build() {
final private BundleHost bundleHost;
void startActivateDeactiveTask(BundlePath path, boolean activate) {
path.setBusy(true);
bundleStatusProvider.notifyTableChanged();
new TaskLauncher(new Task((activate ? "Activating" : "Deactivating ") + " bundle...") {
@Override
public void run(TaskMonitor monitor) throws CancelledException {
try {
bundleHost.setActive(path, activate);
path.setActive(activate);
}
catch (Exception e) {
Msg.showError(this, GhidraScriptComponentProvider.this.getComponent(),
"activation failed", e);
}
finally {
path.setBusy(false);
bundleStatusProvider.notifyTableChanged();
}
}
}, null, 1000);
}
private void build() {
bundleStatusProvider = new BundleStatusProvider(plugin.getTool(), plugin.getName());
bundleStatusProvider.addListener(new BundlePathManagerListener() {
@ -744,8 +768,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
@Override
public void bundleEnablementChanged(BundlePath path, boolean enabled) {
System.err.printf("XXXX %s is now %s\n", path.toString(),
enabled ? "enabled" : "disabled");
if (path.isActive()) {
startActivateDeactiveTask(path, false);
}
if (path.isDirectory()) {
performRefresh();
}
@ -753,8 +779,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
@Override
public void bundleActivationChanged(BundlePath path, boolean newValue) {
System.err.printf("XXXX %s is now %s\n", path.toString(),
newValue ? "active" : "inactive");
startActivateDeactiveTask(path, newValue);
}
});
@ -1026,7 +1051,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
bundleStatusProvider.restoreState(saveState);
// pull in the just-loaded paths
List<BundlePath> paths = bundleStatusProvider.getModel().getPaths();
List<ResourceFile> paths = bundleStatusProvider.getModel().getPaths();
GhidraScriptUtil.setScriptBundlePaths(paths);
actionManager.restoreUserDefinedKeybindings(saveState);
actionManager.restoreScriptsThatAreInTool(saveState);
@ -1162,4 +1187,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
}
public List<ResourceFile> getWritableScriptDirectories() {
BundleStatusModel m = bundleStatusProvider.getModel();
return m.getPaths().stream().filter(ResourceFile::isDirectory).filter(
m::isWriteable).collect(Collectors.toList());
}
}

View File

@ -26,6 +26,7 @@ import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.eclipse.EclipseConnection;
import ghidra.app.plugin.core.eclipse.EclipseIntegrationOptionsPlugin;
import ghidra.app.script.GhidraState;
import ghidra.app.script.osgi.BundleHost;
import ghidra.app.services.*;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.ToolOptions;
@ -48,12 +49,15 @@ import ghidra.util.task.TaskListener;
//@formatter:on
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
private GhidraScriptComponentProvider provider;
final private GhidraScriptComponentProvider provider;
final private BundleHost bundleHost;
public GhidraScriptMgrPlugin(PluginTool tool) {
super(tool, true, true, true);
provider = new GhidraScriptComponentProvider(this);
bundleHost = BundleHost.getInstance();
provider = new GhidraScriptComponentProvider(this, bundleHost);
}
@Override

View File

@ -19,7 +19,8 @@ import java.awt.BorderLayout;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
@ -31,7 +32,6 @@ import docking.widgets.MultiLineLabel;
import docking.widgets.label.GLabel;
import docking.widgets.list.ListPanel;
import generic.jar.ResourceFile;
import generic.util.Path;
import ghidra.app.script.*;
import ghidra.util.HelpLocation;
@ -49,7 +49,7 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
public SaveDialog(Component parent, String title,
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
HelpLocation help) {
this(parent, title, componentProvider, getScriptPaths(), scriptFile, help);
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(), scriptFile, help);
}
public SaveDialog(Component parent, String title,
@ -85,21 +85,6 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
DockingWindowManager.showDialog(parent, this);
}
private static List<ResourceFile> getScriptPaths() {
List<ResourceFile> newPaths = new ArrayList<>();
List<ResourceFile> scriptPaths = GhidraScriptUtil.getScriptSourceDirectories();
for (ResourceFile directory : scriptPaths) {
Path path = GhidraScriptUtil.getScriptPath(directory);
if (path != null && !path.isReadOnly()) {
newPaths.add(directory);
}
}
Collections.sort(newPaths);
return newPaths;
}
private JPanel buildNamePanel() {
nameField = new JTextField(20);
nameField.setText(scriptFile == null ? "" : scriptFile.getName());

View File

@ -21,7 +21,10 @@ import generic.jar.ResourceFile;
import generic.util.Path;
public class BundlePath extends Path {
final Type type;
boolean active = false;
boolean busy = false;
public static enum Type {
BndScript, Jar, SourceDir, INVALID
@ -55,47 +58,43 @@ public class BundlePath extends Path {
return Type.INVALID;
}
final Type type;
public BundlePath(File path) {
super(path);
type = getType(getPath());
}
public Type getType() {
return type;
}
public BundlePath(ResourceFile rf) {
super(rf);
BundlePath(String path, boolean enabled, boolean readonly) {
super(path, enabled, false /*editable */, readonly);
type = getType(getPath());
}
public BundlePath(String absolutePath) {
super(absolutePath);
BundlePath(ResourceFile path, boolean enabled, boolean readonly) {
super(path, enabled, false /* editable */, readonly);
type = getType(getPath());
}
public BundlePath(String a, boolean b, boolean c, boolean d) {
super(a, b, c, d);
type = getType(getPath());
}
public BundlePath(ResourceFile a, boolean b, boolean c, boolean d) {
super(a, b, c, d);
type = getType(getPath());
}
public boolean isActive() {
return active;
}
public void setActive(Boolean b) {
active = b;
}
public boolean isDirectory() {
return getPath().isDirectory();
}
public boolean isActive() {
return active;
}
@Override
public boolean isEditable() {
return false;
}
public void setActive(boolean b) {
active = b;
}
public void setBusy(boolean b) {
busy = b;
}
public boolean getBusy() {
return busy;
}
}

View File

@ -16,8 +16,8 @@
*/
package ghidra.app.plugin.core.script.osgi;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import docking.widgets.table.AbstractSortedTableModel;
import generic.jar.ResourceFile;
@ -41,20 +41,25 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
}
boolean editable(BundlePath path) {
return true;
return false;
}
Object getValue(BundlePath path) {
return path;
return null;
}
void setValue(BundlePath path, Object aValue) {
// do nothing
throw new RuntimeException(name + " is not editable!");
}
}
Column enabledColumn = new Column("Enabled", Boolean.class) {
@Override
boolean editable(BundlePath path) {
return path.exists();
}
@Override
Object getValue(BundlePath path) {
return path.isEnabled();
@ -67,6 +72,11 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
}
};
Column activeColumn = new Column("Active", Boolean.class) {
@Override
boolean editable(BundlePath path) {
return path.exists(); // XXX maybe only if it's already enabled
}
@Override
Object getValue(BundlePath path) {
return path.isActive();
@ -93,15 +103,12 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
Column pathColumn = new Column("Path", BundlePath.class) {
@Override
boolean editable(BundlePath path) {
return true;
return false;
}
@Override
void setValue(BundlePath path, Object aValue) {
if (path.isEditable()) {
BundlePath newpath = (BundlePath) aValue;
path.setPath(newpath.getPath());
}
Object getValue(BundlePath path) {
return path;
}
};
Column badColumn = new Column("INVALID", Object.class);
@ -118,26 +125,21 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
}
private BundleStatusProvider provider;
private List<BundlePath> paths = new ArrayList<>();
private List<BundlePath> paths;
BundleStatusModel(BundleStatusProvider provider) {
super();
this.provider = provider;
this.paths.addAll(dedupPaths(GhidraScriptUtil.getDefaultScriptBundles()));
// add unmodifiable paths
this.paths = GhidraScriptUtil.getSystemScriptPaths().stream().distinct().map(
f -> new BundlePath(f, true, true)).collect(Collectors.toList());
// add user path
this.paths.add(0, new BundlePath(GhidraScriptUtil.getUserScriptDirectory(), true, false));
fireTableDataChanged();
}
private List<BundlePath> dedupPaths(List<BundlePath> newPaths) {
List<BundlePath> dedupedPaths = new ArrayList<>();
for (BundlePath path : newPaths) {
if (!dedupedPaths.contains(path)) {
dedupedPaths.add(path);
}
}
return dedupedPaths;
}
void clear() {
paths.clear();
}
@ -146,21 +148,16 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
return new ArrayList<BundlePath>(paths);
}
public List<BundlePath> getPaths() {
List<BundlePath> list = new ArrayList<>();
public List<ResourceFile> getPaths() {
List<ResourceFile> list = new ArrayList<>();
for (BundlePath path : paths) {
if (path.isEnabled()) {
list.add(path);
list.add(path.getPath());
}
}
return list;
}
public void setPaths(List<BundlePath> paths) {
this.paths = new ArrayList<>(paths);
fireTableDataChanged();
}
void addPath(BundlePath path) {
if (paths.contains(path)) {
return;
@ -176,7 +173,7 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
list.add(paths.get(selectedRow));
}
for (BundlePath path : list) {
if (path.isEditable()) {
if (!path.isReadOnly()) {
paths.remove(path);
}
else {
@ -259,11 +256,48 @@ public class BundleStatusModel extends AbstractSortedTableModel<BundlePath> {
return false;
}
}
BundlePath p = new BundlePath(dir);
p.setEnabled(true);
BundlePath p = new BundlePath(dir, true, false);
addPath(p);
Preferences.setProperty(BundleStatusProvider.preferenceForLastSelectedBundle, dir.getAbsolutePath());
Preferences.setProperty(BundleStatusProvider.preferenceForLastSelectedBundle,
dir.getAbsolutePath());
provider.fireBundlesChanged();
return true;
}
/**
* Test whether the given <code>bundle</code> is managed and not marked readonly
* @param bundle the path to test
* @return true if the bundle is managed and not marked readonly
*/
public boolean isWriteable(ResourceFile bundle) {
Optional<BundlePath> o = paths.stream().filter(
bp -> bp.isDirectory() && bp.getPath().equals(bundle)).findFirst();
return o.isPresent() && !o.get().isReadOnly();
}
/**
* This is for testing only!
*
* each path is marked editable and non-readonly
*
* @param testingPaths the paths to use
*/
public void setPathsForTesting(List<String> testingPaths) {
this.paths = testingPaths.stream().map(f -> new BundlePath(f, true, false)).collect(
Collectors.toList());
fireTableDataChanged();
}
/**
* This is for testing only!
*
* insert path, marked editable and non-readonly
* @param index index to insert at
* @param path the path to insert
*/
public void insertPathForTesting(int index, String path) {
paths.add(0, new BundlePath(path, true, false));
fireTableRowsInserted(0, 0);
}
}

View File

@ -22,11 +22,13 @@ import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableColumn;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import docking.widgets.table.*;
import generic.jar.ResourceFile;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.PluginTool;
@ -51,6 +53,10 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
private GhidraFileFilter filter;
private ArrayList<BundlePathManagerListener> listeners = new ArrayList<>();
public void notifyTableChanged() {
bundlePathTable.notifyTableChanged(new TableModelEvent(bundleStatusModel));
}
void fireBundlesChanged() {
for (BundlePathManagerListener listener : listeners) {
listener.bundlesChanged();
@ -108,7 +114,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
panel = new JPanel(new BorderLayout(5, 5));
selectionColor = new Color(204, 204, 255);
addButton = new JButton(ResourceManager.loadImage("images/Plus.png"));
addButton.setName("AddBundle");
addButton.setToolTipText("Display file chooser to add bundles to list");
@ -138,6 +144,9 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
bundlePathTable.setSelectionForeground(Color.BLACK);
bundlePathTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// to allow custom cell renderers
bundlePathTable.setAutoCreateColumnsFromModel(false);
int skinnyWidth = 50;
TableColumn column =
@ -152,6 +161,27 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
column.setMinWidth(skinnyWidth);
column.setMaxWidth(skinnyWidth);
column.setWidth(skinnyWidth);
column.setCellRenderer(new GBooleanCellRenderer() {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
BundlePath path = (BundlePath) data.getRowObject();
Component x = super.getTableCellRendererComponent(data);
if (path.getBusy()) {
cb.setVisible(false);
cb.setEnabled(false);
setHorizontalAlignment(SwingConstants.CENTER);
setText("...");
}
else {
cb.setVisible(true);
cb.setEnabled(true);
setText("");
}
return x;
}
});
column = bundlePathTable.getColumnModel().getColumn(bundleStatusModel.typeColumn.index);
@ -163,13 +193,13 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
column.setCellRenderer(new GTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
JLabel renderer = (JLabel) super.getTableCellRendererComponent(data);
JLabel c = (JLabel) super.getTableCellRendererComponent(data);
BundlePath path = (BundlePath) data.getValue();
if (!path.exists()) {
renderer.setForeground(Color.RED);
c.setForeground(Color.RED);
}
return renderer;
return c;
}
});
@ -262,7 +292,7 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
Preferences.setProperty(preferenceForLastSelectedBundle,
files.get(0).getAbsolutePath());
for (File element : files) {
BundlePath p = new BundlePath(element);
BundlePath p = new BundlePath(new ResourceFile(element), true, false);
bundleStatusModel.addPath(p);
}
fireBundlesChanged();
@ -283,22 +313,19 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
String[] pathArr = new String[paths.size()];
boolean[] enableArr = new boolean[paths.size()];
boolean[] editArr = new boolean[paths.size()];
boolean[] readArr = new boolean[paths.size()];
int index = 0;
for (BundlePath path : paths) {
pathArr[index] = path.getPathAsString();
enableArr[index] = path.isEnabled();
editArr[index] = path.isEditable();
readArr[index] = path.isReadOnly();
++index;
}
ss.putStrings("BundleManagerPanel_PATH", pathArr);
ss.putBooleans("BundleManagerPanel_ENABLE", enableArr);
ss.putBooleans("BundleManagerPanel_EDIT", editArr);
ss.putBooleans("BundleManagerPanel_READ", readArr);
ss.putStrings("BundleStatus_PATH", pathArr);
ss.putBooleans("BundleStatus_ENABLE", enableArr);
ss.putBooleans("BundleStatus_READ", readArr);
}
/**
@ -306,22 +333,21 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
* @param ss the SaveState object
*/
public void restoreState(SaveState ss) {
String[] pathArr = ss.getStrings("BundleManagerPanel_PATH", new String[0]);
String[] pathArr = ss.getStrings("BundleStatus_PATH", new String[0]);
if (pathArr.length == 0) {
return;
}
boolean[] enableArr =
ss.getBooleans("BundleManagerPanel_ENABLE", new boolean[pathArr.length]);
boolean[] editArr = ss.getBooleans("BundleManagerPanel_EDIT", new boolean[pathArr.length]);
boolean[] readArr = ss.getBooleans("BundleManagerPanel_READ", new boolean[pathArr.length]);
ss.getBooleans("BundleStatus_ENABLE", new boolean[pathArr.length]);
boolean[] readArr = ss.getBooleans("BundleStatus_READ", new boolean[pathArr.length]);
List<BundlePath> oldPaths = bundleStatusModel.getAllPaths();
bundleStatusModel.clear();
for (int i = 0; i < pathArr.length; i++) {
BundlePath path = new BundlePath(pathArr[i], enableArr[i], editArr[i], readArr[i]);
BundlePath path = new BundlePath(pathArr[i], enableArr[i], readArr[i]);
BundlePath oldPath = getPath(path.getPathAsString(), oldPaths);
if (oldPath != null) {
if (!oldPath.isEditable()) {
@ -361,7 +387,4 @@ public class BundleStatusProvider extends ComponentProviderAdapter {
bundlePathTable.dispose();
}
void selectRow(int rowIndex) {
bundlePathTable.selectRow(rowIndex);
}
}

View File

@ -24,7 +24,6 @@ import java.util.stream.Collectors;
import org.apache.commons.collections4.map.LazyMap;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.framework.Application;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
@ -48,7 +47,7 @@ public class GhidraScriptUtil {
*/
public static String USER_SCRIPTS_DIR = buildUserScriptsDirectory();
private static List<BundlePath> scriptBundlePaths = new ArrayList<>();
private static List<ResourceFile> scriptBundlePaths = new ArrayList<>();
private static Map<ResourceFile, ScriptInfo> scriptFileToInfoMap = new HashMap<>();
@ -56,7 +55,8 @@ public class GhidraScriptUtil {
LazyMap.lazyMap(new HashMap<String, List<ResourceFile>>(), () -> new ArrayList<>());
static {
scriptBundlePaths = getDefaultScriptBundles();
scriptBundlePaths = getSystemScriptPaths();
scriptBundlePaths.add(0, getUserScriptDirectory());
}
/**
@ -81,25 +81,22 @@ public class GhidraScriptUtil {
* Returns a list of the default script directories.
* @return a list of the default script directories
*/
public static List<BundlePath> getDefaultScriptBundles() {
List<BundlePath> pathsList = new ArrayList<>();
public static List<ResourceFile> getSystemScriptPaths() {
List<ResourceFile> pathsList = new ArrayList<>();
addScriptPaths(pathsList, SCRIPTS_SUBDIR_NAME);
addScriptPaths(pathsList, DEV_SCRIPTS_SUBDIR_NAME);
Collections.sort(pathsList);
// this one should always be first
pathsList.add(0, new BundlePath(new ResourceFile(USER_SCRIPTS_DIR), true, false, false));
return pathsList;
}
private static void addScriptPaths(List<BundlePath> pathsList, String directoryName) {
Iterable<ResourceFile> files = Application.findModuleSubDirectories(directoryName);
for (ResourceFile file : files) {
pathsList.add(new BundlePath(file, true, false, true));
}
public static ResourceFile getUserScriptDirectory() {
return new ResourceFile(USER_SCRIPTS_DIR);
}
private static void addScriptPaths(List<ResourceFile> pathsList, String directoryName) {
pathsList.addAll(Application.findModuleSubDirectories(directoryName));
}
/**
@ -144,39 +141,18 @@ public class GhidraScriptUtil {
* @return a list of the current script directories
*/
public static List<ResourceFile> getScriptSourceDirectories() {
ArrayList<ResourceFile> dirs = new ArrayList<>();
for (BundlePath path : scriptBundlePaths) {
if (path.isDirectory()) {
dirs.add(path.getPath());
}
}
return dirs;
return scriptBundlePaths.stream().filter(ResourceFile::isDirectory).collect(
Collectors.toList());
}
/**
* Sets the script bundle paths
* @param newPaths the new script bundle paths
*/
public static void setScriptBundlePaths(List<BundlePath> newPaths) {
public static void setScriptBundlePaths(List<ResourceFile> newPaths) {
scriptBundlePaths = new ArrayList<>(newPaths);
}
/**
* Returns the PATH for the specified directory.
* @param directory the directory
* @return the path for the specified directory
*/
public static BundlePath getScriptPath(ResourceFile directory) {
if (directory.isDirectory()) {
for (BundlePath path : scriptBundlePaths) {
if (path.getPath().equals(directory)) {
return path;
}
}
}
return null;
}
public static Path getOsgiDir() {
Path usersettings = Application.getUserSettingsDirectory().toPath();
return usersettings.resolve("osgi");
@ -218,7 +194,7 @@ public class GhidraScriptUtil {
}
Set<ResourceFile> dirs = new HashSet<>();
for (BundlePath path : scriptBundlePaths) {
for (ResourceFile scriptDir : scriptBundlePaths) {
//
// Assumed structure of script dir path:
// /some/path/Ghidra/Features/Module/ghidra_scripts
@ -226,7 +202,6 @@ public class GhidraScriptUtil {
// Desired path:
// /some/path/Ghidra/Features/Module/bin/scripts
ResourceFile scriptDir = path.getPath();
ResourceFile moduleDir = scriptDir.getParentFile();
dirs.add(new ResourceFile(moduleDir, BIN_DIR_NAME + File.separator + "scripts"));
}
@ -381,14 +356,14 @@ public class GhidraScriptUtil {
* @throws IOException if an i/o error occurs
*/
public static ResourceFile createNewScript(GhidraScriptProvider provider,
ResourceFile parentDirectory, List<BundlePath> scriptDirectories) throws IOException {
ResourceFile parentDirectory, List<ResourceFile> scriptDirectories) throws IOException {
String baseName = GhidraScriptConstants.DEFAULT_SCRIPT_NAME;
String extension = provider.getExtension();
return createNewScript(baseName, extension, parentDirectory, scriptDirectories);
}
private static ResourceFile createNewScript(String scriptName, String extension,
ResourceFile parentDirctory, List<BundlePath> scriptDirectories) throws IOException {
ResourceFile parentDirctory, List<ResourceFile> scriptDirectories) throws IOException {
String baseName = scriptName;
String className = baseName + extension;
@ -409,13 +384,12 @@ public class GhidraScriptUtil {
}
/** Returns true if the given filename exists in any of the given directories */
private static ResourceFile findScriptFileInPaths(List<BundlePath> scriptDirectories,
private static ResourceFile findScriptFileInPaths(List<ResourceFile> scriptDirectories,
String filename) {
String validatedName = fixupName(filename);
for (BundlePath path : scriptDirectories) {
ResourceFile resourceFile = path.getPath();
for (ResourceFile resourceFile : scriptDirectories) {
if (resourceFile.isDirectory()) {
ResourceFile file = new ResourceFile(resourceFile, validatedName);
if (file.exists()) {
@ -477,8 +451,8 @@ public class GhidraScriptUtil {
public static List<ResourceFile> getAllScripts() {
List<ResourceFile> scriptList = new ArrayList<>();
for (BundlePath dirPath : scriptBundlePaths) {
updateAvailableScriptFilesForDirectory(scriptList, dirPath.getPath());
for (ResourceFile dirPath : scriptBundlePaths) {
updateAvailableScriptFilesForDirectory(scriptList, dirPath);
}
return scriptList;
}

View File

@ -26,7 +26,7 @@ import ghidra.app.script.osgi.*;
import ghidra.util.Msg;
public class JavaScriptProvider extends GhidraScriptProvider {
public SourceBundleInfo getBundleInfoForSource(ResourceFile sourceFile) {
static public SourceBundleInfo getBundleInfoForSource(ResourceFile sourceFile) {
ResourceFile sourceDir = getSourceDirectoryContaining(sourceFile);
if (sourceDir == null) {
return null;
@ -87,7 +87,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
}
}
public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer)
static public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer)
throws IOException, OSGiException, ClassNotFoundException, InterruptedException {
if (writer == null) {
@ -95,72 +95,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
}
SourceBundleInfo bi = getBundleInfoForSource(sourceFile);
bi.updateFromFilesystem(writer);
// needsCompile => needsBundleActivate
boolean needsCompile = false;
boolean needsBundleActivate = false;
int failing = bi.getFailingSourcesCount();
int newSourcecount = bi.getNewSourcesCount();
long lastBundleActivation = 0; // XXX record last bundle activation in pathmanager
if (failing > 0 && (lastBundleActivation > bi.getLastCompileAttempt())) {
needsCompile = true;
}
if (newSourcecount == 0) {
if (failing > 0) {
writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n",
bi.getSourceDir().toString(), failing, failing > 1 ? "s" : "");
writer.printf("%s\n", bi.getPreviousBuildErrors());
}
if (bi.newManifestFile()) {
needsCompile = true;
}
}
else {
needsCompile = true;
}
needsBundleActivate |= needsCompile;
BundleHost bundle_host = BundleHost.getInstance();
if (needsBundleActivate) {
writer.printf("%s has %d new/updated %d failed in previous build(s)%s\n",
bi.getSourceDir().toString(), newSourcecount, failing,
bi.newManifestFile() ? " and the manifest is new" : "");
// if there a bundle is currently active, uninstall it
Bundle b = bi.getBundle();
if (b != null) {
bundle_host.synchronousUninstall(b);
}
// once we've committed to recompile and regenerate generated classes, delete the old stuff
if (needsCompile) {
bi.deleteOldBinaries();
BundleCompiler bc = new BundleCompiler(bundle_host);
long startTime = System.nanoTime();
bc.compileToExplodedBundle(bi, writer);
long endTime = System.nanoTime();
writer.printf("%3.2f seconds compile time.\n", (endTime - startTime) / 1e9);
}
}
// as much source as possible built, install bundle and start it if necessary
Bundle b = bi.getBundle();
if (b == null) {
b = bi.install();
needsBundleActivate = true;
}
if (needsBundleActivate) {
bundle_host.synchronousStart(b);
}
Bundle b = BundleHost.getInstance().activate(bi, writer);
String classname = bi.classNameForScript(sourceFile);
Class<?> clazz = b.loadClass(classname); // throws ClassNotFoundException
return clazz;

View File

@ -36,6 +36,7 @@ import org.osgi.framework.wiring.*;
import org.osgi.service.log.*;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.*;
@ -532,4 +533,101 @@ public class BundleHost {
}
}
/**
* Change the status of the bundle referenced by <code>path</code>
*
* @param path the bundle path to activate/deactivate
* @param value the new activation value
* @throws InterruptedException
*/
public void setActive(BundlePath path, boolean value) throws InterruptedException {
if (path.isDirectory()) {
SourceBundleInfo sbi = getSourceBundleInfo(path.getPath());
}
Thread.sleep(3000);
}
/**
* synchronously perform steps necessary to activate bundle:
* <ol>
* <li>if source bundle, checkCompile</li>
* <li>b</li>
*
* </ol>
*
* @param bi the bundle info
* @param writer where to write issues
* @throws OSGiException if bundle operations fail
* @throws IOException if there are issues with the contents of the bundle
* @return the activated bundle
* @throws InterruptedException if interrupted while waiting for bundle state change
*/
public Bundle activate(SourceBundleInfo bi, PrintWriter writer)
throws OSGiException, IOException, InterruptedException {
bi.updateFromFilesystem(writer);
// needsCompile => needsBundleActivate
boolean needsCompile = false;
boolean needsBundleActivate = false;
int failing = bi.getFailingSourcesCount();
int newSourcecount = bi.getNewSourcesCount();
long lastBundleActivation = 0; // XXX record last bundle activation in bundlestatusmodel
if (failing > 0 && (lastBundleActivation > bi.getLastCompileAttempt())) {
needsCompile = true;
}
if (newSourcecount == 0) {
if (failing > 0) {
writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n",
bi.getSourceDir().toString(), failing, failing > 1 ? "s" : "");
writer.printf("%s\n", bi.getPreviousBuildErrors());
}
if (bi.newManifestFile()) {
needsCompile = true;
}
}
else {
needsCompile = true;
}
needsBundleActivate |= needsCompile;
if (needsBundleActivate) {
writer.printf("%s has %d new/updated %d failed in previous build(s)%s\n",
bi.getSourceDir().toString(), newSourcecount, failing,
bi.newManifestFile() ? " and the manifest is new" : "");
// if there a bundle is currently active, uninstall it
Bundle b = bi.getBundle();
if (b != null) {
synchronousUninstall(b);
}
// once we've committed to recompile and regenerate generated classes, delete the old stuff
if (needsCompile) {
bi.deleteOldBinaries();
BundleCompiler bc = new BundleCompiler(this);
long startTime = System.nanoTime();
bc.compileToExplodedBundle(bi, writer);
long endTime = System.nanoTime();
writer.printf("%3.2f seconds compile time.\n", (endTime - startTime) / 1e9);
}
}
// as much source as possible built, install bundle and start it if necessary
Bundle b = bi.getBundle();
if (b == null) {
b = bi.install();
needsBundleActivate = true;
}
if (needsBundleActivate) {
synchronousStart(b);
}
return b;
}
}

View File

@ -22,7 +22,6 @@ import java.util.List;
import generic.jar.ResourceFile;
import ghidra.GhidraApplicationLayout;
import ghidra.GhidraLaunchable;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.app.script.*;
import ghidra.framework.Application;
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
@ -31,7 +30,7 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitorAdapter;
import ghidra.util.task.TaskMonitor;
import utility.application.ApplicationLayout;
/**
@ -76,7 +75,7 @@ public class GhidraScriptRunner implements GhidraLaunchable {
try {
PrintWriter writer = new PrintWriter(System.out);
Msg.info(this, "SCRIPT: " + scriptName);
script.execute(scriptState, TaskMonitorAdapter.DUMMY_MONITOR, writer);
script.execute(scriptState, TaskMonitor.DUMMY, writer);
writer.flush();
}
catch (Exception exc) {
@ -100,8 +99,8 @@ public class GhidraScriptRunner implements GhidraLaunchable {
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(scriptSourceFile);
if (provider == null) {
throw new IOException("Missing plugin needed to run scripts of this type. Please "
+ "ensure you have installed the necessary plugin.");
throw new IOException("Missing plugin needed to run scripts of this type. Please " +
"ensure you have installed the necessary plugin.");
}
PrintWriter writer = new PrintWriter(System.out);
@ -192,20 +191,18 @@ public class GhidraScriptRunner implements GhidraLaunchable {
* Gather paths where scripts may be found.
*/
private void initializeScriptPaths() {
List<BundlePath> paths;
List<ResourceFile> paths;
if (scriptPaths == null || scriptPaths.isEmpty()) {
paths = GhidraScriptUtil.getDefaultScriptBundles();
paths = GhidraScriptUtil.getSystemScriptPaths();
paths.add(0, GhidraScriptUtil.getUserScriptDirectory());
}
else {
paths = new ArrayList<>();
for (String path : scriptPaths) {
paths.add(new BundlePath(path, true, false, true));
}
for (BundlePath path : GhidraScriptUtil.getDefaultScriptBundles()) {
if (path.isEnabled() && !paths.contains(path)) {
paths.add(path);
}
paths.add(new ResourceFile(path));
}
paths.addAll(GhidraScriptUtil.getSystemScriptPaths());
paths.add(0, GhidraScriptUtil.getUserScriptDirectory());
}
GhidraScriptUtil.setScriptBundlePaths(paths);

View File

@ -26,7 +26,6 @@ import generic.util.Path;
import ghidra.GhidraApplicationLayout;
import ghidra.GhidraJarApplicationLayout;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.app.script.*;
import ghidra.app.util.headless.HeadlessScript.HeadlessContinuationOption;
import ghidra.app.util.importer.AutoImporter;
@ -661,20 +660,18 @@ public class HeadlessAnalyzer {
*/
private void initializeScriptPaths() {
List<BundlePath> paths;
List<ResourceFile> paths;
if (options.scriptPaths == null || options.scriptPaths.isEmpty()) {
paths = GhidraScriptUtil.getDefaultScriptBundles();
paths = GhidraScriptUtil.getSystemScriptPaths();
paths.add(0, GhidraScriptUtil.getUserScriptDirectory());
}
else {
paths = new ArrayList<>();
for (String path : options.scriptPaths) {
paths.add(new BundlePath(path, true, false, true));
}
for (BundlePath path : GhidraScriptUtil.getDefaultScriptBundles()) {
if (path.isEnabled() && !paths.contains(path)) {
paths.add(path);
}
paths.add(new ResourceFile(path));
}
paths.addAll(GhidraScriptUtil.getSystemScriptPaths());
paths.add(0, GhidraScriptUtil.getUserScriptDirectory());
}
GhidraScriptUtil.setScriptBundlePaths(paths);

View File

@ -48,7 +48,6 @@ import generic.jar.ResourceFile;
import generic.test.TestUtils;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.app.plugin.core.script.osgi.BundleStatusProvider;
import ghidra.app.script.*;
import ghidra.app.services.ConsoleService;
@ -979,10 +978,10 @@ public abstract class AbstractGhidraScriptMgrPluginTest
// destroy any NewScriptxxx files...and Temp ones too
BundleStatusProvider bundleStatusProvider =
(BundleStatusProvider) TestUtils.getInstanceField("bundlePathManager", provider);
List<BundlePath> paths = bundleStatusProvider.getModel().getPaths();
for (BundlePath path : paths) {
File file = path.getPath().getFile(false);
(BundleStatusProvider) TestUtils.getInstanceField("bundleStatusProvider", provider);
List<ResourceFile> paths = bundleStatusProvider.getModel().getPaths();
for (ResourceFile path : paths) {
File file = path.getFile(false);
File[] listFiles = file.listFiles();
if (listFiles == null) {
continue;

View File

@ -19,9 +19,7 @@ import static org.junit.Assert.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.swing.*;
@ -32,7 +30,6 @@ import docking.action.DockingActionIf;
import docking.widgets.filter.FilterTextField;
import docking.widgets.list.ListPanel;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.app.plugin.core.script.osgi.BundleStatusProvider;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.script.JavaScriptProvider;
@ -286,13 +283,11 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
final BundleStatusProvider bundleStatusProvider = waitForComponentProvider(BundleStatusProvider.class);
final File dir = new File(getTestDirectoryPath() + "/test_scripts");
dir.mkdirs();
final ResourceFile dir = new ResourceFile(getTestDirectoryPath() + "/test_scripts");
dir.getFile(false).mkdirs();
SwingUtilities.invokeLater(() -> {
List<BundlePath> paths = bundleStatusProvider.getModel().getPaths();
paths.add(0, new BundlePath(dir));
bundleStatusProvider.getModel().setPaths(paths);
bundleStatusProvider.getModel().insertPathForTesting(0, dir.getAbsolutePath());
});
waitForSwing();
@ -333,7 +328,7 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
assertTrue(newScript.exists());
assertNotNull(newScript);
assertEquals(dir, newScript.getParentFile().getFile(false));
assertEquals(dir.getAbsolutePath(), newScript.getParentFile().getFile(false).getAbsolutePath());
newScript.delete();
dir.delete();

View File

@ -28,7 +28,6 @@ import docking.widgets.tree.GTreeNode;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
import ghidra.app.plugin.core.script.*;
import ghidra.app.plugin.core.script.osgi.BundlePath;
import ghidra.app.plugin.core.script.osgi.BundleStatusProvider;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.services.ConsoleService;
@ -50,10 +49,10 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
performAction("New", "GhidraScriptMgrPlugin", false);
JDialog d = waitForJDialog(null, "New Script: Type", 5000);
JDialog d = waitForJDialog("New Script: Type");
pressButtonByText(d, "OK");
d = waitForJDialog(null, "New Script", 5000);
d = waitForJDialog("New Script");
pressButtonByText(d, "OK");
captureIsolatedProvider(GhidraScriptEditorComponentProvider.class, 597, 600);
@ -111,13 +110,13 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
@Test
public void testScript_Dirs() throws Exception {
List<BundlePath> paths = new ArrayList<>();
paths.add(new BundlePath("$USER_HOME/ghidra_scripts"));
paths.add(new BundlePath("$GHIDRA_HOME/Features/Base/ghidra_scripts"));
paths.add(new BundlePath("/User/defined/invalid/directory"));
List<String> paths = new ArrayList<>();
paths.add("$USER_HOME/ghidra_scripts");
paths.add("$GHIDRA_HOME/Features/Base/ghidra_scripts");
paths.add("/User/defined/invalid/directory");
BundleStatusProvider bundleStatusProvider = showProvider(BundleStatusProvider.class);
bundleStatusProvider.getModel().setPaths(paths);
bundleStatusProvider.getModel().setPathsForTesting(paths);
waitForComponentProvider(BundleStatusProvider.class);
captureComponent(bundleStatusProvider.getComponent());