]> git.ipfire.org Git - thirdparty/mtr.git/commitdiff
report: json: reworked with libjansson 355/head
authorKulemin Alexander <ltWolfik@yandex.ru>
Thu, 9 Jul 2020 16:40:57 +0000 (19:40 +0300)
committerKulemin Alexander <ltWolfik@yandex.ru>
Thu, 9 Jul 2020 21:49:24 +0000 (00:49 +0300)
Makefile.am
configure.ac
man/mtr.8.in
ui/display.c
ui/display.h
ui/mtr.c
ui/report.c

index 7fed5ce426ee56cb843f025c1799eaf07ffba988..7719be140cc2c6e64532e9e3f8d535dd27974fdd 100644 (file)
@@ -92,8 +92,8 @@ ASAN_CFLAGS += -fsanitize=address
 endif
 
 mtr_INCLUDES = $(GLIB_CFLAGS) -I$(top_builddir) -I$(top_srcdir)
-mtr_CFLAGS = $(GTK_CFLAGS) $(NCURSES_CFLAGS) $(ASAN_CFLAGS)
-mtr_LDADD = $(GTK_LIBS) $(NCURSES_LIBS) $(RESOLV_LIBS)
+mtr_CFLAGS = $(GTK_CFLAGS) $(NCURSES_CFLAGS) $(ASAN_CFLAGS) $(JANSSON_CFLAGS)
+mtr_LDADD = $(GTK_LIBS) $(NCURSES_LIBS) $(RESOLV_LIBS) $(JANSSON_LIBS)
 
 
 mtr_packet_SOURCES = \
index 2ec0a79ef476b730d4a8416b16c6a4dfb825fbea..88b508c18fc55c372cd1795dc4931a11fc474d9b 100644 (file)
@@ -104,6 +104,15 @@ AS_IF([test "x$with_gtk" = "xyes"],
 ])
 AM_CONDITIONAL([WITH_GTK], [test "x$with_gtk" = xyes])
 
