Hi François, This is not a full review, but as just discussed face-to-face, here are some points from me:
1) Could we also please please enable this for non-management interface use case? I guess it's just adding the status version number 4 in option/config validator (and the manpage). 2) With 1), perhaps it would be more clear to use *version* in your message/description so that it's about the --status-version option, not the --status option. 3) I believe the output structure is not valid JSON with the "END" string at the end. (FYI I'm about to upload a patch to include more documentation on the --status-version option and to include a new field as well.) -- Gert On 10-11-17 17:39, François Kooman wrote: > 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"); > ------------------------------------------------------------------------------ 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