Skip to content

Commit

Permalink
client: make spawn return SpawnResult
Browse files Browse the repository at this point in the history
  • Loading branch information
doronz88 committed Feb 20, 2022
1 parent 01a1afa commit 63aa13d
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 8 deletions.
15 changes: 9 additions & 6 deletions src/rpcclient/rpcclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
import sys
import typing
from collections import namedtuple
from enum import Enum
from select import select
from socket import socket
Expand Down Expand Up @@ -34,6 +35,8 @@
except ImportError:
logging.warning('termios not available on your system. some functionality may not work as expected')

SpawnResult = namedtuple('SpawnResult', 'error pid stdout')

INVALID_PID = 0xffffffff
CHUNK_SIZE = 1024

Expand Down Expand Up @@ -199,7 +202,7 @@ def get_dummy_block(self) -> Symbol:
return self.symbol(dummy_block_t.parse(self._recvall(8)))

def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, stdin=sys.stdin, stdout=sys.stdout,
tty=False, background=False):
tty=False, background=False) -> SpawnResult:
"""
spawn a new process and forward its stdin, stdout & stderr
Expand All @@ -209,7 +212,7 @@ def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, st
:param stdout: a file object to write both stdout and stderr to
:param tty: should enable raw tty mode
:param background: should execute process in background
:return: error code
:return: a SpawnResult. error is None if background is requested
"""
if argv is None:
argv = self.DEFAULT_ARGV
Expand All @@ -229,15 +232,15 @@ def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, st
if background:
# if in background was requested, we can just detach this connection
self.reconnect()
return
return SpawnResult(error=None, pid=pid, stdout=stdout)

self._sock.setblocking(False)

if tty:
self._prepare_terminal()
try:
result = self._execution_loop(stdin, stdout)
except: # noqa: E722
error = self._execution_loop(stdin, stdout)
except Exception: # noqa: E722
# this is important to really catch every exception here, even exceptions not inheriting from Exception
# so the controlling terminal will remain working with its previous settings
if tty:
Expand All @@ -249,7 +252,7 @@ def spawn(self, argv: typing.List[str] = None, envp: typing.List[str] = None, st
self._restore_terminal()
self.reconnect()

return result
return SpawnResult(error=error, pid=pid, stdout=stdout)

def symbol(self, symbol: int):
""" at a symbol object from a given address """
Expand Down
18 changes: 16 additions & 2 deletions src/rpcclient/tests/test_spawn.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@
])
def test_spawn_sanity(client, argv, expected_stdout, errorcode):
stdout = StringIO()
assert errorcode == client.spawn(argv, stdout=stdout, stdin=b'')
assert errorcode == client.spawn(argv, stdout=stdout, stdin=b'').error

stdout.seek(0)
assert expected_stdout == stdout.read().strip()


def test_spawn_bad_value_stress(client):
for i in range(1000):
assert 256 == client.spawn(['/bin/ls', 'INVALID_PATH'], stdout=StringIO(), stdin=b'')
stdout = StringIO()
assert 256 == client.spawn(['/bin/ls', 'INVALID_PATH'], stdout=stdout, stdin=b'').error

stdout.seek(0)
assert 'ls: INVALID_PATH: No such file or directory' == stdout.read().strip()


def test_spawn_background(client):
spawn_result = client.spawn(['/bin/sleep', '5'], stdout=StringIO(), stdin=b'', background=True)

# when running in background, no error is returned
assert spawn_result.error is None

# instead, we can just make sure it ran by sending it a kill and don't fail
client.processes.kill(spawn_result.pid)

0 comments on commit 63aa13d

Please sign in to comment.