+AC_ARG_WITH([jansson],
+  [AS_HELP_STRING([--without-jansson], [Build without JSON output])],
+  [], [with_jansson=yes])
+AS_IF([test "x$with_jansson" = "xyes"],
+  [PKG_CHECK_MODULES([JANSSON], [jansson],
+    [AC_DEFINE([HAVE_JANSSON], [1], [Define if jansson library available])],
+    [with_jansson=no])
+])
+
 # Find ncurses
 AC_ARG_WITH([ncurses],
   [AS_HELP_STRING([--without-ncurses], [Build without the ncurses interface])],
@@ -259,6 +268,7 @@ echo "ipv6    :$USES_IPV6"
 echo "ipinfo  :$with_ipinfo"
 echo "ncurses :$with_ncurses"
 echo "gtk     :$with_gtk"
+echo "jansson :$with_jansson"
 echo "cap     :$have_cap"
 echo "libs    :$LIBS"
 echo "cflags  :$CFLAGS"
index e8c54047e082549891fe893b483686b09cf76666..9d13535043e205ad6d087fb491a936c36c9d392c 100644 (file)
@@ -267,6 +267,9 @@ Use this option to tell
 .B mtr
 to use the JSON output format.  This format is better suited for
 automated processing of the measurement results.
+Jansson library must have been available on the system when
+.B mtr
+was built for this to work.
 .TP
 .B \-p\fR, \fB\-\-split
 Use this option to set
index e533f97e3c3a0b812fe81647d79b43b2d4f556a7..7127be520b63ef7842a8468730c3f4add18c3652 100644 (file)
@@ -81,9 +81,11 @@ void display_open(
     case DisplayTXT:
         txt_open();
         break;
+#ifdef HAVE_JANSSON
     case DisplayJSON:
         json_open();
         break;
+#endif
     case DisplayXML:
         xml_open();
         break;
@@ -127,9 +129,11 @@ void display_close(
     case DisplayTXT:
         txt_close(ctl);
         break;
+#ifdef HAVE_JANSSON
     case DisplayJSON:
         json_close(ctl);
         break;
+#endif
     case DisplayXML:
         xml_close(ctl);
         break;
index 5b37b56c82883ecf9ed2c8fee57a19dc898a6a8e..3eaa291b4c3e912a4cc8d1ad98f58febcd113009 100644 (file)
@@ -41,7 +41,9 @@ enum {
     DisplayXML,
     DisplayCSV,
     DisplayTXT,
-    DisplayJSON
+#ifdef HAVE_JANSSON
+    DisplayJSON,
+#endif
 };
 
 enum {
index bfc89c37e9778307c1f2ee0dc188095daf749096..b33a13684233fd9d577925d76c12914cf4763e86 100644 (file)
--- a/ui/mtr.c
+++ b/ui/mtr.c
@@ -349,7 +349,9 @@ static void parse_arg(
 #endif
         {"raw", 0, NULL, 'l'},
         {"csv", 0, NULL, 'C'},
+#ifdef HAVE_JANSSON
         {"json", 0, NULL, 'j'},
+#endif
         {"displaymode", 1, NULL, OPT_DISPLAYMODE},
         {"split", 0, NULL, 'p'},        /* BL */
         /* maybe above should change to -d 'x' */
@@ -445,9 +447,11 @@ static void parse_arg(
         case 'C':
             ctl->DisplayMode = DisplayCSV;
             break;
+#ifdef HAVE_JANSSON
         case 'j':
             ctl->DisplayMode = DisplayJSON;
             break;
+#endif
         case 'x':
             ctl->DisplayMode = DisplayXML;
             break;
@@ -644,7 +648,9 @@ static void parse_arg(
 
     if (ctl->DisplayMode == DisplayReport ||
         ctl->DisplayMode == DisplayTXT ||
+#ifdef HAVE_JANSSON
         ctl->DisplayMode == DisplayJSON ||
+#endif
         ctl->DisplayMode == DisplayXML ||
         ctl->DisplayMode == DisplayRaw || ctl->DisplayMode == DisplayCSV)
         ctl->Interactive = 0;
index 97a3fa48ac75bdeaa5daca841528e5bd9f59ce55..1f5bd285cadb7181d205d6267259c490b436bbda 100644 (file)
 #include <string.h>
 #include <strings.h>
 #include <time.h>
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+#endif
+#ifdef HAVE_ERROR_H
+#include <error.h>
+#else
+#include "portability/error.h"
+#endif
 
 #include "mtr.h"
 #include "report.h"
@@ -269,111 +277,111 @@ void txt_close(
     report_close(ctl);
 }
 
-
+#ifdef HAVE_JANSSON
 void json_open(
     void)
 {
 }
 
-
-void json_close(
-    struct mtr_ctl *ctl)
+void json_close(struct mtr_ctl *ctl)
 {
-    int i, j, at, first, max;
+    int i, j, at, max;
+    int ret;
+    char buf[128];
+    json_t *jreport, *jmtr, *jhubs, *jh;
     ip_t *addr;
     char name[MAX_FORMAT_STR];
 
-    printf("{\n");
-    printf("  \"report\": {\n");
-    printf("    \"mtr\": {\n");
-    printf("      \"src\": \"%s\",\n", ctl->LocalHostname);
-    printf("      \"dst\": \"%s\",\n", ctl->Hostname);
-    printf("      \"tos\": \"0x%X\",\n", ctl->tos);
+    jmtr = json_pack("{ss ss si si}",
+                     "src", ctl->LocalHostname,
+                     "dst", ctl->Hostname,
+                     "tos", ctl->tos,
+                     "tests", ctl->MaxPing);
+    if (!jmtr)
+        goto on_error;
+
     if (ctl->cpacketsize >= 0) {
-        printf("      \"psize\": \"%d\",\n", ctl->cpacketsize);
+        snprintf(buf, sizeof(buf), "%d", ctl->cpacketsize);
     } else {
-        printf("      \"psize\": \"rand(%d-%d)\",\n", MINPACKET,
-               -ctl->cpacketsize);
+        snprintf(buf, sizeof(buf), "rand(%d-%d)", MINPACKET, -ctl->cpacketsize);
     }
+    ret = json_object_set_new(jmtr, "psize", json_string(buf));
+    if (ret == -1)
+        goto on_error;
+
     if (ctl->bitpattern >= 0) {
-        printf("      \"bitpattern\": \"0x%02X\",\n",
-               (unsigned char) (ctl->bitpattern));
+        snprintf(buf, sizeof(buf), "0x%02X", (unsigned char)(ctl->bitpattern));
     } else {
-        printf("      \"bitpattern\": \"rand(0x00-FF)\",\n");
+        snprintf(buf, sizeof(buf), "rand(0x00-FF)");
     }
-    printf("      \"tests\": \"%d\"\n", ctl->MaxPing);
-    printf("    },\n");
 
-    printf("    \"hubs\": [");
+    ret = json_object_set_new(jmtr, "bitpattern", json_string(buf));
+    if (ret == -1)
+        goto on_error;
+
+    jhubs = json_array();
+    if (!jhubs)
+        goto on_error;
 
     max = net_max(ctl);
-    at = first = net_min(ctl);
+    at = net_min(ctl);
     for (; at < max; at++) {
         addr = net_addr(at);
         snprint_addr(ctl, name, sizeof(name), addr);
 
-        if (at == first) {
-            printf("{\n");
-        } else {
-            printf("    {\n");
-        }
-        printf("      \"count\": \"%d\",\n", at + 1);
-        printf("      \"host\": \"%s\",\n", name);
+        jh = json_pack("{si ss}", "count", at + 1, "host", name);
+        if (!jh)
+            goto on_error;
+
 #ifdef HAVE_IPINFO
         if(!ctl->ipinfo_no) {
-          char* fmtinfo = fmt_ipinfo(ctl, addr);
-          if (fmtinfo != NULL) fmtinfo = trim(fmtinfo, '\0');
-          printf("      \"ASN\": \"%s\",\n", fmtinfo);
+            char* fmtinfo = fmt_ipinfo(ctl, addr);
+            if (fmtinfo != NULL)
+                fmtinfo = trim(fmtinfo, '\0');
+
+            ret = json_object_set_new(jh, "ASN", json_string(fmtinfo));
+            if (ret == -1)
+                goto on_error;
         }
 #endif
-        for (i = 0; i < MAXFLD; i++) {
-            const char *format;
 
+        for (i = 0; i < MAXFLD; i++) {
             j = ctl->fld_index[ctl->fld_active[i]];
 
-            /* Commas */
-            if (i + 1 == MAXFLD) {
-                printf("\n");
-            } else if (j > 0 && i != 0) {
-                printf(",\n");
-            }
-
             if (j <= 0)
-                continue;       /* Field nr 0, " " shouldn't be printed in this method. */
+                continue; /* Field nr 0, " " shouldn't be printed in this method. */
 
-            /* Format value */
-            format = data_fields[j].format;
-            if (strchr(format, 'f')) {
-                format = "%.2f";
-            } else {
-                format = "%d";
-            }
-
-            /* Format json line */
-            snprintf(name, sizeof(name), "%s%s", "      \"%s\": ", format);
-
-            /* Output json line */
             if (strchr(data_fields[j].format, 'f')) {
-                /* 1000.0 is a temporay hack for stats usec to ms, impacted net_loss. */
-                printf(name,
-                       data_fields[j].title,
-                       data_fields[j].net_xxx(at) / 1000.0);
+                ret = json_object_set_new(
+                    jh, data_fields[j].title,
+                    json_real(data_fields[j].net_xxx(at) / 1000.0));
             } else {
-                printf(name,
-                       data_fields[j].title, data_fields[j].net_xxx(at));
+                ret = json_object_set_new(
+                    jh, data_fields[j].title,
+                    json_integer(data_fields[j].net_xxx(at)));
             }
+            if (ret == -1)
+                goto on_error;
         }
-        if (at + 1 == max) {
-            printf("    }");
-        } else {
-            printf("    },\n");
-        }
+
+        ret = json_array_append_new(jhubs, jh);
+        if (ret == -1)
+            goto on_error;
     }
-    printf("]\n");
-    printf("  }\n");
-    printf("}\n");
-}
 
+    jreport = json_pack("{s{so so}}", "report", "mtr", jmtr, "hubs", jhubs);
+
+    ret = json_dumpf(jreport, stdout, JSON_INDENT(4) | JSON_REAL_PRECISION(5));
+    if (ret == -1)
+        goto on_error;
+
+    printf("\n"); // bash promt should be on new line
+    json_decref(jreport);
+    return;
+on_error:
+    error(EXIT_FAILURE, 0, "json_close failed");
+}
+#endif
 
 
 void xml_open(