netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* iproute2 v7: full ss json support and general output simplification
@ 2015-09-10 19:34 Matthias Tafelmeier
  2015-09-10 19:34 ` [PATCH v7 01/10] ss: rooted out ss type declarations for output formatters Matthias Tafelmeier
                   ` (9 more replies)
  0 siblings, 10 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:34 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

- add full JSON support for ss
- Patchset provides a general and easy to use abstraction to extend ss later
- Patchset size is large to minimize daily use ("user" should not deal with
  	formation (json, human readble) later on)
- Patches 7/10 and 8/10 illustrate how to extend ss for new data to support human readble and json
	output. 
- Example_Usages: 1. ss -jt to print out all tcp related information formatted in json
		  2. ss --json -a to print out all info (also summary) 

STATS:

  ss: rooted out ss type declarations for output formatters
  ss: created formatters for json and hr
  ss: removed obsolet fmt functions
  ss: prepare timer for output handler usage
  ss: replaced old output with new generic output mechanisms
  ss: renaming and export of current_filter
  ss: symmetrical subhandler output extension example
  ss: symmetrical formatter extension example
  ss: fixed free on local array for valid json output
  ss: activate json_writer excluded logic

 include/json_writer.h |   1 +
 lib/json_writer.c     |   7 -
 misc/Makefile         |   2 +-
 misc/ss.c             | 971 +++++++++++++++++---------------------------------
 misc/ss_hr_fmt.c      | 321 +++++++++++++++++
 misc/ss_hr_fmt.h      |   9 +
 misc/ss_json_fmt.c    | 475 ++++++++++++++++++++++++
 misc/ss_json_fmt.h    |  26 ++
 misc/ss_out_fmt.c     | 137 +++++++
 misc/ss_out_fmt.h     |  92 +++++
 misc/ss_types.h       | 186 ++++++++++
 11 files changed, 1567 insertions(+), 660 deletions(-)
 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
 create mode 100644 misc/ss_types.h

-- 

Abstract: 

This patch set originates from the necessity to upgrade ss with the possibility
to output in json format. Not to clutter up ss too much, the author of the
patch decided to come up with a simple distributor to handler approach. That
is, the distributor poses the mechanical interface which passes the output
requests coming from ss to the appropriate handler. This simplifies the
interaction with ss and provides a maximum of future extensiblity. Not to
forget, ss loses weight thereby since output implemented in ss itself does
migrate to the appropriate handler. Additionally, because types are shared
amongst handlers, the distributor and ss, the author conceived, that a separate
containter module for types has to be formed. In future, all type declarations
and extensins go there. 

In sum, the patchset has this voluminous extent since there is no viable way
for putting out syntactically correct human readble and json in a simpler manner.
The requirement for convenient extensibility of output and data is
another justification for the patchset size.

Concept sketch:

                                               formatter1 
                                              ************                
                                              *          *                
                                              *          *                
           ss                           ~~~~~~~>zzzzzzz  *                
     ******************                 ~     *          *                
     *                *                 ~   ###>fffffff  *                
     *                *                 ~   # *          *                
     *                *      distributor~   # ************                
     *   --------     *       ********* ~   #                             
     *   -    --------------  *       * ~   #                             
     *   --------     *    -  *       * ~   #                             
     *                *    ---->++++ ~~~~   #                             
     *                *       *       * ~   #  formatter2 
     *                *    ---->==== ######## ************                
     *   --------     *    -  *       * ~   # *          *                
     *   -    --------------  *       * ~   # *          *                
     *   --------     *       ********* ~   # *          *                
     *                *                 ~~~~#~~>zzzzzzz  *                
     *                *                     # *          *                
     *                *                     ###>fffffff	 *                
     ******************                       *          *                
                                              ************                
						                       
At the moment, the distributor is the ss_out_fmt module while two handlers are
up: namely the ss_json_fmt and the ss_hr_fmt (human readable). You can use
those modules as the main reference for own extensions.

Future Extension:
In the following, I will expand on the expandability of the formatter model.
The explanations advances from the minimal to the most sweeping extension in
mind.

Sub Format Handler Output 
Sketch

		  FormatterX
                  ***********************************                     
                  *                                 *                     
                  *   handlerX 			    *                     
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *                     
                  *   °                        °    *                     
                  *   °    xxxxxxxxxxxxxxc<..  °    *                     
                  *   °                     .  °    *                     
                  *   °    xxxxxxxxxxxxxxc<.. potential context                  
                  *   °        new:         .  °    *      
                  *   °    +++++++++++++++... < * * * * * * * * * * *       
                  *   °                        °    *               *      
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *               *      
                  *               .                 *               *      
                  *               .                 *               *      
                  *               .                 *               *      
                  *   handlerY		  	    *               *      
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *               *      
                  *   °                        °    *               *      
                  *   °    xxxxxxxxxxxxxxx     °    *               *      
                  *   °                        °    *               *      
                  *   °    xxxxxxxxxxxxxxx     °    *               *      
                  *   °                        °    *               *   expand symmetrically   
                  *   °                        °    *               *      
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *               *      
                  *                                 *               *      
                  *                                 *               *      
                  *                                 *               *      
                  ***********************************          	    *
								    *
		 		.				    *
				.				    *
				.				    *
				 				    *
		  FormatterY					    *
                  ***********************************               *     
                  *                                 *               *     
                  *   handlerX			    *               *      
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *               *      
                  *   °                        °    *               *      
                  *   °    zzzzzzzzzzzzzzc<..  °    *               *      
                  *   °                     .  °    *               *      
                  *   °    zzzzzzzzzzzzzzc<.. potential context     *              
                  *   °        new:         .  °    *               *     
                  *   °    +++++++++++++++... < * * * * * * * * * * *     
                  *   °                        °    *                     
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *                     
                  *               .                 *                     
                  *               .                 *                     
                  *               .                 *                     
                  *   handlerY		  	    *                     
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *                     
                  *   °                        °    *                     
                  *   °    zzzzzzzzzzzzzzz     °    *                     
                  *   °                        °    *                     
                  *   °    zzzzzzzzzzzzzzz     °    *                     
                  *   °                        °    *                     
                  *   °                        °    *                     
                  *   °°°°°°°°°°°°°°°°°°°°°°°°°°    *                     
                  *                                 *                     
                  *                                 *                     
                  ***********************************     

Explanation:
If you plan to expand a sub out handler function of a formatter, it
essentially boils down to adding a new printf with an according format and
probably a necessary predicate (condition). Nontheless, care must be taken not
to lose possible context interdependecies out of sight. An examble for the
latter would be the interdependecy of json coma setting terms – in compound
types, you do need a coma between consecutive elements.

More important is the issue about symmetric extensions. Except for the
tcp_out_fmt function implementation – where a macro (CHECK_FMT_ADAPT) is in
place to check for adaptions in the basic tcpstat data structure statically –
no general programmatic approach is in place yet which would prevent asymmetric
extensions. Up to someone devices a holistic solution, this patch relies on the
extenders to deal with asymmetries. Is the aim to have a new output feature
available in all semantically related handlers of n different formatters, then
the expander has to adapt n handlers as shown in the sketch above.

Extend with further Format Handler in Formatter
Sketch:
-provides symmetrical extension intendet


                                                       formatter1
                                                      /--------------------------\
                                                      |                          |
                                                      |    spec_handlerX         |
                                                      |     ################ <** |
                                                      |     ################   * |
             distributor                              |            .           * |
           O-------------------------O                |            .           * |
           |    centr_hub            |                |    new_spec_handler    * |
           | ***>01111110--------------------------+  |     ++++++++++++++++<~ * |
           | *   02222220----------------------+   |  |     ++++++++++++++++ ~ * |
           | *                       |         |   |  |                      ~ * |
           | *                       |         |   |  |      handler_hub1    ~ * |
           | *  _______________      |         |   +--------->0#########0****~** |
           | * |gen_handlerX   |     |         |      |	      0+++++++++0~~~~~   |
           | **********        |     |         |      |                          |
           | * |_______________|     |         |      |                          |
           | *         .             |         |      \--------------------------/
           | *         .             |         |                  .
           | *         .             |         |                  .
           | * +++++++++++++++++     |         |       formatterN .
           | ***new_gen_handler+     |         |      /--------------------------\
           |   +               +     |         |      |                          |
           |   +++++++++++++++++     |         |      |    spec_handlerX         |
           |                         |         |      |     ################ <** |
           O-------------------------O         |      |     ################   * |
                                               |      |            .           * |
                                               |      |            .           * |
                                               |      |    new_spec_handler    * |
                                               |      |     ++++++++++++++++<~ * |
                                               |      |     ++++++++++++++++ ~ * |
                                               |      |                      ~ * |
                                               |      |      handler_hubN    ~ * |
                                               +------------->0#########0****~** |
                                                      |	      0+++++++++0~~~~~   |
                                                      |                          |
                                                      |                          |
                                                      \--------------------------/


Explanation:
As the sketch shows, the distributor works with the help of virtual function
pointer in order to act as a call flow switch. It switches to the approriate
formatter module and its handlers depending on the chosen output format by ss
command input. 

So, to add a new formatter handler symmetrically (up to now that is the only
sensibly conceivable case), the extender must implement a new generic handler
in the distributor and the specific handlers in the formatters. Then the hub
vtable structure type has to be broadend to contain the new function pointer
type for the generic handler. After that, he has to extend and update all
handler hubs with the new handlers location information (function pointer). The
latter ensures the generic switching mechanism used by the generic handler
keeps to be upheld.

Example:
Let's say we want the new "foo" data for every output format retrievable via
ss. Up to now, we have the ss_out_fmt module as the distributor and two
specific handlers: for one the ss_hr_fmt and secondly the ss_json_fmt module.
So we need a specific handler implementation in ss_hr_fmt and ss_json_fmt
modules and after that update the corresponding vtables (handler_hubs) in the
modules.  After that, the distributor, namely ss_out_fmt module, has to get a
generic handler that switches via its vtable hub to either the json formatter
or the human readable formatter, depending on what fmt_type has been chosen by ss.
Before the vtables in the specific modules can be updated, struct fmt_op_hub
which is found in ss_out_fmt's interface header has get extendend with the new
function pointer type.

As soon the new generic handler has been exported via the ss_out_fmt.h module
interface, ss can use the new fmt handler to print out info. It can simply call
the generic function and does not have to deal with formatting specific issues.

Extend for another Formatter 
Sketch:
The Sketch for handler extension should be sufficient for conveying the concept.
Just think of another formatter after formatterN and a new entry in the central
vtable of the distributor to reach this new formatter.

Explanation:
Nothing breathtaking has to be done when someone needs an new formatter module
for let's be image – out of pure hypothetical endeavors – xml ss output. First,
implement the new formatter with all the offered interfaces in the
distributor. Register all handlers in the local specific vtable hub.
Then, register the local vtable hub in the generic vtable hub of the
distributor to reach your new handler when chosen. Provide the client
code - here ss - with a new fmt_type option acceptance. Before the
option can do anything, you have to declare the new fmt_type. That's
it. No further adaptions in ss would be necessary.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v7 01/10] ss: rooted out ss type declarations for output formatters
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
@ 2015-09-10 19:34 ` Matthias Tafelmeier
  2015-09-10 19:35 ` [PATCH v7 02/10] ss: created formatters for json and hr Matthias Tafelmeier
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:34 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

The prospected output formatters and ss do share type declarations like
slabstat or tcpstat so that the decision has been made to centralize
those declarations in ss_types.h.  Potential future declarations shall
be placed there. The latter should help amend the extent of ss.c as
well.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
---
 misc/ss.c       | 186 +-------------------------------------------------------
 misc/ss_types.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 187 insertions(+), 185 deletions(-)
 create mode 100644 misc/ss_types.h

diff --git a/misc/ss.c b/misc/ss.c
index 2f34962..83775d1 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -27,6 +27,7 @@
 #include <getopt.h>
 #include <stdbool.h>
 
+#include "ss_types.h"
 #include "utils.h"
 #include "rt_names.h"
 #include "ll_map.h"
@@ -113,55 +114,17 @@ static const char *UDP_PROTO = "udp";
 static const char *RAW_PROTO = "raw";
 static const char *dg_proto = NULL;
 
-enum
-{
-	TCP_DB,
-	DCCP_DB,
-	UDP_DB,
-	RAW_DB,
-	UNIX_DG_DB,
-	UNIX_ST_DB,
-	UNIX_SQ_DB,
-	PACKET_DG_DB,
-	PACKET_R_DB,
-	NETLINK_DB,
-	MAX_DB
-};
 
 #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))
 #define ALL_DB ((1<<MAX_DB)-1)
 #define INET_DBM ((1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB)|(1<<RAW_DB))
 
-enum {
-	SS_UNKNOWN,
-	SS_ESTABLISHED,
-	SS_SYN_SENT,
-	SS_SYN_RECV,
-	SS_FIN_WAIT1,
-	SS_FIN_WAIT2,
-	SS_TIME_WAIT,
-	SS_CLOSE,
-	SS_CLOSE_WAIT,
-	SS_LAST_ACK,
-	SS_LISTEN,
-	SS_CLOSING,
-	SS_MAX
-};
-
 #define SS_ALL ((1 << SS_MAX) - 1)
 #define SS_CONN (SS_ALL & ~((1<<SS_LISTEN)|(1<<SS_CLOSE)|(1<<SS_TIME_WAIT)|(1<<SS_SYN_RECV)))
 
 #include "ssfilter.h"
 
-struct filter
-{
-	int dbs;
-	int states;
-	int families;
-	struct ssfilter *f;
-};
-
 static const struct filter default_dbs[MAX_DB] = {
 	[TCP_DB] = {
 		.states   = SS_CONN,
@@ -376,16 +339,6 @@ static FILE *ephemeral_ports_open(void)
 	return generic_proc_open("PROC_IP_LOCAL_PORT_RANGE", "sys/net/ipv4/ip_local_port_range");
 }
 
-struct user_ent {
-	struct user_ent	*next;
-	unsigned int	ino;
-	int		pid;
-	int		fd;
-	char		*process;
-	char		*process_ctx;
-	char		*socket_ctx;
-};
-
 #define USER_ENT_HASH_SIZE	256
 struct user_ent *user_ent_hash[USER_ENT_HASH_SIZE];
 
@@ -540,12 +493,6 @@ static void user_ent_hash_build(void)
 	closedir(dir);
 }
 
-enum entry_types {
-	USERS,
-	PROC_CTX,
-	PROC_SOCK_CTX
-};
-
 #define ENTRY_BUF_SIZE 512
 static int find_entry(unsigned ino, char **buf, int type)
 {
@@ -618,17 +565,6 @@ next:
 	return cnt;
 }
 
-/* Get stats from slab */
-
-struct slabstat
-{
-	int socks;
-	int tcp_ports;
-	int tcp_tws;
-	int tcp_syns;
-	int skbs;
-};
-
 static struct slabstat slabstat;
 
 static const char *slabstat_ids[] =
