From 55e6c53efff055e3f8098ee1f70997aecaaeb834 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Thu, 17 Oct 2024 17:58:00 +0200 Subject: [PATCH] bridge: Report PID for a spawn stream channel Add the spawned PID to the channel's "ready" message. This will be useful for #21021 --- doc/protocol.md | 3 +++ src/cockpit/channel.py | 6 +++++- src/cockpit/channels/stream.py | 4 +++- test/pytest/test_bridge.py | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/doc/protocol.md b/doc/protocol.md index e63ecd90240e..11b16b663cff 100644 --- a/doc/protocol.md +++ b/doc/protocol.md @@ -853,6 +853,9 @@ following options can be specified: size of the terminal window. Values must be integers between 0 and 0xffff. This option is only valid if "pty" is true. +For type "spawn", the `ready` message contains a "pid" field with the spawned +process ID. + If an "done" is sent to the bridge on this channel, then the socket and/or pipe input is shutdown. The channel will send an "done" when the output of the socket or pipe is done. diff --git a/src/cockpit/channel.py b/src/cockpit/channel.py index 40d81b370a6f..6f469fe0de6a 100644 --- a/src/cockpit/channel.py +++ b/src/cockpit/channel.py @@ -370,6 +370,7 @@ class ProtocolChannel(Channel, asyncio.Protocol): _send_pongs: bool = True _last_ping: 'JsonObject | None' = None _create_transport_task: 'asyncio.Task[asyncio.Transport] | None' = None + _ready_info: 'JsonObject | None' = None # read-side EOF handling _close_on_eof: bool = False @@ -400,7 +401,10 @@ def create_transport_done(self, task: 'asyncio.Task[asyncio.Transport]') -> None return self.connection_made(transport) - self.ready() + if self._ready_info is not None: + self.ready(**self._ready_info) + else: + self.ready() def connection_made(self, transport: asyncio.BaseTransport) -> None: assert isinstance(transport, asyncio.Transport) diff --git a/src/cockpit/channels/stream.py b/src/cockpit/channels/stream.py index 46144a2611a3..d07a18d602df 100644 --- a/src/cockpit/channels/stream.py +++ b/src/cockpit/channels/stream.py @@ -109,7 +109,9 @@ async def create_transport(self, loop: asyncio.AbstractEventLoop, options: JsonO try: transport = SubprocessTransport(loop, self, args, pty=pty, window=window, env=env, cwd=cwd, stderr=stderr) - logger.debug('Spawned process args=%s pid=%i', args, transport.get_pid()) + pid = transport.get_pid() + self._ready_info = {'pid': pid} + logger.debug('Spawned process args=%s pid=%i', args, pid) return transport except FileNotFoundError as error: raise ChannelError('not-found') from error diff --git a/test/pytest/test_bridge.py b/test/pytest/test_bridge.py index efc58513ad1c..678998619703 100644 --- a/test/pytest/test_bridge.py +++ b/test/pytest/test_bridge.py @@ -770,6 +770,9 @@ async def serve_page(reader, writer): assert control['channel'] == ch command = control['command'] if command == 'ready': + if 'spawn' in args: + assert isinstance(control['pid'], int) + assert os.readlink(f"/proc/{control['pid']}/exe").endswith("/cat") # If we get ready, it's our turn to send data first. # Hopefully we didn't receive any before. assert not saw_data