This patch creates a central formatter module that acts as a kind of
switch. From there, more specific handler modules for the certain output
formats are called. Up to now, humand readable and json do exist.

That prepares ss for potential output format extensions in the future.
With the help of such an apparatus, extensions should get done
conveniently as well.

For a completely new output format, a new handler module must be created
and should be constructed like its relatives (for ex.: ss_json_fmt.c).
Moreover, its functions need to get registered with the central output
distributor. The latter can be done in that the according fmt_op_hub of
the new handler module is registered in the fmt_op_hub array.

Solely extending tcp_stats output shall boil down to extending the
according handler function with the new predicate and its value. The
context of the output subparts are important. With JSON, for instance,
you have to ensure, that the comas are set at the right places.

Further, an interim solution for all tcp_stats extensions is to check
that all those muddle through to all fmt handlers by STATICAL_ASSERTING
that.  Interim is the solution, since a central structure would be much
more worthwile for maintainability and this method does not ensure
correct output fmt extension in a foolproof manner.

Extension Examples:
See according sub patches in these series.

Signed-off-by: Matthias Tafelmeier <matthias.tafelme...@gmx.net>
Suggested-by: Hagen Paul Pfeifer <ha...@jauu.net>
---
 misc/Makefile      |   2 +-
 misc/ss_hr_fmt.c   | 258 +++++++++++++++++++++++++++++++++
 misc/ss_hr_fmt.h   |   9 ++
 misc/ss_json_fmt.c | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 misc/ss_json_fmt.h |  26 ++++
 misc/ss_out_fmt.c  | 127 +++++++++++++++++
 misc/ss_out_fmt.h  |  83 +++++++++++
 7 files changed, 912 insertions(+), 1 deletion(-)
 create mode 100644 misc/ss_hr_fmt.c
 create mode 100644 misc/ss_hr_fmt.h
 create mode 100644 misc/ss_json_fmt.c
 create mode 100644 misc/ss_json_fmt.h
 create mode 100644 misc/ss_out_fmt.c
 create mode 100644 misc/ss_out_fmt.h

diff --git a/misc/Makefile b/misc/Makefile
index 6185217..2fe3555 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -1,4 +1,4 @@
-SSOBJ=ss.o ssfilter.o
+SSOBJ=ss.o ssfilter.o ss_hr_fmt.o ss_json_fmt.o ss_out_fmt.o
 LNSTATOBJ=lnstat.o lnstat_util.o
 
 TARGETS=ss nstat ifstat rtacct arpd lnstat