@@ -713,75 +649,6 @@ static const char *sstate_namel[] = {
 	[SS_CLOSING] = "closing",
 };
 
-struct sockstat
-{
-	struct sockstat	   *next;
-	unsigned int	    type;
-	uint16_t	    prot;
-	inet_prefix	    local;
-	inet_prefix	    remote;
-	int		    lport;
-	int		    rport;
-	int		    state;
-	int		    rq, wq;
-	unsigned	    ino;
-	unsigned	    uid;
-	int		    refcnt;
-	unsigned int	    iface;
-	unsigned long long  sk;
-	char *name;
-	char *peer_name;
-};
-
-struct dctcpstat
-{
-	unsigned int	ce_state;
-	unsigned int	alpha;
-	unsigned int	ab_ecn;
-	unsigned int	ab_tot;
-	bool		enabled;
-};
-
-struct tcpstat
-{
-	struct sockstat	    ss;
-	int		    timer;
-	int		    timeout;
-	int		    probes;
-	char		    cong_alg[16];
-	double		    rto, ato, rtt, rttvar;
-	int		    qack, cwnd, ssthresh, backoff;
-	double		    send_bps;
-	int		    snd_wscale;
-	int		    rcv_wscale;
-	int		    mss;
-	unsigned int	    lastsnd;
-	unsigned int	    lastrcv;
-	unsigned int	    lastack;
-	double		    pacing_rate;
-	double		    pacing_rate_max;
-	unsigned long long  bytes_acked;
-	unsigned long long  bytes_received;
-	unsigned int	    segs_out;
-	unsigned int	    segs_in;
-	unsigned int	    unacked;
-	unsigned int	    retrans;
-	unsigned int	    retrans_total;
-	unsigned int	    lost;
-	unsigned int	    sacked;
-	unsigned int	    fackets;
-	unsigned int	    reordering;
-	double		    rcv_rtt;
-	int		    rcv_space;
-	bool		    has_ts_opt;
-	bool		    has_sack_opt;
-	bool		    has_ecn_opt;
-	bool		    has_ecnseen_opt;
-	bool		    has_fastopen_opt;
-	bool		    has_wscale_opt;
-	struct dctcpstat    *dctcp;
-};
-
 static void sock_state_print(struct sockstat *s, const char *sock_name)
 {
 	if (netid_width)
@@ -855,13 +722,6 @@ static const char *print_ms_timer(int timeout)
 	return buf;
 }
 
-struct scache {
-	struct scache *next;
-	int port;
-	char *name;
-	const char *proto;
-};
-
 struct scache *rlist;
 
 static void init_service_resolver(void)
@@ -1026,13 +886,6 @@ static void inet_addr_print(const inet_prefix *a, int port, unsigned int ifindex
 			ifname);
 }
 
-struct aafilter
-{
-	inet_prefix	addr;
-	int		port;
-	struct aafilter *next;
-};
-
 static int inet2_addr_match(const inet_prefix *a, const inet_prefix *p,
 			    int plen)
 {
@@ -2188,11 +2041,6 @@ static int sockdiag_send(int family, int fd, int protocol, struct filter *f)
 	return 0;
 }
 
-struct inet_diag_arg {
-	struct filter *f;
-	int protocol;
-};
-
 static int show_one_inet_sock(const struct sockaddr_nl *addr,
 		struct nlmsghdr *h, void *arg)
 {
@@ -3223,10 +3071,6 @@ static int netlink_show(struct filter *f)
 	return 0;
 }
 
-struct sock_diag_msg {
-	__u8 sdiag_family;
-};
-
 static int generic_show_sock(const struct sockaddr_nl *addr,
 		struct nlmsghdr *nlh, void *arg)
 {
@@ -3281,11 +3125,6 @@ Exit:
 	return ret;
 }
 
-struct snmpstat
-{
-	int tcp_estab;
-};
-
 static int get_snmp_int(char *proto, char *key, int *result)
 {
 	char buf[1024];
@@ -3330,28 +3169,6 @@ static int get_snmp_int(char *proto, char *key, int *result)
 	return -1;
 }
 
-
-/* Get stats from sockstat */
-
-struct ssummary
-{
-	int socks;
-	int tcp_mem;
-	int tcp_total;
-	int tcp_orphans;
-	int tcp_tws;
-	int tcp4_hashed;
-	int udp4;
-	int raw4;
-	int frag4;
-	int frag4_mem;
-	int tcp6_hashed;
-	int udp6;
-	int raw6;
-	int frag6;
-	int frag6_mem;
-};
-
 static void get_sockstat_line(char *line, struct ssummary *s)
 {
 	char id[256], rem[256];
@@ -3818,7 +3635,6 @@ int main(int argc, char *argv[])
 	    (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB))))
 		init_service_resolver();
 
-
 	if (current_filter.dbs == 0) {
 		fprintf(stderr, "ss: no socket tables to show with such filter.\n");
 		exit(0);
diff --git a/misc/ss_types.h b/misc/ss_types.h
new file mode 100644
index 0000000..b4dfdc1
--- /dev/null
+++ b/misc/ss_types.h
@@ -0,0 +1,186 @@
+#ifndef SS_STRUCTS_H
+#define SS_STRUCTS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "utils.h"
+
+enum {
+	TCP_DB,
+	DCCP_DB,
+	UDP_DB,
+	RAW_DB,
+	UNIX_DG_DB,
+	UNIX_ST_DB,
+	UNIX_SQ_DB,
+	PACKET_DG_DB,
+	PACKET_R_DB,
+	NETLINK_DB,
+	MAX_DB
+};
+
+enum {
+	SS_UNKNOWN,
+	SS_ESTABLISHED,
+	SS_SYN_SENT,
+	SS_SYN_RECV,
+	SS_FIN_WAIT1,
+	SS_FIN_WAIT2,
+	SS_TIME_WAIT,
+	SS_CLOSE,
+	SS_CLOSE_WAIT,
+	SS_LAST_ACK,
+	SS_LISTEN,
+	SS_CLOSING,
+	SS_MAX
+};
+
+struct filter {
+	int dbs;
+	int states;
+	int families;
+	struct ssfilter *f;
+};
+
+struct user_ent {
+	struct user_ent *next;
+	unsigned int ino;
+	int pid;
+	int fd;
+	char *process;
+	char *process_ctx;
+	char *socket_ctx;
+};
+
+/* Get stats from slab */
+
+struct slabstat {
+	int socks;
+	int tcp_ports;
+	int tcp_tws;
+	int tcp_syns;
+	int skbs;
+};
+
+struct sockstat {
+	struct sockstat    *next;
+	unsigned int        type;
+	uint16_t            prot;
+	inet_prefix         local;
+	inet_prefix         remote;
+	int                 lport;
+	int                 rport;
+	int                 state;
+	int                 rq, wq;
+	unsigned            ino;
+	unsigned            uid;
+	int                 refcnt;
+	unsigned int        iface;
+	unsigned long long  sk;
+	char *name;
+	char *peer_name;
+};
+
+struct dctcpstat {
+	unsigned int ce_state;
+	unsigned int alpha;
+	unsigned int ab_ecn;
+	unsigned int ab_tot;
+	bool enabled;
+};
+
+#pragma pack(push, 1)
+
+struct tcpstat {
+	struct sockstat     ss;
+	int                 timer;
+	int                 timeout;
+	int                 probes;
+	char                cong_alg[16];
+	double              rto, ato, rtt, rttvar;
+	int                 qack, cwnd, ssthresh, backoff;
+	double              send_bps;
+	int                 snd_wscale;
+	int                 rcv_wscale;
+	int                 mss;
+	unsigned int        lastsnd;
+	unsigned int        lastrcv;
+	unsigned int        lastack;
+	double              pacing_rate;
+	double              pacing_rate_max;
+	unsigned long long  bytes_acked;
+	unsigned long long  bytes_received;
+	unsigned int        segs_out;
+	unsigned int        segs_in;
+	unsigned int        unacked;
+	unsigned int        retrans;
+	unsigned int        retrans_total;
+	unsigned int        lost;
+	unsigned int        sacked;
+	unsigned int        fackets;
+	unsigned int        reordering;
+	bool                has_ts_opt;
+	bool                has_sack_opt;
+	bool                has_ecn_opt;
+	bool                has_ecnseen_opt;
+	bool                has_fastopen_opt;
+	bool                has_wscale_opt;
+	struct dctcpstat    *dctcp;
+	double              rcv_rtt;
+	int                 rcv_space;
+};
+
+#pragma pack(pop)
+
+struct scache {
+	struct scache *next;
+	int port;
+	char *name;
+	const char *proto;
+};
+
+struct aafilter {
+	inet_prefix addr;
+	int port;
+	struct aafilter *next;
+};
+
+struct snmpstat {
+	int tcp_estab;
+};
+
+struct sock_diag_msg {
+	__u8 sdiag_family;
+};
+
+struct inet_diag_arg {
+	struct filter *f;
+	int protocol;
+};
+
+/* Get stats from sockstat */
+
+struct ssummary {
+	int socks;
+	int tcp_mem;
+	int tcp_total;
+	int tcp_orphans;
+	int tcp_tws;
+	int tcp4_hashed;
+	int udp4;
+	int raw4;
+	int frag4;
+	int frag4_mem;
+	int tcp6_hashed;
+	int udp6;
+	int raw6;
+	int frag6;
+	int frag6_mem;
+};
+
+enum entry_types {
+	USERS,
+	PROC_CTX,
+	PROC_SOCK_CTX
+};
+#endif				/* SS_STRUCTS_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 02/10] ss: created formatters for json and hr
  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 ` Matthias Tafelmeier
  2015-09-23 23:26   ` Stephen Hemminger
  2015-10-27 12:21   ` Phil Sutter
  2015-09-10 19:35 ` [PATCH v7 03/10] ss: removed obsolet fmt functions Matthias Tafelmeier
                   ` (7 subsequent siblings)
  9 siblings, 2 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

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.tafelmeier@gmx.net>
Suggested-by: Hagen Paul Pfeifer <hagen@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

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 03/10] ss: removed obsolet fmt functions
  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-10 19:35 ` Matthias Tafelmeier
  2015-09-10 19:35 ` [PATCH v7 04/10] ss: prepare timer for output handler usage Matthias Tafelmeier
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

Those functions are obsoleted since the new fmt handler mechanism
subsumes their tasks. Rendundancy would be contradictory to
the new mechanism.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
---
 misc/ss.c | 190 --------------------------------------------------------------
 1 file changed, 190 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 83775d1..4cc6991 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -649,43 +649,6 @@ static const char *sstate_namel[] = {
 	[SS_CLOSING] = "closing",
 };
 
-static void sock_state_print(struct sockstat *s, const char *sock_name)
-{
-	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_print(struct sockstat *s)
-{
-	if (s->uid)
-		printf(" uid:%u", s->uid);
-
-	printf(" ino:%u", s->ino);
-	printf(" sk:%llx", s->sk);
-}
-
-static void sock_addr_print_width(int addr_len, const char *addr, char *delim,
-		int port_len, const char *port, const char *ifname)
-{
-	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_addr_print(const char *addr, char *delim, const char *port,
-		const char *ifname)
-{
-	sock_addr_print_width(addr_width, addr, delim, serv_width, port, ifname);
-}
-
 static const char *tmr_name[] = {
 	"off",
 	"on",
@@ -695,33 +658,6 @@ static const char *tmr_name[] = {
 	"unknown"
 };
 
-static const 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;
-}
-
 struct scache *rlist;
 
 static void init_service_resolver(void)
@@ -1484,122 +1420,6 @@ static int proc_inet_split_line(char *line, char **loc, char **rem, char **data)
 	return 0;
 }
 
-static 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;
-}
-
-static void tcp_stats_print(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[0])
-		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->bytes_acked)
-		printf(" bytes_acked:%llu", s->bytes_acked);
-	if (s->bytes_received)
-		printf(" bytes_received:%llu", s->bytes_received);
-	if (s->segs_out)
-		printf(" segs_out:%u", s->segs_out);
-	if (s->segs_in)
-		printf(" segs_in:%u", s->segs_in);
-
-	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);
-	if (s->rcv_space)
-		printf(" rcv_space:%d", s->rcv_space);
-}
-
-static void tcp_timer_print(struct tcpstat *s)
-{
-	if (s->timer) {
-		if (s->timer > 4)
-			s->timer = 5;
-		printf(" timer:(%s,%s,%d)",
-				tmr_name[s->timer],
-				print_ms_timer(s->timeout),
-				s->retrans);
-	}
-}
-
 static int tcp_show_line(char *line, const struct filter *f, int family)
 {
 	int rto = 0, ato = 0;
@@ -2694,16 +2514,6 @@ static int packet_stats_print(struct sockstat *s, const struct filter *f)
 	return 0;
 }
 
-static void packet_show_ring(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);
-}
-
 static int packet_show_sock(const struct sockaddr_nl *addr,
 		struct nlmsghdr *nlh, void *arg)
 {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 04/10] ss: prepare timer for output handler usage
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (2 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 03/10] ss: removed obsolet fmt functions Matthias Tafelmeier
@ 2015-09-10 19:35 ` Matthias Tafelmeier
  2015-09-10 19:35 ` [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms Matthias Tafelmeier
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

Minor preparation Patch

Renamed, and exported timer to not have to pass it as a function local
parameter argument.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
---
 misc/ss.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/misc/ss.c b/misc/ss.c
index 4cc6991..e7ea041 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -649,7 +649,7 @@ static const char *sstate_namel[] = {
 	[SS_CLOSING] = "closing",
 };
 
-static const char *tmr_name[] = {
+const char *ss_timer_name[] = {
 	"off",
 	"on",
 	"keepalive",
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (3 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 04/10] ss: prepare timer for output handler usage Matthias Tafelmeier
@ 2015-09-10 19:35 ` Matthias Tafelmeier
  2015-10-27 13:04   ` Phil Sutter
  2015-09-10 19:35 ` [PATCH v7 06/10] ss: renaming and export of current_filter Matthias Tafelmeier
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

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;
 
 #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(&current_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(&current_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(&current_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(&current_filter);
+		} else
+			netlink_show(&current_filter);
+	}
+	if (current_filter.dbs & PACKET_DBM) {
+		if (json_output) {
+			jsonw_name(json_wr, "PACKET");
+			jsonw_start_array(json_wr);
+			packet_show(&current_filter);
+			jsonw_end_array(json_wr);
+		} else
+			packet_show(&current_filter);
+	}
+	if (current_filter.dbs & UNIX_DBM) {
+		if (json_output) {
+			jsonw_name(json_wr, "UNIX");
+			jsonw_start_array(json_wr);
+			unix_show(&current_filter);
+			jsonw_end_array(json_wr);
+		} else
+			unix_show(&current_filter);
+	}
+	if (current_filter.dbs & (1<<RAW_DB)) {
+		if (json_output) {
+			jsonw_name(json_wr, "RAW");
+			jsonw_start_array(json_wr);
+			raw_show(&current_filter);
+			jsonw_end_array(json_wr);
+		} else
+			raw_show(&current_filter);
+	}
+	if (current_filter.dbs & (1<<UDP_DB)) {
+		if (json_output) {
+			jsonw_name(json_wr, "UDP");
+			jsonw_start_array(json_wr);
+			udp_show(&current_filter);
+			jsonw_end_array(json_wr);
+		} else
+			udp_show(&current_filter);
+	}
+	if (current_filter.dbs & (1<<TCP_DB)) {
+		if (json_output) {
+			jsonw_name(json_wr, "TCP");
+			jsonw_start_array(json_wr);
+			tcp_show(&current_filter, IPPROTO_TCP);
+			jsonw_end_array(json_wr);
+		} else
+			tcp_show(&current_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(&current_filter, IPPROTO_DCCP);
+			jsonw_end_array(json_wr);
+		} else
+			tcp_show(&current_filter, IPPROTO_DCCP);
+	}
+
+	if (json_output)
+		jsonw_destroy(&json_wr);
 
 	fflush(stdout);
 
 	if (follow_events)
 		exit(handle_follow_request(&current_filter));
 
