From: Phil Sutter <phil@nwl.cc>
To: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
Cc: netdev@vger.kernel.org, hagen@jauu.net, shemminger@osdl.org,
fw@strlen.de, edumazet@google.com, daniel@iogearbox.net
Subject: Re: [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms
Date: Tue, 27 Oct 2015 14:04:56 +0100 [thread overview]
Message-ID: <20151027130456.GD26876@orbit.nwl.cc> (raw)
In-Reply-To: <1441913708-15532-6-git-send-email-matthias.tafelmeier@gmx.net>
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
next prev parent reply other threads:[~2015-10-27 13:04 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
2015-09-10 19:34 ` [PATCH v7 01/10] ss: rooted out ss type declarations for output formatters Matthias Tafelmeier
2015-09-10 19:35 ` [PATCH v7 02/10] ss: created formatters for json and hr Matthias Tafelmeier
2015-09-23 23:26 ` Stephen Hemminger
2015-09-24 17:43 ` Matthias Tafelmeier
[not found] ` <bc9cacb94de24857a7d9e13960bfae56@HQ1WP-EXMB11.corp.brocade.com>
2015-09-24 22:16 ` Stephen Hemminger
2015-09-25 6:01 ` Matthias Tafelmeier
2015-10-28 8:07 ` Matthias Tafelmeier
2015-10-28 8:28 ` Phil Sutter
2015-10-28 11:57 ` Matthias Tafelmeier
2015-10-28 15:05 ` Phil Sutter
2015-10-28 15:50 ` Matthias Tafelmeier
[not found] ` <b114412a541d4273a9a7a1c0ae4777df@HQ1WP-EXMB11.corp.brocade.com>
2015-11-02 23:06 ` Stephen Hemminger
2015-11-03 6:54 ` Matthias Tafelmeier
2015-10-27 12:21 ` Phil Sutter
2015-10-27 13:47 ` David Miller
2015-10-27 13:35 ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 03/10] ss: removed obsolet fmt functions Matthias Tafelmeier
2015-09-10 19:35 ` [PATCH v7 04/10] ss: prepare timer for output handler usage Matthias Tafelmeier
2015-09-10 19:35 ` [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms Matthias Tafelmeier
2015-10-27 13:04 ` Phil Sutter [this message]
2015-09-10 19:35 ` [PATCH v7 06/10] ss: renaming and export of current_filter Matthias Tafelmeier
2015-10-27 13:10 ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 07/10] ss: symmetrical subhandler output extension example Matthias Tafelmeier
2015-10-27 13:13 ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 08/10] ss: symmetrical formatter " Matthias Tafelmeier
2015-10-27 13:17 ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 09/10] ss: fixed free on local array for valid json output Matthias Tafelmeier
2015-10-27 13:19 ` Phil Sutter
2015-09-10 19:35 ` [PATCH v7 10/10] ss: activate json_writer excluded logic Matthias Tafelmeier
2015-10-27 13:21 ` Phil Sutter
2015-10-28 2:39 ` Stephen Hemminger
2015-10-28 8:17 ` Phil Sutter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20151027130456.GD26876@orbit.nwl.cc \
--to=phil@nwl.cc \
--cc=daniel@iogearbox.net \
--cc=edumazet@google.com \
--cc=fw@strlen.de \
--cc=hagen@jauu.net \
--cc=matthias.tafelmeier@gmx.net \
--cc=netdev@vger.kernel.org \
--cc=shemminger@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.