Message ID | 1441913708-15532-6-git-send-email-matthias.tafelmeier@gmx.net |
---|---|
State | Changes Requested, archived |
Delegated to: | stephen hemminger |
Headers | show |
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.tafelmeier@gmx.net> > Suggested-by: Hagen Paul Pfeifer <hagen@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 majordomo@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 majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
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; #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); } - 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); + } + + 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); 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); } } if (show_mem || show_tcpinfo) { - printf("\n\t"); 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), 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); } 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"); + + 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); } } 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; 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); + } + } + 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) { - 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); + } 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); + } + + 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();
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.tafelmeier@gmx.net> Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net> --- misc/ss.c | 487 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 266 insertions(+), 221 deletions(-)