diff --git a/misc/ss_hr_fmt.c b/misc/ss_hr_fmt.c
new file mode 100644
index 0000000..2f09838
--- /dev/null
+++ b/misc/ss_hr_fmt.c
@@ -0,0 +1,258 @@
+#include <linux/sock_diag.h>
+#include <linux/rtnetlink.h>
+#include "ss_out_fmt.h"
+#include "ss_types.h"
+#include "ss_hr_fmt.h"
+
+static void tcp_stats_hr_fmt(struct tcpstat *s)
+{
+       char b1[64];
+
+       if (s->has_ts_opt)
+               printf(" ts");
+       if (s->has_sack_opt)
+               printf(" sack");
+       if (s->has_ecn_opt)
+               printf(" ecn");
+       if (s->has_ecnseen_opt)
+               printf(" ecnseen");
+       if (s->has_fastopen_opt)
+               printf(" fastopen");
+       if (s->cong_alg)
+               printf(" %s", s->cong_alg);
+       if (s->has_wscale_opt)
+               printf(" wscale:%d,%d", s->snd_wscale, s->rcv_wscale);
+       if (s->rto)
+               printf(" rto:%g", s->rto);
+       if (s->backoff)
+               printf(" backoff:%u", s->backoff);
+       if (s->rtt)
+               printf(" rtt:%g/%g", s->rtt, s->rttvar);
+       if (s->ato)
+               printf(" ato:%g", s->ato);
+
+       if (s->qack)
+               printf(" qack:%d", s->qack);
+       if (s->qack & 1)
+               printf(" bidir");
+
+       if (s->mss)
+               printf(" mss:%d", s->mss);
+       if (s->cwnd)
+               printf(" cwnd:%d", s->cwnd);
+       if (s->ssthresh)
+               printf(" ssthresh:%d", s->ssthresh);
+
+       if (s->dctcp && s->dctcp->enabled) {
+               struct dctcpstat *dctcp = s->dctcp;
+
+               printf(" dctcp:(ce_state:%u,alpha:%u,ab_ecn:%u,ab_tot:%u)",
+               dctcp->ce_state, dctcp->alpha, dctcp->ab_ecn,
+               dctcp->ab_tot);
+       } else if (s->dctcp) {
+               printf(" dctcp:fallback_mode");
+       }
+
+       if (s->send_bps)
+               printf(" send %sbps", sprint_bw(b1, s->send_bps));
+       if (s->lastsnd)
+               printf(" lastsnd:%u", s->lastsnd);
+       if (s->lastrcv)
+               printf(" lastrcv:%u", s->lastrcv);
+       if (s->lastack)
+               printf(" lastack:%u", s->lastack);
+
+       if (s->pacing_rate) {
+               printf(" pacing_rate %sbps", sprint_bw(b1, s->pacing_rate));
+               if (s->pacing_rate_max)
+                       printf("/%sbps", sprint_bw(b1, s->pacing_rate_max));
+       }
+
+       if (s->unacked)
+               printf(" unacked:%u", s->unacked);
+       if (s->retrans || s->retrans_total)
+               printf(" retrans:%u/%u", s->retrans, s->retrans_total);
+       if (s->lost)
+               printf(" lost:%u", s->lost);
+       if (s->sacked && s->ss.state != SS_LISTEN)
+               printf(" sacked:%u", s->sacked);
+       if (s->fackets)
+               printf(" fackets:%u", s->fackets);
+       if (s->reordering != 3)
+               printf(" reordering:%d", s->reordering);
+       if (s->rcv_rtt)
+               printf(" rcv_rtt:%g", s->rcv_rtt);
+
+       CHECK_FMT_ADAPT(s->rcv_space, s,
+       hr_handler_must_be_adapted_accordingly_when_json_fmt_is_extended);
+}
+
+static void tcp_timer_hr_fmt(struct tcpstat *s)
+{
+       if (s->timer) {
+               if (s->timer > 4)
+                       s->timer = 5;
+               printf(" timer:(%s,%s,%d)",
+               ss_timer_name[s->timer],
+               print_ms_timer(s->timeout), s->retrans);
+       }
+}
+
+static void sock_state_hr_fmt(struct sockstat *s, const char **sstate_name,
+               const char *sock_name, int netid_width, int state_width)
+{
+       if (netid_width)
+               printf("%-*s ", netid_width, sock_name);
+       if (state_width)
+               printf("%-*s ", state_width, sstate_name[s->state]);
+
+       printf("%-6d %-6d ", s->rq, s->wq);
+}
+
+static void sock_details_hr_fmt(struct sockstat *s, int type, unsigned groups,
+                       unsigned long long cb)
+{
+       if (type == GENERIC_DETAIL && s->uid)
+               printf(" uid:%u", s->uid);
+
+       if (type == GENERIC_DETAIL) {
+               printf(" ino:%u", s->ino);
+               printf(" sk:%llx", s->sk);
+       }
+
+       if (type == NETLINK_DETAIL)
+               printf(" sk=%llx cb=%llx groups=0x%08x", s->sk, cb, groups);
+
+}
+
+static void sock_addr_hr_fmt(const char *addr, int addr_len,
+               char *delim, int port_len,
+               const char *port, const char *ifname,
+               const char *peer_kind)
+{
+       if (ifname) {
+               printf("%*s%%%s%s%-*s ", addr_len, addr, ifname, delim,
+               port_len, port);
+       } else {
+               printf("%*s%s%-*s ", addr_len, addr, delim, port_len, port);
+       }
+}
+
+static void sock_users_hr_fmt(char *out)
+{
+       printf(" users:(%s)", out);
+}
+
+static void sock_summary_hr_fmt(struct ssummary *s, struct snmpstat *sn,
+                       struct 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");
+}
+
+static void sock_conn_hr_fmt(unsigned char mask)
+{
+       printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>');
+}
+
+static void mem_hr_fmt(const struct inet_diag_meminfo *minfo)
+{
+       printf(" mem:(r%u,w%u,f%u,t%u)",
+       minfo->idiag_rmem,
+       minfo->idiag_wmem, minfo->idiag_fmem, minfo->idiag_tmem);
+}
+
+static void skmem_hr_fmt(const __u32 *skmeminfo,
+               struct rtattr **tb, int 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(")");
+}
+
+static void bpf_filter_hr_fmt(struct sock_filter *fil, int num)
+{
+       printf("\n\tbpf filter (%d): ", num);
+       while (num) {
+               printf(" 0x%02x %u %u %u,",
+               fil->code, fil->jt, fil->jf, fil->k);
+               num--;
+               fil++;
+       }
+}
+
+static void opt_hr_fmt(char *opt)
+{
+       printf(" opt:\"%s\"", opt);
+}
+
+static void proc_hr_fmt(int serv_width, char *pid_ctx)
+{
+       if (pid_ctx != NULL) {
+               printf("proc_ctx=%-*s ", serv_width, pid_ctx);
+               free(pid_ctx);
+       } else {
+               printf("proc_ctx=%-*s ", serv_width, "unavailable");
+       }
+}
+
+static void packet_show_ring_hr_fmt(struct packet_diag_ring *ring)
+{
+       printf("blk_size:%d", ring->pdr_block_size);
+       printf(",blk_nr:%d", ring->pdr_block_nr);
+       printf(",frm_size:%d", ring->pdr_frame_size);
+       printf(",frm_nr:%d", ring->pdr_frame_nr);
+       printf(",tmo:%d", ring->pdr_retire_tmo);
+       printf(",features:0x%x", ring->pdr_features);
+}
+
+const struct fmt_op_hub hr_output_op = {
+       .tcp_stats_fmt = tcp_stats_hr_fmt,
+       .tcp_timer_fmt = tcp_timer_hr_fmt,
+       .sock_state_fmt = sock_state_hr_fmt,
+       .sock_details_fmt = sock_details_hr_fmt,
+       .sock_addr_fmt = sock_addr_hr_fmt,
+       .sock_users_fmt = sock_users_hr_fmt,
+       .sock_summary_fmt = sock_summary_hr_fmt,
+       .sock_conn_fmt = sock_conn_hr_fmt,
+       .mem_fmt = mem_hr_fmt,
+       .skmem_fmt = skmem_hr_fmt,
+       .bpf_filter_fmt = bpf_filter_hr_fmt,
+       .opt_fmt = opt_hr_fmt,
+       .proc_fmt = proc_hr_fmt,
+       .packet_show_ring_fmt = packet_show_ring_hr_fmt,
+};
diff --git a/misc/ss_hr_fmt.h b/misc/ss_hr_fmt.h
new file mode 100644
index 0000000..969fb17
--- /dev/null
+++ b/misc/ss_hr_fmt.h
@@ -0,0 +1,9 @@
+#ifndef SS_HR_FMT_H
+#define SS_HR_FMT_H
+
+#include <linux/inet_diag.h>
+#include <linux/pkt_sched.h>
+#include <linux/filter.h>
+#include "ss_types.h"
+
+#endif                         /* SS_HR_FMT_H */
diff --git a/misc/ss_json_fmt.c b/misc/ss_json_fmt.c
new file mode 100644
index 0000000..a927952
--- /dev/null
+++ b/misc/ss_json_fmt.c
@@ -0,0 +1,408 @@
+#include <linux/sock_diag.h>
+#include <inttypes.h>
+#include <linux/rtnetlink.h>
+#include <json_writer.h>
+#include <stdio.h>
+#include "ss_out_fmt.h"
+#include "ss_types.h"
+#include "ss_json_fmt.h"
+
+#define SHOW_TCP_INFO  (show_tcpinfo && \
+                       (ss_current_filter.dbs & (1<<TCP_DB) || \
+                       ss_current_filter.dbs & (1<<DCCP_DB)))
+#define SHOW_MEM       (show_mem && \
+                       ss_current_filter.dbs & (1<<UDP_DB) || \
+                       ss_current_filter.dbs & (1<<TCP_DB))
+
+/* generic auxiliary mechanism for json related dangling delimiter issues*/
+void res_json_fmt_branch(int pred, char bound)
+{
+       if (pred) {
+               if (bound == ' ') {
+                       printf(",");
+                       goto newl;
+               }
+               printf("%c,", bound);
+       } else {
+               if (bound == ' ')
+                       goto newl;
+               printf("%c", bound);
+       }
+ newl:
+       printf("\n");
+}
+
+static void make_userout_valid(char *out)
+{
+       /*replace quote (") by space */
+       char *tmp;
+
+       while ((tmp = strstr(out, "\"")) != NULL)
+               *tmp = ' ';
+}
+
+static void print_serv_or_port(char *serv_o_port)
+{
+       char buf[8];
+
+       if (resolve_services == 0)
+               strcpy(buf, "port");
+       else
+               strcpy(buf, "service");
+
+       jsonw_string_field(json_wr, buf, serv_o_port);
+}
+
+/* hex conversion helper */
+static void jsonw_hex_as_str_outp(json_writer_t *self, uint64_t num)
+{
+       char tmp[17];
+
+       sprintf(tmp, "%"PRIx64, num);
+       jsonw_string(self, tmp);
+}
+
+static void jsonw_hex_field_outp(json_writer_t *self, const char *prop, 
uint64_t num)
+{
+       jsonw_name(self, prop);
+       jsonw_hex_as_str_outp(self, num);
+}
+
+static void tcp_stats_json_fmt(struct tcpstat *s)
+{
+       char b1[64];
+
+       if (s->has_ts_opt)
+               jsonw_bool_field(json_wr, "ts", true);
+       if (s->has_sack_opt)
+               jsonw_bool_field(json_wr, "sack", true);
+       if (s->has_ecn_opt)
+               jsonw_bool_field(json_wr, "ecn", true);
+       if (s->has_ecnseen_opt)
+               jsonw_bool_field(json_wr, "ecnseen", true);
+       if (s->has_fastopen_opt)
+               jsonw_bool_field(json_wr, "fastopen", true);
+       if (s->cong_alg)
+               jsonw_bool_field(json_wr, "cong_alg", true);
+       if (s->has_wscale_opt) {
+               jsonw_name(json_wr, "wscale");
+               jsonw_start_object(json_wr);
+               jsonw_int_field(json_wr, "snd", s->snd_wscale);
+               jsonw_int_field(json_wr, "rcv", s->rcv_wscale);
+               jsonw_end_object(json_wr);
+       }
+       if (s->rto)
+               jsonw_float_field(json_wr, "rto", s->rto);
+       if (s->backoff)
+               jsonw_uint_field(json_wr, "backoff", s->backoff);
+       if (s->rtt)
+               jsonw_float_field(json_wr, "rtt", s->rtt/s->rttvar);
+       if (s->ato)
+               jsonw_float_field(json_wr, "ato", s->ato);
+       if (s->qack)
+               jsonw_int_field(json_wr, "quack", s->qack);
+       if (s->qack & 1)
+               jsonw_bool_field(json_wr, "bidir", true);
+       if (s->mss)
+               jsonw_int_field(json_wr, "mss", s->mss);
+       if (s->cwnd && s->cwnd != 2)
+               jsonw_int_field(json_wr, "cwnd", s->cwnd);
+       if (s->ssthresh)
+               jsonw_int_field(json_wr, "ssthresh", s->ssthresh);
+       if (s->dctcp && s->dctcp->enabled) {
+               struct dctcpstat *dctcp = s->dctcp;
+
+               jsonw_name(json_wr, "dctcpinfo");
+               jsonw_start_object(json_wr);
+               jsonw_uint_field(json_wr, "ce_state", dctcp->ce_state);
+               jsonw_uint_field(json_wr, "alpha", dctcp->alpha);
+               jsonw_uint_field(json_wr, "ab_ecn", dctcp->ab_ecn);
+               jsonw_uint_field(json_wr, "ab_tot", dctcp->ab_tot);
+               jsonw_end_object(json_wr);
+       } else if (s->dctcp) {
+               jsonw_bool_field(json_wr, "fallback_mode", true);
+       }
+       if (s->send_bps)
+               jsonw_string_field(json_wr, "send_bps",
+                             sprint_bw(b1, s->send_bps));
+       if (s->lastsnd)
+               jsonw_uint_field(json_wr, "lastsnd", s->lastsnd);
+       if (s->lastrcv)
+               jsonw_uint_field(json_wr, "lastrcv", s->lastsnd);
+       if (s->lastack)
+               jsonw_uint_field(json_wr, "lastack", s->lastsnd);
+       if (s->pacing_rate) {
+               jsonw_name(json_wr, "pacing_rate");
+               jsonw_start_object(json_wr);
+               jsonw_string_field(json_wr, "pacing_rate_bps",
+               sprint_bw(b1, s->pacing_rate));
+               if (s->pacing_rate_max)
+                       jsonw_string_field(json_wr, "pacing_rate_max_bps",
+                                 sprint_bw(b1, s->pacing_rate_max));
+               jsonw_end_object(json_wr);
+       }
+       if (s->unacked)
+               jsonw_uint_field(json_wr, "unacked", s->unacked);
+       if (s->retrans || s->retrans_total) {
+               jsonw_name(json_wr, "retrans_obj");
+               jsonw_start_object(json_wr);
+               jsonw_uint_field(json_wr, "retrans", s->retrans);
+               jsonw_uint_field(json_wr, "retrans_total", s->retrans_total);
+               jsonw_end_object(json_wr);
+       }
+       if (s->lost)
+               jsonw_uint_field(json_wr, "lost", s->lost);
+       if (s->sacked && (s->ss.state != SS_LISTEN))
+               jsonw_uint_field(json_wr, "sacked", s->sacked);
+       if (s->fackets)
+               jsonw_uint_field(json_wr, "fackets", s->fackets);
+       if (s->reordering != 3)
+               jsonw_int_field(json_wr, "reordering", s->reordering);
+       if (s->rcv_rtt)
+               jsonw_float_field(json_wr, "rcv_rtt", s->rcv_rtt);
+
+       /*deal with special case */
+       res_json_fmt_branch(s->ss.state == SS_LISTEN, ' ');
+
+       CHECK_FMT_ADAPT(s->rcv_space, s,
+       json_handler_must_be_adapted_accordingly_when_hr_fmt_is_extended);
+}
+
+static void tcp_timer_json_fmt(struct tcpstat *s)
+{
+
+       if (s->timer)
+               if (s->timer > 4)
+                       s->timer = 5;
+       jsonw_name(json_wr, "timer");
+       jsonw_start_object(json_wr);
+       jsonw_string_field(json_wr, "ss_timer_name",
+                       ss_timer_name[s->timer]);
+       jsonw_string_field(json_wr, "print_ms_timer",
+                       print_ms_timer(s->timeout));
+       jsonw_int_field(json_wr, "retrans", s->retrans);
+       jsonw_end_object(json_wr);
+}
+
+static void sock_state_json_fmt(struct sockstat *s, const char **sstate_name,
+                       const char *sock_name, int netid_width,
+                       int state_width)
+{
+
+       if (netid_width)
+               jsonw_string_field(json_wr, "Netid", sock_name);
+
+       if (state_width)
+               jsonw_string_field(json_wr, "State", sstate_name[s->state]);
+
+       jsonw_int_field(json_wr, "Recv-Q", s->rq);
+       jsonw_int_field(json_wr, "Send-Q", s->wq);
+}
+
+static void sock_details_json_fmt(struct sockstat *s, int type, unsigned 
groups,
+                       unsigned long long cb)
+{
+       if (type == GENERIC_DETAIL && s->uid)
+               jsonw_uint_field(json_wr, "uid", s->uid);
+       if (type == GENERIC_DETAIL)
+               jsonw_uint_field(json_wr, "ino", s->sk);
+       jsonw_hex_field_outp(json_wr, "sk", s->sk);
+
+       if (type == NETLINK_DETAIL) {
+               jsonw_hex_field_outp(json_wr, "cb", cb);
+               jsonw_hex_field_outp(json_wr, "groups=0x", groups);
+       }
+}
+
+static void sock_addr_json_fmt(const char *addr, int addr_len, char *delim,
+                       int port_len, const char *port, const char *ifname,
+                       const char *peer_kind)
+{
+       jsonw_name(json_wr, peer_kind);
+       jsonw_start_object(json_wr);
+       jsonw_string_field(json_wr, "addr", addr);
+       if (NULL != ifname)
+               jsonw_string_field(json_wr, "interface", ifname);
+       print_serv_or_port((char *)port);
+       jsonw_end_object(json_wr);
+}
+
+static void sock_users_json_fmt(char *out)
+{
+       make_userout_valid(out);
+
+       jsonw_string_field(json_wr, "users", out);
+}
+
+static void sock_summary_json_fmt(struct ssummary *s, struct snmpstat *sn,
+                       struct slabstat *slabstat)
+{
+       jsonw_name(json_wr, "summary");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "total", s->socks);
+       jsonw_int_field(json_wr, "kernel", slabstat->socks);
+
+       jsonw_name(json_wr, "TCP");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "total",
+                       s->tcp_total + slabstat->tcp_syns + s->tcp_tws);
+       jsonw_int_field(json_wr, "estab", sn->tcp_estab);
+       jsonw_int_field(json_wr, "closed",
+                       s->tcp_total - (s->tcp4_hashed + s->tcp6_hashed - 
s->tcp_tws));
+       jsonw_int_field(json_wr, "orphaned", s->tcp_orphans);
+       jsonw_int_field(json_wr, "synrecv", slabstat->tcp_syns);
+       jsonw_name(json_wr, "timewait");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "ssummary_tcp_tws", s->tcp_tws);
+       jsonw_int_field(json_wr, "slabstat_tcp_tws", slabstat->tcp_tws);
+       jsonw_end_object(json_wr);
+       jsonw_int_field(json_wr, "ports", slabstat->tcp_ports);
+       jsonw_end_object(json_wr);
+
+       jsonw_name(json_wr, "trans_over_net_prop");
+       jsonw_start_object(json_wr);
+       jsonw_name(json_wr, "*");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "Total", slabstat->socks);
+       jsonw_string_field(json_wr, "IP", "-");
+       jsonw_string_field(json_wr, "IPv6", "-");
+       jsonw_end_object(json_wr);
+       jsonw_end_object(json_wr);
+
+       jsonw_name(json_wr, "RAW");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "Total", s->raw4 + s->raw6);
+       jsonw_int_field(json_wr, "IP", s->raw4);
+       jsonw_int_field(json_wr, "IPv6", s->raw6);
+       jsonw_end_object(json_wr);
+
+       jsonw_name(json_wr, "UDP");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "Total", s->udp4 + s->udp6);
+       jsonw_int_field(json_wr, "IP", s->udp4);
+       jsonw_int_field(json_wr, "IPv6", s->udp6);
+       jsonw_end_object(json_wr);
+
+       jsonw_name(json_wr, "TCP");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "Total", s->tcp4_hashed + s->tcp6_hashed);
+       jsonw_int_field(json_wr, "IP", s->tcp4_hashed);
+       jsonw_int_field(json_wr, "IPv6", s->tcp6_hashed);
+       jsonw_end_object(json_wr);
+
+       jsonw_name(json_wr, "INET");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "Total",
+                       s->raw4 + s->udp4 + s->tcp4_hashed +
+                       s->raw6 + s->udp6 + s->tcp6_hashed);
+       jsonw_int_field(json_wr, "IP", s->raw4 + s->udp4 + s->tcp4_hashed);
+       jsonw_int_field(json_wr, "IPv6", s->raw6 + s->udp6 + s->tcp6_hashed);
+       jsonw_end_object(json_wr);
+
+       jsonw_name(json_wr, "FRAG");
+       jsonw_start_object(json_wr);
+       jsonw_int_field(json_wr, "Total", s->frag4 + s->frag6);
+       jsonw_int_field(json_wr, "IP", s->frag4);
+       jsonw_int_field(json_wr, "IPv6", s->frag6);
+       jsonw_end_object(json_wr);
+
+       jsonw_end_object(json_wr);
+}
+
+static void sock_conn_json_fmt(unsigned char mask)
+{
+       char tmp[4];
+
+       sprintf(tmp, "%c-%c",  mask & 1 ? '-' : '<',
+       mask & 2 ? '-' : '>');
+       jsonw_string_field(json_wr, "shutdown", tmp);
+}
+
+static void mem_json_fmt(const struct inet_diag_meminfo *minfo)
+{
+       jsonw_name(json_wr, "mem");
+       jsonw_start_object(json_wr);
+       jsonw_uint_field(json_wr, "r", minfo->idiag_rmem);
+       jsonw_uint_field(json_wr, "w", minfo->idiag_wmem);
+       jsonw_uint_field(json_wr, "f", minfo->idiag_fmem);
+       jsonw_uint_field(json_wr, "t", minfo->idiag_fmem);
+       jsonw_end_object(json_wr);
+}
+
+static void skmem_json_fmt(const __u32 *skmeminfo, struct rtattr **tb,
+               int attrtype)
+{
+       jsonw_name(json_wr, "skmem");
+       jsonw_start_object(json_wr);
+       jsonw_uint_field(json_wr, "r", skmeminfo[SK_MEMINFO_RMEM_ALLOC]);
+       jsonw_uint_field(json_wr, "rb", skmeminfo[SK_MEMINFO_RCVBUF]);
+       jsonw_uint_field(json_wr, "t", skmeminfo[SK_MEMINFO_WMEM_ALLOC]);
+       jsonw_uint_field(json_wr, "tb", skmeminfo[SK_MEMINFO_SNDBUF]);
+       jsonw_uint_field(json_wr, "f", skmeminfo[SK_MEMINFO_FWD_ALLOC]);
+       jsonw_uint_field(json_wr, "w", skmeminfo[SK_MEMINFO_WMEM_QUEUED]);
+       jsonw_uint_field(json_wr, "o", skmeminfo[SK_MEMINFO_OPTMEM]);
+       if (RTA_PAYLOAD(tb[attrtype]) >=
+       (SK_MEMINFO_BACKLOG + 1) * sizeof(__u32))
+               jsonw_uint_field(json_wr, "bl", skmeminfo[SK_MEMINFO_BACKLOG]);
+       jsonw_end_object(json_wr);
+}
+
+static void bpf_filter_json_fmt(struct sock_filter *fil, int num)
+{
+       jsonw_name(json_wr, "bpf_filter");
+       jsonw_start_array(json_wr);
+       jsonw_int(json_wr, num);
+       while (num) {
+               jsonw_start_object(json_wr);
+               jsonw_hex_as_str_outp(json_wr, fil->code);
+               jsonw_uint(json_wr, fil->jt);
+               jsonw_uint(json_wr, fil->jf);
+               jsonw_uint(json_wr, fil->k);
+               jsonw_int(json_wr, num--);
+               fil++;
+               jsonw_end_object(json_wr);
+       }
+       jsonw_end_array(json_wr);
+}
+
+static void opt_json_fmt(char *opt)
+{
+       jsonw_string_field(json_wr, "opt", opt);
+}
+
+static void proc_json_fmt(int serv_width, char *pid_ctx)
+{
+       if (pid_ctx != NULL) {
+               jsonw_string_field(json_wr, "proc_ctx", pid_ctx);
+               free(pid_ctx);
+       } else {
+               jsonw_string_field(json_wr, "proc_ctx", "unavailable");
+       }
+}
+
+static void packet_show_ring_json_fmt(struct packet_diag_ring *ring)
+{
+       jsonw_int_field(json_wr, "blk_size", ring->pdr_block_size);
+       jsonw_int_field(json_wr, "blk_nr", ring->pdr_block_nr);
+       jsonw_int_field(json_wr, "frm_size", ring->pdr_frame_size);
+       jsonw_int_field(json_wr, "frm_nr", ring->pdr_frame_nr);
+       jsonw_int_field(json_wr, "tmo", ring->pdr_retire_tmo);
+       jsonw_hex_field_outp(json_wr, "features_0x", ring->pdr_features);
+}
+
+const struct fmt_op_hub json_output_op = {
+       .tcp_stats_fmt = tcp_stats_json_fmt,
+       .tcp_timer_fmt = tcp_timer_json_fmt,
+       .sock_state_fmt = sock_state_json_fmt,
+       .sock_details_fmt = sock_details_json_fmt,
+       .sock_addr_fmt = sock_addr_json_fmt,
+       .sock_users_fmt = sock_users_json_fmt,
+       .sock_summary_fmt = sock_summary_json_fmt,
+       .sock_conn_fmt = sock_conn_json_fmt,
+       .mem_fmt = mem_json_fmt,
+       .skmem_fmt = skmem_json_fmt,
+       .bpf_filter_fmt = bpf_filter_json_fmt,
+       .opt_fmt = opt_json_fmt,
+       .proc_fmt = proc_json_fmt,
+       .packet_show_ring_fmt = packet_show_ring_json_fmt,
+};
diff --git a/misc/ss_json_fmt.h b/misc/ss_json_fmt.h
new file mode 100644
index 0000000..13a6f1d
--- /dev/null
+++ b/misc/ss_json_fmt.h
@@ -0,0 +1,26 @@
+#ifndef SS_JSON_FMT_H
+#define SS_JSON_FMT_H
+
+#include <linux/inet_diag.h>
+#include <linux/pkt_sched.h>
+#include <linux/filter.h>
+#include <json_writer.h>
+#include "ss_types.h"
+
+extern int json_output;
+extern json_writer_t *json_wr;
+extern int resolve_services;
+extern int show_options;
+extern int show_details;
+extern int show_users;
+extern int show_mem;
+extern int show_tcpinfo;
+extern int show_bpf;
+extern int show_proc_ctx;
+extern int show_sock_ctx;
+extern struct filter ss_current_filter;
+
+/*generic auxiliary mechanism for json related dangling delimiter issues*/
+void res_json_fmt_branch(int pred, char bound);
+
+#endif                         /* SS_JSON_FMT_H */
diff --git a/misc/ss_out_fmt.c b/misc/ss_out_fmt.c
new file mode 100644
index 0000000..57bc24e
--- /dev/null
+++ b/misc/ss_out_fmt.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include "ss_out_fmt.h"
+const struct fmt_op_hub *fmt_op_hub[] = {
+       /*human readble */
+       &hr_output_op,
+       /*json */
+       &json_output_op
+};
+
+
+
+char *sprint_bw(char *buf, double bw)
+{
+       if (bw > 1000000.)
+               sprintf(buf, "%.1fM", bw / 1000000.);
+       else if (bw > 1000.)
+               sprintf(buf, "%.1fK", bw / 1000.);
+       else
+               sprintf(buf, "%g", bw);
+
+       return buf;
+}
+
+char *print_ms_timer(int timeout)
+{
+       static char buf[64];
+       int secs, msecs, minutes;
+
+       if (timeout < 0)
+               timeout = 0;
+       secs = timeout / 1000;
+       minutes = secs / 60;
+       secs = secs % 60;
+       msecs = timeout % 1000;
+       buf[0] = 0;
+       if (minutes) {
+               msecs = 0;
+               snprintf(buf, sizeof(buf) - 16, "%dmin", minutes);
+               if (minutes > 9)
+                       secs = 0;
+       }
+       if (secs) {
+               if (secs > 9)
+                       msecs = 0;
+               sprintf(buf + strlen(buf), "%d%s", secs, msecs ? "." : "sec");
+       }
+       if (msecs)
+               sprintf(buf + strlen(buf), "%03dms", msecs);
+       return buf;
+}
+
+void tcp_stats_fmt(struct tcpstat *s)
+{
+       fmt_op_hub[fmt_type]->tcp_stats_fmt(s);
+}
+
+void tcp_timer_fmt(struct tcpstat *s)
+{
+       fmt_op_hub[fmt_type]->tcp_timer_fmt(s);
+}
+
+void sock_state_fmt(struct sockstat *s, const char **sstate_name,
+               const char *sock_name, int netid_width, int state_width)
+{
+       fmt_op_hub[fmt_type]->sock_state_fmt(s, sstate_name, sock_name,
+                                       netid_width, state_width);
+}
+
+void sock_details_fmt(struct sockstat *s, int type,
+               unsigned groups, unsigned long long cb)
+{
+       fmt_op_hub[fmt_type]->sock_details_fmt(s, type, groups, cb);
+}
+
+void sock_addr_fmt(const char *addr, int addr_len, char *delim,
+               int port_len, const char *port, const char *ifname,
+               const char *peer_kind)
+{
+       fmt_op_hub[fmt_type]->sock_addr_fmt(addr, addr_len,
+                                       delim, port_len, port, ifname,
+                                       peer_kind);
+}
+
+void sock_users_fmt(char *out)
+{
+       fmt_op_hub[fmt_type]->sock_users_fmt(out);
+}
+
+void sock_summary_fmt(struct ssummary *s, struct snmpstat *sn,
+               struct slabstat *slabstat)
+{
+       fmt_op_hub[fmt_type]->sock_summary_fmt(s, sn, slabstat);
+}
+
+void sock_conn_fmt(unsigned char mask)
+{
+       fmt_op_hub[fmt_type]->sock_conn_fmt(mask);
+}
+
+void mem_fmt(const struct inet_diag_meminfo *minfo)
+{
+       fmt_op_hub[fmt_type]->mem_fmt(minfo);
+}
+
+void skmem_fmt(const __u32 *skmeminfo, struct rtattr **tb, int attrtype)
+{
+       fmt_op_hub[fmt_type]->skmem_fmt(skmeminfo, tb, attrtype);
+}
+
+void bpf_filter_fmt(struct sock_filter *f, int num)
+{
+       fmt_op_hub[fmt_type]->bpf_filter_fmt(f, num);
+}
+
+void opt_fmt(char *opt)
+{
+       fmt_op_hub[fmt_type]->opt_fmt(opt);
+}
+
+void proc_fmt(int serv_width, char *pid_ctx)
+{
+       fmt_op_hub[fmt_type]->proc_fmt(serv_width, pid_ctx);
+}
+void packet_show_ring_fmt(struct packet_diag_ring *ring)
+{
+       fmt_op_hub[fmt_type]->packet_show_ring_fmt(ring);
+}
diff --git a/misc/ss_out_fmt.h b/misc/ss_out_fmt.h
new file mode 100644
index 0000000..8608cde
--- /dev/null
+++ b/misc/ss_out_fmt.h
@@ -0,0 +1,83 @@
+#ifndef SS_OUT_FMT_H
+#define SS_OUT_FMT_H
+
+#include "ss_hr_fmt.h"
+#include "ss_json_fmt.h"
+#include "ss_types.h"
+#include <utils.h>
+#include <linux/inet_diag.h>
+#include <linux/pkt_sched.h>
+#include <linux/filter.h>
+#include <linux/netdevice.h>
+#include <linux/packet_diag.h>
+
+#define GENERIC_DETAIL 0
+#define NETLINK_DETAIL 1
+
+/* when extending the tcp fmt handler,
+ * you have to pass the last cond of the tcp_stats
+ * struct as sentinel to this macro to ensure compilability
+ * of ss: this enforces a symmetrical extension of the
+ * individual formatting handlers (json, human readable)
+ */
+#define CHECK_FMT_ADAPT(SENTINEL, BEGIN) \
+       BUILD_BUG_ON(!((((void *)&SENTINEL - (void *)BEGIN) + sizeof(SENTINEL)) 
\
+       / sizeof(*BEGIN)))
+
+
+extern int json_output;
+extern enum out_fmt_type fmt_type;
+extern const struct fmt_op_hub hr_output_op;
+extern const struct fmt_op_hub json_output_op;
+extern const char *ss_timer_name[];
+
+enum out_fmt_type { FMT_HR, FMT_JSON };
+
+struct fmt_op_hub {
+       void (*tcp_stats_fmt)(struct tcpstat *s);
+       void (*tcp_timer_fmt)(struct tcpstat *s);
+       void (*sock_state_fmt)(struct sockstat *s, const char **sstate_name,
+                               const char *sock_name, int netid_width,
+                               int state_width);
+       void (*sock_details_fmt)(struct sockstat *s, int type,
+                               unsigned groups, unsigned long long cb);
+       void (*sock_addr_fmt)(const char *addr, int addr_len, char *delim,
+                       int port_len, const char *port,
+                       const char *ifname, const char *peer_kind);
+       void (*sock_users_fmt)(char *out);
+       void (*sock_summary_fmt)(struct ssummary *s, struct snmpstat *sn,
+                               struct slabstat *slabstat);
+       void (*sock_conn_fmt)(unsigned char mask);
+       void (*mem_fmt)(const struct inet_diag_meminfo *minfo);
+       void (*skmem_fmt)(const __u32 *skmeminfo, struct rtattr **tb,
+                       int attrtype);
+       void (*bpf_filter_fmt)(struct sock_filter *f, int num);
+       void (*opt_fmt)(char *opt);
+       void (*proc_fmt)(int serv_width, char *pid_ctx);
+       void (*packet_show_ring_fmt)(struct packet_diag_ring *ring);
+};
+
+void tcp_stats_fmt(struct tcpstat *s);
+void tcp_timer_fmt(struct tcpstat *s);
+void sock_state_fmt(struct sockstat *s, const char **sstate_name,
+               const char *sock_name, int netid_width, int state_width);
+void sock_details_fmt(struct sockstat *s, int type,
+               unsigned groups, unsigned long long cb);
+void sock_addr_fmt(const char *addr, int addr_len, char *delim, int port_len,
+               const char *port, const char *ifname, const char *peer_kind);
+void sock_users_fmt(char *out);
+void sock_summary_fmt(struct ssummary *s, struct snmpstat *sn,
+               struct slabstat *slabstat);
+void sock_conn_fmt(unsigned char mask);
+void mem_fmt(const struct inet_diag_meminfo *minfo);
+void skmem_fmt(const __u32 *skmeminfo, struct rtattr **tb, int attrtype);
+void bpf_filter_fmt(struct sock_filter *f, int num);
+void opt_fmt(char *opt);
+void proc_fmt(int serv_width, char *pid_ctx);
+void packet_show_ring_fmt(struct packet_diag_ring *ring);
+
+/*unisonly utilized formatting parts*/
+char *sprint_bw(char *buf, double bw);
+char *print_ms_timer(int timeout);
+
+#endif                         /* SS_OUT_FMT_H */
-- 
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

Reply via email to