Skip to content

Commit

Permalink
bridge: Report PID for a spawn stream channel
Browse files Browse the repository at this point in the history
Add the spawned PID to the channel's "ready" message. This will be
useful for cockpit-project#21021
  • Loading branch information
martinpitt committed Oct 18, 2024
1 parent 26846d6 commit 55e6c53
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 2 deletions.
3 changes: 3 additions & 0 deletions doc/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 5 additions & 1 deletion src/cockpit/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/cockpit/channels/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions test/pytest/test_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 55e6c53

Please sign in to comment.