且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

将tcpdump输出写入压缩/ gzip压缩文件

更新时间:2023-10-11 13:14:52

发生了什么



解释在这里发生的情况:

What Was Happening

To explain what happens here:


  • Ctrl + C 发送SIGINT到整个过程组。这意味着它不仅终止 tcpdump ,而且终止 gzip 。 (您尝试通过将内容移入后台进程,从而移出同一进程组来避免这种情况的解决方法。)

  • stdout默认仅在输出为TTY;当输出到FIFO时,将对其进行块缓冲,只有在有足够大的块可用时,才通过写入左侧过程中的数据来提高效率。因此,在许多情况下,您可以使用 stdbuf -oL 或类似的方法禁用此功能。但是...

  • gzip 就其性质而言无法完全无缓冲地运行。这是因为基于块的压缩算法需要将数据收集到 block 中;批量分析该内容; & c。

  • Ctrl+C sends a SIGINT to the entire process group. That means it doesn't just terminate tcpdump, but also terminates gzip. (The workarounds you were attempting try to avoid this by moving content into background processes, and thus out of the same process group).
  • stdout is line-buffered by default only when output is to a TTY; when output is to a FIFO, it's block-buffered, allowing greater efficiency by writing data from the left-hand process only when a sufficiently larger chunk is available. In many situations, you could thus just use stdbuf -oL or similar to disable this. However...
  • gzip by its nature cannot operate completely unbuffered. This is because block-based compression algorithms need to collect data into, well, blocks; analyze that content in bulk; &c.

因此,如果 gzip tcpdump 同时终止,这意味着不能保证 tcpdump 实际上将能够刷新其输出缓冲区和然后让 gzip 读取,压缩和写入该刷新数据,然后在 gzip 自身退出信号之前

So, if gzip and tcpdump are terminated at the same time, that means there's no assurance that tcpdump will actually be able to flush its output buffer, and then have gzip read, compress and write that flushed data, before gzip itself exits from the signal it received at the same time.

请注意,包含单词 Interactive的标题下的代码段旨在用于 interactive use

Note that the code snippets under headers containing the word "Interactive" are intended for interactive use.

作为surefire解决方案,将 gzip 完全移出带,因此当您在 tcpdump 命令上按ctrl + c时,它不容易发送SIGINT:

As a surefire solution, move the gzip completely out-of-band, so it isn't prone to being sent a SIGINT when you press ctrl+c on the tcpdump command:

exec 3> >(gzip -c >test.gz)  # Make FD 3 point to gzip
tcpdump -l -i eth0 >&3       # run tcpdump **AS A SEPARATE COMMAND** writing to that fd
exec 3>&-                    # later, after you cancelled tcpdump, close the FD.






可靠的交互式解决方法(适用于任何POSIX Shell)



同样的东西,但是稍长一些并且不依赖进程替换:


A Reliable Interactive Workaround (For Any POSIX Shell)

Same thing, but slightly longer and not relying on process substitution:

mkfifo test.fifo                            # create a named FIFO
gzip -c <test.fifo >test.gz & gzip_pid="$!" # start gzip, reading from that named FIFO
tcpdump -l -i eth0 >test.fifo               # start tcpdump, writing to that named FIFO
rm test.fifo                                # delete the FIFO when done
wait "$gzip_pid"                            # ...and wait for gzip to exit

请注意 wait 将具有gzip进程的退出状态,因此您可以确定它是否遇到错误。

Note that the wait will have the exit status of the gzip process, so you can determine whether it encountered an error.

如果我们正在运行脚本,则设置信号处理程序是适当的因此我们可以显式处理SIGINT(通过杀死 tcpdump):

If we're running a script, then it's appropriate to set up a signal handler so we can handle SIGINT (by killing only tcpdump) explicitly:

#!/bin/sh
[ "$#" -gt 0 ] || {
  echo "Usage: ${0##*/} file.tcpdump.gz [tcpdump-args]" >&2
  echo "  Example: ${0##*/} foo.tcpdump.gz -l -i eth0" >&2
  exit 1
}
outfile=$1; shift
fifo=test-$$.fifo # for real code, put this in a unique temporary directory

trap '[ -n "$tcpdump_pid" ] && kill "$tcpdump_pid"' INT
trap 'rm -f -- "$fifo"' EXIT

rm -f -- "$fifo"; mkfifo "$fifo" || exit
gzip -c >"$outfile" <"$fifo" & gzip_pid=$!

# avoid trying to run tcpdump if gzip obviously failed to start
{ [ -n "$gzip_pid" ] && [ "$gzip_pid" -gt 0 ] && kill -0 "$gzip_pid"; } || exit 1

tcpdump "$@" >"$fifo" & tcpdump_pid=$!

# return exit status of tcpdump if it fails, or gzip if tcpdump succeeds
wait "$tcpdump_pid" || wait "$gzip_pid"