tp_sig_handler() calls fflush() and tp_meter_stop() directly from signal
context. tp_meter_stop() allocates a netlink message with nlmsg_alloc() and
sends it via nl_send_auto_complete() - none of these functions (nor fflush)
are async-signal-safe.

Let the signal handler only set a flag. tp_recv_result() now waits for the
result notification with poll(), which is not restarted when a signal
arrives, and sends the CANCEL request from the main flow before continuing
to wait for the (then canceled) test result.

Fixes: f109b3473f86 ("batctl: introduce throughput meter support")
Signed-off-by: Sven Eckelmann <[email protected]>
---
 throughputmeter.c | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/throughputmeter.c b/throughputmeter.c
index 6799c93..dbb88f6 100644
--- a/throughputmeter.c
+++ b/throughputmeter.c
@@ -17,6 +17,7 @@
 #include <inttypes.h>
 #include <limits.h>
 #include <net/if.h>
+#include <poll.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -34,6 +35,7 @@
 
 static struct ether_addr *dst_mac;
 static struct state *tp_state;
+static volatile sig_atomic_t tp_aborted;
 
 struct tp_result {
        int error;
@@ -194,8 +196,15 @@ static int no_seq_check(struct nl_msg *msg __maybe_unused,
        return NL_OK;
 }
 
+static int tp_meter_stop(struct state *state, struct ether_addr *dst_mac);
+
 static int tp_recv_result(struct nl_sock *sock, struct tp_result *result)
 {
+       struct pollfd pfd = {
+               .fd = nl_socket_get_fd(sock),
+               .events = POLLIN,
+       };
+       bool cancel_sent = false;
        struct nl_cb *cb;
        int err = 0;
        int ret;
@@ -207,6 +216,23 @@ static int tp_recv_result(struct nl_sock *sock, struct 
tp_result *result)
        nl_cb_err(cb, NL_CB_CUSTOM, tpmeter_nl_print_error, result);
 
        while (result->error == 0 && !result->found) {
+               if (tp_aborted && !cancel_sent) {
+                       cancel_sent = true;
+                       tp_meter_stop(tp_state, dst_mac);
+               }
+
+               /* wake up regularly to notice an abort even when the signal
+                * arrived outside of poll()
+                */
+               ret = poll(&pfd, 1, 1000);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       break;
+               }
+               if (ret == 0)
+                       continue;
+
                ret = nl_recvmsgs(sock, cb);
                if (ret < 0)
                        break;
@@ -295,8 +321,7 @@ void tp_sig_handler(int sig)
        switch (sig) {
        case SIGINT:
        case SIGTERM:
-               fflush(stdout);
-               tp_meter_stop(tp_state, dst_mac);
+               tp_aborted = 1;
                break;
        default:
                break;

-- 
2.47.3

Reply via email to