-	if (current_filter.dbs & (1<<NETLINK_DB))
-		netlink_show(&current_filter);
-	if (current_filter.dbs & PACKET_DBM)
-		packet_show(&current_filter);
-	if (current_filter.dbs & UNIX_DBM)
-		unix_show(&current_filter);
-	if (current_filter.dbs & (1<<RAW_DB))
-		raw_show(&current_filter);
-	if (current_filter.dbs & (1<<UDP_DB))
-		udp_show(&current_filter);
-	if (current_filter.dbs & (1<<TCP_DB))
-		tcp_show(&current_filter, IPPROTO_TCP);
-	if (current_filter.dbs & (1<<DCCP_DB))
-		tcp_show(&current_filter, IPPROTO_DCCP);
-
 	if (show_users || show_proc_ctx || show_sock_ctx)
 		user_ent_destroy();
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 06/10] ss: renaming and export of current_filter
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (4 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms Matthias Tafelmeier
@ 2015-09-10 19:35 ` 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
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

Exported current_filter as ss_current_filter, because in
the fmt handlers, I need that piece of info to resolve out issues of json.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
---
 misc/ss.c | 154 +++++++++++++++++++++++++++++++-------------------------------
 1 file changed, 78 insertions(+), 76 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 7a1b6eb..6ff40a3 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -106,6 +106,7 @@ int show_sock_ctx = 0;
 int user_ent_hash_build_init = 0;
 int follow_events = 0;
 int json_output = 0;
+int json_first_elem = 1;
 
 int netid_width;
 int state_width;
@@ -200,7 +201,7 @@ static const struct filter default_afs[AF_MAX] = {
 };
 
 static int do_default = 1;
-static struct filter current_filter;
+struct filter ss_current_filter;
 
 static void filter_db_set(struct filter *f, int db)
 {
@@ -1182,7 +1183,7 @@ void *parse_hostcond(char *addr, bool is_port)
 	struct aafilter a = { .port = -1 };
 	struct aafilter *res;
 	int fam = preferred_family;
-	struct filter *f = &current_filter;
+	struct filter *f = &ss_current_filter;
 
 	if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) {
 		char *p;
@@ -1281,9 +1282,9 @@ void *parse_hostcond(char *addr, bool is_port)
 			if (get_integer(&a.port, port, 0)) {
 				struct servent *se1 = NULL;
 				struct servent *se2 = NULL;
-				if (current_filter.dbs&(1<<UDP_DB))
+				if (ss_current_filter.dbs & (1 << UDP_DB))
 					se1 = getservbyname(port, UDP_PROTO);
-				if (current_filter.dbs&(1<<TCP_DB))
+				if (ss_current_filter.dbs & (1 << TCP_DB))
 					se2 = getservbyname(port, TCP_PROTO);
 				if (se1 && se2 && se1->s_port != se2->s_port) {
 					fprintf(stderr, "Error: ambiguous port \"%s\".\n", port);
@@ -1297,9 +1298,9 @@ void *parse_hostcond(char *addr, bool is_port)
 					struct scache *s;
 					for (s = rlist; s; s = s->next) {
 						if ((s->proto == UDP_PROTO &&
-						     (current_filter.dbs&(1<<UDP_DB))) ||
+						     (ss_current_filter.dbs&(1<<UDP_DB))) ||
 						    (s->proto == TCP_PROTO &&
-						     (current_filter.dbs&(1<<TCP_DB)))) {
+						     (ss_current_filter.dbs&(1<<TCP_DB)))) {
 							if (s->name && strcmp(s->name, port) == 0) {
 								if (a.port > 0 && a.port != s->port) {
 									fprintf(stderr, "Error: ambiguous port \"%s\".\n", port);
@@ -3220,19 +3221,19 @@ int main(int argc, char *argv[])
 			follow_events = 1;
 			break;
 		case 'd':
-			filter_db_set(&current_filter, DCCP_DB);
+			filter_db_set(&ss_current_filter, DCCP_DB);
 			break;
 		case 't':
-			filter_db_set(&current_filter, TCP_DB);
+			filter_db_set(&ss_current_filter, TCP_DB);
 			break;
 		case 'u':
-			filter_db_set(&current_filter, UDP_DB);
+			filter_db_set(&ss_current_filter, UDP_DB);
 			break;
 		case 'w':
-			filter_db_set(&current_filter, RAW_DB);
+			filter_db_set(&ss_current_filter, RAW_DB);
 			break;
 		case 'x':
-			filter_af_set(&current_filter, AF_UNIX);
+			filter_af_set(&ss_current_filter, AF_UNIX);
 			break;
 		case 'a':
 			state_filter = SS_ALL;
@@ -3241,25 +3242,25 @@ int main(int argc, char *argv[])
 			state_filter = (1 << SS_LISTEN) | (1 << SS_CLOSE);
 			break;
 		case '4':
-			filter_af_set(&current_filter, AF_INET);
+			filter_af_set(&ss_current_filter, AF_INET);
 			break;
 		case '6':
-			filter_af_set(&current_filter, AF_INET6);
+			filter_af_set(&ss_current_filter, AF_INET6);
 			break;
 		case '0':
-			filter_af_set(&current_filter, AF_PACKET);
+			filter_af_set(&ss_current_filter, AF_PACKET);
 			break;
 		case 'f':
 			if (strcmp(optarg, "inet") == 0)
-				filter_af_set(&current_filter, AF_INET);
+				filter_af_set(&ss_current_filter, AF_INET);
 			else if (strcmp(optarg, "inet6") == 0)
-				filter_af_set(&current_filter, AF_INET6);
+				filter_af_set(&ss_current_filter, AF_INET6);
 			else if (strcmp(optarg, "link") == 0)
-				filter_af_set(&current_filter, AF_PACKET);
+				filter_af_set(&ss_current_filter, AF_PACKET);
 			else if (strcmp(optarg, "unix") == 0)
-				filter_af_set(&current_filter, AF_UNIX);
+				filter_af_set(&ss_current_filter, AF_UNIX);
 			else if (strcmp(optarg, "netlink") == 0)
-				filter_af_set(&current_filter, AF_NETLINK);
+				filter_af_set(&ss_current_filter, AF_NETLINK);
 			else if (strcmp(optarg, "help") == 0)
 				help();
 			else {
@@ -3272,9 +3273,9 @@ int main(int argc, char *argv[])
 		{
 			char *p, *p1;
 			if (!saw_query) {
-				current_filter.dbs = 0;
+				ss_current_filter.dbs = 0;
 				state_filter = state_filter ?
-				               state_filter : SS_CONN;
+							state_filter : SS_CONN;
 				saw_query = 1;
 				do_default = 0;
 			}
@@ -3283,44 +3284,44 @@ int main(int argc, char *argv[])
 				if ((p1 = strchr(p, ',')) != NULL)
 					*p1 = 0;
 				if (strcmp(p, "all") == 0) {
-					filter_default_dbs(&current_filter);
+					filter_default_dbs(&ss_current_filter);
 				} else if (strcmp(p, "inet") == 0) {
-					filter_db_set(&current_filter, UDP_DB);
-					filter_db_set(&current_filter, DCCP_DB);
-					filter_db_set(&current_filter, TCP_DB);
-					filter_db_set(&current_filter, RAW_DB);
+					filter_db_set(&ss_current_filter, UDP_DB);
+					filter_db_set(&ss_current_filter, DCCP_DB);
+					filter_db_set(&ss_current_filter, TCP_DB);
+					filter_db_set(&ss_current_filter, RAW_DB);
 				} else if (strcmp(p, "udp") == 0) {
-					filter_db_set(&current_filter, UDP_DB);
+					filter_db_set(&ss_current_filter, UDP_DB);
 				} else if (strcmp(p, "dccp") == 0) {
-					filter_db_set(&current_filter, DCCP_DB);
+					filter_db_set(&ss_current_filter, DCCP_DB);
 				} else if (strcmp(p, "tcp") == 0) {
-					filter_db_set(&current_filter, TCP_DB);
+					filter_db_set(&ss_current_filter, TCP_DB);
 				} else if (strcmp(p, "raw") == 0) {
-					filter_db_set(&current_filter, RAW_DB);
+					filter_db_set(&ss_current_filter, RAW_DB);
 				} else if (strcmp(p, "unix") == 0) {
-					filter_db_set(&current_filter, UNIX_ST_DB);
-					filter_db_set(&current_filter, UNIX_DG_DB);
-					filter_db_set(&current_filter, UNIX_SQ_DB);
+					filter_db_set(&ss_current_filter, UNIX_ST_DB);
+					filter_db_set(&ss_current_filter, UNIX_DG_DB);
+					filter_db_set(&ss_current_filter, UNIX_SQ_DB);
 				} else if (strcasecmp(p, "unix_stream") == 0 ||
 					   strcmp(p, "u_str") == 0) {
-					filter_db_set(&current_filter, UNIX_ST_DB);
+					filter_db_set(&ss_current_filter, UNIX_ST_DB);
 				} else if (strcasecmp(p, "unix_dgram") == 0 ||
 					   strcmp(p, "u_dgr") == 0) {
-					filter_db_set(&current_filter, UNIX_DG_DB);
+					filter_db_set(&ss_current_filter, UNIX_DG_DB);
 				} else if (strcasecmp(p, "unix_seqpacket") == 0 ||
 					   strcmp(p, "u_seq") == 0) {
-					filter_db_set(&current_filter, UNIX_SQ_DB);
+					filter_db_set(&ss_current_filter, UNIX_SQ_DB);
 				} else if (strcmp(p, "packet") == 0) {
-					filter_db_set(&current_filter, PACKET_R_DB);
-					filter_db_set(&current_filter, PACKET_DG_DB);
+					filter_db_set(&ss_current_filter, PACKET_R_DB);
+					filter_db_set(&ss_current_filter, PACKET_DG_DB);
 				} else if (strcmp(p, "packet_raw") == 0 ||
 					   strcmp(p, "p_raw") == 0) {
-					filter_db_set(&current_filter, PACKET_R_DB);
+					filter_db_set(&ss_current_filter, PACKET_R_DB);
 				} else if (strcmp(p, "packet_dgram") == 0 ||
 					   strcmp(p, "p_dgr") == 0) {
-					filter_db_set(&current_filter, PACKET_DG_DB);
+					filter_db_set(&ss_current_filter, PACKET_DG_DB);
 				} else if (strcmp(p, "netlink") == 0) {
-					filter_db_set(&current_filter, NETLINK_DB);
+					filter_db_set(&ss_current_filter, NETLINK_DB);
 				} else {
 					fprintf(stderr, "ss: \"%s\" is illegal socket table id\n", p);
 					usage();
@@ -3386,7 +3387,7 @@ int main(int argc, char *argv[])
 
 	/* Now parse filter... */
 	if (argc == 0 && filter_fp) {
-		if (ssfilter_parse(&current_filter.f, 0, NULL, filter_fp))
+		if (ssfilter_parse(&ss_current_filter.f, 0, NULL, filter_fp))
 			usage();
 	}
 
@@ -3412,32 +3413,32 @@ int main(int argc, char *argv[])
 
 	if (do_default) {
 		state_filter = state_filter ? state_filter : SS_CONN;
-		filter_default_dbs(&current_filter);
+		filter_default_dbs(&ss_current_filter);
 	}
 
-	filter_states_set(&current_filter, state_filter);
-	filter_merge_defaults(&current_filter);
+	filter_states_set(&ss_current_filter, state_filter);
+	filter_merge_defaults(&ss_current_filter);
 
 	if (resolve_services && resolve_hosts &&
-	    (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB))))
+	    (ss_current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB))))
 		init_service_resolver();
 
-	if (current_filter.dbs == 0) {
+	if (ss_current_filter.dbs == 0) {
 		fprintf(stderr, "ss: no socket tables to show with such filter.\n");
 		exit(0);
 	}
-	if (current_filter.families == 0) {
+	if (ss_current_filter.families == 0) {
 		fprintf(stderr, "ss: no families to show with such filter.\n");
 		exit(0);
 	}
-	if (current_filter.states == 0) {
+	if (ss_current_filter.states == 0) {
 		fprintf(stderr, "ss: no socket states to show with such filter.\n");
 		exit(0);
 	}
 
 	if (dump_tcpdiag) {
 		FILE *dump_fp = stdout;
-		if (!(current_filter.dbs & (1<<TCP_DB))) {
+		if (!(ss_current_filter.dbs & (1<<TCP_DB))) {
 			fprintf(stderr, "ss: tcpdiag dump requested and no tcp in filter.\n");
 			exit(0);
 		}
@@ -3450,7 +3451,7 @@ int main(int argc, char *argv[])
 		}
 		jsonw_name(json_wr, "TCP");
 		jsonw_start_array(json_wr);
-		inet_show_netlink(&current_filter, dump_fp, IPPROTO_TCP);
+		inet_show_netlink(&ss_current_filter, dump_fp, IPPROTO_TCP);
 		jsonw_end_array(json_wr);
 		jsonw_destroy(&json_wr);
 		fflush(dump_fp);
@@ -3466,15 +3467,15 @@ int main(int argc, char *argv[])
 		}
 	}
 
-	if (ssfilter_parse(&current_filter.f, argc, argv, filter_fp))
+	if (ssfilter_parse(&ss_current_filter.f, argc, argv, filter_fp))
 		usage();
 
 	netid_width = 0;
-	if (current_filter.dbs&(current_filter.dbs-1))
+	if (ss_current_filter.dbs & (ss_current_filter.dbs - 1))
 		netid_width = 5;
 
 	state_width = 0;
-	if (current_filter.states&(current_filter.states-1))
+	if (ss_current_filter.states & (ss_current_filter.states - 1))
 		state_width = 10;
 
 	screen_width = 80;
@@ -3527,67 +3528,68 @@ int main(int argc, char *argv[])
 
 	fflush(stdout);
 
-	if (current_filter.dbs & (1<<NETLINK_DB)) {
+	if (ss_current_filter.dbs & (1<<NETLINK_DB)) {
 		if (json_output) {
 			jsonw_name(json_wr, "NETLINK");
 			jsonw_start_array(json_wr);
-			netlink_show(&current_filter);
+			netlink_show(&ss_current_filter);
+			jsonw_end_array(json_wr);
 		} else
-			netlink_show(&current_filter);
+			netlink_show(&ss_current_filter);
 	}
-	if (current_filter.dbs & PACKET_DBM) {
+	if (ss_current_filter.dbs & PACKET_DBM) {
 		if (json_output) {
 			jsonw_name(json_wr, "PACKET");
 			jsonw_start_array(json_wr);
-			packet_show(&current_filter);
+			packet_show(&ss_current_filter);
 			jsonw_end_array(json_wr);
 		} else
-			packet_show(&current_filter);
+			packet_show(&ss_current_filter);
 	}
-	if (current_filter.dbs & UNIX_DBM) {
+	if (ss_current_filter.dbs & UNIX_DBM) {
 		if (json_output) {
 			jsonw_name(json_wr, "UNIX");
 			jsonw_start_array(json_wr);
-			unix_show(&current_filter);
+			unix_show(&ss_current_filter);
 			jsonw_end_array(json_wr);
 		} else
-			unix_show(&current_filter);
+			unix_show(&ss_current_filter);
 	}
-	if (current_filter.dbs & (1<<RAW_DB)) {
+	if (ss_current_filter.dbs & (1<<RAW_DB)) {
 		if (json_output) {
 			jsonw_name(json_wr, "RAW");
 			jsonw_start_array(json_wr);
-			raw_show(&current_filter);
+			raw_show(&ss_current_filter);
 			jsonw_end_array(json_wr);
 		} else
-			raw_show(&current_filter);
+			raw_show(&ss_current_filter);
 	}
-	if (current_filter.dbs & (1<<UDP_DB)) {
+	if (ss_current_filter.dbs & (1<<UDP_DB)) {
 		if (json_output) {
 			jsonw_name(json_wr, "UDP");
 			jsonw_start_array(json_wr);
-			udp_show(&current_filter);
+			udp_show(&ss_current_filter);
 			jsonw_end_array(json_wr);
 		} else
-			udp_show(&current_filter);
+			udp_show(&ss_current_filter);
 	}
-	if (current_filter.dbs & (1<<TCP_DB)) {
+	if (ss_current_filter.dbs & (1<<TCP_DB)) {
 		if (json_output) {
 			jsonw_name(json_wr, "TCP");
 			jsonw_start_array(json_wr);
-			tcp_show(&current_filter, IPPROTO_TCP);
+			tcp_show(&ss_current_filter, IPPROTO_TCP);
 			jsonw_end_array(json_wr);
 		} else
-			tcp_show(&current_filter, IPPROTO_TCP);
+			tcp_show(&ss_current_filter, IPPROTO_TCP);
 	}
-	if (current_filter.dbs & (1<<DCCP_DB)) {
+	if (ss_current_filter.dbs & (1<<DCCP_DB)) {
 		if (json_output) {
 			jsonw_name(json_wr, "DCCP");
 			jsonw_start_array(json_wr);
-			tcp_show(&current_filter, IPPROTO_DCCP);
+			tcp_show(&ss_current_filter, IPPROTO_DCCP);
 			jsonw_end_array(json_wr);
 		} else
-			tcp_show(&current_filter, IPPROTO_DCCP);
+			tcp_show(&ss_current_filter, IPPROTO_DCCP);
 	}
 
 	if (json_output)
@@ -3596,7 +3598,7 @@ int main(int argc, char *argv[])
 	fflush(stdout);
 
 	if (follow_events)
-		exit(handle_follow_request(&current_filter));
+		exit(handle_follow_request(&ss_current_filter));
 
 	if (show_users || show_proc_ctx || show_sock_ctx)
 		user_ent_destroy();
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 07/10] ss: symmetrical subhandler output extension example
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (5 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 06/10] ss: renaming and export of current_filter Matthias Tafelmeier
@ 2015-09-10 19:35 ` Matthias Tafelmeier
  2015-10-27 13:13   ` Phil Sutter
  2015-09-10 19:35 ` [PATCH v7 08/10] ss: symmetrical formatter " Matthias Tafelmeier
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

This small sized patch shall convey the locations which have to be
changed for a symmetrical output extension. Symmetrical means in this
context all existing semantically related handlers in the diverse
formatters (for hr and json up to now).

Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
---
 misc/ss_hr_fmt.c   | 2 ++
 misc/ss_json_fmt.c | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/misc/ss_hr_fmt.c b/misc/ss_hr_fmt.c
index 2f09838..4046ebf 100644
--- a/misc/ss_hr_fmt.c
+++ b/misc/ss_hr_fmt.c
@@ -82,6 +82,8 @@ static void tcp_stats_hr_fmt(struct tcpstat *s)
 		printf(" reordering:%d", s->reordering);
 	if (s->rcv_rtt)
 		printf(" rcv_rtt:%g", s->rcv_rtt);
+	if (s->rcv_space)
+		printf(" rcv_space:%d", s->rcv_space);
 
 	CHECK_FMT_ADAPT(s->rcv_space, s,
 	hr_handler_must_be_adapted_accordingly_when_json_fmt_is_extended);
diff --git a/misc/ss_json_fmt.c b/misc/ss_json_fmt.c
index a927952..1dff57a 100644
--- a/misc/ss_json_fmt.c
+++ b/misc/ss_json_fmt.c
@@ -160,6 +160,8 @@ static void tcp_stats_json_fmt(struct tcpstat *s)
 		jsonw_int_field(json_wr, "reordering", s->reordering);
 	if (s->rcv_rtt)
 		jsonw_float_field(json_wr, "rcv_rtt", s->rcv_rtt);
+	if (s->rcv_space)
+		jsonw_int_field(json_wr, "rcv_space", s->rcv_space);
 
 	/*deal with special case */
 	res_json_fmt_branch(s->ss.state == SS_LISTEN, ' ');
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 08/10] ss: symmetrical formatter extension example
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (6 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 07/10] ss: symmetrical subhandler output extension example Matthias Tafelmeier
@ 2015-09-10 19:35 ` 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-09-10 19:35 ` [PATCH v7 10/10] ss: activate json_writer excluded logic Matthias Tafelmeier
  9 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

