Parsing CSV can be cumbersome in depending software, so instead
offer JSON as well using "status 4".

The "status 4" command uses the same keys/values as "status 2"
and "status 3", just formatted as JSON.
---
 src/openvpn/multi.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 82a0b9d9..3e82431b 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1014,6 +1014,102 @@ multi_print_status(struct multi_context *m, struct 
status_output *so, const int
 
             status_printf(so, "END");
         }
+        else if (version == 4)
+        {
+            /*
+             * Status file version 4 (JSON)
+             */
+            status_printf(so, "{");
+            status_printf(so, "\"TITLE\": \"%s\",", title_string);
+            status_printf(so, "\"TIME\": \"%s\",", time_string(now, 0, false, 
&gc_top));
+            /* UNIX_TIME added here instead of making it part of TIME as well
+               as in status 2/3 */
+            status_printf(so, "\"UNIX_TIME\": %u,", (unsigned int)now);
+            status_printf(so, "\"CLIENT_LIST\": [");
+
+            /* JSON needs a "," between items, but not at the last position,
+               because I was unable to figure out how to determine if we
+               iterate over the last item, we inverse it by prepending
+               additional items with a comma */
+            int is_first = 1;
+            hash_iterator_init(m->hash, &hi);
+            while ((he = hash_iterator_next(&hi)))
+            {
+                struct gc_arena gc = gc_new();
+                const struct multi_instance *mi = (struct multi_instance *) 
he->value;
+
+                if (!mi->halt)
+                {
+                    if(0 == is_first) {
+                        status_printf(so, ",");
+                    } else {
+                        is_first = 0;
+                    }
+                    status_printf(so, "{");
+                    status_printf(so, "\"Common Name\": \"%s\",", 
tls_common_name(mi->context.c2.tls_multi, false));
+                    status_printf(so, "\"Real Address\": \"%s\",", 
mroute_addr_print(&mi->real, &gc));
+                    status_printf(so, "\"Virtual Address\": \"%s\",", 
print_in_addr_t(mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc));
+                    status_printf(so, "\"Virtual IPv6 Address\": \"%s\",", 
print_in6_addr(mi->reporting_addr_ipv6, IA_EMPTY_IF_UNDEF, &gc));
+                    status_printf(so, "\"Bytes Received\": " counter_format 
",", mi->context.c2.link_read_bytes);
+                    status_printf(so, "\"Bytes Sent\": " counter_format ",", 
mi->context.c2.link_write_bytes);
+                    status_printf(so, "\"Connected Since\": \"%s\",", 
time_string(mi->created, 0, false, &gc));
+                    status_printf(so, "\"Connected Since (time_t)\": %u,", 
(unsigned int)mi->created);
+                    status_printf(so, "\"Username\": \"%s\",", 
tls_username(mi->context.c2.tls_multi, false));
+#ifdef MANAGEMENT_DEF_AUTH
+                    status_printf(so, "\"Client ID\": %lu,", 
mi->context.c2.mda_context.cid);
+#endif
+                    status_printf(so, "\"Peer ID\": %" PRIu32, 
mi->context.c2.tls_multi ? mi->context.c2.tls_multi->peer_id : UINT32_MAX);
+                    status_printf(so, "}");
+                }
+                gc_free(&gc);
+            }
+            hash_iterator_free(&hi);
+
+            status_printf(so, "],");
+            status_printf(so, "\"ROUTING_TABLE\": [");
+
+            is_first = 1;
+            hash_iterator_init(m->vhash, &hi);
+            while ((he = hash_iterator_next(&hi)))
+            {
+                struct gc_arena gc = gc_new();
+                const struct multi_route *route = (struct multi_route *) 
he->value;
+
+                if (multi_route_defined(m, route))
+                {
+                    const struct multi_instance *mi = route->instance;
+                    const struct mroute_addr *ma = &route->addr;
+                    char flags[2] = {0, 0};
+
+                    if (route->flags & MULTI_ROUTE_CACHE)
+                    {
+                        flags[0] = 'C';
+                    }
+                        if(0 == is_first) {
+                            status_printf(so, ",");
+                        } else {
+                            is_first = 0;
+                        }
+                        status_printf(so, "{\"Virtual Address\": \"%s%s\",", 
mroute_addr_print(ma, &gc), flags);
+                        status_printf(so, "\"Common Name\": \"%s\",", 
tls_common_name(mi->context.c2.tls_multi, false));
+                        status_printf(so, "\"Real Address\": \"%s\",", 
mroute_addr_print(&mi->real, &gc));
+                        status_printf(so, "\"Last Ref\": \"%s\",", 
time_string(route->last_reference, 0, false, &gc));
+                        status_printf(so, "\"Last Ref (time_t)\": %u}", 
(unsigned int)route->last_reference);
+                }
+                gc_free(&gc);
+            }
+            hash_iterator_free(&hi);
+            status_printf(so, "]");
+
+            if (m->mbuf)
+            {
+                status_printf(so, ",\"GLOBAL_STATS\": {");
+                status_printf(so, "\"Max bcast/mcast queue length\": %d", 
mbuf_maximum_queued(m->mbuf));
+                status_printf(so, "}");
+            }
+            status_printf(so, "}");
+            status_printf(so, "END");
+        }
         else
         {
             status_printf(so, "ERROR: bad status format version number");
-- 
2.13.6


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to