Skip to content

Commit

Permalink
use fctnl for non-blocking readline of subprocess
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfgang Hotwagner committed Mar 12, 2024
1 parent 266a405 commit 02f96b7
Showing 1 changed file with 16 additions and 9 deletions.
25 changes: 16 additions & 9 deletions src/attackmate/executors/shell/shellexecutor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
commands in AttackMate.
"""

import os
import subprocess
from subprocess import TimeoutExpired
from datetime import datetime
Expand Down Expand Up @@ -47,9 +48,18 @@ def popen_close(self, proc):
proc.wait(timeout=10)

@staticmethod
def enqueue_output(stdout, queue):
for line in iter(stdout.readline, b''):
queue.put(line)
def non_block_read(stdout):
fd = stdout.fileno()
try:
import fcntl
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
except ImportError:
raise ExecException("The 'fcntl' module is not available. This functionality requires a Unix-like operating system.")
try:
return stdout.read()
except:
return b""

def popen_noninteractive(self, proc: subprocess.Popen, cmd: bytes, timeout=None) -> str:
self.logger.debug("Running non interactive command")
Expand All @@ -71,16 +81,13 @@ def popen_interactive(self, proc: subprocess.Popen, cmd: bytes, timeout: int=5,
proc.stdin.write(cmd)
proc.stdin.flush()

t = Thread(target=ShellExecutor.enqueue_output, args=(proc.stdout, q))
t.daemon = True
t.start()

outline = b''
if read:
begin = datetime.now()
while (datetime.now() - begin ).total_seconds() < timeout:
if q.qsize() > 0:
outline += q.get_nowait()
tmp = self.non_block_read(proc.stdout)
if tmp:
outline += tmp
begin = datetime.now() # reset timer when data comes

return outline.decode()
Expand Down

0 comments on commit 02f96b7

Please sign in to comment.