This commit shall show shortly where to place changes when one wants to
extend an ss output formatter with a new handler (format print
procedure). The extension is done symmetrically. That means, every up to
now existing formatter is extended with a semantically equivalent
handler (hr and json formatter).

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
---
 misc/ss_hr_fmt.c   | 65 ++++++++++++++++++++++++++++++++++++++++++++++++--
 misc/ss_json_fmt.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 misc/ss_out_fmt.c  | 10 ++++++++
 misc/ss_out_fmt.h  | 10 ++++++++
 4 files changed, 150 insertions(+), 4 deletions(-)

diff --git a/misc/ss_hr_fmt.c b/misc/ss_hr_fmt.c
index 4046ebf..a2bef60 100644
--- a/misc/ss_hr_fmt.c
+++ b/misc/ss_hr_fmt.c
@@ -85,8 +85,8 @@ static void tcp_stats_hr_fmt(struct tcpstat *s)
 	if (s->rcv_space)
 		printf(" rcv_space:%d", s->rcv_space);
 
-	CHECK_FMT_ADAPT(s->rcv_space, s,
-	hr_handler_must_be_adapted_accordingly_when_json_fmt_is_extended);
+	/*hr handler must be adapted accordingly when json fmt is extended*/
+	CHECK_FMT_ADAPT(s->rcv_space, s);
 }
 
 static void tcp_timer_hr_fmt(struct tcpstat *s)
@@ -242,6 +242,66 @@ static void packet_show_ring_hr_fmt(struct packet_diag_ring *ring)
 	printf(",features:0x%x", ring->pdr_features);
 }
 
+static void packet_details_hr_fmt(struct packet_diag_info *pinfo,
+		struct packet_diag_ring *ring_rx,
+		struct packet_diag_ring *ring_tx,
+		uint32_t fanout,
+		bool has_fanout)
+{
+	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_fmt(ring_rx);
+		printf(")");
+	}
+	if (ring_tx) {
+		printf("\n\tring_tx(");
+		packet_show_ring_fmt(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(")");
+	}
+}
+
 const struct fmt_op_hub hr_output_op = {
 	.tcp_stats_fmt = tcp_stats_hr_fmt,
 	.tcp_timer_fmt = tcp_timer_hr_fmt,
@@ -257,4 +317,5 @@ const struct fmt_op_hub hr_output_op = {
 	.opt_fmt = opt_hr_fmt,
 	.proc_fmt = proc_hr_fmt,
 	.packet_show_ring_fmt = packet_show_ring_hr_fmt,
+	.packet_details_fmt = packet_details_hr_fmt
 };
diff --git a/misc/ss_json_fmt.c b/misc/ss_json_fmt.c
index 1dff57a..9b50832 100644
--- a/misc/ss_json_fmt.c
+++ b/misc/ss_json_fmt.c
@@ -166,8 +166,8 @@ static void tcp_stats_json_fmt(struct tcpstat *s)
 	/*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);
+	/*json handler must be adapted accordingly when hr fmt is extended*/
+	CHECK_FMT_ADAPT(s->rcv_space, s);
 }
 
 static void tcp_timer_json_fmt(struct tcpstat *s)
@@ -392,6 +392,70 @@ static void packet_show_ring_json_fmt(struct packet_diag_ring *ring)
 	jsonw_hex_field_outp(json_wr, "features_0x", ring->pdr_features);
 }
 
+static void packet_details_json_fmt(struct packet_diag_info *pinfo,
+		struct packet_diag_ring *ring_rx,
+		struct packet_diag_ring *ring_tx,
+		uint32_t fanout,
+		bool has_fanout)
+{
+	char tmp_out[16];
+
+	if (pinfo) {
+		jsonw_int_field(json_wr, "ver", pinfo->pdi_version);
+		jsonw_int_field(json_wr, "cpy_thresh", pinfo->pdi_copy_thresh);
+		if (pinfo->pdi_flags & PDI_RUNNING)
+			sprintf(tmp_out, "running");
+		if (pinfo->pdi_flags & PDI_AUXDATA)
+			sprintf(tmp_out, "_auxdata");
+		if (pinfo->pdi_flags & PDI_ORIGDEV)
+			sprintf(tmp_out, "_origdev");
+		if (pinfo->pdi_flags & PDI_VNETHDR)
+			sprintf(tmp_out, "_vnethdr");
+		if (pinfo->pdi_flags & PDI_LOSS)
+			sprintf(tmp_out, "_loss");
+		if (!pinfo->pdi_flags)
+			sprintf(tmp_out, "0");
+		jsonw_string_field(json_wr, "flags", tmp_out);
+	}
+	if (ring_rx) {
+		jsonw_name(json_wr, "ring_rx");
+		jsonw_start_object(json_wr);
+		packet_show_ring_fmt(ring_rx);
+		jsonw_end_object(json_wr);
+	}
+	if (ring_tx) {
+		jsonw_name(json_wr, "ring_tx");
+		jsonw_start_object(json_wr);
+		packet_show_ring_fmt(ring_tx);
+		jsonw_end_object(json_wr);
+	}
+	if (has_fanout) {
+		uint16_t type = (fanout >> 16) & 0xffff;
+
+		jsonw_name(json_wr, "fanout");
+		jsonw_start_object(json_wr);
+		jsonw_int_field(json_wr, "id", fanout & 0xffff);
+
+		if (type == 0)
+			sprintf(tmp_out, "hash");
+		else if (type == 1)
+			sprintf(tmp_out, "lb");
+		else if (type == 2)
+			sprintf(tmp_out, "cpu");
+		else if (type == 3)
+			sprintf(tmp_out, "roll");
+		else if (type == 4)
+			sprintf(tmp_out, "random");
+		else if (type == 5)
+			sprintf(tmp_out, "qm");
+		else
+			sprintf(tmp_out, "0x%x", type);
+
+		jsonw_string_field(json_wr, "type", tmp_out);
+		jsonw_end_object(json_wr);
+	}
+}
+
 const struct fmt_op_hub json_output_op = {
 	.tcp_stats_fmt = tcp_stats_json_fmt,
 	.tcp_timer_fmt = tcp_timer_json_fmt,
@@ -407,4 +471,5 @@ const struct fmt_op_hub json_output_op = {
 	.opt_fmt = opt_json_fmt,
 	.proc_fmt = proc_json_fmt,
 	.packet_show_ring_fmt = packet_show_ring_json_fmt,
+	.packet_details_fmt = packet_details_json_fmt
 };
diff --git a/misc/ss_out_fmt.c b/misc/ss_out_fmt.c
index 57bc24e..6d92201 100644
--- a/misc/ss_out_fmt.c
+++ b/misc/ss_out_fmt.c
@@ -125,3 +125,13 @@ void packet_show_ring_fmt(struct packet_diag_ring *ring)
 {
 	fmt_op_hub[fmt_type]->packet_show_ring_fmt(ring);
 }
+void packet_details_fmt(struct packet_diag_info *pinfo,
+		struct packet_diag_ring *ring_rx,
+		struct packet_diag_ring *ring_tx,
+		uint32_t fanout,
+		bool has_fanout)
+{
+	fmt_op_hub[fmt_type]->packet_details_fmt(pinfo,
+			ring_rx, ring_tx, fanout, has_fanout);
+}
+
diff --git a/misc/ss_out_fmt.h b/misc/ss_out_fmt.h
index 8608cde..bdc786b 100644
--- a/misc/ss_out_fmt.h
+++ b/misc/ss_out_fmt.h
@@ -55,6 +55,11 @@ struct fmt_op_hub {
 	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 (*packet_details_fmt)(struct packet_diag_info *pinfo,
+		struct packet_diag_ring *ring_rx,
+		struct packet_diag_ring *ring_tx,
+		uint32_t fanout,
+		bool has_fanout);
 };
 
 void tcp_stats_fmt(struct tcpstat *s);
@@ -75,6 +80,11 @@ 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 packet_details_fmt(struct packet_diag_info *pinfo,
+		struct packet_diag_ring *ring_rx,
+		struct packet_diag_ring *ring_tx,
+		uint32_t fanout,
+		bool has_fanout);
 
 /*unisonly utilized formatting parts*/
 char *sprint_bw(char *buf, double bw);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 09/10] ss: fixed free on local array for valid json output
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (7 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 08/10] ss: symmetrical formatter " Matthias Tafelmeier
@ 2015-09-10 19:35 ` 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
  9 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

Minor fix to enable json output. Freeing of automatic char array name
which will get freed after function stack cleanup. Another one after
tcp_stats_fmt for freeing automatic tcpstats struct instance.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
---
 misc/ss.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/misc/ss.c b/misc/ss.c
index 6ff40a3..80aebe6 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -1660,10 +1660,6 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
 		s.segs_out = info->tcpi_segs_out;
 		s.segs_in = info->tcpi_segs_in;
 		tcp_stats_fmt(&s);
-		if (s.dctcp)
-			free(s.dctcp);
-		if (s.cong_alg)
-			free(s.cong_alg);
 	}
 }
 
@@ -2362,8 +2358,6 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh,
 	if (json_output)
 		jsonw_end_object(json_wr);
 
-	if (name)
-		free(name);
 	return 0;
 }
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v7 10/10] ss: activate json_writer excluded logic
  2015-09-10 19:34 iproute2 v7: full ss json support and general output simplification Matthias Tafelmeier
                   ` (8 preceding siblings ...)
  2015-09-10 19:35 ` [PATCH v7 09/10] ss: fixed free on local array for valid json output Matthias Tafelmeier
@ 2015-09-10 19:35 ` Matthias Tafelmeier
  2015-10-27 13:21   ` Phil Sutter
  9 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-10 19:35 UTC (permalink / raw)
  To: netdev; +Cc: hagen, shemminger, fw, edumazet, daniel

This small patch extends the lib json_writer module for formerly
deactivated functionality.

Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
---
 include/json_writer.h | 1 +
 lib/json_writer.c     | 7 -------
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/include/json_writer.h b/include/json_writer.h
index ab9a008..3e2bbff 100644
--- a/include/json_writer.h
+++ b/include/json_writer.h
@@ -15,6 +15,7 @@
 #ifndef _JSON_WRITER_H_
 #define _JSON_WRITER_H_
 
+#include <stdio.h>
 #include <stdbool.h>
 #include <stdint.h>
 
diff --git a/lib/json_writer.c b/lib/json_writer.c
index 2af16e1..dfed4d7 100644
--- a/lib/json_writer.c
+++ b/lib/json_writer.c
@@ -12,7 +12,6 @@
  * Authors:	Stephen Hemminger <stephen@networkplumber.org>
  */
 
-#include <stdio.h>
 #include <stdbool.h>
 #include <stdarg.h>
 #include <assert.h>
@@ -201,7 +200,6 @@ void jsonw_bool(json_writer_t *self, bool val)
 	jsonw_printf(self, "%s", val ? "true" : "false");
 }
 
-#ifdef notused
 void jsonw_null(json_writer_t *self)
 {
 	jsonw_printf(self, "null");
@@ -211,7 +209,6 @@ void jsonw_float(json_writer_t *self, double num)
 {
 	jsonw_printf(self, "%g", num);
 }
-#endif
 
 void jsonw_uint(json_writer_t *self, uint64_t num)
 {
@@ -236,13 +233,11 @@ void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
 	jsonw_bool(self, val);
 }
 
-#ifdef notused
 void jsonw_float_field(json_writer_t *self, const char *prop, double val)
 {
 	jsonw_name(self, prop);
 	jsonw_float(self, val);
 }
-#endif
 
 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
 {
@@ -256,13 +251,11 @@ void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
 	jsonw_int(self, num);
 }
 
-#ifdef notused
 void jsonw_null_field(json_writer_t *self, const char *prop)
 {
 	jsonw_name(self, prop);
 	jsonw_null(self);
 }
-#endif
 
 #ifdef TEST
 int main(int argc, char **argv)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  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-10-27 12:21   ` Phil Sutter
  1 sibling, 2 replies; 33+ messages in thread
