trace2: disable tr2_dst before warning on write errors

If writing a trace2 message fails, we optionally warn the user of this fact. However, in 0ee10fd (usage: add trace2 entry upon warning(), 2020-11-23), we added a trace entry to the warning() function. This means that we can enter an infinite loop of failing trace2 writes and warnings. Fix this by disabling the failing trace2 destination before issuing the warning. Additionally, trace2 sets a default SIGPIPE handler (tr2main_signal_handler) when it is initialized. This handler generates its own trace2 messages when a signal is received. If a trace2 write fails due to a broken pipe, this handler will run and then cause another failed write. Fix this by temporarily ignoring SIGPIPE while writing trace2 messages. This is safe because the write will still fail, and we will disable the failing destination. Signed-off-by: Josh Steadmon <steadmon@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Josh Steadmon committed Nov 18, 2021 at 14:01 UTC 538ac746048fab0ed32b972e34eaabb9fb3f15b9
1 file changed +7 -2
trace2/tr2_dst.c
+7 -2
@@ -1,4 +1,5 @@
1 #include "cache.h"
2 +#include "sigchain.h"
3 #include "trace2/tr2_dst.h"
4 #include "trace2/tr2_sid.h"
5 #include "trace2/tr2_sysenv.h"
@@ -360,6 +361,7 @@ int tr2_dst_trace_want(struct tr2_dst *dst)
361 void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
362 {
363 int fd = tr2_dst_get_trace_fd(dst);
364 + ssize_t bytes;
365
366 strbuf_complete_line(buf_line); /* ensure final NL on buffer */
367
@@ -378,12 +380,15 @@ void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
380 *
381 * If we get an IO error, just close the trace dst.
382 */
381 - if (write(fd, buf_line->buf, buf_line->len) >= 0)
383 + sigchain_push(SIGPIPE, SIG_IGN);
384 + bytes = write(fd, buf_line->buf, buf_line->len);
385 + sigchain_pop(SIGPIPE);
386 + if (bytes >= 0)
387 return;
388
389 + tr2_dst_trace_disable(dst);
390 if (tr2_dst_want_warning())
391 warning("unable to write trace to '%s': %s",
392 tr2_sysenv_display_name(dst->sysenv_var),
393 strerror(errno));
388 - tr2_dst_trace_disable(dst);
394 }