Skip to content

Commit

Permalink
Fix #136: Provide a way for the launcher to connect to adapter on ano…
Browse files Browse the repository at this point in the history
…ther host

Allow specifying host:port for the launcher to connect to. Use command line argument instead of environment variable to facilitate propagation.

Fix encoding of command line arguments when propagating them to the launcher in "internalConsole" mode.
  • Loading branch information
int19h committed Apr 22, 2020
1 parent 44307f8 commit fc45d33
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 19 deletions.
25 changes: 17 additions & 8 deletions src/debugpy/adapter/launchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ def terminate_debuggee(self):
pass


def spawn_debuggee(session, start_request, launcher_path, args, console, console_title, sudo):
def spawn_debuggee(
session, start_request, launcher_path, args, console, console_title, sudo
):
# -E tells sudo to propagate environment variables to the target process - this
# is necessary for launcher to get DEBUGPY_LAUNCHER_PORT and DEBUGPY_LOG_DIR.
cmdline = ["sudo", "-E"] if sudo else []

cmdline += [sys.executable, launcher_path]
cmdline += args
env = {}

arguments = dict(start_request.arguments)
Expand All @@ -90,15 +90,14 @@ def on_launcher_connected(sock):
)
except Exception as exc:
raise start_request.cant_handle(
"{0} couldn't create listener socket for launcher: {1}",
session,
exc,
"{0} couldn't create listener socket for launcher: {1}", session, exc
)

try:
_, launcher_port = listener.getsockname()
cmdline += [str(launcher_port), "--"]
cmdline += args

env[str("DEBUGPY_LAUNCHER_PORT")] = str(launcher_port)
if log.log_dir is not None:
env[str("DEBUGPY_LOG_DIR")] = compat.filename_str(log.log_dir)
if log.stderr.levels != {"warning", "error"}:
Expand All @@ -107,6 +106,14 @@ def on_launcher_connected(sock):
if console == "internalConsole":
log.info("{0} spawning launcher: {1!r}", session, cmdline)
try:
for i, arg in enumerate(cmdline):
try:
cmdline[i] = compat.filename_str(arg)
except UnicodeEncodeError as exc:
raise start_request.cant_handle(
"Invalid command line argument {0!j}: {1}", arg, exc
)

# If we are talking to the client over stdio, sys.stdin and sys.stdout
# are redirected to avoid mangling the DAP message stream. Make sure
# the launcher also respects that.
Expand Down Expand Up @@ -138,7 +145,9 @@ def on_launcher_connected(sock):

# If using sudo, it might prompt for password, and launcher won't start running
# until the user enters it, so don't apply timeout in that case.
if not session.wait_for(lambda: session.launcher, timeout=(None if sudo else 10)):
if not session.wait_for(
lambda: session.launcher, timeout=(None if sudo else 10)
):
raise start_request.cant_handle("Timed out waiting for launcher to connect")

try:
Expand Down
4 changes: 2 additions & 2 deletions src/debugpy/launcher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
"""DAP message channel to the adapter."""


def connect(launcher_port):
def connect(host, port):
from debugpy.common import messaging, sockets
from debugpy.launcher import handlers

global channel
assert channel is None

sock = sockets.create_client()
sock.connect(("127.0.0.1", launcher_port))
sock.connect((host, port))

stream = messaging.JsonIOStream.from_socket(sock, "Adapter")
channel = messaging.JsonMessageChannel(stream, handlers=handlers)
Expand Down
25 changes: 16 additions & 9 deletions src/debugpy/launcher/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,22 @@ def main():
# will also exit, so it doesn't need to observe the signal directly.
signal.signal(signal.SIGINT, signal.SIG_IGN)

def option(name, type, *args):
try:
return type(os.environ.pop(name, *args))
except Exception:
log.reraise_exception("Error parsing {0!r}:", name)

launcher_port = option("DEBUGPY_LAUNCHER_PORT", int)

launcher.connect(launcher_port)
# Everything before "--" is command line arguments for the launcher itself,
# and everything after "--" is command line arguments for the debuggee.
sep = sys.argv.index("--")
launcher_argv = sys.argv[1:sep]
sys.argv = sys.argv[sep + 1:]

# The first argument specifies the host/port on which the adapter is waiting
# for launcher to connect. It's either host:port, or just port.
adapter = launcher_argv[0]
host, sep, port = adapter.partition(":")
if not sep:
host = "127.0.0.1"
port = adapter
port = int(port)

launcher.connect(host, port)
launcher.channel.wait()

if debuggee.process is not None:
Expand Down

0 comments on commit fc45d33

Please sign in to comment.