From: Stephen Hemminger @ 2015-09-23 23:26 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

Having JSON output is going to be a real plus for programatic parsing.
My understanding of best practice with JSON is that it is best to output values
in best machine readable form, the format is not really meant for humans to
directly read.

Therefore I don't like the code that reformats numbers as hex.
If the values are better displayed in hex, then it is up to the program
parsing and presenting that to the user to do that. The JSON should
just put out numeric values as numeric.

> +/* 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);
> +}
> +

Other than that, my only other discomfort is that this patch set
makes the code grow so much larger and it becomes more complex for future
developers.

Maybe it is time to rewrite it in a better language ;-)

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-09-23 23:26   ` Stephen Hemminger
@ 2015-09-24 17:43     ` Matthias Tafelmeier
       [not found]     ` <bc9cacb94de24857a7d9e13960bfae56@HQ1WP-EXMB11.corp.brocade.com>
  1 sibling, 0 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-24 17:43 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA384

On 09/24/2015 01:26 AM, Stephen Hemminger wrote:
> Having JSON output is going to be a real plus for programatic
> parsing. My understanding of best practice with JSON is that it is
> best to output values in best machine readable form, the format is
> not really meant for humans to directly read.
> 
> Therefore I don't like the code that reformats numbers as hex. If
> the values are better displayed in hex, then it is up to the
> program parsing and presenting that to the user to do that. The
> JSON should just put out numeric values as numeric.
> 
>> +/* 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); +} +

I acknowledge that.

> Other than that, my only other discomfort is that this patch set 
> makes the code grow so much larger and it becomes more complex for
> future developers.

Really? Somehow I disagree: I mean most of the code has been copied
over to semantically related modules. So not too much new code has
been added. Modularization in essence should help to get up with
complexity as I see things.

> Maybe it is time to rewrite it in a better language ;-)

I did not get that. Do you mean iproute2?



-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBCQAGBQJWBDZSAAoJEOAWT1uK3zQ7lz4H/33aiWxf+Xzk9jjuEToyMMUL
qjhzMKkADOvSABHa0bsYUxuQrT1ZHYiv7yYWbn5N/3yUwnoFpivfQEO+YStfVtQy
kCW0CNkyhIC8Hd379vVtZf2eLgGhlHDNudfJvZ/F29Rdsb1NjRoWb3qjXmUX0Ns3
T9Ee7UHbN1oQuL4C1+XtFTl0mG7/UtIC5vk6/LTRY7G0oqmcfIzG26STiP5Q4++m
72JQP9jv9pAZzVoYMcnuj4vkVJ3rKfUoQS6Ejz4Y1tXlBeU3iXvL0Pb6h5EDWf7L
iNbk60NT9Y/KSWwIaf8iTFeH5K+NcgT+JQOe0iFH930Nme7lCXf38vuzBIsSfX0=
=fcNC
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
       [not found]     ` <bc9cacb94de24857a7d9e13960bfae56@HQ1WP-EXMB11.corp.brocade.com>
@ 2015-09-24 22:16       ` Stephen Hemminger
  2015-09-25  6:01         ` Matthias Tafelmeier
  0 siblings, 1 reply; 33+ messages in thread
From: Stephen Hemminger @ 2015-09-24 22:16 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, 24 Sep 2015 17:43:46 +0000
Matthias Tafelmeier <matthias.tafelmeier@gmx.net> wrote:

> > Maybe it is time to rewrite it in a better language ;-)  
> 
> I did not get that. Do you mean iproute2?

I meant that the amount of formatting and string processing in ss is
getting to be a significant fraction of the code. Languages other C
provide more OO and better string handling that might help.
But mostly it was a joke.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-09-24 22:16       ` Stephen Hemminger
@ 2015-09-25  6:01         ` Matthias Tafelmeier
  2015-10-28  8:07           ` Matthias Tafelmeier
       [not found]           ` <b114412a541d4273a9a7a1c0ae4777df@HQ1WP-EXMB11.corp.brocade.com>
  0 siblings, 2 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-09-25  6:01 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA384

On 09/25/2015 12:16 AM, Stephen Hemminger wrote:
> On Thu, 24 Sep 2015 17:43:46 +0000 Matthias Tafelmeier 
> <matthias.tafelmeier@gmx.net> wrote:
> 
>>> Maybe it is time to rewrite it in a better language ;-)
>> 
>> I did not get that. Do you mean iproute2?
> 
> I meant that the amount of formatting and string processing in ss 
> is getting to be a significant fraction of the code. Languages 
> other C provide more OO and better string handling that might help.
> But mostly it was a joke.
> 

I suspected you meant that but I was not sure since little out of
context. ;)

Well, then we should wait for another voice aimed at the complexity of
the patchset before amending and resending me the patchset.


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBCQAGBQJWBONXAAoJEOAWT1uK3zQ7M2UIANB3xiPOr4/hB3LVvgv4Vv1s
xMSVLbhGV2FDWSI3KaLIAov9b4ZFevM1usozAhKwHypw0pjtoZzrL6x78fgEumOz
juOnz7iQJZJ0mP3+N+CUHKT/uDX43fkDMCyrpfF7ZnsLJ77KT/WNcb1trtxkm8W7
0+YZp++qve9GELVhy5eLJHbmt36i7DTTGnby+l9SkLuz53QL0zkidmbHyYVfLgDD
kPT2AHOrvKhg4+vs3o1TdOPGUSXe5dnK+LVwRlu4f1FH1G029V4bS4Pa4MTlYzSM
Zl7j8jjk9LsmWN86SYFxAngcVu6iHP6tHqAKHbIDYCzr28B2nEYVwFqIe4PJp9Q=
=YT+J
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  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-10-27 12:21   ` Phil Sutter
  2015-10-27 13:47     ` David Miller
  1 sibling, 1 reply; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 12:21 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, Sep 10, 2015 at 09:35:00PM +0200, Matthias Tafelmeier wrote:
> 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.tafelmeier@gmx.net>
> Suggested-by: Hagen Paul Pfeifer <hagen@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 */

Is it necessary to include these headers here? I usually try to avoid
including headers from other headers if not too inconvenient.

OTOH this file seems to be the counterpart to ss_json_fmt.h, but without
real content it's of little use. Not sure if following patches populate
it further, but if it stays like this I'd rather drop it and include the
above headers directly where they are needed instead.

> 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);
> +}

How about:
#define SVCNAME (resolve_services ? "service" : "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);

Human readable output tries to limit the output by printing those flags
only if present. While this makes parsing the output harder, it is
convenient for humans. In case of json output, I don't see the benefit
here. So I'd rather:

	json_bool_field(json_wr, "ts", s->has_ts_opt);

This simplifies the code here and probably parsing as well.

> +	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);

How about a wrapper:

		jsonw_start_object-name(json_wr, "wscale");


> +		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);

More or less same as above: Json parsers will know what it means if
these fields are zero. So I don't see why they should not be
unconditionally included into the output?

> +	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]);

This raises the question if json output should be limited at all.
Assuming that it will be parsed and formatted before presented to humans
anyway, the only reason to allow for filters and the like to be applied
if json output is active to limit the amount of data generated. OTOH it
makes code handling the data less flexible, e.g. if you wanted to
present the data on a web page you might have to repeat the call to ss
with different options if I decided to show the Netid column of given
output as well. And maybe that's not desireable at all, since the output
might change and I'm interested in the open sockets at the time I opened
the page instead of at the time I changed the display. I'm not a big fan
of MVC either, but disconnecting the view from the data collecting is
IMHO a useful thing to do.

