On Thu, Sep 10, 2015 at 09:35:03PM +0200, Matthias Tafelmeier wrote: > This patch just adds the -j and --json flag to ss. Also it ensures proper > stats components bracketization – that goes for ex. TCP, UDP, NETLINK etc. > > Moreover, this patch prevents human readable headers to be printed. > > Most importantly, with this patch, the new prepared interface to the > generic output handlers is used to replace the accustomed output > logic. > > Signed-off-by: Matthias Tafelmeier <matthias.tafelme...@gmx.net> > Suggested-by: Hagen Paul Pfeifer <ha...@jauu.net> > --- > misc/ss.c | 487 > ++++++++++++++++++++++++++++++++++---------------------------- > 1 file changed, 266 insertions(+), 221 deletions(-) > > diff --git a/misc/ss.c b/misc/ss.c > index e7ea041..7a1b6eb 100644 > --- a/misc/ss.c > +++ b/misc/ss.c > @@ -26,6 +26,7 @@ > #include <fnmatch.h> > #include <getopt.h> > #include <stdbool.h> > +#include <json_writer.h> > > #include "ss_types.h" > #include "utils.h" > @@ -34,6 +35,9 @@ > #include "libnetlink.h" > #include "namespace.h" > #include "SNAPSHOT.h" > +#include "ss_out_fmt.h" > +#include "ss_json_fmt.h" > +#include "ss_types.h" > > #include <linux/tcp.h> > #include <linux/sock_diag.h> > @@ -101,6 +105,7 @@ int show_sock_ctx = 0; > /* If show_users & show_proc_ctx only do user_ent_hash_build() once */ > int user_ent_hash_build_init = 0; > int follow_events = 0; > +int json_output = 0; > > int netid_width; > int state_width; > @@ -109,11 +114,14 @@ int addr_width; > int serv_width; > int screen_width; > > +enum out_fmt_type fmt_type = FMT_HR; > + > static const char *TCP_PROTO = "tcp"; > static const char *UDP_PROTO = "udp"; > static const char *RAW_PROTO = "raw"; > static const char *dg_proto = NULL; > > +json_writer_t *json_wr;
Not sure if doable, but IMHO this should be hidden in ss_json_fmt.c. > #define PACKET_DBM ((1<<PACKET_DG_DB)|(1<<PACKET_R_DB)) > #define UNIX_DBM ((1<<UNIX_DG_DB)|(1<<UNIX_ST_DB)|(1<<UNIX_SQ_DB)) > @@ -716,7 +724,6 @@ static int is_ephemeral(int port) > return (port >= ip_local_port_min && port<= ip_local_port_max); > } > > - Export changes like this one into a dedicated patch at the start of the series? > static const char *__resolve_service(int port) > { > struct scache *c; > @@ -790,7 +797,8 @@ do_numeric: > return buf; > } > > -static void inet_addr_print(const inet_prefix *a, int port, unsigned int > ifindex) > +static void inet_addr_print(const inet_prefix *a, int port, > + unsigned int ifindex, char *peer_kind) > { > char buf[1024]; > const char *ap = buf; > @@ -818,8 +826,8 @@ static void inet_addr_print(const inet_prefix *a, int > port, unsigned int ifindex > est_len -= strlen(ifname) + 1; /* +1 for percent char */ > } > > - sock_addr_print_width(est_len, ap, ":", serv_width, > resolve_service(port), > - ifname); > + sock_addr_fmt(ap, est_len, ":", serv_width, resolve_service(port), > + ifname, peer_kind); > } > > static int inet2_addr_match(const inet_prefix *a, const inet_prefix *p, > @@ -1351,21 +1359,29 @@ static void inet_stats_print(struct sockstat *s, int > protocol) > { > char *buf = NULL; > > - sock_state_print(s, proto_name(protocol)); > + sock_state_fmt(s, sstate_name, proto_name(protocol), > + netid_width, state_width); > > - inet_addr_print(&s->local, s->lport, s->iface); > - inet_addr_print(&s->remote, s->rport, 0); > + if (json_output) { > + jsonw_name(json_wr, "peers"); > + jsonw_start_object(json_wr); > + } How about an additional callback: fmt_new_object("peers"); The hr variant will be a no op, the json variant uses it's private (see above) json_wr object. > + inet_addr_print(&s->local, s->lport, s->iface, "local"); > + inet_addr_print(&s->remote, s->rport, 0, "remote"); > + if (json_output) > + jsonw_end_object(json_wr); > > if (show_proc_ctx || show_sock_ctx) { > if (find_entry(s->ino, &buf, > - (show_proc_ctx & show_sock_ctx) ? > - PROC_SOCK_CTX : PROC_CTX) > 0) { > - printf(" users:(%s)", buf); > + (show_proc_ctx & show_sock_ctx) ? > + PROC_SOCK_CTX : PROC_CTX) > 0) { > + sock_users_fmt(buf); > free(buf); > } > } else if (show_users) { > if (find_entry(s->ino, &buf, USERS) > 0) { > - printf(" users:(%s)", buf); > + sock_users_fmt(buf); > free(buf); > } > } > @@ -1469,16 +1485,16 @@ static int tcp_show_line(char *line, const struct > filter *f, int family) > inet_stats_print(&s.ss, IPPROTO_TCP); > > if (show_options) > - tcp_timer_print(&s); > + tcp_timer_fmt(&s); > > if (show_details) { > - sock_details_print(&s.ss); > + sock_details_fmt(&s.ss, GENERIC_DETAIL, 0, 0); > if (opt[0]) > - printf(" opt:\"%s\"", opt); > + opt_fmt(opt); > } > > if (show_tcpinfo) > - tcp_stats_print(&s); > + tcp_stats_fmt(&s); > > printf("\n"); > return 0; > @@ -1522,31 +1538,14 @@ static void print_skmeminfo(struct rtattr *tb[], int > attrtype) > const struct inet_diag_meminfo *minfo = > RTA_DATA(tb[INET_DIAG_MEMINFO]); > > - printf(" mem:(r%u,w%u,f%u,t%u)", > - minfo->idiag_rmem, > - minfo->idiag_wmem, > - minfo->idiag_fmem, > - minfo->idiag_tmem); > + mem_fmt(minfo); > } > return; > } > > skmeminfo = RTA_DATA(tb[attrtype]); > > - printf(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u", > - skmeminfo[SK_MEMINFO_RMEM_ALLOC], > - skmeminfo[SK_MEMINFO_RCVBUF], > - skmeminfo[SK_MEMINFO_WMEM_ALLOC], > - skmeminfo[SK_MEMINFO_SNDBUF], > - skmeminfo[SK_MEMINFO_FWD_ALLOC], > - skmeminfo[SK_MEMINFO_WMEM_QUEUED], > - skmeminfo[SK_MEMINFO_OPTMEM]); > - > - if (RTA_PAYLOAD(tb[attrtype]) >= > - (SK_MEMINFO_BACKLOG + 1) * sizeof(__u32)) > - printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]); > - > - printf(")"); > + skmem_fmt(skmeminfo, tb, attrtype); > } > > #define TCPI_HAS_OPT(info, opt) !!(info->tcpi_options & (opt)) > @@ -1659,8 +1658,11 @@ static void tcp_show_info(const struct nlmsghdr *nlh, > struct inet_diag_msg *r, > s.bytes_received = info->tcpi_bytes_received; > s.segs_out = info->tcpi_segs_out; > s.segs_in = info->tcpi_segs_in; > - tcp_stats_print(&s); > - free(s.dctcp); > + tcp_stats_fmt(&s); > + if (s.dctcp) > + free(s.dctcp); > + if (s.cong_alg) > + free(s.cong_alg); > } > } > > @@ -1684,6 +1686,8 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct > filter *f, int protocol) > s.iface = r->id.idiag_if; > s.sk = cookie_sk_get(&r->id.idiag_cookie[0]); > > + jsonw_start_object(json_wr); > + > if (s.local.family == AF_INET) { > s.local.bytelen = s.remote.bytelen = 4; > } else { > @@ -1707,29 +1711,27 @@ static int inet_show_sock(struct nlmsghdr *nlh, > struct filter *f, int protocol) > t.timer = r->idiag_timer; > t.timeout = r->idiag_expires; > t.retrans = r->idiag_retrans; > - tcp_timer_print(&t); > + tcp_timer_fmt(&t); > } > > if (show_details) { > - sock_details_print(&s); > - if (s.local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) { > - unsigned char v6only; > - v6only = *(__u8 *)RTA_DATA(tb[INET_DIAG_SKV6ONLY]); > - printf(" v6only:%u", v6only); > - } > + sock_details_fmt(&s, GENERIC_DETAIL, 0, 0); Looks like v6only state got optimized out. At least I don't see it being exported in the sock_details_fmt callbacks. > if (tb[INET_DIAG_SHUTDOWN]) { > unsigned char mask; > mask = *(__u8 *)RTA_DATA(tb[INET_DIAG_SHUTDOWN]); > - printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : > '>'); > + sock_conn_fmt(mask); While it is questionable in general whether json output should follow this kind of symbolism, it appears that both callbacks do the same INET_DIAG_SHUTDOWN value encoding. Unnecessary and error-prone code duplication IMO. As a side-note, one could also implement sock_conn_fmt() to accept a format-string, although that's maybe not worth the effort. > } > } > > if (show_mem || show_tcpinfo) { > - printf("\n\t"); Hmm. Doesn't this change hr output? Not that it's not worth improving, but I don't think it should be part of this series. > tcp_show_info(nlh, r, tb); > } > > - printf("\n"); > + if (json_output) > + jsonw_end_object(json_wr); > + else > + printf("\n"); > + > return 0; > } > > @@ -2080,7 +2082,10 @@ static int dgram_show_line(char *line, const struct > filter *f, int family) > inet_stats_print(&s, dg_proto == UDP_PROTO ? IPPROTO_UDP : 0); > > if (show_details && opt[0]) > - printf(" opt:\"%s\"", opt); > + opt_fmt(opt); > + > + if (json_output) > + jsonw_end_object(json_wr); > > printf("\n"); > return 0; > @@ -2257,27 +2262,39 @@ static void unix_stats_print(struct sockstat *list, > struct filter *f) > continue; > } > > - sock_state_print(s, unix_netid_name(s->type)); > + sock_state_fmt(s, sstate_name, > + unix_netid_name(s->type), netid_width, state_width); > > - sock_addr_print(s->name ?: "*", " ", > - int_to_str(s->lport, port_name), NULL); > - sock_addr_print(peer, " ", int_to_str(s->rport, port_name), > - NULL); > + if (json_output) { > + jsonw_name(json_wr, "peers"); > + jsonw_start_object(json_wr); > + } > + > + sock_addr_fmt(s->name ?: "*", addr_width, > + " ", serv_width, > + int_to_str(s->lport, port_name), > + NULL, "local"); > + sock_addr_fmt(peer, addr_width, " ", serv_width, > + int_to_str(s->rport, port_name), > + NULL, "remote"); > + if (json_output) > + jsonw_end_object(json_wr); > > if (show_proc_ctx || show_sock_ctx) { > if (find_entry(s->ino, &ctx_buf, > - (show_proc_ctx & show_sock_ctx) ? > - PROC_SOCK_CTX : PROC_CTX) > 0) { > - printf(" users:(%s)", ctx_buf); > + (show_proc_ctx & show_sock_ctx) ? > + PROC_SOCK_CTX : PROC_CTX) > 0) { > + sock_users_fmt(ctx_buf); > free(ctx_buf); > } > } else if (show_users) { > if (find_entry(s->ino, &ctx_buf, USERS) > 0) { > - printf(" users:(%s)", ctx_buf); > + sock_users_fmt(ctx_buf); > free(ctx_buf); > } > } > - printf("\n"); > + if (!json_output) > + printf("\n"); > } > } > > @@ -2290,7 +2307,9 @@ static int unix_show_sock(const struct sockaddr_nl > *addr, struct nlmsghdr *nlh, > char name[128]; > struct sockstat stat = { .name = "*", .peer_name = "*" }; > > - parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1), > + jsonw_start_object(json_wr); > + > + parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr *)(r + 1), Yet another unrelated change. Although I surely repeat myself: Fixing up coding style is good, but complicating this patch with it is not. Might go well along with empty line cleanup and other "meta" changes into an initial patch. > nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); > > stat.type = r->udiag_type; > @@ -2323,21 +2342,27 @@ static int unix_show_sock(const struct sockaddr_nl > *addr, struct nlmsghdr *nlh, > return 0; > > unix_stats_print(&stat, f); > - > if (show_mem) { > - printf("\t"); > + if (!json_output) > + printf("\t"); > print_skmeminfo(tb, UNIX_DIAG_MEMINFO); Can't you just move the tab printing into mem_hr_fmt()? (Maybe I miss something here.) > } > if (show_details) { > if (tb[UNIX_DIAG_SHUTDOWN]) { > unsigned char mask; > mask = *(__u8 *)RTA_DATA(tb[UNIX_DIAG_SHUTDOWN]); > - printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : > '>'); > + sock_conn_fmt(mask); > } > } > if (show_mem || show_details) > - printf("\n"); > + if (!json_output) > + printf("\n"); > + > + if (json_output) > + jsonw_end_object(json_wr); > > + if (name) > + free(name); > return 0; > } > > @@ -2479,7 +2504,8 @@ static int packet_stats_print(struct sockstat *s, const > struct filter *f) > return 1; > } > > - sock_state_print(s, s->type == SOCK_RAW ? "p_raw" : "p_dgr"); > + sock_state_fmt(s, sstate_name, s->type == SOCK_RAW ? "p_raw" : "p_dgr", > + netid_width, state_width); > > if (s->prot == 3) > addr = "*"; > @@ -2491,25 +2517,37 @@ static int packet_stats_print(struct sockstat *s, > const struct filter *f) > else > port = xll_index_to_name(s->iface); > > - sock_addr_print(addr, ":", port, NULL); > - sock_addr_print("", "*", "", NULL); > + if (json_output) { > + jsonw_name(json_wr, "peers"); > + jsonw_start_object(json_wr); > + } > + > + sock_addr_fmt(addr, addr_width, ":", > + serv_width, port, > + NULL, "local"); > + sock_addr_fmt("", addr_width, "*", > + serv_width, "", > + NULL, "remote"); addr_width and serv_width belong to hr formatting only, json ignores them altogether. Therefore hide them in hr output code. > + if (json_output) > + jsonw_end_object(json_wr); > > if (show_proc_ctx || show_sock_ctx) { > if (find_entry(s->ino, &buf, > - (show_proc_ctx & show_sock_ctx) ? > - PROC_SOCK_CTX : PROC_CTX) > 0) { > - printf(" users:(%s)", buf); > + (show_proc_ctx & show_sock_ctx) ? > + PROC_SOCK_CTX : PROC_CTX) > 0) { > + sock_users_fmt(buf); Yet another case of unrelated changes (indenting fixup), this time combined with real changes. Hard to spot what's really going on here. > free(buf); > } > } else if (show_users) { > if (find_entry(s->ino, &buf, USERS) > 0) { > - printf(" users:(%s)", buf); > + sock_users_fmt(buf); > free(buf); > } > } > > if (show_details) > - sock_details_print(s); > + sock_details_fmt(s, GENERIC_DETAIL, 0, 0); > > return 0; > } > @@ -2526,7 +2564,9 @@ static int packet_show_sock(const struct sockaddr_nl > *addr, > uint32_t fanout = 0; > bool has_fanout = false; > > - parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1), > + jsonw_start_object(json_wr); > + > + parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr *)(r + 1), > nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); > > /* use /proc/net/packet if all info are not available */ > @@ -2566,60 +2606,9 @@ static int packet_show_sock(const struct sockaddr_nl > *addr, > if (packet_stats_print(&stat, f)) > return 0; > > - if (show_details) { > - if (pinfo) { > - printf("\n\tver:%d", pinfo->pdi_version); > - printf(" cpy_thresh:%d", pinfo->pdi_copy_thresh); > - printf(" flags( "); > - if (pinfo->pdi_flags & PDI_RUNNING) > - printf("running"); > - if (pinfo->pdi_flags & PDI_AUXDATA) > - printf(" auxdata"); > - if (pinfo->pdi_flags & PDI_ORIGDEV) > - printf(" origdev"); > - if (pinfo->pdi_flags & PDI_VNETHDR) > - printf(" vnethdr"); > - if (pinfo->pdi_flags & PDI_LOSS) > - printf(" loss"); > - if (!pinfo->pdi_flags) > - printf("0"); > - printf(" )"); > - } > - if (ring_rx) { > - printf("\n\tring_rx("); > - packet_show_ring(ring_rx); > - printf(")"); > - } > - if (ring_tx) { > - printf("\n\tring_tx("); > - packet_show_ring(ring_tx); > - printf(")"); > - } > - if (has_fanout) { > - uint16_t type = (fanout >> 16) & 0xffff; > - > - printf("\n\tfanout("); > - printf("id:%d,", fanout & 0xffff); > - printf("type:"); > - > - if (type == 0) > - printf("hash"); > - else if (type == 1) > - printf("lb"); > - else if (type == 2) > - printf("cpu"); > - else if (type == 3) > - printf("roll"); > - else if (type == 4) > - printf("random"); > - else if (type == 5) > - printf("qm"); > - else > - printf("0x%x", type); > - > - printf(")"); > - } > - } > + if (show_details) > + packet_details_fmt(pinfo, > + ring_rx, ring_tx, fanout, has_fanout); > > if (show_bpf && tb[PACKET_DIAG_FILTER]) { > struct sock_filter *fil = > @@ -2627,15 +2616,15 @@ static int packet_show_sock(const struct sockaddr_nl > *addr, > int num = RTA_PAYLOAD(tb[PACKET_DIAG_FILTER]) / > sizeof(struct sock_filter); > > - printf("\n\tbpf filter (%d): ", num); > - while (num) { > - printf(" 0x%02x %u %u %u,", > - fil->code, fil->jt, fil->jf, fil->k); > - num--; > - fil++; > - } > + bpf_filter_fmt(fil, num); > } > - printf("\n"); > + > + if (json_output) > + jsonw_end_object(json_wr); > + else > + printf("\n"); > + > + > return 0; > } > > @@ -2713,6 +2702,7 @@ static int netlink_show_one(struct filter *f, > SPRINT_BUF(prot_buf) = {}; > const char *prot_name; > char procname[64] = {}; > + char *rem = "remote"; > > st.state = SS_CLOSE; > st.rq = rq; > @@ -2728,7 +2718,7 @@ static int netlink_show_one(struct filter *f, > return 1; > } > > - sock_state_print(&st, "nl"); > + sock_state_fmt(&st, sstate_name, "nl", netid_width, state_width); > > if (resolve_services) > prot_name = nl_proto_n2a(prot, prot_buf, sizeof(prot_buf)); > @@ -2760,17 +2750,27 @@ static int netlink_show_one(struct filter *f, > int_to_str(pid, procname); > } > > - sock_addr_print(prot_name, ":", procname, NULL); > + if (json_output) { > + jsonw_name(json_wr, "peers"); > + jsonw_start_object(json_wr); > + } > + > + sock_addr_fmt(prot_name, addr_width, ":", serv_width, > + procname, NULL, "local"); > > if (state == NETLINK_CONNECTED) { > char dst_group_buf[30]; > char dst_pid_buf[30]; > - sock_addr_print(int_to_str(dst_group, dst_group_buf), ":", > - int_to_str(dst_pid, dst_pid_buf), NULL); > + sock_addr_fmt(int_to_str(dst_group, dst_group_buf), addr_width, > + ":", serv_width, int_to_str(dst_pid, > dst_pid_buf), > + NULL, rem); > } else { > - sock_addr_print("", "*", "", NULL); > + sock_addr_fmt("", addr_width, "*", serv_width, "", NULL, rem); > } > > + if (json_output) > + jsonw_end_object(json_wr); > + > char *pid_context = NULL; > if (show_proc_ctx) { > /* The pid value will either be: > @@ -2784,16 +2784,11 @@ static int netlink_show_one(struct filter *f, > else if (pid > 0) > getpidcon(pid, &pid_context); > > - if (pid_context != NULL) { > - printf("proc_ctx=%-*s ", serv_width, pid_context); > - free(pid_context); > - } else { > - printf("proc_ctx=%-*s ", serv_width, "unavailable"); > - } > + proc_fmt(serv_width, pid_context); > } > > if (show_details) { > - printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups); > + sock_details_fmt(&st, NETLINK_DETAIL, groups, cb); > } > printf("\n"); > > @@ -2809,7 +2804,9 @@ static int netlink_show_sock(const struct sockaddr_nl > *addr, > int rq = 0, wq = 0; > unsigned long groups = 0; > > - parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr*)(r+1), > + jsonw_start_object(json_wr); > + > + parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr *)(r + 1), > nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); > > if (tb[NETLINK_DIAG_GROUPS] && RTA_PAYLOAD(tb[NETLINK_DIAG_GROUPS])) > @@ -2835,6 +2832,9 @@ static int netlink_show_sock(const struct sockaddr_nl > *addr, > printf("\n"); > } > > + if (json_output) > + jsonw_end_object(json_wr); > + > return 0; > } > > @@ -3042,32 +3042,7 @@ static int print_summary(void) > > get_slabstat(&slabstat); > > - printf("Total: %d (kernel %d)\n", s.socks, slabstat.socks); > - > - printf("TCP: %d (estab %d, closed %d, orphaned %d, synrecv %d, > timewait %d/%d), ports %d\n", > - s.tcp_total + slabstat.tcp_syns + s.tcp_tws, > - sn.tcp_estab, > - s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws), > - s.tcp_orphans, > - slabstat.tcp_syns, > - s.tcp_tws, slabstat.tcp_tws, > - slabstat.tcp_ports > - ); > - > - printf("\n"); > - printf("Transport Total IP IPv6\n"); > - printf("* %-9d %-9s %-9s\n", slabstat.socks, "-", "-"); > - printf("RAW %-9d %-9d %-9d\n", s.raw4+s.raw6, s.raw4, s.raw6); > - printf("UDP %-9d %-9d %-9d\n", s.udp4+s.udp6, s.udp4, s.udp6); > - printf("TCP %-9d %-9d %-9d\n", s.tcp4_hashed+s.tcp6_hashed, > s.tcp4_hashed, s.tcp6_hashed); > - printf("INET %-9d %-9d %-9d\n", > - s.raw4+s.udp4+s.tcp4_hashed+ > - s.raw6+s.udp6+s.tcp6_hashed, > - s.raw4+s.udp4+s.tcp4_hashed, > - s.raw6+s.udp6+s.tcp6_hashed); > - printf("FRAG %-9d %-9d %-9d\n", s.frag4+s.frag6, s.frag4, s.frag6); > - > - printf("\n"); > + sock_summary_fmt(&s, &sn, &slabstat); > > return 0; > } > @@ -3095,6 +3070,7 @@ static void _usage(FILE *dest) > " -z, --contexts display process and socket SELinux security > contexts\n" > " -N, --net switch to the specified network namespace name\n" > "\n" > +" -j, --json format output in JSON\n" > " -4, --ipv4 display only IP version 4 sockets\n" > " -6, --ipv6 display only IP version 6 sockets\n" > " -0, --packet display PACKET sockets\n" > @@ -3194,6 +3170,7 @@ static const struct option long_opts[] = { > { "help", 0, 0, 'h' }, > { "context", 0, 0, 'Z' }, > { "contexts", 0, 0, 'z' }, > + { "json", 0, 0, 'j' }, > { "net", 1, 0, 'N' }, > { 0 } > > @@ -3209,7 +3186,7 @@ int main(int argc, char *argv[]) > int ch; > int state_filter = 0; > > - while ((ch = getopt_long(argc, argv, > "dhaletuwxnro460spbEf:miA:D:F:vVzZN:", > + while ((ch = getopt_long(argc, argv, > "dhaletuwxnro460spbEf:miA:D:F:vVzZN:j", > long_opts, NULL)) != EOF) { > switch(ch) { > case 'n': > @@ -3390,6 +3367,12 @@ int main(int argc, char *argv[]) > if (netns_switch(optarg)) > exit(1); > break; > + case 'j': > + json_wr = jsonw_new(stdout); > + jsonw_pretty(json_wr, true); > + fmt_type = FMT_JSON; > + json_output = 1; > + break; Careful here: Users may very well call 'ss -j -j'. Unless it's impossible for this code to leak or get screwed up by invoking it multiple times, rather just set 'json_output = 1' or maybe better just 'fmt_type = FMT_JSON' and have a switch(fmt_type) afterwards which does the necessary init (hint: init callback ;). > case 'h': > case '?': > help(); > @@ -3401,12 +3384,6 @@ int main(int argc, char *argv[]) > argc -= optind; > argv += optind; > > - if (do_summary) { > - print_summary(); > - if (do_default && argc == 0) > - exit(0); > - } > - > /* Now parse filter... */ > if (argc == 0 && filter_fp) { > if (ssfilter_parse(¤t_filter.f, 0, NULL, filter_fp)) > @@ -3471,11 +3448,24 @@ int main(int argc, char *argv[]) > exit(-1); > } > } > + jsonw_name(json_wr, "TCP"); > + jsonw_start_array(json_wr); > inet_show_netlink(¤t_filter, dump_fp, IPPROTO_TCP); > + jsonw_end_array(json_wr); > + jsonw_destroy(&json_wr); > fflush(dump_fp); > exit(0); > } > > + if (do_summary) { > + print_summary(); > + if (do_default && argc == 0) { > + if (json_output) > + jsonw_destroy(&json_wr); > + exit(0); > + } > + } Uhm, why move that do_summary conditional down here? Can't this lead to extra output before the summary? > if (ssfilter_parse(¤t_filter.f, argc, argv, filter_fp)) > usage(); > > @@ -3497,62 +3487,117 @@ int main(int argc, char *argv[]) > } > } > > - addrp_width = screen_width; > - addrp_width -= netid_width+1; > - addrp_width -= state_width+1; > - addrp_width -= 14; > + if (!json_output) { Perfect case for init callbacks. > - if (addrp_width&1) { > - if (netid_width) > - netid_width++; > - else if (state_width) > - state_width++; > - } > + addrp_width = screen_width; > + addrp_width -= netid_width + 1; > + addrp_width -= state_width + 1; > + addrp_width -= 14; > > - addrp_width /= 2; > - addrp_width--; > + if (addrp_width & 1) { > + if (netid_width) > + netid_width++; > + else if (state_width) > + state_width++; > + } > > - serv_width = resolve_services ? 7 : 5; > + addrp_width /= 2; > + addrp_width--; > > - if (addrp_width < 15+serv_width+1) > - addrp_width = 15+serv_width+1; > + serv_width = resolve_services ? 7 : 5; > > - addr_width = addrp_width - serv_width - 1; > + if (addrp_width < 15 + serv_width + 1) > + addrp_width = 15 + serv_width + 1; > > - if (netid_width) > - printf("%-*s ", netid_width, "Netid"); > - if (state_width) > - printf("%-*s ", state_width, "State"); > - printf("%-6s %-6s ", "Recv-Q", "Send-Q"); > + addr_width = addrp_width - serv_width - 1; > + if (netid_width) > + printf("%-*s ", netid_width, "Netid"); > + if (state_width) > + printf("%-*s ", state_width, "State"); > + printf("%-6s %-6s ", "Recv-Q", "Send-Q"); > > - /* Make enough space for the local/remote port field */ > - addr_width -= 13; > - serv_width += 13; > + /* Make enough space for the local/remote port field */ > + addr_width -= 13; > + serv_width += 13; > > - printf("%*s:%-*s %*s:%-*s\n", > - addr_width, "Local Address", serv_width, "Port", > - addr_width, "Peer Address", serv_width, "Port"); > + printf("%*s:%-*s %*s:%-*s\n", > + addr_width, "Local Address", serv_width, "Port", > + addr_width, "Peer Address", serv_width, "Port"); > + } > + > + fflush(stdout); > + > + if (current_filter.dbs & (1<<NETLINK_DB)) { > + if (json_output) { > + jsonw_name(json_wr, "NETLINK"); > + jsonw_start_array(json_wr); > + netlink_show(¤t_filter); Missing call to jsonw_end_array() here? > + } else > + netlink_show(¤t_filter); > + } > + if (current_filter.dbs & PACKET_DBM) { > + if (json_output) { > + jsonw_name(json_wr, "PACKET"); > + jsonw_start_array(json_wr); > + packet_show(¤t_filter); > + jsonw_end_array(json_wr); > + } else > + packet_show(¤t_filter); > + } > + if (current_filter.dbs & UNIX_DBM) { > + if (json_output) { > + jsonw_name(json_wr, "UNIX"); > + jsonw_start_array(json_wr); > + unix_show(¤t_filter); > + jsonw_end_array(json_wr); > + } else > + unix_show(¤t_filter); > + } > + if (current_filter.dbs & (1<<RAW_DB)) { > + if (json_output) { > + jsonw_name(json_wr, "RAW"); > + jsonw_start_array(json_wr); > + raw_show(¤t_filter); > + jsonw_end_array(json_wr); > + } else > + raw_show(¤t_filter); > + } > + if (current_filter.dbs & (1<<UDP_DB)) { > + if (json_output) { > + jsonw_name(json_wr, "UDP"); > + jsonw_start_array(json_wr); > + udp_show(¤t_filter); > + jsonw_end_array(json_wr); > + } else > + udp_show(¤t_filter); > + } > + if (current_filter.dbs & (1<<TCP_DB)) { > + if (json_output) { > + jsonw_name(json_wr, "TCP"); > + jsonw_start_array(json_wr); > + tcp_show(¤t_filter, IPPROTO_TCP); > + jsonw_end_array(json_wr); > + } else > + tcp_show(¤t_filter, IPPROTO_TCP); > + } > + if (current_filter.dbs & (1<<DCCP_DB)) { > + if (json_output) { > + jsonw_name(json_wr, "DCCP"); > + jsonw_start_array(json_wr); > + tcp_show(¤t_filter, IPPROTO_DCCP); > + jsonw_end_array(json_wr); > + } else > + tcp_show(¤t_filter, IPPROTO_DCCP); > + } Frankly, this whole construct is ugly. The question about whether filters should affect json output at all aside, this should be cleaned up. In the very basic form, move the json_output conditional into jsonw_name(), jsonw_start_array() and jsonw_end_array(). You could also move the json function calls into the *_show() functions. > + > + if (json_output) > + jsonw_destroy(&json_wr); > > fflush(stdout); > > if (follow_events) > exit(handle_follow_request(¤t_filter)); > > - if (current_filter.dbs & (1<<NETLINK_DB)) > - netlink_show(¤t_filter); > - if (current_filter.dbs & PACKET_DBM) > - packet_show(¤t_filter); > - if (current_filter.dbs & UNIX_DBM) > - unix_show(¤t_filter); > - if (current_filter.dbs & (1<<RAW_DB)) > - raw_show(¤t_filter); > - if (current_filter.dbs & (1<<UDP_DB)) > - udp_show(¤t_filter); > - if (current_filter.dbs & (1<<TCP_DB)) > - tcp_show(¤t_filter, IPPROTO_TCP); > - if (current_filter.dbs & (1<<DCCP_DB)) > - tcp_show(¤t_filter, IPPROTO_DCCP); > - > if (show_users || show_proc_ctx || show_sock_ctx) > user_ent_destroy(); > > -- > 1.9.1 > > -- > To unsubscribe from this list: send the line "unsubscribe netdev" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html