Use bpf_prog_stream_open() in its default blocking mode when following a program stream. This lets bpftool wait for later output instead of exiting as soon as it drains the currently buffered bytes. Flush each chunk as it arrives so redirected output is not held in stdio buffers while the next read blocks. A stream descriptor does not keep its program alive, so bpftool drops the program descriptor once the stream is open. The command then ends with EOF when the program is unloaded and otherwise runs until interrupted, as bpftool prog tracelog already does for the trace pipe. Install SIGINT, SIGHUP and SIGTERM handlers without SA_RESTART so an interrupted read ends the loop, check the stop flag before each read so a signal that lands while output is being printed ends it as well, and exit successfully in both cases. Restore the previous dispositions afterwards so batch mode keeps its usual signal behavior. Report errors from the fallback read path too. Fall back to BPF_PROG_STREAM_READ_BY_FD when the open command returns EINVAL. The bpf() syscall uses EINVAL for unknown commands, so this keeps a new bpftool compatible with kernels that provide program streams but predate their file descriptor interface. On such kernels the command still dumps the buffered output and exits. Document both behaviors. Signed-off-by: Kumar Kartikeya Dwivedi --- .../bpftool/Documentation/bpftool-prog.rst | 5 ++ tools/bpf/bpftool/prog.c | 62 ++++++++++++++++--- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 90fe8c61bf42..ef7e00eb5e47 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -186,6 +186,11 @@ bpftool prog tracelog { stdout | stderr } *PROG* error messages to the standard error stream. This facility should be used only for debugging purposes. + On kernels that support opening a stream as a file descriptor, bpftool + keeps printing new output as the program produces it, until the program is + unloaded or is hit. Older kernels only allow dumping the output + buffered so far, after which bpftool exits. + bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*] Run BPF program *PROG* in the kernel testing infrastructure for BPF, meaning that the program works on the data and context provided by the diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 24e40dfab469..f0241dada548 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -1119,21 +1119,69 @@ enum prog_tracelog_mode { TRACE_STDERR, }; +static volatile sig_atomic_t stream_stop; + +static void stop_stream(int signo) +{ + stream_stop = 1; +} + +/* Consumes prog_fd. */ static int prog_tracelog_stream(int prog_fd, enum prog_tracelog_mode mode) { + /* No SA_RESTART: an interrupted read() must return EINTR to end the loop. */ + const struct sigaction act = { .sa_handler = stop_stream }; + const int signals[] = { SIGHUP, SIGINT, SIGTERM }; + struct sigaction old[ARRAY_SIZE(signals)]; FILE *file = mode == TRACE_STDOUT ? stdout : stderr; int stream_id = mode == TRACE_STDOUT ? 1 : 2; char buf[512]; - int ret; + unsigned int i; + int fd, ret; + + fd = bpf_prog_stream_open(prog_fd, stream_id, NULL); + if (fd == -EINVAL) { + /* Kernel predates BPF_PROG_STREAM_OPEN: dump buffered output and exit. */ + do { + ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL); + if (ret > 0) + fwrite(buf, sizeof(buf[0]), ret, file); + } while (ret > 0); + if (ret < 0) + p_err("failed to read stream: %s", strerror(-ret)); + close(prog_fd); + goto out; + } + /* + * The stream descriptor does not keep the program alive. Drop the + * program reference so that reads return EOF once the program is gone. + */ + close(prog_fd); + if (fd < 0) { + p_err("failed to open stream: %s", strerror(-fd)); + return -1; + } + stream_stop = 0; + for (i = 0; i < ARRAY_SIZE(signals); i++) + sigaction(signals[i], &act, &old[i]); ret = 0; - do { - ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL); - if (ret > 0) - fwrite(buf, sizeof(buf[0]), ret, file); - } while (ret > 0); - + while (!stream_stop) { + ret = read(fd, buf, sizeof(buf)); + if (ret <= 0) + break; + fwrite(buf, sizeof(buf[0]), ret, file); + fflush(file); + } + if (ret < 0 && !(stream_stop && errno == EINTR)) + p_err("failed to read stream: %s", strerror(errno)); + else + ret = 0; + for (i = 0; i < ARRAY_SIZE(signals); i++) + sigaction(signals[i], &old[i], NULL); + close(fd); +out: fflush(file); return ret ? -1 : 0; } -- 2.53.0