> +
> +	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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 05/10] ss: replaced old output with new generic output mechanisms
  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
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:04 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

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(&current_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(&current_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(&current_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(&current_filter);

Missing call to jsonw_end_array() here?

> +		} else
> +			netlink_show(&current_filter);
> +	}
> +	if (current_filter.dbs & PACKET_DBM) {
> +		if (json_output) {
> +			jsonw_name(json_wr, "PACKET");
> +			jsonw_start_array(json_wr);
> +			packet_show(&current_filter);
> +			jsonw_end_array(json_wr);
> +		} else
> +			packet_show(&current_filter);
> +	}
> +	if (current_filter.dbs & UNIX_DBM) {
> +		if (json_output) {
> +			jsonw_name(json_wr, "UNIX");
> +			jsonw_start_array(json_wr);
> +			unix_show(&current_filter);
> +			jsonw_end_array(json_wr);
> +		} else
> +			unix_show(&current_filter);
> +	}
> +	if (current_filter.dbs & (1<<RAW_DB)) {
> +		if (json_output) {
> +			jsonw_name(json_wr, "RAW");
> +			jsonw_start_array(json_wr);
> +			raw_show(&current_filter);
> +			jsonw_end_array(json_wr);
> +		} else
> +			raw_show(&current_filter);
> +	}
> +	if (current_filter.dbs & (1<<UDP_DB)) {
> +		if (json_output) {
> +			jsonw_name(json_wr, "UDP");
> +			jsonw_start_array(json_wr);
> +			udp_show(&current_filter);
> +			jsonw_end_array(json_wr);
> +		} else
> +			udp_show(&current_filter);
> +	}
> +	if (current_filter.dbs & (1<<TCP_DB)) {
> +		if (json_output) {
> +			jsonw_name(json_wr, "TCP");
> +			jsonw_start_array(json_wr);
> +			tcp_show(&current_filter, IPPROTO_TCP);
> +			jsonw_end_array(json_wr);
> +		} else
> +			tcp_show(&current_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(&current_filter, IPPROTO_DCCP);
> +			jsonw_end_array(json_wr);
> +		} else
> +			tcp_show(&current_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(&current_filter));
>  
> -	if (current_filter.dbs & (1<<NETLINK_DB))
> -		netlink_show(&current_filter);
> -	if (current_filter.dbs & PACKET_DBM)
> -		packet_show(&current_filter);
> -	if (current_filter.dbs & UNIX_DBM)
> -		unix_show(&current_filter);
> -	if (current_filter.dbs & (1<<RAW_DB))
> -		raw_show(&current_filter);
> -	if (current_filter.dbs & (1<<UDP_DB))
> -		udp_show(&current_filter);
> -	if (current_filter.dbs & (1<<TCP_DB))
> -		tcp_show(&current_filter, IPPROTO_TCP);
> -	if (current_filter.dbs & (1<<DCCP_DB))
> -		tcp_show(&current_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

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 06/10] ss: renaming and export of current_filter
  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
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:10 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, Sep 10, 2015 at 09:35:04PM +0200, Matthias Tafelmeier wrote:
> Exported current_filter as ss_current_filter, because in
> the fmt handlers, I need that piece of info to resolve out issues of json.

This patch should come before the patches using the new name.

> Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
> ---
>  misc/ss.c | 154 +++++++++++++++++++++++++++++++-------------------------------
>  1 file changed, 78 insertions(+), 76 deletions(-)
> 
> diff --git a/misc/ss.c b/misc/ss.c
> index 7a1b6eb..6ff40a3 100644
> --- a/misc/ss.c
> +++ b/misc/ss.c
> @@ -106,6 +106,7 @@ int show_sock_ctx = 0;
>  int user_ent_hash_build_init = 0;
>  int follow_events = 0;
>  int json_output = 0;
> +int json_first_elem = 1;

This seems to be unrelated?

>  
>  int netid_width;
>  int state_width;
> @@ -200,7 +201,7 @@ static const struct filter default_afs[AF_MAX] = {
>  };
>  
>  static int do_default = 1;
> -static struct filter current_filter;
> +struct filter ss_current_filter;
>  
>  static void filter_db_set(struct filter *f, int db)
>  {
> @@ -1182,7 +1183,7 @@ void *parse_hostcond(char *addr, bool is_port)
>  	struct aafilter a = { .port = -1 };
>  	struct aafilter *res;
>  	int fam = preferred_family;
> -	struct filter *f = &current_filter;
> +	struct filter *f = &ss_current_filter;

Is it necessary to rename the function? The scope of ss is not too big,
so I don't think clarification is necessary. At least this would shrink
down this patch to almost nothing. :)

>  
>  	if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) {
>  		char *p;
> @@ -1281,9 +1282,9 @@ void *parse_hostcond(char *addr, bool is_port)
>  			if (get_integer(&a.port, port, 0)) {
>  				struct servent *se1 = NULL;
>  				struct servent *se2 = NULL;
> -				if (current_filter.dbs&(1<<UDP_DB))
> +				if (ss_current_filter.dbs & (1 << UDP_DB))
>  					se1 = getservbyname(port, UDP_PROTO);
> -				if (current_filter.dbs&(1<<TCP_DB))
> +				if (ss_current_filter.dbs & (1 << TCP_DB))
>  					se2 = getservbyname(port, TCP_PROTO);
>  				if (se1 && se2 && se1->s_port != se2->s_port) {
>  					fprintf(stderr, "Error: ambiguous port \"%s\".\n", port);
> @@ -1297,9 +1298,9 @@ void *parse_hostcond(char *addr, bool is_port)
>  					struct scache *s;
>  					for (s = rlist; s; s = s->next) {
>  						if ((s->proto == UDP_PROTO &&
> -						     (current_filter.dbs&(1<<UDP_DB))) ||
> +						     (ss_current_filter.dbs&(1<<UDP_DB))) ||
>  						    (s->proto == TCP_PROTO &&
> -						     (current_filter.dbs&(1<<TCP_DB)))) {
> +						     (ss_current_filter.dbs&(1<<TCP_DB)))) {
>  							if (s->name && strcmp(s->name, port) == 0) {
>  								if (a.port > 0 && a.port != s->port) {
>  									fprintf(stderr, "Error: ambiguous port \"%s\".\n", port);
> @@ -3220,19 +3221,19 @@ int main(int argc, char *argv[])
>  			follow_events = 1;
>  			break;
>  		case 'd':
> -			filter_db_set(&current_filter, DCCP_DB);
> +			filter_db_set(&ss_current_filter, DCCP_DB);
>  			break;
>  		case 't':
> -			filter_db_set(&current_filter, TCP_DB);
> +			filter_db_set(&ss_current_filter, TCP_DB);
>  			break;
>  		case 'u':
> -			filter_db_set(&current_filter, UDP_DB);
> +			filter_db_set(&ss_current_filter, UDP_DB);
>  			break;
>  		case 'w':
> -			filter_db_set(&current_filter, RAW_DB);
> +			filter_db_set(&ss_current_filter, RAW_DB);
>  			break;
>  		case 'x':
> -			filter_af_set(&current_filter, AF_UNIX);
> +			filter_af_set(&ss_current_filter, AF_UNIX);
>  			break;
>  		case 'a':
>  			state_filter = SS_ALL;
> @@ -3241,25 +3242,25 @@ int main(int argc, char *argv[])
>  			state_filter = (1 << SS_LISTEN) | (1 << SS_CLOSE);
>  			break;
>  		case '4':
> -			filter_af_set(&current_filter, AF_INET);
> +			filter_af_set(&ss_current_filter, AF_INET);
>  			break;
>  		case '6':
> -			filter_af_set(&current_filter, AF_INET6);
> +			filter_af_set(&ss_current_filter, AF_INET6);
>  			break;
>  		case '0':
> -			filter_af_set(&current_filter, AF_PACKET);
> +			filter_af_set(&ss_current_filter, AF_PACKET);
>  			break;
>  		case 'f':
>  			if (strcmp(optarg, "inet") == 0)
> -				filter_af_set(&current_filter, AF_INET);
> +				filter_af_set(&ss_current_filter, AF_INET);
>  			else if (strcmp(optarg, "inet6") == 0)
> -				filter_af_set(&current_filter, AF_INET6);
> +				filter_af_set(&ss_current_filter, AF_INET6);
>  			else if (strcmp(optarg, "link") == 0)
> -				filter_af_set(&current_filter, AF_PACKET);
> +				filter_af_set(&ss_current_filter, AF_PACKET);
>  			else if (strcmp(optarg, "unix") == 0)
> -				filter_af_set(&current_filter, AF_UNIX);
> +				filter_af_set(&ss_current_filter, AF_UNIX);
>  			else if (strcmp(optarg, "netlink") == 0)
> -				filter_af_set(&current_filter, AF_NETLINK);
> +				filter_af_set(&ss_current_filter, AF_NETLINK);
>  			else if (strcmp(optarg, "help") == 0)
>  				help();
>  			else {
> @@ -3272,9 +3273,9 @@ int main(int argc, char *argv[])
>  		{
>  			char *p, *p1;
>  			if (!saw_query) {
> -				current_filter.dbs = 0;
> +				ss_current_filter.dbs = 0;
>  				state_filter = state_filter ?
> -				               state_filter : SS_CONN;
> +							state_filter : SS_CONN;
>  				saw_query = 1;
>  				do_default = 0;
>  			}
> @@ -3283,44 +3284,44 @@ int main(int argc, char *argv[])
>  				if ((p1 = strchr(p, ',')) != NULL)
>  					*p1 = 0;
>  				if (strcmp(p, "all") == 0) {
> -					filter_default_dbs(&current_filter);
> +					filter_default_dbs(&ss_current_filter);
>  				} else if (strcmp(p, "inet") == 0) {
> -					filter_db_set(&current_filter, UDP_DB);
> -					filter_db_set(&current_filter, DCCP_DB);
> -					filter_db_set(&current_filter, TCP_DB);
> -					filter_db_set(&current_filter, RAW_DB);
> +					filter_db_set(&ss_current_filter, UDP_DB);
> +					filter_db_set(&ss_current_filter, DCCP_DB);
> +					filter_db_set(&ss_current_filter, TCP_DB);
> +					filter_db_set(&ss_current_filter, RAW_DB);
>  				} else if (strcmp(p, "udp") == 0) {
> -					filter_db_set(&current_filter, UDP_DB);
> +					filter_db_set(&ss_current_filter, UDP_DB);
>  				} else if (strcmp(p, "dccp") == 0) {
> -					filter_db_set(&current_filter, DCCP_DB);
> +					filter_db_set(&ss_current_filter, DCCP_DB);
>  				} else if (strcmp(p, "tcp") == 0) {
> -					filter_db_set(&current_filter, TCP_DB);
> +					filter_db_set(&ss_current_filter, TCP_DB);
>  				} else if (strcmp(p, "raw") == 0) {
> -					filter_db_set(&current_filter, RAW_DB);
> +					filter_db_set(&ss_current_filter, RAW_DB);
>  				} else if (strcmp(p, "unix") == 0) {
> -					filter_db_set(&current_filter, UNIX_ST_DB);
> -					filter_db_set(&current_filter, UNIX_DG_DB);
> -					filter_db_set(&current_filter, UNIX_SQ_DB);
> +					filter_db_set(&ss_current_filter, UNIX_ST_DB);
> +					filter_db_set(&ss_current_filter, UNIX_DG_DB);
> +					filter_db_set(&ss_current_filter, UNIX_SQ_DB);
>  				} else if (strcasecmp(p, "unix_stream") == 0 ||
>  					   strcmp(p, "u_str") == 0) {
> -					filter_db_set(&current_filter, UNIX_ST_DB);
> +					filter_db_set(&ss_current_filter, UNIX_ST_DB);
>  				} else if (strcasecmp(p, "unix_dgram") == 0 ||
>  					   strcmp(p, "u_dgr") == 0) {
> -					filter_db_set(&current_filter, UNIX_DG_DB);
> +					filter_db_set(&ss_current_filter, UNIX_DG_DB);
>  				} else if (strcasecmp(p, "unix_seqpacket") == 0 ||
>  					   strcmp(p, "u_seq") == 0) {
> -					filter_db_set(&current_filter, UNIX_SQ_DB);
> +					filter_db_set(&ss_current_filter, UNIX_SQ_DB);
>  				} else if (strcmp(p, "packet") == 0) {
> -					filter_db_set(&current_filter, PACKET_R_DB);
> -					filter_db_set(&current_filter, PACKET_DG_DB);
> +					filter_db_set(&ss_current_filter, PACKET_R_DB);
> +					filter_db_set(&ss_current_filter, PACKET_DG_DB);
>  				} else if (strcmp(p, "packet_raw") == 0 ||
>  					   strcmp(p, "p_raw") == 0) {
> -					filter_db_set(&current_filter, PACKET_R_DB);
> +					filter_db_set(&ss_current_filter, PACKET_R_DB);
>  				} else if (strcmp(p, "packet_dgram") == 0 ||
>  					   strcmp(p, "p_dgr") == 0) {
> -					filter_db_set(&current_filter, PACKET_DG_DB);
> +					filter_db_set(&ss_current_filter, PACKET_DG_DB);
>  				} else if (strcmp(p, "netlink") == 0) {
> -					filter_db_set(&current_filter, NETLINK_DB);
> +					filter_db_set(&ss_current_filter, NETLINK_DB);
>  				} else {
>  					fprintf(stderr, "ss: \"%s\" is illegal socket table id\n", p);
>  					usage();
> @@ -3386,7 +3387,7 @@ int main(int argc, char *argv[])
>  
>  	/* Now parse filter... */
>  	if (argc == 0 && filter_fp) {
> -		if (ssfilter_parse(&current_filter.f, 0, NULL, filter_fp))
> +		if (ssfilter_parse(&ss_current_filter.f, 0, NULL, filter_fp))
>  			usage();
>  	}
>  
> @@ -3412,32 +3413,32 @@ int main(int argc, char *argv[])
>  
>  	if (do_default) {
>  		state_filter = state_filter ? state_filter : SS_CONN;
> -		filter_default_dbs(&current_filter);
> +		filter_default_dbs(&ss_current_filter);
>  	}
>  
> -	filter_states_set(&current_filter, state_filter);
> -	filter_merge_defaults(&current_filter);
> +	filter_states_set(&ss_current_filter, state_filter);
> +	filter_merge_defaults(&ss_current_filter);
>  
>  	if (resolve_services && resolve_hosts &&
> -	    (current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB))))
> +	    (ss_current_filter.dbs&(UNIX_DBM|(1<<TCP_DB)|(1<<UDP_DB)|(1<<DCCP_DB))))
>  		init_service_resolver();
>  
> -	if (current_filter.dbs == 0) {
> +	if (ss_current_filter.dbs == 0) {
>  		fprintf(stderr, "ss: no socket tables to show with such filter.\n");
>  		exit(0);
>  	}
> -	if (current_filter.families == 0) {
> +	if (ss_current_filter.families == 0) {
>  		fprintf(stderr, "ss: no families to show with such filter.\n");
>  		exit(0);
>  	}
> -	if (current_filter.states == 0) {
> +	if (ss_current_filter.states == 0) {
>  		fprintf(stderr, "ss: no socket states to show with such filter.\n");
>  		exit(0);
>  	}
>  
>  	if (dump_tcpdiag) {
>  		FILE *dump_fp = stdout;
> -		if (!(current_filter.dbs & (1<<TCP_DB))) {
> +		if (!(ss_current_filter.dbs & (1<<TCP_DB))) {
>  			fprintf(stderr, "ss: tcpdiag dump requested and no tcp in filter.\n");
>  			exit(0);
>  		}
> @@ -3450,7 +3451,7 @@ int main(int argc, char *argv[])
>  		}
>  		jsonw_name(json_wr, "TCP");
>  		jsonw_start_array(json_wr);
> -		inet_show_netlink(&current_filter, dump_fp, IPPROTO_TCP);
> +		inet_show_netlink(&ss_current_filter, dump_fp, IPPROTO_TCP);
>  		jsonw_end_array(json_wr);
>  		jsonw_destroy(&json_wr);
>  		fflush(dump_fp);
> @@ -3466,15 +3467,15 @@ int main(int argc, char *argv[])
>  		}
>  	}
>  
> -	if (ssfilter_parse(&current_filter.f, argc, argv, filter_fp))
> +	if (ssfilter_parse(&ss_current_filter.f, argc, argv, filter_fp))
>  		usage();
>  
>  	netid_width = 0;
> -	if (current_filter.dbs&(current_filter.dbs-1))
> +	if (ss_current_filter.dbs & (ss_current_filter.dbs - 1))
>  		netid_width = 5;
>  
>  	state_width = 0;
> -	if (current_filter.states&(current_filter.states-1))
> +	if (ss_current_filter.states & (ss_current_filter.states - 1))
>  		state_width = 10;
>  
>  	screen_width = 80;
> @@ -3527,67 +3528,68 @@ int main(int argc, char *argv[])
>  
>  	fflush(stdout);
>  
> -	if (current_filter.dbs & (1<<NETLINK_DB)) {
> +	if (ss_current_filter.dbs & (1<<NETLINK_DB)) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "NETLINK");
>  			jsonw_start_array(json_wr);
> -			netlink_show(&current_filter);
> +			netlink_show(&ss_current_filter);
> +			jsonw_end_array(json_wr);
>  		} else
> -			netlink_show(&current_filter);
> +			netlink_show(&ss_current_filter);
>  	}
> -	if (current_filter.dbs & PACKET_DBM) {
> +	if (ss_current_filter.dbs & PACKET_DBM) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "PACKET");
>  			jsonw_start_array(json_wr);
> -			packet_show(&current_filter);
> +			packet_show(&ss_current_filter);
>  			jsonw_end_array(json_wr);
>  		} else
> -			packet_show(&current_filter);
> +			packet_show(&ss_current_filter);
>  	}
> -	if (current_filter.dbs & UNIX_DBM) {
> +	if (ss_current_filter.dbs & UNIX_DBM) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "UNIX");
>  			jsonw_start_array(json_wr);
> -			unix_show(&current_filter);
> +			unix_show(&ss_current_filter);
>  			jsonw_end_array(json_wr);
>  		} else
> -			unix_show(&current_filter);
> +			unix_show(&ss_current_filter);
>  	}
> -	if (current_filter.dbs & (1<<RAW_DB)) {
> +	if (ss_current_filter.dbs & (1<<RAW_DB)) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "RAW");
>  			jsonw_start_array(json_wr);
> -			raw_show(&current_filter);
> +			raw_show(&ss_current_filter);
>  			jsonw_end_array(json_wr);
>  		} else
> -			raw_show(&current_filter);
> +			raw_show(&ss_current_filter);
>  	}
> -	if (current_filter.dbs & (1<<UDP_DB)) {
> +	if (ss_current_filter.dbs & (1<<UDP_DB)) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "UDP");
>  			jsonw_start_array(json_wr);
> -			udp_show(&current_filter);
> +			udp_show(&ss_current_filter);
>  			jsonw_end_array(json_wr);
>  		} else
> -			udp_show(&current_filter);
> +			udp_show(&ss_current_filter);
>  	}
> -	if (current_filter.dbs & (1<<TCP_DB)) {
> +	if (ss_current_filter.dbs & (1<<TCP_DB)) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "TCP");
>  			jsonw_start_array(json_wr);
> -			tcp_show(&current_filter, IPPROTO_TCP);
> +			tcp_show(&ss_current_filter, IPPROTO_TCP);
>  			jsonw_end_array(json_wr);
>  		} else
> -			tcp_show(&current_filter, IPPROTO_TCP);
> +			tcp_show(&ss_current_filter, IPPROTO_TCP);
>  	}
> -	if (current_filter.dbs & (1<<DCCP_DB)) {
> +	if (ss_current_filter.dbs & (1<<DCCP_DB)) {
>  		if (json_output) {
>  			jsonw_name(json_wr, "DCCP");
>  			jsonw_start_array(json_wr);
> -			tcp_show(&current_filter, IPPROTO_DCCP);
> +			tcp_show(&ss_current_filter, IPPROTO_DCCP);
>  			jsonw_end_array(json_wr);
>  		} else
> -			tcp_show(&current_filter, IPPROTO_DCCP);
> +			tcp_show(&ss_current_filter, IPPROTO_DCCP);
>  	}
>  
>  	if (json_output)
> @@ -3596,7 +3598,7 @@ int main(int argc, char *argv[])
>  	fflush(stdout);
>  
>  	if (follow_events)
> -		exit(handle_follow_request(&current_filter));
> +		exit(handle_follow_request(&ss_current_filter));
>  
>  	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

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 07/10] ss: symmetrical subhandler output extension example
  2015-09-10 19:35 ` [PATCH v7 07/10] ss: symmetrical subhandler output extension example Matthias Tafelmeier
@ 2015-10-27 13:13   ` Phil Sutter
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:13 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, Sep 10, 2015 at 09:35:05PM +0200, Matthias Tafelmeier wrote:
> This small sized patch shall convey the locations which have to be
> changed for a symmetrical output extension. Symmetrical means in this
> context all existing semantically related handlers in the diverse
> formatters (for hr and json up to now).

