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