diff --git a/Ghidra/Features/BSim/certification.manifest b/Ghidra/Features/BSim/certification.manifest index 12c5480cf9..137ac8dcbf 100755 --- a/Ghidra/Features/BSim/certification.manifest +++ b/Ghidra/Features/BSim/certification.manifest @@ -42,6 +42,8 @@ src/main/help/help/topics/BSimSearchPlugin/images/BSimSearchDialog.png||GHIDRA|| src/main/help/help/topics/BSimSearchPlugin/images/ManageServersDialog.png||GHIDRA||||END| src/main/resources/bsim.log4j.xml||GHIDRA||||END| src/main/resources/images/checkmark_yellow.gif||GHIDRA||||END| +src/main/resources/images/connect.png||FAMFAMFAM Icons - CC 2.5||||END| +src/main/resources/images/disconnect.png||FAMFAMFAM Icons - CC 2.5||||END| src/main/resources/images/flag_green.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/resources/images/preferences-desktop-user-password.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| src/main/resources/images/preferences-web-browser-shortcuts-32.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END| diff --git a/Ghidra/Features/BSim/data/bsim.theme.properties b/Ghidra/Features/BSim/data/bsim.theme.properties index c0efedbea0..497af941d8 100644 --- a/Ghidra/Features/BSim/data/bsim.theme.properties +++ b/Ghidra/Features/BSim/data/bsim.theme.properties @@ -14,4 +14,7 @@ icon.bsim.results.status.ignored = checkmark_yellow.gif icon.bsim.functions.table = FunctionScope.gif +icon.bsim.connected = connect.png +icon.bsim.disconnected = disconnect.png + [Dark Defaults] diff --git a/Ghidra/Features/BSim/ghidra_scripts/AddProgramToH2BSimDatabaseScript.java b/Ghidra/Features/BSim/ghidra_scripts/AddProgramToH2BSimDatabaseScript.java index 648768ce7c..adbe50e621 100644 --- a/Ghidra/Features/BSim/ghidra_scripts/AddProgramToH2BSimDatabaseScript.java +++ b/Ghidra/Features/BSim/ghidra_scripts/AddProgramToH2BSimDatabaseScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -71,22 +71,17 @@ public class AddProgramToH2BSimDatabaseScript extends GhidraScript { askValues("Select Database File", null, values); File h2DbFile = values.getFile(DATABASE); + BSimServerInfo serverInfo = + new BSimServerInfo(DBType.file, null, 0, h2DbFile.getAbsolutePath()); - FunctionDatabase h2Database = null; - try { - BSimServerInfo serverInfo = - new BSimServerInfo(DBType.file, null, 0, h2DbFile.getAbsolutePath()); - h2Database = BSimClientFactory.buildClient(serverInfo, false); - BSimH2FileDataSource bds = - BSimH2FileDBConnectionManager.getDataSourceIfExists(h2Database.getServerInfo()); - if (bds == null) { - popup(h2DbFile.getAbsolutePath() + " is not an H2 database file"); - return; - } - if (bds.getActiveConnections() > 0) { - popup("There is an existing connection to the database."); - return; - } + BSimH2FileDataSource existingBDS = + BSimH2FileDBConnectionManager.getDataSourceIfExists(serverInfo); + if (existingBDS != null && existingBDS.getActiveConnections() > 0) { + popup("There is an existing connection to the database."); + return; + } + + try (FunctionDatabase h2Database = BSimClientFactory.buildClient(serverInfo, false)) { h2Database.initialize(); DatabaseInformation dbInfo = h2Database.getInfo(); @@ -169,11 +164,13 @@ public class AddProgramToH2BSimDatabaseScript extends GhidraScript { } finally { - if (h2Database != null) { - h2Database.close(); + if (existingBDS == null) { + // Dispose database source if it did not previously exist BSimH2FileDataSource bds = - BSimH2FileDBConnectionManager.getDataSourceIfExists(h2Database.getServerInfo()); - bds.dispose(); + BSimH2FileDBConnectionManager.getDataSourceIfExists(serverInfo); + if (bds != null) { + bds.dispose(); + } } } } diff --git a/Ghidra/Features/BSim/ghidra_scripts/CreateH2BSimDatabaseScript.java b/Ghidra/Features/BSim/ghidra_scripts/CreateH2BSimDatabaseScript.java index ef332f6d64..f30b1c65d5 100644 --- a/Ghidra/Features/BSim/ghidra_scripts/CreateH2BSimDatabaseScript.java +++ b/Ghidra/Features/BSim/ghidra_scripts/CreateH2BSimDatabaseScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,7 +31,6 @@ import ghidra.features.bsim.query.file.BSimH2FileDBConnectionManager; import ghidra.features.bsim.query.file.BSimH2FileDBConnectionManager.BSimH2FileDataSource; import ghidra.features.bsim.query.protocol.*; import ghidra.util.MessageType; -import ghidra.util.Msg; public class CreateH2BSimDatabaseScript extends GhidraScript { private static final String NAME = "Database Name"; @@ -80,31 +79,27 @@ public class CreateH2BSimDatabaseScript extends GhidraScript { askValues("Enter Database Parameters", "Enter values required to create a new BSim H2 database.", values); - FunctionDatabase h2Database = null; - try { - String databaseName = values.getString(NAME); - File dbDir = values.getFile(DIRECTORY); - String template = values.getChoice(DATABASE_TEMPLATE); - String functionTagsCSV = values.getString(FUNCTION_TAGS); - List tags = parseCSV(functionTagsCSV); + String databaseName = values.getString(NAME); + File dbDir = values.getFile(DIRECTORY); + String template = values.getChoice(DATABASE_TEMPLATE); + String functionTagsCSV = values.getString(FUNCTION_TAGS); + List tags = parseCSV(functionTagsCSV); - String exeCatCSV = values.getString(EXECUTABLE_CATEGORIES); - List cats = parseCSV(exeCatCSV); + String exeCatCSV = values.getString(EXECUTABLE_CATEGORIES); + List cats = parseCSV(exeCatCSV); - File dbFile = new File(dbDir, databaseName); + File dbFile = new File(dbDir, databaseName); + BSimServerInfo serverInfo = + new BSimServerInfo(DBType.file, null, 0, dbFile.getAbsolutePath()); - BSimServerInfo serverInfo = - new BSimServerInfo(DBType.file, null, 0, dbFile.getAbsolutePath()); - h2Database = BSimClientFactory.buildClient(serverInfo, false); - BSimH2FileDataSource bds = - BSimH2FileDBConnectionManager.getDataSourceIfExists(h2Database.getServerInfo()); - if (bds.getActiveConnections() > 0) { - //if this happens, there is a connection to the database but the - //database file was deleted - Msg.showError(this, null, "Connection Error", - "There is an existing connection to the database!"); - return; - } + BSimH2FileDataSource existingBDS = + BSimH2FileDBConnectionManager.getDataSourceIfExists(serverInfo); + if (existingBDS != null && existingBDS.getActiveConnections() > 0) { + popup("There is an existing connection to the database."); + return; + } + + try (FunctionDatabase h2Database = BSimClientFactory.buildClient(serverInfo, false)) { CreateDatabase command = new CreateDatabase(); command.info = new DatabaseInformation(); @@ -140,11 +135,13 @@ public class CreateH2BSimDatabaseScript extends GhidraScript { popup("Database " + values.getString(NAME) + " created successfully!"); } finally { - if (h2Database != null) { - h2Database.close(); + if (existingBDS == null) { + // Dispose database source if it did not previously exist BSimH2FileDataSource bds = - BSimH2FileDBConnectionManager.getDataSourceIfExists(h2Database.getServerInfo()); - bds.dispose(); + BSimH2FileDBConnectionManager.getDataSourceIfExists(serverInfo); + if (bds != null) { + bds.dispose(); + } } } diff --git a/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/BSimSearch.html b/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/BSimSearch.html index bdbfd102af..ffecfeb75d 100644 --- a/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/BSimSearch.html +++ b/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/BSimSearch.html @@ -57,18 +57,24 @@ entry shows a name for the BSim database, its type (postgres, elastic, or file), a host ip and port (if applicable), and finally the number of active connections.

-

There are three primary actions for this dialog:

+

There are four primary actions for this dialog:

    -
  •  Add a new database/server definition - a - Define Server Dialog will be shown.
  • +
  •  Add a new BSim database/server definition - an + Add BSim Server Dialog will be shown.
  •  Delete a database/server definition - The - selected entry will be deleted.
  • + selected entry will be deleted. This action will force an immediate disconnect for an + active/idle connection and should be used with care. + +
  •   + Connect or disconnect an inactive database/server connection. This action is not supported + by Elastic database servers.
  •  Change password - A change password - dialog will appear for the selected entry
  • + dialog will appear for the selected database server. Action is disabled for databases + which do not use a password (e.g., Local H2 File database).

Defining a new BSim server/database

diff --git a/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/images/ManageServersDialog.png b/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/images/ManageServersDialog.png index abe6af8494..59f713dc8c 100644 Binary files a/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/images/ManageServersDialog.png and b/Ghidra/Features/BSim/src/main/help/help/topics/BSimSearchPlugin/images/ManageServersDialog.png differ diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimSearchPlugin.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimSearchPlugin.java index 1b47e7aa23..0abaed6e10 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimSearchPlugin.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimSearchPlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -72,7 +72,7 @@ public class BSimSearchPlugin extends ProgramPlugin { private Set searchResultsProviders = new HashSet<>(); private Set overviewProviders = new HashSet<>(); - private BSimServerManager serverManager = new BSimServerManager(); + private BSimServerManager serverManager = BSimServerManager.getBSimServerManager(); private BSimSearchService searchService; private BSimServerCache lastUsedServerCache = null; diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerManager.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimServerManager.java similarity index 65% rename from Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerManager.java rename to Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimServerManager.java index 9e1559af07..df21cbbbfb 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerManager.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/BSimServerManager.java @@ -4,25 +4,25 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.features.bsim.gui.search.dialog; +package ghidra.features.bsim.gui; import java.io.File; import java.io.IOException; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; -import ghidra.features.bsim.query.BSimPostgresDBConnectionManager; +import ghidra.features.bsim.gui.search.dialog.BSimServerManagerListener; +import ghidra.features.bsim.query.*; import ghidra.features.bsim.query.BSimPostgresDBConnectionManager.BSimPostgresDataSource; -import ghidra.features.bsim.query.BSimServerInfo; import ghidra.features.bsim.query.BSimServerInfo.DBType; import ghidra.features.bsim.query.file.BSimH2FileDBConnectionManager; import ghidra.features.bsim.query.file.BSimH2FileDBConnectionManager.BSimH2FileDataSource; @@ -36,12 +36,24 @@ import ghidra.util.Swing; * Managers BSim database server definitions and connections */ public class BSimServerManager { - // TODO: Do not allow removal of active server. Dispose data source when removed. + + private static BSimServerManager instance; + + /** + * Get static singleton instance for BSimServerManager + * @return BSimServerManager instance + */ + static synchronized BSimServerManager getBSimServerManager() { + if (instance == null) { + instance = new BSimServerManager(); + } + return instance; + } private Set serverInfos = new HashSet<>(); private List listeners = new CopyOnWriteArrayList<>(); - public BSimServerManager() { + private BSimServerManager() { List files = Application.getUserSettingsFiles("bsim", ".server.properties"); for (File file : files) { BSimServerInfo info = readBsimServerInfoFile(file); @@ -51,6 +63,10 @@ public class BSimServerManager { } } + /** + * Get list of defined servers. Method must be invoked from swing thread only. + * @return list of defined servers + */ public Set getServerInfos() { return new HashSet<>(serverInfos); } @@ -108,6 +124,10 @@ public class BSimServerManager { return serverFile.delete(); } + /** + * Add server to list. Method must be invoked from swing thread only. + * @param newServerInfo new BSim DB server + */ public void addServer(BSimServerInfo newServerInfo) { if (saveBSimServerInfo(newServerInfo)) { serverInfos.add(newServerInfo); @@ -115,28 +135,42 @@ public class BSimServerManager { } } - public boolean removeServer(BSimServerInfo info, boolean force) { + private static boolean disposeServer(BSimServerInfo info, boolean force) { DBType dbType = info.getDBType(); if (dbType == DBType.file) { - BSimH2FileDataSource ds = BSimH2FileDBConnectionManager.getDataSource(info); - int active = ds.getActiveConnections(); - if (active != 0) { - if (!force) { + BSimH2FileDataSource ds = BSimH2FileDBConnectionManager.getDataSourceIfExists(info); + if (ds != null) { + int active = ds.getActiveConnections(); + if (active != 0 && !force) { return false; } ds.dispose(); } } else if (dbType == DBType.postgres) { - BSimPostgresDataSource ds = BSimPostgresDBConnectionManager.getDataSource(info); - int active = ds.getActiveConnections(); - if (active != 0) { - if (!force) { + BSimPostgresDataSource ds = BSimPostgresDBConnectionManager.getDataSourceIfExists(info); + if (ds != null) { + int active = ds.getActiveConnections(); + if (active != 0 && !force) { return false; } ds.dispose(); } } + return true; + } + + /** + * Remove BSim DB server from list. Method must be invoked from swing thread only. + * Specified server datasource will be dispose unless it is active or force is true. + * @param info BSim DB server to be removed + * @param force true if server datasource should be disposed even when active. + * @return true if server disposed and removed from list + */ + public boolean removeServer(BSimServerInfo info, boolean force) { + if (!disposeServer(info, force)) { + return false; + } if (serverInfos.remove(info)) { removeServerFileFromSettings(info); notifyServerListChanged(); @@ -160,26 +194,38 @@ public class BSimServerManager { }); } - public static int getActiveConnections(BSimServerInfo serverInfo) { + /** + * Convenience method to get existing BSim JDBC datasource + * @param serverInfo BSim DB server info + * @return BSim DB datasource or null if not instantiated or server does not support a + * {@link BSimJDBCDataSource}. + */ + public static BSimJDBCDataSource getDataSourceIfExists(BSimServerInfo serverInfo) { switch (serverInfo.getDBType()) { case postgres: - BSimPostgresDataSource postgresDs = - BSimPostgresDBConnectionManager.getDataSourceIfExists(serverInfo); - if (postgresDs != null) { - return postgresDs.getActiveConnections(); - } - break; + return BSimPostgresDBConnectionManager.getDataSourceIfExists(serverInfo); case file: - BSimH2FileDataSource h2FileDs = - BSimH2FileDBConnectionManager.getDataSourceIfExists(serverInfo); - if (h2FileDs != null) { - return h2FileDs.getActiveConnections(); - } - break; + return BSimH2FileDBConnectionManager.getDataSourceIfExists(serverInfo); default: - break; + return null; + } + } + + /** + * Convenience method to get a new or existing BSim JDBC datasource + * @param serverInfo BSim DB server info + * @return BSim DB datasource or null if server does not support a + * {@link BSimJDBCDataSource}. + */ + public static BSimJDBCDataSource getDataSource(BSimServerInfo serverInfo) { + switch (serverInfo.getDBType()) { + case postgres: + return BSimPostgresDBConnectionManager.getDataSource(serverInfo); + case file: + return BSimH2FileDBConnectionManager.getDataSource(serverInfo); + default: + return null; } - return -1; } } diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/AbstractBSimSearchDialog.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/AbstractBSimSearchDialog.java index ff999ee822..9ed6829018 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/AbstractBSimSearchDialog.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/AbstractBSimSearchDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,6 +30,7 @@ import docking.widgets.EmptyBorderButton; import docking.widgets.combobox.GComboBox; import docking.widgets.textfield.FloatingPointTextField; import generic.theme.Gui; +import ghidra.features.bsim.gui.BSimServerManager; import ghidra.features.bsim.query.BSimServerInfo; import ghidra.features.bsim.query.description.DatabaseInformation; import ghidra.features.bsim.query.facade.QueryDatabaseException; diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimOverviewDialog.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimOverviewDialog.java index 2b44b19bbb..a4afa85649 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimOverviewDialog.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimOverviewDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,6 +16,7 @@ package ghidra.features.bsim.gui.search.dialog; import ghidra.features.bsim.gui.BSimSearchPlugin; +import ghidra.features.bsim.gui.BSimServerManager; import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.listing.Program; import ghidra.util.HelpLocation; diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialog.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialog.java index d0df4f8bba..9b9318638e 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialog.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -30,6 +30,7 @@ import docking.widgets.textfield.IntegerTextField; import generic.theme.GIcon; import ghidra.app.services.GoToService; import ghidra.features.bsim.gui.BSimSearchPlugin; +import ghidra.features.bsim.gui.BSimServerManager; import ghidra.features.bsim.gui.filters.BSimFilterType; import ghidra.features.bsim.query.description.DatabaseInformation; import ghidra.framework.plugintool.PluginTool; diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerDialog.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerDialog.java index a2c90f1c67..bec9aeee2e 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerDialog.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,21 +16,25 @@ package ghidra.features.bsim.gui.search.dialog; import java.awt.BorderLayout; +import java.sql.Connection; +import java.sql.SQLException; import javax.swing.*; import org.bouncycastle.util.Arrays; -import docking.DialogComponentProvider; -import docking.DockingWindowManager; -import docking.action.DockingAction; +import docking.*; +import docking.action.*; import docking.action.builder.ActionBuilder; +import docking.action.builder.ToggleActionBuilder; import docking.widgets.OptionDialog; import docking.widgets.PasswordChangeDialog; import docking.widgets.table.GFilterTable; import docking.widgets.table.GTable; import generic.theme.GIcon; +import ghidra.features.bsim.gui.BSimServerManager; import ghidra.features.bsim.query.*; +import ghidra.features.bsim.query.BSimServerInfo.DBType; import ghidra.features.bsim.query.FunctionDatabase.Error; import ghidra.features.bsim.query.FunctionDatabase.ErrorCategory; import ghidra.framework.plugintool.PluginTool; @@ -42,15 +46,14 @@ import resources.Icons; */ public class BSimServerDialog extends DialogComponentProvider { - // TODO: Add connected status indicator (not sure how this relates to elastic case which will likely have a Session concept) - // TODO: Add "Disconnect" action (only works when active connections is 0; does not apply to elastic) - private PluginTool tool; private BSimServerManager serverManager; private BSimServerTableModel serverTableModel; - private GFilterTable filterTable; + private GFilterTable serverTable; private BSimServerInfo lastAdded = null; + private ToggleDockingAction dbConnectionAction; + public BSimServerDialog(PluginTool tool, BSimServerManager serverManager) { super("BSim Server Manager"); this.tool = tool; @@ -60,7 +63,7 @@ public class BSimServerDialog extends DialogComponentProvider { addDismissButton(); setPreferredSize(600, 400); notifyContextChanged(); // kick actions to initialized enabled state - setHelpLocation(new HelpLocation("BSimSearchPlugin","BSim_Servers_Dialog" )); + setHelpLocation(new HelpLocation("BSimSearchPlugin", "BSim_Servers_Dialog")); } @Override @@ -70,34 +73,101 @@ public class BSimServerDialog extends DialogComponentProvider { } private void createToolbarActions() { - HelpLocation help = new HelpLocation("BSimSearchPlugin","Manage_Servers_Actions" ); - + HelpLocation help = new HelpLocation("BSimSearchPlugin", "Manage_Servers_Actions"); + DockingAction addServerAction = - new ActionBuilder("Add Server", "Dialog").toolBarIcon(Icons.ADD_ICON) - .helpLocation(help) - .onAction(e -> defineBsimServer()) - .build(); + new ActionBuilder("Add BSim Database", "Dialog").toolBarIcon(Icons.ADD_ICON) + .helpLocation(help) + .onAction(e -> defineBsimServer()) + .build(); addAction(addServerAction); DockingAction removeServerAction = - new ActionBuilder("Delete Server", "Dialog").toolBarIcon(Icons.DELETE_ICON) - .helpLocation(help) - .onAction(e -> deleteBsimServer()) - .enabledWhen(c -> hasSelection()) - .build(); + new ActionBuilder("Delete BSim Database", "Dialog").toolBarIcon(Icons.DELETE_ICON) + .helpLocation(help) + .onAction(e -> deleteBsimServer()) + .enabledWhen(c -> hasSelection()) + .build(); addAction(removeServerAction); - DockingAction changePasswordAction = new ActionBuilder("Change User Password", "Dialog") - .helpLocation(help) - .toolBarIcon(new GIcon("icon.bsim.change.password")) - .onAction(e -> changePassword()) - .enabledWhen(c -> hasSelection()) - .build(); + dbConnectionAction = + new ToggleActionBuilder("Toggle Database Connection", "Dialog").helpLocation(help) + .toolBarIcon(new GIcon("icon.bsim.disconnected")) + .onAction(e -> toggleSelectedJDBCDataSourceConnection()) + .enabledWhen(c -> isNonActiveJDBCDataSourceSelected(c)) + .build(); + addAction(dbConnectionAction); + + DockingAction changePasswordAction = + new ActionBuilder("Change User Password", "Dialog").helpLocation(help) + .toolBarIcon(new GIcon("icon.bsim.change.password")) + .onAction(e -> changePassword()) + .enabledWhen(c -> canChangePassword()) + .build(); addAction(changePasswordAction); } + private void toggleSelectedJDBCDataSourceConnection() { + + BSimServerInfo serverInfo = serverTable.getSelectedRowObject(); + if (serverInfo == null || serverInfo.getDBType() == DBType.elastic) { + return; + } + + BSimJDBCDataSource dataSource = BSimServerManager.getDataSourceIfExists(serverInfo); + if (dataSource == null) { + // connect + dataSource = BSimServerManager.getDataSource(serverInfo); + try (Connection connection = dataSource.getConnection()) { + // do nothing + } + catch (SQLException e) { + Msg.showError(this, rootPanel, "BSim Connection Failure", e.getMessage()); + } + } + else { + dataSource.dispose(); + } + serverTableModel.fireTableDataChanged(); + notifyContextChanged(); + } + + private boolean isNonActiveJDBCDataSourceSelected(ActionContext c) { + BSimServerInfo serverInfo = serverTable.getSelectedRowObject(); + if (serverInfo == null) { + return false; + } + + // TODO: May need connection listener on dataSource to facilitate GUI update, + // although modal dialog avoids the issue somewhat + + dbConnectionAction.setDescription(dbConnectionAction.getName()); + + ConnectionPoolStatus status = serverTableModel.getConnectionPoolStatus(serverInfo); + if (status.isActive) { + + // Show connected icon + dbConnectionAction + .setToolBarData(new ToolBarData(new GIcon("icon.bsim.connected"), null)); + dbConnectionAction.setSelected(true); + dbConnectionAction.setDescription("Disconnect idle BSim Database connection"); + + // disconnect permitted when no active connections + return status.activeCount == 0; + } + + // Show disconnected icon (elastic always shown as disconnected) + dbConnectionAction + .setToolBarData(new ToolBarData(new GIcon("icon.bsim.disconnected"), null)); + dbConnectionAction.setSelected(false); + dbConnectionAction.setDescription("Connect BSim Database"); + + // Action never enabled for elastic DB (i.e., does not use pooled JDBC data source) + return serverInfo.getDBType() != DBType.elastic; + } + private void changePassword() { - BSimServerInfo serverInfo = filterTable.getSelectedRowObject(); + BSimServerInfo serverInfo = serverTable.getSelectedRowObject(); if (serverInfo == null) { return; } @@ -141,8 +211,13 @@ public class BSimServerDialog extends DialogComponentProvider { } } + private boolean canChangePassword() { + BSimServerInfo serverInfo = serverTable.getSelectedRowObject(); + return serverInfo != null && serverInfo.getDBType() != DBType.file; + } + private void deleteBsimServer() { - BSimServerInfo selected = filterTable.getSelectedRowObject(); + BSimServerInfo selected = serverTable.getSelectedRowObject(); if (selected != null) { int answer = OptionDialog.showYesNoDialog(getComponent(), "Delete Server Configuration?", @@ -152,7 +227,7 @@ public class BSimServerDialog extends DialogComponentProvider { answer = OptionDialog.showOptionDialogWithCancelAsDefaultButton(getComponent(), "Active Server Configuration!", "Database connections are still active!\n" + - "Are you sure you want to delete server?", + "Are you sure you want to terminate connections and delete server?", "Yes", OptionDialog.WARNING_MESSAGE); if (answer == OptionDialog.YES_OPTION) { serverManager.removeServer(selected, true); @@ -169,7 +244,7 @@ public class BSimServerDialog extends DialogComponentProvider { if (newServerInfo != null) { serverManager.addServer(newServerInfo); lastAdded = newServerInfo; - Swing.runLater(() -> filterTable.setSelectedRowObject(newServerInfo)); + Swing.runLater(() -> serverTable.setSelectedRowObject(newServerInfo)); } } @@ -178,11 +253,11 @@ public class BSimServerDialog extends DialogComponentProvider { panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); serverTableModel = new BSimServerTableModel(serverManager); - filterTable = new GFilterTable<>(serverTableModel); - GTable table = filterTable.getTable(); + serverTable = new GFilterTable<>(serverTableModel); + GTable table = serverTable.getTable(); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getSelectionModel().addListSelectionListener(e -> notifyContextChanged()); - panel.add(filterTable, BorderLayout.CENTER); + panel.add(serverTable, BorderLayout.CENTER); if (serverTableModel.getRowCount() > 0) { table.setRowSelectionInterval(0, 0); @@ -192,7 +267,7 @@ public class BSimServerDialog extends DialogComponentProvider { } private boolean hasSelection() { - return filterTable.getSelectedRowObject() != null; + return serverTable.getSelectedRowObject() != null; } public BSimServerInfo getLastAdded() { diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerTableModel.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerTableModel.java index b26aeea431..8bed9cc704 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerTableModel.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/BSimServerTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,27 +16,33 @@ package ghidra.features.bsim.gui.search.dialog; import java.awt.Component; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import javax.swing.Icon; import javax.swing.JLabel; import docking.widgets.table.*; import ghidra.docking.settings.Settings; +import ghidra.features.bsim.gui.BSimServerManager; import ghidra.features.bsim.query.BSimServerInfo; import ghidra.features.bsim.query.BSimServerInfo.DBType; import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProviderStub; -import ghidra.program.model.listing.Program; import ghidra.util.table.column.AbstractGColumnRenderer; import ghidra.util.table.column.GColumnRenderer; -import ghidra.util.table.field.AbstractProgramBasedDynamicTableColumn; /** - * Table model for BSim database server definitions + * Table model for BSim database server definitions. + * + * NOTE: This implementation assumes modal dialog use and non-changing connection state + * while instance is in-use. This was done to avoid adding a conection listener which could + * introduce excessive overhead into the connection pool use. */ public class BSimServerTableModel extends GDynamicColumnTableModel { + private List servers; + private Map statusCache = new HashMap<>(); + private BSimServerManager serverManager; private BSimServerManagerListener listener = new BSimServerManagerListener() { @Override @@ -63,8 +69,18 @@ public class BSimServerTableModel extends GDynamicColumnTableModel new ConnectionPoolStatus(s)); } @Override @@ -74,7 +90,7 @@ public class BSimServerTableModel extends GDynamicColumnTableModel { + extends AbstractDynamicTableColumn { private GColumnRenderer renderer = new AbstractGColumnRenderer<>() { @Override public Component getTableCellRendererComponent(GTableCellRenderingData data) { @@ -112,11 +128,8 @@ public class BSimServerTableModel extends GDynamicColumnTableModel { + private static class TypeColumn + extends AbstractDynamicTableColumn { + + @Override + public String getColumnName() { + return "Type"; + } + + @Override + public String getValue(BSimServerInfo serverInfo, Settings settings, Object data, + ServiceProvider provider) throws IllegalArgumentException { + + return serverInfo.getDBType().toString(); + } + + @Override + public int getColumnPreferredWidth() { + return 80; + } + } + + private static class HostColumn + extends AbstractDynamicTableColumn { @Override public String getColumnName() { @@ -140,8 +174,8 @@ public class BSimServerTableModel extends GDynamicColumnTableModel { + private static class PortColumn + extends AbstractDynamicTableColumn { @Override public String getColumnName() { @@ -161,64 +195,78 @@ public class BSimServerTableModel extends GDynamicColumnTableModel { + private static class ConnectionStatusColumnRenderer + extends AbstractGColumnRenderer { + + private static final ConnectionStatusColumnRenderer INSTANCE = + new ConnectionStatusColumnRenderer(); @Override - public String getColumnName() { - return "Active Connections"; - } + public Component getTableCellRendererComponent(GTableCellRenderingData data) { - @Override - public Integer getValue(BSimServerInfo serverInfo, Settings settings, Program data, - ServiceProvider provider) throws IllegalArgumentException { - int activeConnections = BSimServerManager.getActiveConnections(serverInfo); - if (activeConnections < 0) { - return null; + JLabel c = (JLabel) super.getTableCellRendererComponent(data); + + ConnectionPoolStatus status = (ConnectionPoolStatus) data.getValue(); + + // NOTE: Custom column renderer has neem established with future use of + // status icon in mind (e.g., H2 mixed-mode server enabled) + + Icon icon = null; // NOTE: may need default filler icon + String text = null; + if (status.isActive) { + text = Integer.toString(status.activeCount) + " / " + + Integer.toString(status.idleCount); } - return activeConnections; + c.setText(text); + c.setIcon(icon); + return c; } @Override - public int getColumnPreferredWidth() { - return 80; + public String getFilterString(ConnectionPoolStatus t, Settings settings) { + return null; // Filtering not supported } + } - private class TypeColumn - extends AbstractProgramBasedDynamicTableColumn { + private class ConnectionStatusColumn + extends AbstractDynamicTableColumn { @Override public String getColumnName() { - return "Type"; + return "Active/Idle Connections"; } @Override - public String getValue(BSimServerInfo serverInfo, Settings settings, Program data, - ServiceProvider provider) throws IllegalArgumentException { - - return serverInfo.getDBType().toString(); + public ConnectionPoolStatus getValue(BSimServerInfo serverInfo, Settings settings, + Object data, ServiceProvider provider) throws IllegalArgumentException { + return getConnectionPoolStatus(serverInfo); } @Override public int getColumnPreferredWidth() { - return 80; + return 150; + } + + @Override + public GColumnRenderer getColumnRenderer() { + return ConnectionStatusColumnRenderer.INSTANCE; } } diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/ConnectionPoolStatus.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/ConnectionPoolStatus.java new file mode 100644 index 0000000000..dc9d7b2dfe --- /dev/null +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/ConnectionPoolStatus.java @@ -0,0 +1,44 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.features.bsim.gui.search.dialog; + +import ghidra.features.bsim.gui.BSimServerManager; +import ghidra.features.bsim.query.BSimJDBCDataSource; +import ghidra.features.bsim.query.BSimServerInfo; + +class ConnectionPoolStatus { + BSimServerInfo serverInfo; + + final boolean isActive; + final int activeCount; + final int idleCount; + + ConnectionPoolStatus(BSimServerInfo serverInfo) { + this.serverInfo = serverInfo; + + BSimJDBCDataSource dataSource = BSimServerManager.getDataSourceIfExists(serverInfo); + if (dataSource == null) { + isActive = false; + activeCount = 0; + idleCount = 0; + } + else { + isActive = true; + activeCount = dataSource.getActiveConnections(); + idleCount = dataSource.getIdleConnections(); + } + } +} diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/CreateBsimServerInfoDialog.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/CreateBsimServerInfoDialog.java index badb9638b2..f31c59cd4d 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/CreateBsimServerInfoDialog.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/gui/search/dialog/CreateBsimServerInfoDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -91,8 +91,7 @@ public class CreateBsimServerInfoDialog extends DialogComponentProvider { public boolean acceptServer(BSimServerInfo serverInfo) { // FIXME: Use task to correct dialog parenting issue caused by password prompt String errorMessage = null; - try { - FunctionDatabase database = BSimClientFactory.buildClient(serverInfo, true); + try (FunctionDatabase database = BSimClientFactory.buildClient(serverInfo, true)) { if (database.initialize()) { return true; } diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimClientFactory.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimClientFactory.java index 7b207d34ee..291e47316c 100755 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimClientFactory.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimClientFactory.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -118,7 +118,10 @@ public class BSimClientFactory { } /** - * Given the URL for a BSim server construct the appropriate BSim client object (implementing FunctionDatabase) + * Given the URL for a BSim server construct the appropriate BSim client object + * (implementing FunctionDatabase). Returned instance must be + * {@link FunctionDatabase#close() closed} when done using it to prevent depletion + * of database connections. * @param bsimServerInfo BSim server details * @param async true if database commits should be asynchronous * @return the database client diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimJDBCDataSource.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimJDBCDataSource.java index c0ca147904..5e82082b90 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimJDBCDataSource.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimJDBCDataSource.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -48,4 +48,15 @@ public interface BSimJDBCDataSource { */ int getActiveConnections(); + /** + * Get the number of idle connections in the associated connection pool + * @return number of idle connections + */ + int getIdleConnections(); + + /** + * Dispose pooled datasource. + */ + void dispose(); + } diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimPostgresDBConnectionManager.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimPostgresDBConnectionManager.java index c0dafe4370..70aea9aa2b 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimPostgresDBConnectionManager.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/BSimPostgresDBConnectionManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -41,7 +41,8 @@ public class BSimPostgresDBConnectionManager { private static HashMap dataSourceMap = new HashMap<>(); - public static BSimPostgresDataSource getDataSource(BSimServerInfo postgresServerInfo) { + public static synchronized BSimPostgresDataSource getDataSource( + BSimServerInfo postgresServerInfo) { if (postgresServerInfo.getDBType() != DBType.postgres) { throw new IllegalArgumentException("expected postgres server info"); } @@ -54,19 +55,20 @@ public class BSimPostgresDBConnectionManager { return getDataSource(new BSimServerInfo(postgresUrl)); } - public static BSimPostgresDataSource getDataSourceIfExists(BSimServerInfo serverInfo) { + public static synchronized BSimPostgresDataSource getDataSourceIfExists( + BSimServerInfo serverInfo) { return dataSourceMap.get(serverInfo); } - private static synchronized void remove(BSimServerInfo serverInfo) { + private static synchronized void remove(BSimServerInfo serverInfo, boolean force) { BSimPostgresDataSource ds = dataSourceMap.get(serverInfo); if (ds == null) { return; } int n = ds.bds.getNumActive(); - if (n != 0) { - System.out - .println("Unable to remove data source which has " + n + " active connections"); + if (n != 0 && !force) { + Msg.error(BSimPostgresDBConnectionManager.class, + "Unable to remove data source which has " + n + " active connections"); return; } ds.close(); @@ -113,8 +115,9 @@ public class BSimPostgresDBConnectionManager { bds.setUsername(userName); } + @Override public void dispose() { - remove(serverInfo); + remove(serverInfo, true); } private void close() { @@ -143,6 +146,11 @@ public class BSimPostgresDBConnectionManager { return bds.getNumActive(); } + @Override + public int getIdleConnections() { + return bds.getNumIdle(); + } + /** * Update password on {@link BasicDataSource} for use with future connect attempts. * Has no affect if username does not match username on data source. diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java index 4a85e5e54d..6998da5447 100755 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/FunctionDatabase.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -33,7 +33,9 @@ import ghidra.features.bsim.query.facade.SFOverviewInfo; import ghidra.features.bsim.query.facade.SFQueryInfo; import ghidra.features.bsim.query.protocol.*; import ghidra.framework.Application; +import ghidra.program.model.data.DataUtilities; import ghidra.util.Msg; +import ghidra.util.StringUtilities; public interface FunctionDatabase extends AutoCloseable { @@ -239,7 +241,15 @@ public interface FunctionDatabase extends AutoCloseable { if (res == 3) { throw new LSHException("Query signature data has no setting information"); } - throw new LSHException("Query signature data does not match database"); + throw new LSHException("Query signature data " + + getFormattedVersion(manage.getMajorVersion(), manage.getMinorVersion(), + manage.getSettings()) + + " does not match database " + + getFormattedVersion(info.major, info.minor, info.settings)); + } + + private static String getFormattedVersion(int maj, int min, int settings) { + return String.format("%d.%d:0x%02x", maj, min, settings); } public static boolean checkSettingsForInsert(DescriptionManager manage, @@ -262,8 +272,11 @@ public interface FunctionDatabase extends AutoCloseable { if (res == 3) { throw new LSHException("Trying to insert signature data with no setting information"); } - throw new LSHException( - "Trying to insert signature data with settings that don't match database"); + throw new LSHException("Trying to insert signature data " + + getFormattedVersion(manage.getMajorVersion(), manage.getMinorVersion(), + manage.getSettings()) + + " with settings that don't match database " + + getFormattedVersion(info.major, info.minor, info.settings)); } public static String constructFatalError(int flags, ExecutableRecord newrec, diff --git a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java index a5d13af20f..b7ad1d0629 100644 --- a/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java +++ b/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/file/BSimH2FileDBConnectionManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,6 +28,7 @@ import ghidra.features.bsim.query.*; import ghidra.features.bsim.query.BSimServerInfo.DBType; import ghidra.features.bsim.query.FunctionDatabase.ConnectionType; import ghidra.features.bsim.query.FunctionDatabase.Status; +import ghidra.util.Msg; public class BSimH2FileDBConnectionManager { @@ -44,7 +45,7 @@ public class BSimH2FileDBConnectionManager { * Get all H2 File DB data sorces which exist in the JVM. * @return all H2 File DB data sorces */ - public static Collection getAllDataSources() { + public static synchronized Collection getAllDataSources() { // Create copy to avoid potential concurrent modification return Collections.unmodifiableCollection(new ArrayList<>(dataSourceMap.values())); } @@ -57,7 +58,7 @@ public class BSimH2FileDBConnectionManager { * @throws IllegalArgumentException if {@code fileServerInfo} does not specify an * H2 File DB type. */ - public static BSimH2FileDataSource getDataSource(BSimServerInfo fileServerInfo) { + public static synchronized BSimH2FileDataSource getDataSource(BSimServerInfo fileServerInfo) { if (fileServerInfo.getDBType() != DBType.file) { throw new IllegalArgumentException("expected file info"); } @@ -79,26 +80,26 @@ public class BSimH2FileDBConnectionManager { * @return existing H2 File data source or null if server info does not correspond to an * H2 File or has not be established as an H2 File data source. */ - public static BSimH2FileDataSource getDataSourceIfExists(BSimServerInfo serverInfo) { + public static synchronized BSimH2FileDataSource getDataSourceIfExists( + BSimServerInfo serverInfo) { return dataSourceMap.get(serverInfo); } - private static synchronized void remove(BSimServerInfo serverInfo, boolean force) { + private static synchronized boolean remove(BSimServerInfo serverInfo, boolean force) { BSimH2FileDataSource ds = dataSourceMap.get(serverInfo); if (ds == null) { - return; + return true; } int n = ds.bds.getNumActive(); - if (n != 0) { - System.out - .println("Unable to remove data source which has " + n + " active connections"); - if (!force) { - return; - } + if (n != 0 && !force) { + Msg.error(BSimH2FileDBConnectionManager.class, + "Unable to remove data source which has " + n + " active connections"); + return false; } ds.close(); dataSourceMap.remove(serverInfo); BSimVectorStoreManager.remove(serverInfo); + return true; } /** @@ -123,20 +124,31 @@ public class BSimH2FileDBConnectionManager { return serverInfo; } + @Override public void dispose() { BSimH2FileDBConnectionManager.remove(serverInfo, true); } /** - * Delete the database files associated with this H2 File DB. When complete - * this data source will no longer be valid and should no tbe used. + * Delete the database files associated with this H2 File DB. This will fail immediately + * if active connections exist. Otherwise removal will be attempted and this data source + * will no longer be valid. + * @return true if DB sucessfully removed */ - public void delete() { - dispose(); + public synchronized boolean delete() { File dbf = new File(serverInfo.getDBName()); - // TODO: Should we check for lock on database - could be another process + if (getActiveConnections() != 0) { + Msg.error(this, "Failed to delete active database: " + dbf); + return false; + } + + dispose(); + + if (dbf.isFile()) { + return true; + } String name = dbf.getName(); int ix = name.lastIndexOf(BSimServerInfo.H2_FILE_EXTENSION); @@ -145,6 +157,13 @@ public class BSimH2FileDBConnectionManager { } DeleteDbFiles.execute(dbf.getParent(), name, true); + + if (!dbf.isFile()) { + return true; + } + + Msg.error(this, "Failed to delete database: " + dbf); + return false; } /** @@ -181,6 +200,11 @@ public class BSimH2FileDBConnectionManager { return bds.getNumActive(); } + @Override + public int getIdleConnections() { + return bds.getNumIdle(); + } + private String getH2FileUrl() { // Remove H2 db file extension if present diff --git a/Ghidra/Features/BSim/src/main/resources/images/connect.png b/Ghidra/Features/BSim/src/main/resources/images/connect.png new file mode 100644 index 0000000000..024138eb33 Binary files /dev/null and b/Ghidra/Features/BSim/src/main/resources/images/connect.png differ diff --git a/Ghidra/Features/BSim/src/main/resources/images/disconnect.png b/Ghidra/Features/BSim/src/main/resources/images/disconnect.png new file mode 100644 index 0000000000..b335cb11c4 Binary files /dev/null and b/Ghidra/Features/BSim/src/main/resources/images/disconnect.png differ diff --git a/Ghidra/Features/BSim/src/screen/java/help/screenshot/BSimSearchPluginScreenShots.java b/Ghidra/Features/BSim/src/screen/java/help/screenshot/BSimSearchPluginScreenShots.java index 98244f090c..c18470ef4a 100755 --- a/Ghidra/Features/BSim/src/screen/java/help/screenshot/BSimSearchPluginScreenShots.java +++ b/Ghidra/Features/BSim/src/screen/java/help/screenshot/BSimSearchPluginScreenShots.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,8 +23,7 @@ import org.junit.Test; import docking.DockingWindowManager; import docking.action.DockingActionIf; import ghidra.app.services.ProgramManager; -import ghidra.features.bsim.gui.BSimSearchPlugin; -import ghidra.features.bsim.gui.BSimSearchPluginTestHelper; +import ghidra.features.bsim.gui.*; import ghidra.features.bsim.gui.overview.BSimOverviewProvider; import ghidra.features.bsim.gui.overview.BSimOverviewTestHelper; import ghidra.features.bsim.gui.search.dialog.*; diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/BSimSearchPluginTestHelper.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/BSimSearchPluginTestHelper.java index c80999009e..9982e019ce 100644 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/BSimSearchPluginTestHelper.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/BSimSearchPluginTestHelper.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,7 +15,6 @@ */ package ghidra.features.bsim.gui; -import ghidra.features.bsim.gui.search.dialog.BSimServerManager; import ghidra.features.bsim.query.facade.SFQueryServiceFactory; public class BSimSearchPluginTestHelper { diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/QueryFilterTest.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/QueryFilterTest.java index 72ffd6a255..57a5a17fdd 100755 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/QueryFilterTest.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/QueryFilterTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -65,6 +65,7 @@ public class QueryFilterTest extends AbstractBSimPluginTest { assertEquals(SQL_TRUTH, sql); } + @Override protected void initializeTool() throws Exception { super.initializeTool(); goTo(FUN1_ADDR); @@ -80,7 +81,6 @@ public class QueryFilterTest extends AbstractBSimPluginTest { * * @param ids resolution IDs * @return the query string - * @throws SQLException if there is a problem creating the filter */ private String generateSQL(IDSQLResolution[] ids) { try { @@ -88,7 +88,8 @@ public class QueryFilterTest extends AbstractBSimPluginTest { BSimFilter filter = filterSet.getBSimFilter(); BSimSqlClause sql = SQLEffects.createFilter(filter, ids, null); return sql.whereClause().trim(); - } catch (SQLException e) { + } + catch (SQLException e) { throw new AssertException(e); } } diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/overview/BSimOverviewTestHelper.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/overview/BSimOverviewTestHelper.java index 7be8bb04ff..36c18c254d 100644 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/overview/BSimOverviewTestHelper.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/overview/BSimOverviewTestHelper.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,8 +15,7 @@ */ package ghidra.features.bsim.gui.overview; -import ghidra.features.bsim.gui.BSimSearchPlugin; -import ghidra.features.bsim.gui.BSimSearchPluginTestHelper; +import ghidra.features.bsim.gui.*; import ghidra.features.bsim.gui.search.dialog.*; import ghidra.features.bsim.query.BSimServerInfo; import ghidra.features.bsim.query.FunctionDatabase; diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimFilterPanelTest.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimFilterPanelTest.java index 98cf419d50..db9127b523 100755 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimFilterPanelTest.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimFilterPanelTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ package ghidra.features.bsim.gui.search.dialog; import static org.junit.Assert.*; import java.sql.SQLException; -import java.util.*; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,7 +30,6 @@ import ghidra.features.bsim.query.SQLFunctionDatabase; import ghidra.features.bsim.query.client.*; import ghidra.features.bsim.query.facade.FunctionDatabaseTestDouble; import ghidra.features.bsim.query.protocol.BSimFilter; -import ghidra.program.database.symbol.FunctionSymbol; /** * Tests the filtering components of BSim accessible from the UI. This will cover the @@ -44,9 +43,9 @@ import ghidra.program.database.symbol.FunctionSymbol; */ public class BSimFilterPanelTest extends AbstractBSimPluginTest { - private Set selectedFunctions = new HashSet<>(); private BSimFilterPanel filterPanel; + @Override @Before public void setUp() throws Exception { super.setUp(); @@ -57,6 +56,7 @@ public class BSimFilterPanelTest extends AbstractBSimPluginTest { filterPanel = BSimSearchDialogTestHelper.getFilterPanel(searchDialog); } + @Override @After public void tearDown() throws Exception { close(searchDialog); diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialogTestHelper.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialogTestHelper.java index 4792197296..ac1cb0ef01 100644 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialogTestHelper.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/gui/search/dialog/BSimSearchDialogTestHelper.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,8 +17,7 @@ package ghidra.features.bsim.gui.search.dialog; import java.util.Set; -import ghidra.features.bsim.gui.BSimSearchPlugin; -import ghidra.features.bsim.gui.BSimSearchPluginTestHelper; +import ghidra.features.bsim.gui.*; import ghidra.features.bsim.query.BSimServerInfo; import ghidra.features.bsim.query.FunctionDatabase; import ghidra.features.bsim.query.facade.TestBSimServerInfo; diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/query/inmemory/BSimH2DatabaseManagerTest.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/file/BSimH2DatabaseManagerTest.java similarity index 98% rename from Ghidra/Features/BSim/src/test.slow/java/ghidra/query/inmemory/BSimH2DatabaseManagerTest.java rename to Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/file/BSimH2DatabaseManagerTest.java index 3878d4caa6..f3dd96c173 100644 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/query/inmemory/BSimH2DatabaseManagerTest.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/file/BSimH2DatabaseManagerTest.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.query.inmemory; +package ghidra.features.bsim.query.file; import static org.junit.Assert.*; @@ -26,7 +26,6 @@ import ghidra.features.bsim.query.*; import ghidra.features.bsim.query.BSimServerInfo.DBType; import ghidra.features.bsim.query.FunctionDatabase.Error; import ghidra.features.bsim.query.description.DatabaseInformation; -import ghidra.features.bsim.query.file.BSimH2FileDBConnectionManager; import ghidra.features.bsim.query.file.BSimH2FileDBConnectionManager.BSimH2FileDataSource; import ghidra.features.bsim.query.protocol.CreateDatabase; import ghidra.features.bsim.query.protocol.ResponseInfo; @@ -50,7 +49,7 @@ public class BSimH2DatabaseManagerTest extends AbstractGhidraHeadedIntegrationTe @After public void tearDown() { - //cleanup(); + cleanup(); } private File getTempDbDir() { @@ -77,7 +76,7 @@ public class BSimH2DatabaseManagerTest extends AbstractGhidraHeadedIntegrationTe } private BSimServerInfo createDatabase(String databaseName, List tags, - List execats, String expectedError) { + List execats, String expectedError) { BSimServerInfo h2DbInfo = getBsimServerInfo(databaseName); Msg.debug(this, "Creating H2 File DB: " + h2DbInfo); diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/query/test/BSimServerTest.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/test/BSimServerTest.java similarity index 99% rename from Ghidra/Features/BSim/src/test.slow/java/ghidra/query/test/BSimServerTest.java rename to Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/test/BSimServerTest.java index b1073152bf..07154c507b 100755 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/query/test/BSimServerTest.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/test/BSimServerTest.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.query.test; +package ghidra.features.bsim.query.test; import static org.junit.Assert.*; diff --git a/Ghidra/Features/BSim/src/test.slow/java/ghidra/query/test/BSimServerTestUtil.java b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/test/BSimServerTestUtil.java similarity index 99% rename from Ghidra/Features/BSim/src/test.slow/java/ghidra/query/test/BSimServerTestUtil.java rename to Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/test/BSimServerTestUtil.java index 7a1e2f1728..d1ffa67c49 100755 --- a/Ghidra/Features/BSim/src/test.slow/java/ghidra/query/test/BSimServerTestUtil.java +++ b/Ghidra/Features/BSim/src/test.slow/java/ghidra/features/bsim/query/test/BSimServerTestUtil.java @@ -4,16 +4,16 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.query.test; +package ghidra.features.bsim.query.test; import java.io.*;