While this kind of tutorial in patch form is a nice idea (and sadly
often the only way to learn how things work), I'd prefer this to be part
of a README which describes how things work from generic to specific.
This has the added benefit of providing a place for documentation should
the interface change.

> 
> Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
> Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
> ---
>  misc/ss_hr_fmt.c   | 2 ++
>  misc/ss_json_fmt.c | 2 ++
>  2 files changed, 4 insertions(+)
> 
> diff --git a/misc/ss_hr_fmt.c b/misc/ss_hr_fmt.c
> index 2f09838..4046ebf 100644
> --- a/misc/ss_hr_fmt.c
> +++ b/misc/ss_hr_fmt.c
> @@ -82,6 +82,8 @@ static void tcp_stats_hr_fmt(struct tcpstat *s)
>  		printf(" reordering:%d", s->reordering);
>  	if (s->rcv_rtt)
>  		printf(" rcv_rtt:%g", s->rcv_rtt);
> +	if (s->rcv_space)
> +		printf(" rcv_space:%d", s->rcv_space);
>  
>  	CHECK_FMT_ADAPT(s->rcv_space, s,
>  	hr_handler_must_be_adapted_accordingly_when_json_fmt_is_extended);
> diff --git a/misc/ss_json_fmt.c b/misc/ss_json_fmt.c
> index a927952..1dff57a 100644
> --- a/misc/ss_json_fmt.c
> +++ b/misc/ss_json_fmt.c
> @@ -160,6 +160,8 @@ static void tcp_stats_json_fmt(struct tcpstat *s)
>  		jsonw_int_field(json_wr, "reordering", s->reordering);
>  	if (s->rcv_rtt)
>  		jsonw_float_field(json_wr, "rcv_rtt", s->rcv_rtt);
> +	if (s->rcv_space)
> +		jsonw_int_field(json_wr, "rcv_space", s->rcv_space);
>  
>  	/*deal with special case */
>  	res_json_fmt_branch(s->ss.state == SS_LISTEN, ' ');
> -- 
> 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

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 08/10] ss: symmetrical formatter extension example
  2015-09-10 19:35 ` [PATCH v7 08/10] ss: symmetrical formatter " Matthias Tafelmeier
@ 2015-10-27 13:17   ` Phil Sutter
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:17 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, Sep 10, 2015 at 09:35:06PM +0200, Matthias Tafelmeier wrote:
> This commit shall show shortly where to place changes when one wants to
> extend an ss output formatter with a new handler (format print
> procedure). The extension is done symmetrically. That means, every up to
> now existing formatter is extended with a semantically equivalent
> handler (hr and json formatter).
> 
> Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
> Suggested-by: Hagen Paul Pfeifer <hagen@jauu.net>
> ---
>  misc/ss_hr_fmt.c   | 65 ++++++++++++++++++++++++++++++++++++++++++++++++--
>  misc/ss_json_fmt.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  misc/ss_out_fmt.c  | 10 ++++++++
>  misc/ss_out_fmt.h  | 10 ++++++++
>  4 files changed, 150 insertions(+), 4 deletions(-)
> 
> diff --git a/misc/ss_hr_fmt.c b/misc/ss_hr_fmt.c
> index 4046ebf..a2bef60 100644
> --- a/misc/ss_hr_fmt.c
> +++ b/misc/ss_hr_fmt.c
> @@ -85,8 +85,8 @@ static void tcp_stats_hr_fmt(struct tcpstat *s)
>  	if (s->rcv_space)
>  		printf(" rcv_space:%d", s->rcv_space);
>  
> -	CHECK_FMT_ADAPT(s->rcv_space, s,
> -	hr_handler_must_be_adapted_accordingly_when_json_fmt_is_extended);
> +	/*hr handler must be adapted accordingly when json fmt is extended*/
> +	CHECK_FMT_ADAPT(s->rcv_space, s);

Unrelated change? Patch 02/10 introduces CHECK_FMT_ADAPT macro which
takes just two parameters. Quite confusing, especially since this patch
is meant to be documentation.

>  }
>  
>  static void tcp_timer_hr_fmt(struct tcpstat *s)
> @@ -242,6 +242,66 @@ static void packet_show_ring_hr_fmt(struct packet_diag_ring *ring)
>  	printf(",features:0x%x", ring->pdr_features);
>  }
>  
> +static void packet_details_hr_fmt(struct packet_diag_info *pinfo,
> +		struct packet_diag_ring *ring_rx,
> +		struct packet_diag_ring *ring_tx,
> +		uint32_t fanout,
> +		bool has_fanout)
> +{
> +	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_fmt(ring_rx);
> +		printf(")");
> +	}
> +	if (ring_tx) {
> +		printf("\n\tring_tx(");
> +		packet_show_ring_fmt(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(")");
> +	}
> +}
> +
>  const struct fmt_op_hub hr_output_op = {
>  	.tcp_stats_fmt = tcp_stats_hr_fmt,
>  	.tcp_timer_fmt = tcp_timer_hr_fmt,
> @@ -257,4 +317,5 @@ const struct fmt_op_hub hr_output_op = {
>  	.opt_fmt = opt_hr_fmt,
>  	.proc_fmt = proc_hr_fmt,
>  	.packet_show_ring_fmt = packet_show_ring_hr_fmt,
> +	.packet_details_fmt = packet_details_hr_fmt
>  };
> diff --git a/misc/ss_json_fmt.c b/misc/ss_json_fmt.c
> index 1dff57a..9b50832 100644
> --- a/misc/ss_json_fmt.c
> +++ b/misc/ss_json_fmt.c
> @@ -166,8 +166,8 @@ static void tcp_stats_json_fmt(struct tcpstat *s)
>  	/*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);
> +	/*json handler must be adapted accordingly when hr fmt is extended*/
> +	CHECK_FMT_ADAPT(s->rcv_space, s);
>  }
>  
>  static void tcp_timer_json_fmt(struct tcpstat *s)
> @@ -392,6 +392,70 @@ static void packet_show_ring_json_fmt(struct packet_diag_ring *ring)
>  	jsonw_hex_field_outp(json_wr, "features_0x", ring->pdr_features);
>  }
>  
> +static void packet_details_json_fmt(struct packet_diag_info *pinfo,
> +		struct packet_diag_ring *ring_rx,
> +		struct packet_diag_ring *ring_tx,
> +		uint32_t fanout,
> +		bool has_fanout)
> +{
> +	char tmp_out[16];
> +
> +	if (pinfo) {
> +		jsonw_int_field(json_wr, "ver", pinfo->pdi_version);
> +		jsonw_int_field(json_wr, "cpy_thresh", pinfo->pdi_copy_thresh);
> +		if (pinfo->pdi_flags & PDI_RUNNING)
> +			sprintf(tmp_out, "running");
> +		if (pinfo->pdi_flags & PDI_AUXDATA)
> +			sprintf(tmp_out, "_auxdata");
> +		if (pinfo->pdi_flags & PDI_ORIGDEV)
> +			sprintf(tmp_out, "_origdev");
> +		if (pinfo->pdi_flags & PDI_VNETHDR)
> +			sprintf(tmp_out, "_vnethdr");
> +		if (pinfo->pdi_flags & PDI_LOSS)
> +			sprintf(tmp_out, "_loss");
> +		if (!pinfo->pdi_flags)
> +			sprintf(tmp_out, "0");
> +		jsonw_string_field(json_wr, "flags", tmp_out);
> +	}
> +	if (ring_rx) {
> +		jsonw_name(json_wr, "ring_rx");
> +		jsonw_start_object(json_wr);
> +		packet_show_ring_fmt(ring_rx);
> +		jsonw_end_object(json_wr);
> +	}
> +	if (ring_tx) {
> +		jsonw_name(json_wr, "ring_tx");
> +		jsonw_start_object(json_wr);
> +		packet_show_ring_fmt(ring_tx);
> +		jsonw_end_object(json_wr);
> +	}
> +	if (has_fanout) {
> +		uint16_t type = (fanout >> 16) & 0xffff;
> +
> +		jsonw_name(json_wr, "fanout");
> +		jsonw_start_object(json_wr);
> +		jsonw_int_field(json_wr, "id", fanout & 0xffff);
> +
> +		if (type == 0)
> +			sprintf(tmp_out, "hash");
> +		else if (type == 1)
> +			sprintf(tmp_out, "lb");
> +		else if (type == 2)
> +			sprintf(tmp_out, "cpu");
> +		else if (type == 3)
> +			sprintf(tmp_out, "roll");
> +		else if (type == 4)
> +			sprintf(tmp_out, "random");
> +		else if (type == 5)
> +			sprintf(tmp_out, "qm");
> +		else
> +			sprintf(tmp_out, "0x%x", type);
> +
> +		jsonw_string_field(json_wr, "type", tmp_out);
> +		jsonw_end_object(json_wr);
> +	}
> +}
> +
>  const struct fmt_op_hub json_output_op = {
>  	.tcp_stats_fmt = tcp_stats_json_fmt,
>  	.tcp_timer_fmt = tcp_timer_json_fmt,
> @@ -407,4 +471,5 @@ const struct fmt_op_hub json_output_op = {
>  	.opt_fmt = opt_json_fmt,
>  	.proc_fmt = proc_json_fmt,
>  	.packet_show_ring_fmt = packet_show_ring_json_fmt,
> +	.packet_details_fmt = packet_details_json_fmt
>  };
> diff --git a/misc/ss_out_fmt.c b/misc/ss_out_fmt.c
> index 57bc24e..6d92201 100644
> --- a/misc/ss_out_fmt.c
> +++ b/misc/ss_out_fmt.c
> @@ -125,3 +125,13 @@ void packet_show_ring_fmt(struct packet_diag_ring *ring)
>  {
>  	fmt_op_hub[fmt_type]->packet_show_ring_fmt(ring);
>  }
> +void packet_details_fmt(struct packet_diag_info *pinfo,
> +		struct packet_diag_ring *ring_rx,
> +		struct packet_diag_ring *ring_tx,
> +		uint32_t fanout,
> +		bool has_fanout)
> +{
> +	fmt_op_hub[fmt_type]->packet_details_fmt(pinfo,
> +			ring_rx, ring_tx, fanout, has_fanout);
> +}
> +
> diff --git a/misc/ss_out_fmt.h b/misc/ss_out_fmt.h
> index 8608cde..bdc786b 100644
> --- a/misc/ss_out_fmt.h
> +++ b/misc/ss_out_fmt.h
> @@ -55,6 +55,11 @@ struct fmt_op_hub {
>  	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 (*packet_details_fmt)(struct packet_diag_info *pinfo,
> +		struct packet_diag_ring *ring_rx,
> +		struct packet_diag_ring *ring_tx,
> +		uint32_t fanout,
> +		bool has_fanout);
>  };
>  
>  void tcp_stats_fmt(struct tcpstat *s);
> @@ -75,6 +80,11 @@ 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 packet_details_fmt(struct packet_diag_info *pinfo,
> +		struct packet_diag_ring *ring_rx,
> +		struct packet_diag_ring *ring_tx,
> +		uint32_t fanout,
> +		bool has_fanout);
>  
>  /*unisonly utilized formatting parts*/
>  char *sprint_bw(char *buf, double bw);
> -- 
> 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

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 09/10] ss: fixed free on local array for valid json output
  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
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:19 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, Sep 10, 2015 at 09:35:07PM +0200, Matthias Tafelmeier wrote:
> Minor fix to enable json output. Freeing of automatic char array name
> which will get freed after function stack cleanup. Another one after
> tcp_stats_fmt for freeing automatic tcpstats struct instance.
> 
> Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
> ---
>  misc/ss.c | 6 ------
>  1 file changed, 6 deletions(-)
> 
> diff --git a/misc/ss.c b/misc/ss.c
> index 6ff40a3..80aebe6 100644
> --- a/misc/ss.c
> +++ b/misc/ss.c
> @@ -1660,10 +1660,6 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
>  		s.segs_out = info->tcpi_segs_out;
>  		s.segs_in = info->tcpi_segs_in;
>  		tcp_stats_fmt(&s);
> -		if (s.dctcp)
> -			free(s.dctcp);
> -		if (s.cong_alg)
> -			free(s.cong_alg);
>  	}
>  }
>  
> @@ -2362,8 +2358,6 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh,
>  	if (json_output)
>  		jsonw_end_object(json_wr);
>  
> -	if (name)
> -		free(name);
>  	return 0;
>  }

