Cleanup the old code and support for JSON output. The non-json output is the same as before the patch. Signed-off-by: Stephen Hemminger --- v2 - shorten lines and extra blanks in non-json output genl/ctrl.c | 261 ++++++++++++++++++++++++------------------- genl/genl.c | 8 +- include/libnetlink.h | 2 +- lib/libnetlink.c | 87 +++++++++------ man/man8/genl.8 | 16 ++- 5 files changed, 218 insertions(+), 156 deletions(-) diff --git a/genl/ctrl.c b/genl/ctrl.c index 72a9b013..17930b30 100644 --- a/genl/ctrl.c +++ b/genl/ctrl.c @@ -36,66 +36,143 @@ 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, " %s", fp_str); +} + +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 +214,40 @@ 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 +255,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 +313,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 +343,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 +360,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 #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