Warm tip: This article is reproduced from serverfault.com, please click

python-subprocess.Popen():在子执行期间更改stderr

(python - subprocess.Popen(): change stderr during child's execution)

发布于 2020-12-04 08:34:51

目标:我正在尝试将一个Python脚本放在一起,以捕获由于执行代码块而发生的网络流量。为简单起见,假设我要记录对的调用产生的网络流量socket.gethostbyname('example.com')注意:我不能只是简单地tcpdumpgethostbyname()返回终止,因为要测量的实际代码块会触发其他外部代码,因此我无法确定该外部代码何时完成执行(因此,我必须离开tcpdump运行足够长的时间)因为很有可能我记录了此外部代码生成的所有流量)。

方法:我正在使用subprocessstart tcpdump使用它的and选项告诉几秒钟tcpdump后终止自身,例如:duration-G-W

duration = 15
nif = 'en0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
tcpdump_proc = subprocess.Popen(cmd)
socket.gethostbyname('example.com')
time.sleep(duration + 5) # sleep longer than tcpdump is running

这样做的问题是,Popen()返回之前 tcpdump已完全启动并正在运行,因此gethostbyname()不会捕获到呼叫所产生的部分/全部流量我显然可以time.sleep(x)在调用之前添加一个时间,gethostbyname()以便给tcpdump它一些时间来加速启动,但这不是一个可移植的解决方案(我不能随便选择一些方法,x < duration因为功能强大的系统会比功能较弱的系统更早地捕获数据包)。

为了解决这个问题,我的想法是解析tcpdump输出,以寻找以下内容stderr

tcpdump: listening on en0, link-type EN10MB (Ethernet), capture size 262144 bytes

因此,我需要附加到stderr,但是问题是我不想承诺读取其所有输出,因为我需要继续执行我的代码以实际执行我要测量的代码块(gethostbyname()在此示例中),而不是被困在一个循环中,从读取stderr

我可以通过添加一个信号量来解决此问题,该信号量会阻止主线程继续进行gethostbyname()调用,并stderr在它从中读取上述字符串时让后台线程从该信号量读取并减少该信号量(以使主线程继续前进)stderr,但是我d如果可能,请保持代码为单线程。

据我了解,这是一个很大的NONO,可subprocess.PIPE用于stderr并且stdout不承诺读取所有输出,因为当缓冲区填满时,子级最终将阻塞。但是,如果你仅对读取输出的第一部分感兴趣,你可以“分离”(销毁)管道中期执行吗?从本质上讲,我想得到这样的结果:

duration = 15
nif = 'en0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]
tcpdump_proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
for l in tcpdump_proc.stderr:
    if 'tcpdump: listening on' in l:
        break
socket.gethostbyname('example.com')
time.sleep(duration) # sleep at least as long as tcpdump is running

我还需要在框内添加些什么if来“重新分配”谁负责阅读stderr我可以重新设置stderrNonetcpdump_proc.stderr = None)吗?还是应该打电话给我tcpdump_proc.stderr.close()tcpdump如果提前打电话提早终止)?

也很可能是我错过了一些显而易见的事情,并且有一种更好的方法来实现我想要的目标-如果是这样,请启发我:)。

提前致谢 :)

Questioner
Janus Varmarken
Viewed
0
Maurice Meyer 2020-12-04 21:07:57

收到消息后,你可以在detach()close()stderr上使用listening on

import subprocess
import time

duration = 10
nif = 'eth0'
pcap = 'dns.pcap'
cmd = ['tcpdump', '-G', str(duration), '-W', '1', '-i', nif, '-w', pcap]

proc = subprocess.Popen(
    cmd, shell=False, stderr=subprocess.PIPE, bufsize=1, text=True
)
for i, line in enumerate(proc.stderr):
    print('read %d lines from stderr' % i)
    if 'listening on' in line:
        print('detach stderr!')
        proc.stderr.detach()
        break

while proc.poll() is None:
    print("doing something else while tcpdump is runnning!")
    time.sleep(2)

print(proc.returncode)
print(proc.stderr.read())

出去:

read 0 lines from stderr
detach stderr!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
doing something else while tcpdump is runnning!
0
Traceback (most recent call last):
  File "x.py", line 24, in <module>
    print(proc.stderr.read())
ValueError: underlying buffer has been detached

笔记:

我尚未检查stderr数据实际上发生了什么,但是分离stderr似乎对tcpdump没有任何影响。