* [PATCH iproute2-next v2 2/3] utils: timestamp: add JSON support
2026-02-23 15:28 [PATCH iproute2-next v2 0/3] ip mptcp monitor: add JSON support Matthieu Baerts (NGI0)
2026-02-23 15:28 ` [PATCH iproute2-next v2 1/3] mptcp: uniform stream closure Matthieu Baerts (NGI0)
@ 2026-02-23 15:28 ` Matthieu Baerts (NGI0)
2026-02-23 15:28 ` [PATCH iproute2-next v2 3/3] mptcp: monitor: " Matthieu Baerts (NGI0)
2026-02-25 16:30 ` [PATCH iproute2-next v2 0/3] ip mptcp " patchwork-bot+netdevbpf
3 siblings, 0 replies; 5+ messages in thread
From: Matthieu Baerts (NGI0) @ 2026-02-23 15:28 UTC (permalink / raw)
To: Stephen Hemminger, David Ahern; +Cc: netdev, mptcp, Matthieu Baerts (NGI0)
Supporting JSON is easy with the helpers. The biggest modification is to
extract the end value. Different keys are used depending on the short or
long timestamp option being used, to ease the parsing.
No behavioural changes intended for the moment, this is a preparation for a
future usage of print_timestamp() within a JSON context.
Like in other helpers supporting JSON, the output stream 'fp' is ignored,
which is fine, because it is always set to 'stdout'.
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
---
v2: ignore 'fp' which is always set to 'stdout' (Stephen)
---
lib/utils.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/lib/utils.c b/lib/utils.c
index 13e8c098..1215fe31 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1277,21 +1277,23 @@ int print_timestamp(FILE *fp)
{
struct timeval tv;
struct tm *tm;
+ char ts[40];
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
if (timestamp_short) {
- char tshort[40];
+ size_t len;
- strftime(tshort, sizeof(tshort), "%Y-%m-%dT%H:%M:%S", tm);
- fprintf(fp, "[%s.%06ld] ", tshort, tv.tv_usec);
+ len = strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tm);
+ snprintf(ts + len, sizeof(ts) - len, ".%06ld", tv.tv_usec);
+ print_string(PRINT_ANY, "timestamp_short", "[%s] ", ts);
} else {
char *tstr = asctime(tm);
tstr[strlen(tstr)-1] = 0;
- fprintf(fp, "Timestamp: %s %ld usec\n",
- tstr, tv.tv_usec);
+ snprintf(ts, sizeof(ts), "%s %ld usec", tstr, tv.tv_usec);
+ print_string(PRINT_ANY, "timestamp", "Timestamp: %s\n", ts);
}
return 0;
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH iproute2-next v2 3/3] mptcp: monitor: add JSON support
2026-02-23 15:28 [PATCH iproute2-next v2 0/3] ip mptcp monitor: add JSON support Matthieu Baerts (NGI0)
2026-02-23 15:28 ` [PATCH iproute2-next v2 1/3] mptcp: uniform stream closure Matthieu Baerts (NGI0)
2026-02-23 15:28 ` [PATCH iproute2-next v2 2/3] utils: timestamp: add JSON support Matthieu Baerts (NGI0)
@ 2026-02-23 15:28 ` Matthieu Baerts (NGI0)
2026-02-25 16:30 ` [PATCH iproute2-next v2 0/3] ip mptcp " patchwork-bot+netdevbpf
3 siblings, 0 replies; 5+ messages in thread
From: Matthieu Baerts (NGI0) @ 2026-02-23 15:28 UTC (permalink / raw)
To: Stephen Hemminger, David Ahern; +Cc: netdev, mptcp, Matthieu Baerts (NGI0)
The command 'ip [-t[s]] mptcp monitor' now produces an output with the JSON
format.
The previous text output has been preserved.
The JSON support has been added thanks to the print_*() helpers. Note that
it is also needed to add a signal handler to exit without errors in case of
SIGINT or SIGTERM, and close the JSON. This last step will print the final
']' when stopping 'ip -j mptcp monitor'.
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
---
v2:
- print token with print_0xhex(), and add missing space after ',' (AI)
- use atexit() to close the JSON (Stephen)
- always call delete_json_obj(): nothing is done when JSON is not used.
---
ip/ipmptcp.c | 86 +++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 59 insertions(+), 27 deletions(-)
diff --git a/ip/ipmptcp.c b/ip/ipmptcp.c
index 18ec05a8..3df89db5 100644
--- a/ip/ipmptcp.c
+++ b/ip/ipmptcp.c
@@ -2,6 +2,7 @@
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -476,8 +477,9 @@ static void print_addr(const char *key, int af, struct rtattr *value)
char str[INET6_ADDRSTRLEN];
if (inet_ntop(af, data, str, sizeof(str))) {
- printf(" %s=", key);
- color_fprintf(stdout, ifa_family_color(af), "%s", str);
+ print_string(PRINT_FP, NULL, " %s=", key);
+ print_color_string(PRINT_ANY, ifa_family_color(af), key, "%s",
+ str);
}
}
@@ -485,12 +487,13 @@ static void print_iface(int index)
{
const char *ifname;
- printf(" ifindex=%d", index);
+ print_int(PRINT_ANY, "ifindex", " ifindex=%d", index);
ifname = index ? ll_index_to_name(index) : NULL;
if (ifname) {
- printf(" dev=");
- color_fprintf(stdout, COLOR_IFNAME, "%s", ifname);
+ print_string(PRINT_FP, NULL, " dev=", NULL);
+ print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "%s",
+ ifname);
}
}
@@ -509,30 +512,34 @@ static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
if (n->nlmsg_type != genl_family)
return 0;
+ open_json_object(NULL);
+
if (timestamp)
print_timestamp(stdout);
- if (ghdr->cmd >= ARRAY_SIZE(event_to_str)) {
- printf("[UNKNOWN %u]\n", ghdr->cmd);
+ if (ghdr->cmd >= ARRAY_SIZE(event_to_str) ||
+ event_to_str[ghdr->cmd] == NULL) {
+ char event[40];
+
+ snprintf(event, sizeof(event), "UNKNOWN %u", ghdr->cmd);
+ print_string(PRINT_ANY, "event", "[%s]", event);
goto out;
}
- if (event_to_str[ghdr->cmd] == NULL) {
- printf("[UNKNOWN %u]\n", ghdr->cmd);
- goto out;
- }
-
- printf("[%16s]", event_to_str[ghdr->cmd]);
+ print_string(PRINT_ANY, "event", "[%16s]", event_to_str[ghdr->cmd]);
parse_rtattr(tb, MPTCP_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
if (tb[MPTCP_ATTR_TOKEN])
- printf(" token=%08x", rta_getattr_u32(tb[MPTCP_ATTR_TOKEN]));
+ print_0xhex(PRINT_ANY, "token", " token=%08x",
+ rta_getattr_u32(tb[MPTCP_ATTR_TOKEN]));
if (tb[MPTCP_ATTR_REM_ID])
- printf(" remid=%u", rta_getattr_u8(tb[MPTCP_ATTR_REM_ID]));
+ print_uint(PRINT_ANY, "remid", " remid=%u",
+ rta_getattr_u8(tb[MPTCP_ATTR_REM_ID]));
if (tb[MPTCP_ATTR_LOC_ID])
- printf(" locid=%u", rta_getattr_u8(tb[MPTCP_ATTR_LOC_ID]));
+ print_uint(PRINT_ANY, "locid", " locid=%u",
+ rta_getattr_u8(tb[MPTCP_ATTR_LOC_ID]));
if (tb[MPTCP_ATTR_SADDR4])
print_addr("saddr4", AF_INET, tb[MPTCP_ATTR_SADDR4]);
@@ -543,49 +550,74 @@ static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
if (tb[MPTCP_ATTR_DADDR6])
print_addr("daddr6", AF_INET6, tb[MPTCP_ATTR_DADDR6]);
if (tb[MPTCP_ATTR_SPORT])
- printf(" sport=%u", rta_getattr_be16(tb[MPTCP_ATTR_SPORT]));
+ print_uint(PRINT_ANY, "sport", " sport=%u",
+ rta_getattr_be16(tb[MPTCP_ATTR_SPORT]));
if (tb[MPTCP_ATTR_DPORT])
- printf(" dport=%u", rta_getattr_be16(tb[MPTCP_ATTR_DPORT]));
+ print_uint(PRINT_ANY, "dport", " dport=%u",
+ rta_getattr_be16(tb[MPTCP_ATTR_DPORT]));
if (tb[MPTCP_ATTR_BACKUP])
- printf(" backup=%u", rta_getattr_u8(tb[MPTCP_ATTR_BACKUP]));
+ print_uint(PRINT_ANY, "backup", " backup=%u",
+ rta_getattr_u8(tb[MPTCP_ATTR_BACKUP]));
if (tb[MPTCP_ATTR_ERROR])
- printf(" error=%u", rta_getattr_u8(tb[MPTCP_ATTR_ERROR]));
+ print_uint(PRINT_ANY, "error", " error=%u",
+ rta_getattr_u8(tb[MPTCP_ATTR_ERROR]));
if (tb[MPTCP_ATTR_TIMEOUT])
- printf(" timeout=%u", rta_getattr_u32(tb[MPTCP_ATTR_TIMEOUT]));
+ print_uint(PRINT_ANY, "timeout", " timeout=%u",
+ rta_getattr_u32(tb[MPTCP_ATTR_TIMEOUT]));
if (tb[MPTCP_ATTR_IF_IDX])
print_iface(rta_getattr_s32(tb[MPTCP_ATTR_IF_IDX]));
if (tb[MPTCP_ATTR_RESET_REASON])
- printf(" reset_reason=%u", rta_getattr_u32(tb[MPTCP_ATTR_RESET_REASON]));
+ print_uint(PRINT_ANY, "reset_reason", " reset_reason=%u",
+ rta_getattr_u32(tb[MPTCP_ATTR_RESET_REASON]));
if (tb[MPTCP_ATTR_RESET_FLAGS])
- printf(" reset_flags=0x%x", rta_getattr_u32(tb[MPTCP_ATTR_RESET_FLAGS]));
+ print_0xhex(PRINT_ANY, "reset_flags", " reset_flags=%#x",
+ rta_getattr_u32(tb[MPTCP_ATTR_RESET_FLAGS]));
if (tb[MPTCP_ATTR_FLAGS])
flags = rta_getattr_u16(tb[MPTCP_ATTR_FLAGS]);
if ((flags & MPTCP_PM_EV_FLAG_SERVER_SIDE) ||
(tb[MPTCP_ATTR_SERVER_SIDE] && rta_getattr_u8(tb[MPTCP_ATTR_SERVER_SIDE]))) {
flags &= ~MPTCP_PM_EV_FLAG_SERVER_SIDE;
- printf(" server_side");
+ print_string(PRINT_FP, NULL, " server_side", NULL);
+ print_bool(PRINT_JSON, "server_side", NULL, true);
}
if (flags & MPTCP_PM_EV_FLAG_DENY_JOIN_ID0) {
flags &= ~MPTCP_PM_EV_FLAG_DENY_JOIN_ID0;
- printf(" deny_join_id0");
+ print_string(PRINT_FP, NULL, " deny_join_id0", NULL);
+ print_bool(PRINT_JSON, "deny_join_id0", NULL, true);
}
if (flags) /* remaining bits */
- printf(" flags=0x%x", flags);
+ print_0xhex(PRINT_ANY, "flags", " flags=%#x", flags);
- puts("");
out:
+ print_nl();
+ close_json_object();
fflush(stdout);
+
return 0;
}
+static void sig_exit(int signo)
+{
+ exit(0);
+}
+
static int mptcp_monitor(void)
{
+ struct sigaction sa = { .sa_handler = sig_exit,
+ .sa_flags = SA_RESTART };
+
if (genl_add_mcast_grp(&genl_rth, genl_family, MPTCP_PM_EV_GRP_NAME) < 0) {
perror("can't subscribe to mptcp events");
return 1;
}
+ new_json_obj(json);
+ atexit(delete_json_obj);
+
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
if (rtnl_listen(&genl_rth, mptcp_monitor_msg, stdout) < 0)
return 2;
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread