diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java index 6ab40c88a7..d6f1507a0f 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/dbgeng/DebugInputCallbacks.java @@ -15,17 +15,15 @@ */ package agent.dbgeng.dbgeng; -import java.util.concurrent.CompletableFuture; - /** * The interface for receiving input callbacks via {@code IDebugInputCallbacks} or a newer variant. * - *

* Note: The wrapper implementation will select the appropriate native interface version. */ @FunctionalInterface public interface DebugInputCallbacks { - CompletableFuture startInput(); + + public void startInput(long bufferSize); default void endInput() { // Optional implementation diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java index b5bead36fe..6a8aa21230 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/impl/dbgeng/io/WrapCallbackIDebugInputCallbacks.java @@ -15,10 +15,6 @@ */ package agent.dbgeng.impl.dbgeng.io; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Guid.REFIID; import com.sun.jna.platform.win32.WinDef.ULONG; @@ -30,15 +26,12 @@ import com.sun.jna.ptr.PointerByReference; import agent.dbgeng.dbgeng.DebugInputCallbacks; import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1; import agent.dbgeng.jna.dbgeng.io.*; -import ghidra.util.exception.CancelledException; public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCallbacks { private final DebugClientImpl1 client; private final DebugInputCallbacks cb; private ListenerIDebugInputCallbacks listener; - private final Set> futures = new HashSet<>(); // TODO: Just one? - public WrapCallbackIDebugInputCallbacks(DebugClientImpl1 client, DebugInputCallbacks cb) { this.client = client; this.cb = cb; @@ -82,23 +75,7 @@ public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCall @Override public HRESULT StartInput(ULONG BufferSize) { try { - CompletableFuture future = cb.startInput(); - if (future == null) { - return WinError.S_OK; - } - future.handle((input, exc) -> { - if (exc == null) { - client.getControl().returnInput(input); - } - else if (exc instanceof CancelledException) { - // Normal if another client provides input - } - else { - client.getControl().errln("ERROR getting input: " + exc.getMessage()); - } - futures.remove(future); - return null; - }); + cb.startInput(BufferSize.longValue()); return WinError.S_OK; } catch (Throwable e) { @@ -109,9 +86,6 @@ public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCall @Override public HRESULT EndInput() { try { - for (CompletableFuture future : futures) { - future.cancel(true); - } cb.endInput(); return WinError.S_OK; } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java index 690750736d..de3d2595c5 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListener.java @@ -244,4 +244,10 @@ public interface DbgEventsListener { * @param mask class of output */ void consoleOutput(String output, int mask); + + /** + * @param prompt for console output + */ + void promptChanged(String prompt); + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java index 17e9343b34..80781c35db 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/DbgEventsListenerAdapter.java @@ -142,4 +142,10 @@ public interface DbgEventsListenerAdapter extends DbgEventsListener { public default void consoleOutput(String output, int mask) { // Extension point } + + @Override + public default void promptChanged(String prompt) { + // Extension point + } + } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugInputCallbacks.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugInputCallbacks.java new file mode 100644 index 0000000000..96e8c8f4ad --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgDebugInputCallbacks.java @@ -0,0 +1,43 @@ +/* ### + * 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 agent.dbgeng.manager.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import agent.dbgeng.dbgeng.DebugInputCallbacks; + +public class DbgDebugInputCallbacks implements DebugInputCallbacks { + private DbgManagerImpl manager; + + public DbgDebugInputCallbacks(DbgManagerImpl manager) { + this.manager = manager; + } + + @Override + public void startInput(long bufsize) { + manager.getEventListeners().fire.promptChanged(">>>"); + CompletableFuture cf = new CompletableFuture(); + try { + manager.setContinuation(cf); + manager.getControl().returnInput(cf.get()); + } + catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java index ed87f0ba38..c90d5e21f0 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/manager/impl/DbgManagerImpl.java @@ -43,6 +43,7 @@ import agent.dbgeng.manager.cmd.*; import agent.dbgeng.manager.evt.*; import agent.dbgeng.model.iface1.DbgModelTargetActiveScope; import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; +import agent.dbgeng.model.iface1.DbgModelTargetInterpreter; import ghidra.async.*; import ghidra.comm.util.BitmaskSet; import ghidra.dbg.target.TargetObject; @@ -110,6 +111,7 @@ public class DbgManagerImpl implements DbgManager { private DbgThread eventThread; private volatile boolean waiting = false; private boolean kernelMode = false; + private CompletableFuture continuation; /** * Instantiate a new manager @@ -370,6 +372,7 @@ public class DbgManagerImpl implements DbgManager { status = dbgeng.getControl().getExecutionStatus(); dbgeng.setOutputCallbacks(new DbgDebugOutputCallbacks(this)); dbgeng.setEventCallbacks(new DbgDebugEventCallbacksAdapter(this)); + dbgeng.setInputCallbacks(new DbgDebugInputCallbacks(this)); dbgeng.flushCallbacks(); if (!create) { @@ -1429,6 +1432,13 @@ public class DbgManagerImpl implements DbgManager { @Override public CompletableFuture console(String command) { + if (continuation != null) { + String prompt = command.equals("") ? DbgModelTargetInterpreter.DBG_PROMPT : ">>>"; + getEventListeners().fire.promptChanged(prompt); + continuation.complete(command); + setContinuation(null); + return AsyncUtils.NIL; + } return execute( new DbgConsoleExecCommand(this, command, DbgConsoleExecCommand.Output.CONSOLE)) .thenApply(e -> null); @@ -1498,4 +1508,7 @@ public class DbgManagerImpl implements DbgManager { this.kernelMode = kernelMode; } + public void setContinuation(CompletableFuture continuation) { + this.continuation = continuation; + } } diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java index 4553a1f355..353db9862c 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface1/DbgModelTargetInterpreter.java @@ -29,6 +29,8 @@ import ghidra.dbg.target.TargetInterpreter; */ public interface DbgModelTargetInterpreter extends DbgModelTargetObject, TargetInterpreter { + public static final String DBG_PROMPT = "(kd)"; + @Override public default CompletableFuture execute(String cmd) { return getModel().gateFuture(getManager().console(cmd)); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java index 9845313a78..39ef012538 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/iface2/DbgModelTargetSession.java @@ -15,6 +15,8 @@ */ package agent.dbgeng.model.iface2; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import agent.dbgeng.dbgeng.DebugClient.DebugOutputFlags; @@ -70,6 +72,13 @@ public interface DbgModelTargetSession extends // getListeners().fire.consoleOutput(getProxy(), chan, output); } + @Override + public default void promptChanged(String prompt) { + changeAttributes(List.of(), Map.of( // + PROMPT_ATTRIBUTE_NAME, prompt // + ), "Refreshed"); + } + @Override public default CompletableFuture setActive() { DbgManagerImpl manager = getManager(); diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java index e6ae22671e..c1ef8d19c0 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/java/agent/dbgeng/model/impl/DbgModelTargetSessionImpl.java @@ -22,15 +22,26 @@ import java.util.concurrent.CompletableFuture; import agent.dbgeng.dbgeng.DebugSessionId; import agent.dbgeng.manager.*; import agent.dbgeng.model.iface1.DbgModelSelectableObject; +import agent.dbgeng.model.iface1.DbgModelTargetInterpreter; import agent.dbgeng.model.iface2.DbgModelTargetProcessContainer; import agent.dbgeng.model.iface2.DbgModelTargetSession; import ghidra.dbg.target.schema.*; import ghidra.dbg.util.PathUtils; -@TargetObjectSchemaInfo(name = "Session", elements = { - @TargetElementType(type = Void.class) }, attributes = { - @TargetAttributeType(name = "Attributes", type = DbgModelTargetSessionAttributesImpl.class, fixed = true), - @TargetAttributeType(name = "Processes", type = DbgModelTargetProcessContainerImpl.class, required = true, fixed = true), +@TargetObjectSchemaInfo( + name = "Session", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = "Attributes", + type = DbgModelTargetSessionAttributesImpl.class, + fixed = true), + @TargetAttributeType( + name = "Processes", + type = DbgModelTargetProcessContainerImpl.class, + required = true, + fixed = true), @TargetAttributeType(type = Void.class) }) public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl implements DbgModelTargetSession { @@ -71,7 +82,7 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl processes // ), Map.of( // ACCESSIBLE_ATTRIBUTE_NAME, accessible, // - PROMPT_ATTRIBUTE_NAME, DBG_PROMPT, // + PROMPT_ATTRIBUTE_NAME, DbgModelTargetInterpreter.DBG_PROMPT, // STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE // ), "Initialized");