[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251108202310.31505-1-stephen@networkplumber.org>
Date: Sat, 8 Nov 2025 12:22:54 -0800
From: Stephen Hemminger <stephen@...workplumber.org>
To: netdev@...r.kernel.org
Cc: Stephen Hemminger <stephen@...workplumber.org>
Subject: [PATCH iproute2-next] genl: add json support
Cleaup the old code and support for JSON.
Signed-off-by: Stephen Hemminger <stephen@...workplumber.org>
---
genl/ctrl.c | 253 +++++++++++++++++++++++--------------------
genl/genl.c | 8 +-
include/libnetlink.h | 2 +-
lib/libnetlink.c | 87 +++++++++------
man/man8/genl.8 | 16 ++-
5 files changed, 210 insertions(+), 156 deletions(-)
diff --git a/genl/ctrl.c b/genl/ctrl.c
index 72a9b013..75856d74 100644
--- a/genl/ctrl.c
+++ b/genl/ctrl.c
@@ -36,66 +36,139 @@ static int usage(void)
return -1;
}
-static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
+static void print_ctrl_flag(const char *json_str, const char *fp_str)
{
- fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
- if (!fl) {
- fprintf(fp, "\n");
- return;
- }
- fprintf(fp, "\t\t ");
+ print_string(PRINT_JSON, NULL, NULL, json_str);
+ print_string(PRINT_FP, NULL, fp_str, NULL);
+}
+
+static void print_ctrl_cmd_flags(__u32 fl)
+{
+ print_0xhex(PRINT_FP, "flags", "\n\t\tCapabilities (0x%x):\n", fl);
+ open_json_array(PRINT_JSON, "capabilities");
+
+ if (fl != 0)
+ print_string(PRINT_FP, NULL, "\t\t ", NULL);
if (fl & GENL_ADMIN_PERM)
- fprintf(fp, " requires admin permission;");
+ print_ctrl_flag("admin", " requires admin permission;");
if (fl & GENL_CMD_CAP_DO)
- fprintf(fp, " can doit;");
+ print_ctrl_flag("do", " can doit;");
if (fl & GENL_CMD_CAP_DUMP)
- fprintf(fp, " can dumpit;");
+ print_ctrl_flag("dump", " can dumpit;");
if (fl & GENL_CMD_CAP_HASPOL)
- fprintf(fp, " has policy");
-
- fprintf(fp, "\n");
+ print_ctrl_flag("policy", " has policy");
+ close_json_array(PRINT_ANY, "\n");
}
-static int print_ctrl_cmds(FILE *fp, struct rtattr *arg)
+static void print_ctrl_cmd(const struct rtattr *arg)
{
struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
- if (arg == NULL)
- return -1;
-
parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg);
- if (tb[CTRL_ATTR_OP_ID]) {
- __u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]);
- fprintf(fp, " ID-0x%x ",*id);
- }
+ if (tb[CTRL_ATTR_OP_ID])
+ print_0xhex(PRINT_ANY, "id", " ID-0x%x ", rta_getattr_u32(tb[CTRL_ATTR_OP_ID]));
+
/* we are only gonna do this for newer version of the controller */
- if (tb[CTRL_ATTR_OP_FLAGS]) {
- __u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]);
- print_ctrl_cmd_flags(fp, *fl);
+ if (tb[CTRL_ATTR_OP_FLAGS])
+ print_ctrl_cmd_flags(rta_getattr_u32(tb[CTRL_ATTR_OP_FLAGS]));
+}
+
+static void
+print_ctrl_ops(const struct rtattr *attr)
+{
+ struct rtattr *tb2[GENL_MAX_FAM_OPS];
+ unsigned int i;
+
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, attr);
+
+ open_json_array(PRINT_JSON, "operations");
+ print_string(PRINT_FP, NULL, "\tcommands supported: \n", NULL);
+
+ for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
+ if (!tb2[i])
+ continue;
+
+ open_json_object(NULL);
+ print_uint(PRINT_FP, NULL, "\t\t#%u: ", i);
+ print_ctrl_cmd(tb2[i]);
+ print_string(PRINT_FP, NULL, "\n", NULL);
+ close_json_object();
}
- return 0;
+ /* end of family::cmds definitions .. */
+ close_json_array(PRINT_JSON, NULL);
+ print_string(PRINT_FP, NULL, "\n", NULL);
}
-static int print_ctrl_grp(FILE *fp, struct rtattr *arg)
+static void print_ctrl_grp(const struct rtattr *arg)
{
struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
- if (arg == NULL)
- return -1;
+ open_json_object(NULL);
parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
- if (tb[2]) {
- __u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]);
- fprintf(fp, " ID-0x%x ",*id);
+ if (tb[CTRL_ATTR_MCAST_GRP_ID])
+ print_0xhex(PRINT_ANY, "id", " ID-0x%x ",
+ rta_getattr_u32(tb[CTRL_ATTR_MCAST_GRP_ID]));
+ if (tb[CTRL_ATTR_MCAST_GRP_NAME]) {
+ const char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+ print_string(PRINT_ANY, "name", " name: %s ", name);
}
- if (tb[1]) {
- char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
- fprintf(fp, " name: %s ", name);
+ close_json_object();
+}
+
+static void print_ops(const struct rtattr *attr)
+{
+ const struct rtattr *pos;
+
+ open_json_array(PRINT_JSON, "op");
+
+ rtattr_for_each_nested(pos, attr) {
+ struct rtattr *ptb[CTRL_ATTR_POLICY_DUMP_MAX + 1];
+ struct rtattr *pattrs = RTA_DATA(pos);
+ int plen = RTA_PAYLOAD(pos);
+
+ parse_rtattr_flags(ptb, CTRL_ATTR_POLICY_DUMP_MAX, pattrs, plen, NLA_F_NESTED);
+
+ print_uint(PRINT_ANY, "bits", " op %d policies:", pos->rta_type & ~NLA_F_NESTED);
+
+ if (ptb[CTRL_ATTR_POLICY_DO])
+ print_uint(PRINT_ANY, "do", " do=%u",
+ rta_getattr_u32(ptb[CTRL_ATTR_POLICY_DO]));
+
+ if (ptb[CTRL_ATTR_POLICY_DUMP])
+ print_uint(PRINT_ANY, "dump", " dump=%d",
+ rta_getattr_u32(ptb[CTRL_ATTR_POLICY_DUMP]));
+
}
- return 0;
+ close_json_array(PRINT_JSON, NULL);
+}
+
+static void print_ctrl_mcast(const struct rtattr *attr)
+{
+ struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
+ unsigned int i;
+
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS, attr);
+ open_json_array(PRINT_JSON, "mcast");
+ print_string(PRINT_FP, NULL, "\tmulticast groups:\n", NULL);
+
+ for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
+ if (!tb2[i])
+ continue;
+
+ print_uint(PRINT_FP, NULL, "\t\t#%d: ", i);
+ print_ctrl_grp(tb2[i]);
+
+ /* for next group */
+ print_string(PRINT_FP, NULL, "\n", NULL);
+ }
+
+ /* end of family::groups definitions .. */
+ close_json_array(PRINT_JSON, NULL);
+ print_string(PRINT_FP, NULL, "\n", NULL);
}
/*
@@ -137,98 +210,36 @@ static int print_ctrl(struct rtnl_ctrl_data *ctrl,
parse_rtattr_flags(tb, CTRL_ATTR_MAX, attrs, len, NLA_F_NESTED);
if (tb[CTRL_ATTR_FAMILY_NAME]) {
- char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
- fprintf(fp, "\nName: %s\n",name);
- }
- if (tb[CTRL_ATTR_FAMILY_ID]) {
- __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
- fprintf(fp, "\tID: 0x%x ",*id);
- }
- if (tb[CTRL_ATTR_VERSION]) {
- __u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
- fprintf(fp, " Version: 0x%x ",*v);
- }
- if (tb[CTRL_ATTR_HDRSIZE]) {
- __u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
- fprintf(fp, " header size: %d ",*h);
+ const char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
+ print_string(PRINT_ANY, "family", "\nName: %s\n", name);
}
- if (tb[CTRL_ATTR_MAXATTR]) {
- __u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
- fprintf(fp, " max attribs: %d ",*ma);
- }
- if (tb[CTRL_ATTR_OP_POLICY]) {
- const struct rtattr *pos;
-
- rtattr_for_each_nested(pos, tb[CTRL_ATTR_OP_POLICY]) {
- struct rtattr *ptb[CTRL_ATTR_POLICY_DUMP_MAX + 1];
- struct rtattr *pattrs = RTA_DATA(pos);
- int plen = RTA_PAYLOAD(pos);
- parse_rtattr_flags(ptb, CTRL_ATTR_POLICY_DUMP_MAX,
- pattrs, plen, NLA_F_NESTED);
+ if (tb[CTRL_ATTR_FAMILY_ID])
+ print_0xhex(PRINT_ANY, "id", "\tID: 0x%x ", rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]));
- fprintf(fp, " op %d policies:",
- pos->rta_type & ~NLA_F_NESTED);
+ if (tb[CTRL_ATTR_VERSION])
+ print_0xhex(PRINT_ANY, "version", " Version: 0x%x ", rta_getattr_u32(tb[CTRL_ATTR_VERSION]));
- if (ptb[CTRL_ATTR_POLICY_DO]) {
- __u32 *v = RTA_DATA(ptb[CTRL_ATTR_POLICY_DO]);
+ if (tb[CTRL_ATTR_HDRSIZE])
+ print_uint(PRINT_ANY, "header_size", " header size: %u ", rta_getattr_u32(tb[CTRL_ATTR_HDRSIZE]));
- fprintf(fp, " do=%d", *v);
- }
+ if (tb[CTRL_ATTR_MAXATTR])
+ print_uint(PRINT_ANY, "max_attr", " max attribs: %u ", rta_getattr_u32(tb[CTRL_ATTR_MAXATTR]));
- if (ptb[CTRL_ATTR_POLICY_DUMP]) {
- __u32 *v = RTA_DATA(ptb[CTRL_ATTR_POLICY_DUMP]);
+ if (tb[CTRL_ATTR_OP_POLICY])
+ print_ops(tb[CTRL_ATTR_OP_POLICY]);
- fprintf(fp, " dump=%d", *v);
- }
- }
- }
if (tb[CTRL_ATTR_POLICY])
- nl_print_policy(tb[CTRL_ATTR_POLICY], fp);
+ nl_print_policy(tb[CTRL_ATTR_POLICY]);
/* end of family definitions .. */
- fprintf(fp,"\n");
- if (tb[CTRL_ATTR_OPS]) {
- struct rtattr *tb2[GENL_MAX_FAM_OPS];
- int i=0;
- parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
- fprintf(fp, "\tcommands supported: \n");
- for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
- if (tb2[i]) {
- fprintf(fp, "\t\t#%d: ", i);
- if (0 > print_ctrl_cmds(fp, tb2[i])) {
- fprintf(fp, "Error printing command\n");
- }
- /* for next command */
- fprintf(fp,"\n");
- }
- }
-
- /* end of family::cmds definitions .. */
- fprintf(fp,"\n");
- }
+ print_string(PRINT_FP, NULL, "\n", NULL);
- if (tb[CTRL_ATTR_MCAST_GROUPS]) {
- struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
- int i;
-
- parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
- tb[CTRL_ATTR_MCAST_GROUPS]);
- fprintf(fp, "\tmulticast groups:\n");
-
- for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
- if (tb2[i]) {
- fprintf(fp, "\t\t#%d: ", i);
- if (0 > print_ctrl_grp(fp, tb2[i]))
- fprintf(fp, "Error printing group\n");
- /* for next group */
- fprintf(fp,"\n");
- }
- }
+ if (tb[CTRL_ATTR_OPS])
+ print_ctrl_ops(tb[CTRL_ATTR_OPS]);
- /* end of family::groups definitions .. */
- fprintf(fp,"\n");
- }
+ if (tb[CTRL_ATTR_MCAST_GROUPS])
+ print_ctrl_mcast(tb[CTRL_ATTR_MCAST_GROUPS]);
fflush(fp);
return 0;
@@ -236,7 +247,10 @@ static int print_ctrl(struct rtnl_ctrl_data *ctrl,
static int print_ctrl2(struct nlmsghdr *n, void *arg)
{
- return print_ctrl(NULL, n, arg);
+ open_json_object(NULL);
+ print_ctrl(NULL, n, arg);
+ close_json_object();
+ return 0;
}
static int ctrl_list(int cmd, int argc, char **argv)
@@ -291,6 +305,8 @@ static int ctrl_list(int cmd, int argc, char **argv)
}
}
+ new_json_obj(json);
+
if (cmd == CTRL_CMD_GETFAMILY) {
if (rtnl_talk(&rth, nlh, &answer) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
@@ -319,6 +335,7 @@ static int ctrl_list(int cmd, int argc, char **argv)
ret = 0;
ctrl_done:
+ delete_json_obj();
free(answer);
rtnl_close(&rth);
return ret;
@@ -335,8 +352,8 @@ static int ctrl_listen(int argc, char **argv)
if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
exit(2);
-
- rtnl_close(&rth);
+
+ rtnl_close(&rth);
return 0;
}
diff --git a/genl/genl.c b/genl/genl.c
index 85cc73bb..b497a3ad 100644
--- a/genl/genl.c
+++ b/genl/genl.c
@@ -24,6 +24,7 @@
int show_stats;
int show_details;
int show_raw;
+int json;
static void *BODY;
static struct genl_util *genl_list;
@@ -96,7 +97,8 @@ static void usage(void)
fprintf(stderr,
"Usage: genl [ OPTIONS ] OBJECT [help] }\n"
"where OBJECT := { ctrl etc }\n"
- " OPTIONS := { -s[tatistics] | -d[etails] | -r[aw] | -V[ersion] | -h[elp] }\n");
+ " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[aw] |\n"
+ " -j[son] | -p[retty] }\n");
exit(-1);
}
@@ -115,6 +117,10 @@ int main(int argc, char **argv)
} else if (matches(argv[1], "-Version") == 0) {
printf("genl utility, iproute2-%s\n", version);
exit(0);
+ } else if (matches(argv[1], "-json") == 0) {
+ ++json;
+ } else if (matches(argv[1], "-pretty") == 0) {
+ ++pretty;
} else if (matches(argv[1], "-help") == 0) {
usage();
} else {
diff --git a/include/libnetlink.h b/include/libnetlink.h
index 7074e913..3cd0931a 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -374,6 +374,6 @@ int rtnl_from_file(FILE *, rtnl_listen_filter_t handler,
RTA_OK(attr, RTA_PAYLOAD(nest) - ((char *)(attr) - (char *)RTA_DATA((nest)))); \
(attr) = RTA_TAIL((attr)))
-void nl_print_policy(const struct rtattr *attr, FILE *fp);
+void nl_print_policy(const struct rtattr *attr);
#endif /* __LIBNETLINK_H__ */
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index e2b284e6..305bd4b0 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -23,6 +23,7 @@
#include <linux/nexthop.h>
#include "libnetlink.h"
+#include "json_print.h"
#include "utils.h"
#ifndef __aligned
@@ -1146,7 +1147,7 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
return __rtnl_talk_iov(rtnl, &iov, 1, answer, show_rtnl_err, errfn);
}
-int rtnl_echo_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, int json,
+int rtnl_echo_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, int use_json,
int (*print_info)(struct nlmsghdr *n, void *arg))
{
struct nlmsghdr *answer;
@@ -1158,7 +1159,7 @@ int rtnl_echo_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, int json,
if (ret)
return ret;
- new_json_obj(json);
+ new_json_obj(use_json);
open_json_object(NULL);
print_info(answer, stdout);
close_json_object();
@@ -1585,52 +1586,70 @@ static const char *get_nla_type_str(unsigned int attr)
}
}
-void nl_print_policy(const struct rtattr *attr, FILE *fp)
+static void _nl_print_policy(const struct rtattr *attr)
{
- const struct rtattr *pos;
+ struct rtattr *tp[NL_POLICY_TYPE_ATTR_MAX + 1];
- rtattr_for_each_nested(pos, attr) {
- const struct rtattr *attr;
+ parse_rtattr_nested(tp, ARRAY_SIZE(tp) - 1, attr);
- fprintf(fp, " policy[%u]:", pos->rta_type & ~NLA_F_NESTED);
+ if (tp[NL_POLICY_TYPE_ATTR_TYPE]) {
+ print_uint(PRINT_ANY, "attr", "attr[%u]:",
+ attr->rta_type & ~NLA_F_NESTED);
+ print_string(PRINT_ANY, "type", " type=%s",
+ get_nla_type_str(rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_TYPE])));
+ }
- rtattr_for_each_nested(attr, pos) {
- struct rtattr *tp[NL_POLICY_TYPE_ATTR_MAX + 1];
+ if (tp[NL_POLICY_TYPE_ATTR_POLICY_IDX])
+ print_uint(PRINT_ANY, "policy", " policy:%u",
+ rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_IDX]));
- parse_rtattr_nested(tp, ARRAY_SIZE(tp) - 1, attr);
+ if (tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE])
+ print_uint(PRINT_ANY, "maxattr", " maxattr:%u",
+ rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE]));
- if (tp[NL_POLICY_TYPE_ATTR_TYPE])
- fprintf(fp, "attr[%u]: type=%s",
- attr->rta_type & ~NLA_F_NESTED,
- get_nla_type_str(rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_TYPE])));
+ if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]) {
+ print_s64(PRINT_ANY, "min_value", " range:[%lld",
+ rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S]));
+ print_s64(PRINT_ANY, "max_value", "%lld]",
+ rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]));
+ }
- if (tp[NL_POLICY_TYPE_ATTR_POLICY_IDX])
- fprintf(fp, " policy:%u",
- rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_IDX]));
+ if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]) {
+ print_u64(PRINT_ANY, "min_value", " range:[%llu",
+ rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U]));
+ print_u64(PRINT_ANY, "max_value", "%llu]",
+ rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]));
+ }
- if (tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE])
- fprintf(fp, " maxattr:%u",
- rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE]));
+ if (tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH])
+ print_uint(PRINT_ANY, "min_length", " min len:%u",
+ rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH]));
- if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S])
- fprintf(fp, " range:[%lld,%lld]",
- (signed long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S]),
- (signed long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]));
+ if (tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH])
+ print_uint(PRINT_ANY, "max_length", " max len:%u",
+ rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH]));
+}
+
+void nl_print_policy(const struct rtattr *attr)
+{
+ const struct rtattr *pos;
+
+ open_json_array(PRINT_JSON, NULL);
+ rtattr_for_each_nested(pos, attr) {
+ const struct rtattr *a;
- if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U])
- fprintf(fp, " range:[%llu,%llu]",
- (unsigned long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U]),
- (unsigned long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]));
+ open_json_array(PRINT_JSON, NULL);
- if (tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH])
- fprintf(fp, " min len:%u",
- rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH]));
+ print_uint(PRINT_ANY, "policy", " policy[%u]:", pos->rta_type & ~NLA_F_NESTED);
- if (tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH])
- fprintf(fp, " max len:%u",
- rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH]));
+ rtattr_for_each_nested(a, pos) {
+ open_json_object(NULL);
+ _nl_print_policy(a);
+ close_json_object();
}
+ close_json_array(PRINT_JSON, NULL);
}
+ close_json_array(PRINT_JSON, NULL);
}
int rtnl_tunneldump_req(struct rtnl_handle *rth, int family, int ifindex,
diff --git a/man/man8/genl.8 b/man/man8/genl.8
index b9de594d..7ffde866 100644
--- a/man/man8/genl.8
+++ b/man/man8/genl.8
@@ -4,11 +4,17 @@ genl \- generic netlink utility frontend
.SH SYNOPSIS
.in +8
.ti -8
-.BR genl " [ " -s [ tatistics "] ] [ " -d [ etails "] ] [ " -r [ aw "] ] " OBJECT
-
+.BR genl "[ " OPTIONS " ] " OBJECT
.ti -8
.BR genl " { " -V [ ersion "] | " -h [ elp "] }"
+.ti -8
+.IR OPTIONS " := { "
+\fB\-s\fR[\fItatistics\fR] |
+\fB\-d\fR[\fIetails\fR] |
+\fB\-j\fR[son\fR] |
+\fB\-p\fR[retty\fR] }
+
.ti -8
.IR OBJECT " := { "
.B ctrl
@@ -66,6 +72,12 @@ Show object statistics.
.B \-d, \-details
Show object details.
.TP
+.B -j, \-json
+Output results in JavaScript Object Notation (JSON).
+.TP
+.B -p, \-pretty
+Add indentation for readability.
+.TP
.B \-r, \-raw
Dump raw output only.
.SH SEE ALSO
--
2.51.0
Powered by blists - more mailing lists