Is it necessary to fix earlier patches in this dedicated patch or can't
you just fold these changes into the patches where they belong?

>  
> -- 
> 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

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 10/10] ss: activate json_writer excluded logic
  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
  0 siblings, 1 reply; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:21 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, daniel

On Thu, Sep 10, 2015 at 09:35:08PM +0200, Matthias Tafelmeier wrote:
> This small patch extends the lib json_writer module for formerly
> deactivated functionality.

Why was it deactivated in the first place?

> 
> Signed-off-by: Matthias Tafelmeier <matthias.tafelmeier@gmx.net>
> ---
>  include/json_writer.h | 1 +
>  lib/json_writer.c     | 7 -------
>  2 files changed, 1 insertion(+), 7 deletions(-)
> 
> diff --git a/include/json_writer.h b/include/json_writer.h
> index ab9a008..3e2bbff 100644
> --- a/include/json_writer.h
> +++ b/include/json_writer.h
> @@ -15,6 +15,7 @@
>  #ifndef _JSON_WRITER_H_
>  #define _JSON_WRITER_H_
>  
> +#include <stdio.h>

Does this silently fix another bug, or is it just an unrelated change?

>  #include <stdbool.h>
>  #include <stdint.h>
>  
> diff --git a/lib/json_writer.c b/lib/json_writer.c
> index 2af16e1..dfed4d7 100644
> --- a/lib/json_writer.c
> +++ b/lib/json_writer.c
> @@ -12,7 +12,6 @@
>   * Authors:	Stephen Hemminger <stephen@networkplumber.org>
>   */
>  
> -#include <stdio.h>
>  #include <stdbool.h>
>  #include <stdarg.h>
>  #include <assert.h>
> @@ -201,7 +200,6 @@ void jsonw_bool(json_writer_t *self, bool val)
>  	jsonw_printf(self, "%s", val ? "true" : "false");
>  }
>  
> -#ifdef notused
>  void jsonw_null(json_writer_t *self)
>  {
>  	jsonw_printf(self, "null");
> @@ -211,7 +209,6 @@ void jsonw_float(json_writer_t *self, double num)
>  {
>  	jsonw_printf(self, "%g", num);
>  }
> -#endif
>  
>  void jsonw_uint(json_writer_t *self, uint64_t num)
>  {
> @@ -236,13 +233,11 @@ void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
>  	jsonw_bool(self, val);
>  }
>  
> -#ifdef notused
>  void jsonw_float_field(json_writer_t *self, const char *prop, double val)
>  {
>  	jsonw_name(self, prop);
>  	jsonw_float(self, val);
>  }
> -#endif
>  
>  void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
>  {
> @@ -256,13 +251,11 @@ void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
>  	jsonw_int(self, num);
>  }
>  
> -#ifdef notused
>  void jsonw_null_field(json_writer_t *self, const char *prop)
>  {
>  	jsonw_name(self, prop);
>  	jsonw_null(self);
>  }
> -#endif
>  
>  #ifdef TEST
>  int main(int argc, char **argv)
> -- 
> 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

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-10-27 13:47     ` David Miller
@ 2015-10-27 13:35       ` Phil Sutter
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-27 13:35 UTC (permalink / raw)
  To: David Miller
  Cc: matthias.tafelmeier, netdev, hagen, shemminger, fw, edumazet, daniel

On Tue, Oct 27, 2015 at 06:47:53AM -0700, David Miller wrote:
> 
> Please do not quote an entire large patch in a reply just to common on
> one particular section of the change.
> 
> Instead, trim it to just the necessary context.

OK, I'll make sure to remember this. Better don't read my other emails
of the same thread. :(

Thanks, Phil

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-10-27 12:21   ` Phil Sutter
@ 2015-10-27 13:47     ` David Miller
  2015-10-27 13:35       ` Phil Sutter
  0 siblings, 1 reply; 33+ messages in thread
From: David Miller @ 2015-10-27 13:47 UTC (permalink / raw)
  To: phil; +Cc: matthias.tafelmeier, netdev, hagen, shemminger, fw, edumazet, daniel


Please do not quote an entire large patch in a reply just to common on
one particular section of the change.

Instead, trim it to just the necessary context.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 10/10] ss: activate json_writer excluded logic
  2015-10-27 13:21   ` Phil Sutter
@ 2015-10-28  2:39     ` Stephen Hemminger
  2015-10-28  8:17       ` Phil Sutter
  0 siblings, 1 reply; 33+ messages in thread
From: Stephen Hemminger @ 2015-10-28  2:39 UTC (permalink / raw)
  To: Phil Sutter
  Cc: Matthias Tafelmeier, netdev, hagen, shemminger, fw, edumazet, daniel

On Tue, 27 Oct 2015 14:21:03 +0100
Phil Sutter <phil@nwl.cc> wrote:

> On Thu, Sep 10, 2015 at 09:35:08PM +0200, Matthias Tafelmeier wrote:
> > This small patch extends the lib json_writer module for formerly
> > deactivated functionality.  
> 
> Why was it deactivated in the first place?

The code came from another project that wasn't using this
function.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-09-25  6:01         ` Matthias Tafelmeier
@ 2015-10-28  8:07           ` Matthias Tafelmeier
  2015-10-28  8:28             ` Phil Sutter
       [not found]           ` <b114412a541d4273a9a7a1c0ae4777df@HQ1WP-EXMB11.corp.brocade.com>
  1 sibling, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-10-28  8:07 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev, hagen, shemminger, fw, edumazet, Phil Sutter

[-- Attachment #1: Type: text/plain, Size: 418 bytes --]


>
> Well, then we should wait for another voice aimed at the complexity of
> the patchset before amending and resending me the patchset.
>
>

Well, I perceive that after Sutter has taken over the maintenance
responsibilitiy and answered accordingly that the outstanding
resentments as resolved.
Those resentments were related to the patchsets complexity and size.

Right?

-- 
Matthias Tafelmeier



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 10/10] ss: activate json_writer excluded logic
  2015-10-28  2:39     ` Stephen Hemminger
@ 2015-10-28  8:17       ` Phil Sutter
  0 siblings, 0 replies; 33+ messages in thread
From: Phil Sutter @ 2015-10-28  8:17 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: Matthias Tafelmeier, netdev, hagen, shemminger, fw, edumazet, daniel

On Wed, Oct 28, 2015 at 11:39:41AM +0900, Stephen Hemminger wrote:
> On Tue, 27 Oct 2015 14:21:03 +0100
> Phil Sutter <phil@nwl.cc> wrote:
> 
> > On Thu, Sep 10, 2015 at 09:35:08PM +0200, Matthias Tafelmeier wrote:
> > > This small patch extends the lib json_writer module for formerly
> > > deactivated functionality.  
> > 
> > Why was it deactivated in the first place?
> 
> The code came from another project that wasn't using this
> function.

Ah, I didn't get that the functions he uncomments were not added by his
series in the first place. Still:
- This patch should come before 02/10 which makes use of the uncommented
  functions here.
- jsonw_null() and jsonw_null_field() are still unused, no need to
  uncomment them.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-10-28  8:07           ` Matthias Tafelmeier
@ 2015-10-28  8:28             ` Phil Sutter
  2015-10-28 11:57               ` Matthias Tafelmeier
  0 siblings, 1 reply; 33+ messages in thread
From: Phil Sutter @ 2015-10-28  8:28 UTC (permalink / raw)
  To: Matthias Tafelmeier
  Cc: Stephen Hemminger, netdev, hagen, shemminger, fw, edumazet

On Wed, Oct 28, 2015 at 09:07:47AM +0100, Matthias Tafelmeier wrote:
> 
> >
> > Well, then we should wait for another voice aimed at the complexity of
> > the patchset before amending and resending me the patchset.
> >
> >
> 
> Well, I perceive that after Sutter has taken over the maintenance
> responsibilitiy and answered accordingly that the outstanding
> resentments as resolved.

I did not take over maintenance responsibility (whatever that means to
you precisely). I merely reviewed the patches, focussing on the
technical aspects of both implementation and patch management.

Regarding the concept itself, I think the usability of filters in
combination with json output is worth a discussion as well.

> Those resentments were related to the patchsets complexity and size.

I didn't see any problem with that in the first place. It is indeed a
big change, achieving something like that without a big patch set is
unlikely.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-10-28  8:28             ` Phil Sutter
@ 2015-10-28 11:57               ` Matthias Tafelmeier
  2015-10-28 15:05                 ` Phil Sutter
  0 siblings, 1 reply; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-10-28 11:57 UTC (permalink / raw)
  To: Stephen Hemminger, netdev, hagen, shemminger, fw, edumazet

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA384

> I did not take over maintenance responsibility (whatever that means
> to you precisely). I merely reviewed the patches, focussing on the 
> technical aspects of both implementation and patch management.

Ah, I meant the maintenance of iproute2 as a whole. Though, obviosly I
must have misconceived that.

>> Those resentments were related to the patchsets complexity and
>> size.
> 
> I didn't see any problem with that in the first place. It is indeed
> a big change, achieving something like that without a big patch set
> is unlikely.
> 

Fine, I was just repounding that since Steven Hemminger raised that.
My reasoning here is that I just don't want to kick off restarting
work whith objections still in the minds – since we are already at V7
now.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBCQAGBQJWMLgnAAoJEOAWT1uK3zQ7dMkH/jHps8no3c23LRXGnVaX08Ap
Eha6XWU9pHrCHAM2AF6XI8aKERjS00ycuC12rFKoPZC2sjSXv4PTGFJq9w8AF71K
os5PPi1iZRFQ/0tti7pMkGTmUwRrtHmdfGNKvu79oRJfADaqaNtpZV+4UiS2bPCP
jy+89mA02XXgJpNkJgG/md6wNFHEsJBUGtcx3KSWqYXHHpV2FJoN1H8P28ESVAJA
H2o1De6g7XBbSpigiHX8X69CkzjZor5cYyWF6W5lUNXhGCQ4xqmGJycNKjM3Et/g
OPXvcaRKwv2R06pSYzkQ17tsnm9u8+R/v3nQvFDJGD0+zZJsc+c2by2KTQt6qm4=
=ONCK
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-10-28 11:57               ` Matthias Tafelmeier
@ 2015-10-28 15:05                 ` Phil Sutter
  2015-10-28 15:50                   ` Matthias Tafelmeier
  0 siblings, 1 reply; 33+ messages in thread
From: Phil Sutter @ 2015-10-28 15:05 UTC (permalink / raw)
  To: Matthias Tafelmeier
  Cc: Stephen Hemminger, netdev, hagen, shemminger, fw, edumazet

On Wed, Oct 28, 2015 at 12:57:28PM +0100, Matthias Tafelmeier wrote:
> >> Those resentments were related to the patchsets complexity and
> >> size.
> > 
> > I didn't see any problem with that in the first place. It is indeed
> > a big change, achieving something like that without a big patch set
> > is unlikely.
> > 
> 
> Fine, I was just repounding that since Steven Hemminger raised that.
> My reasoning here is that I just don't want to kick off restarting
> work whith objections still in the minds – since we are already at V7
> now.

Yeah, sorry for not having looked into this earlier. Also, I neither
have nor claim any power of veto. Apart from that, I'm not against this
patch series in general, just trying to help raise it's quality a bit.
Eventually, we don't set anything in stone so everything can be
fixed/improved later on. Except Git history of course, which is
important to get right in relation to bisecting.

Cheers, Phil

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-10-28 15:05                 ` Phil Sutter
@ 2015-10-28 15:50                   ` Matthias Tafelmeier
  0 siblings, 0 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-10-28 15:50 UTC (permalink / raw)
  To: Stephen Hemminger, netdev, hagen, shemminger, fw, edumazet

[-- Attachment #1: Type: text/plain, Size: 575 bytes --]


> Yeah, sorry for not having looked into this earlier. Also, I neither
> have nor claim any power of veto.

No big issue. Maybe Stephen can clarifiy things. I mean acknowledge no
further objections.

>  Apart from that, I'm not against this
> patch series in general, just trying to help raise it's quality a bit.

Many thanks for that.

> Eventually, we don't set anything in stone so everything can be
> fixed/improved later on. Except Git history of course, which is
> important to get right in relation to bisecting.
Absolutely!

-- 
BR

Matthias


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
       [not found]           ` <b114412a541d4273a9a7a1c0ae4777df@HQ1WP-EXMB11.corp.brocade.com>
@ 2015-11-02 23:06             ` Stephen Hemminger
  2015-11-03  6:54               ` Matthias Tafelmeier
  0 siblings, 1 reply; 33+ messages in thread
From: Stephen Hemminger @ 2015-11-02 23:06 UTC (permalink / raw)
  To: Matthias Tafelmeier; +Cc: netdev, hagen, shemminger, fw, edumazet, Phil Sutter

[-- Attachment #1: Type: text/plain, Size: 874 bytes --]

On Wed, 28 Oct 2015 08:07:47 +0000
Matthias Tafelmeier <matthias.tafelmeier@gmx.net> wrote:

> >
> > Well, then we should wait for another voice aimed at the complexity of
> > the patchset before amending and resending me the patchset.
> >
> >  
> 
> Well, I perceive that after Sutter has taken over the maintenance
> responsibilitiy and answ

Your perception is incorrect.
I am still maintaining iproute2. Phil is just providing lots of feedback
and new patches.

The size of the change makes it harder to digest, and I do think
adding JSON support is a good idea. Just concerned about the long
term maintainance overhead. Plus I want the other utilities to have
JSON output as well. Therefore this change is going to take longer
to adopt and hopefull we can figure out a good way to do this kind
of output.

Almost want to go to C++ or something.

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v7 02/10] ss: created formatters for json and hr
  2015-11-02 23:06             ` Stephen Hemminger
@ 2015-11-03  6:54               ` Matthias Tafelmeier
  0 siblings, 0 replies; 33+ messages in thread
From: Matthias Tafelmeier @ 2015-11-03  6:54 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev, hagen, shemminger, fw, edumazet, Phil Sutter

[-- Attachment #1: Type: text/plain, Size: 927 bytes --]

> Your perception is incorrect.
> I am still maintaining iproute2. Phil is just providing lots of feedback
> and new patches.
No offense intendet! I know, Phil has already clarified things in the
other responses. Maybe you overlooked those accidentally.

> The size of the change makes it harder to digest, and I do think
> adding JSON support is a good idea. Just concerned about the long
> term maintainance overhead. Plus I want the other utilities to have
> JSON output as well. Therefore this change is going to take longer
> to adopt and hopefull we can figure out a good way to do this kind
> of output.

I see! Well, do you conceive a decentralized approach or a centralized
one for all the utilities?
Decentralizied would be as things are now – I mean what my patch series
tries to amount to.

> Almost want to go to C++ or something.

Would that be viable or is that a wishful line of thougts?


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2015-11-03  6:55 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).