From 8ff14a6d117e9f02dab23614cde7cc1421a8e83a Mon Sep 17 00:00:00 2001 From: Michel Stam Date: Fri, 18 Jul 2014 17:00:32 +0200 Subject: [PATCH] lldpcli: Add support for JSON-C OpenWRT is shipped with the JSON-C library, which is used for several other packages. Having a separate JSON library (Jansson) doing the exact same thing is wasting flash space. --- configure.ac | 10 +- m4/jansson.m4 | 1 + m4/json-c.m4 | 13 ++ src/client/Makefile.am | 10 +- .../{json_writer.c => jansson_writer.c} | 24 +-- src/client/jsonc_writer.c | 176 ++++++++++++++++++ src/client/lldpcli.c | 7 +- src/client/writer.h | 7 +- 8 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 m4/json-c.m4 rename src/client/{json_writer.c => jansson_writer.c} (93%) create mode 100644 src/client/jsonc_writer.c diff --git a/configure.ac b/configure.ac index 059fdbe5..b29a5033 100644 --- a/configure.ac +++ b/configure.ac @@ -191,10 +191,12 @@ fi AC_ARG_WITH([json], AS_HELP_STRING( [--with-json], - [Enable JSON output via Jansson @<:@default=no@:>@] + [Enable JSON output via janson,json-c,no @<:@default=no@:>@] )) -if test x"$with_json" = x"yes"; then +if test x"$with_json" = x"jansson"; then lldp_CHECK_JANSSON +elif test x"$with_json" = x"json-c"; then + lldp_CHECK_JSONC fi # Seccomp @@ -256,7 +258,9 @@ lldp_ARG_ENABLE([oldies], [compatibility with Linux kernel older than 2.6.18], [ AM_CONDITIONAL([HAVE_CHECK], [test x"$have_check" = x"yes"]) AM_CONDITIONAL([USE_SNMP], [test x"$with_snmp" = x"yes"]) AM_CONDITIONAL([USE_XML], [test x"$with_xml" = x"yes"]) -AM_CONDITIONAL([USE_JSON], [test x"$with_json" = x"yes"]) +AM_CONDITIONAL([USE_JSON], [test x"$with_json" = x"json-c" || test -x"$with_json" = x"jansson"]) +AM_CONDITIONAL([USE_JANSSON], [test x"$with_json" = x"jansson"]) +AM_CONDITIONAL([USE_JSONC], [test x"$with_json" = x"json-c"]) AM_CONDITIONAL([USE_SECCOMP], [test x"$with_seccomp" = x"yes"]) AC_OUTPUT diff --git a/m4/jansson.m4 b/m4/jansson.m4 index ca936d44..770de2ad 100644 --- a/m4/jansson.m4 +++ b/m4/jansson.m4 @@ -9,4 +9,5 @@ AC_DEFUN([lldp_CHECK_JANSSON], [ AC_SUBST([JANSSON_LIBS]) AC_SUBST([JANSSON_CFLAGS]) AC_DEFINE_UNQUOTED([USE_JSON], 1, [Define to indicate to enable JSON support]) + AC_DEFINE_UNQUOTED([USE_JANSSON], 1, [Define to indicate to enable JSON support through jansson]) ]) diff --git a/m4/json-c.m4 b/m4/json-c.m4 new file mode 100644 index 00000000..cfab01eb --- /dev/null +++ b/m4/json-c.m4 @@ -0,0 +1,13 @@ +# +# lldp_CHECK_JSONC +# + +AC_DEFUN([lldp_CHECK_JSONC], [ + PKG_CHECK_MODULES([JSONC], [json-c >= 0], [], + [AC_MSG_ERROR([*** unable to find json-c])]) + + AC_SUBST([JSONC_LIBS]) + AC_SUBST([JSONC_CFLAGS]) + AC_DEFINE_UNQUOTED([USE_JSON], 1, [Define to indicate to enable JSON support]) + AC_DEFINE_UNQUOTED([USE_JSONC], 1, [Define to indicate to enable JSON via json-c support]) +]) diff --git a/src/client/Makefile.am b/src/client/Makefile.am index 4370d85b..af747ea4 100644 --- a/src/client/Makefile.am +++ b/src/client/Makefile.am @@ -28,8 +28,14 @@ lldpcli_CFLAGS += @XML2_CFLAGS@ lldpcli_LDADD += @XML2_LIBS@ endif -if USE_JSON -lldpcli_SOURCES += json_writer.c +if USE_JANSSON +lldpcli_SOURCES += jansson_writer.c lldpcli_CFLAGS += @JANSSON_CFLAGS@ lldpcli_LDADD += @JANSSON_LIBS@ endif + +if USE_JSONC +lldpcli_SOURCES += jsonc_writer.c +lldpcli_CFLAGS += @JSONC_CFLAGS@ +lldpcli_LDADD += @JSONC_LIBS@ +endif diff --git a/src/client/json_writer.c b/src/client/jansson_writer.c similarity index 93% rename from src/client/json_writer.c rename to src/client/jansson_writer.c index bf914a63..3ddfc8ee 100644 --- a/src/client/json_writer.c +++ b/src/client/jansson_writer.c @@ -68,7 +68,7 @@ struct json_element { TAILQ_HEAD(json_writer_private, json_element); static void -json_start(struct writer *w, const char *tag, const char *descr) +jansson_start(struct writer *w, const char *tag, const char *descr) { struct json_writer_private *p = w->priv; struct json_element *current = TAILQ_LAST(p, json_writer_private); @@ -91,7 +91,7 @@ json_start(struct writer *w, const char *tag, const char *descr) } static void -json_attr(struct writer *w, const char *tag, +jansson_attr(struct writer *w, const char *tag, const char *descr, const char *value) { struct json_writer_private *p = w->priv; @@ -107,7 +107,7 @@ json_attr(struct writer *w, const char *tag, } static void -json_data(struct writer *w, const char *data) +jansson_data(struct writer *w, const char *data) { struct json_writer_private *p = w->priv; struct json_element *current = TAILQ_LAST(p, json_writer_private); @@ -115,7 +115,7 @@ json_data(struct writer *w, const char *data) } static void -json_end(struct writer *w) +jansson_end(struct writer *w) { struct json_writer_private *p = w->priv; struct json_element *current = TAILQ_LAST(p, json_writer_private); @@ -132,7 +132,7 @@ json_end(struct writer *w) * `name` key outside (inside a new object). This is a recursive function. We * think the depth will be limited. */ static json_t* -json_cleanup(json_t *el) +jansson_cleanup(json_t *el) { json_t *new; if (el == NULL) return NULL; @@ -187,7 +187,7 @@ json_cleanup(json_t *el) } static void -json_finish(struct writer *w) +jansson_finish(struct writer *w) { struct json_writer_private *p = w->priv; if (TAILQ_EMPTY(p)) { @@ -212,7 +212,7 @@ json_finish(struct writer *w) } struct writer* -json_init(FILE *fh) +jansson_init(FILE *fh) { struct writer *result; struct json_writer_private *priv; @@ -231,11 +231,11 @@ json_init(FILE *fh) if (result == NULL) fatal(NULL, NULL); result->priv = priv; - result->start = json_start; - result->attr = json_attr; - result->data = json_data; - result->end = json_end; - result->finish = json_finish; + result->start = jansson_start; + result->attr = jansson_attr; + result->data = jansson_data; + result->end = jansson_end; + result->finish = jansson_finish; return result; } diff --git a/src/client/jsonc_writer.c b/src/client/jsonc_writer.c new file mode 100644 index 00000000..da780a37 --- /dev/null +++ b/src/client/jsonc_writer.c @@ -0,0 +1,176 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2014 Michel Stam , + * Vincent Bernat + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "writer.h" +#include "../compat/compat.h" +#include "../log.h" + +/* + * { lldp: + * { interface: [ + + * { chassis: + * { id: { type: "mac", value: "60:eb:69:ce:6e:a0" }, + * name: "guybrush", + * descr: "Debian GNU/Linux 7.0 (wheezy)", + * capability: [{type: "bridge", enabled: true}, {type: "router", enabled: false}] }, +* +* +* 60:eb:69:ce:6e:a0 +* guybrush.luffy.cx +* Debian GNU/Linux 7.0 (wheezy) Linux 3.5-trunk-amd64 #1 SMP Debian 3.5.5-1~experimental.1 x86_64 +* 192.168.116.3 +* +* +* +* +* +* fe:86:6f:b6:1e:db +* port1 +* +* 10GigBaseR - R PCS/PMA, unknown PMD. +* +* +* +* +*/ + +/* This list is used as a queue. The queue does not hold reference to the json_t + * element except the first one. */ +struct json_element { + TAILQ_ENTRY(json_element) next; + json_object *el; +}; +TAILQ_HEAD(json_writer_private, json_element); + +static void +jsonc_start(struct writer *w, const char *tag, const char *descr) +{ + struct json_writer_private *p = w->priv; + struct json_element *current = TAILQ_LAST(p, json_writer_private); + struct json_element *new; + json_object *exist = NULL; + + if (!json_object_object_get_ex(current->el, tag, &exist)) { + exist = json_object_new_array(); + json_object_object_add(current->el, tag, exist); + } + + /* Queue the new element. */ + new = malloc(sizeof(*new)); + if (new == NULL) fatal(NULL, NULL); + new->el = json_object_new_object(); + json_object_array_add(exist, new->el); + TAILQ_INSERT_TAIL(p, new, next); +} + +static void +jsonc_attr(struct writer *w, const char *tag, + const char *descr, const char *value) +{ + struct json_writer_private *p = w->priv; + struct json_element *current = TAILQ_LAST(p, json_writer_private); + json_object *jvalue; + if (!strcmp(value, "yes") || !strcmp(value, "on")) + jvalue = json_object_new_boolean(1); + else if (!strcmp(value, "no") || !strcmp(value, "off")) + jvalue = json_object_new_boolean(0); + else + jvalue = json_object_new_string(value); + json_object_object_add(current->el, tag, jvalue); +} + +static void +jsonc_data(struct writer *w, const char *data) +{ + struct json_writer_private *p = w->priv; + struct json_element *current = TAILQ_LAST(p, json_writer_private); + json_object_object_add(current->el, "value", json_object_new_string(data)); +} + +static void +jsonc_end(struct writer *w) +{ + struct json_writer_private *p = w->priv; + struct json_element *current = TAILQ_LAST(p, json_writer_private); + if (current == NULL) { + log_warnx("lldpctl", "unbalanced tags"); + return; + } + TAILQ_REMOVE(p, current, next); + free(current); +} + +static void +jsonc_finish(struct writer *w) +{ + struct json_writer_private *p = w->priv; + if (TAILQ_EMPTY(p)) { + log_warnx("lldpctl", "nothing to output"); + } else if (TAILQ_NEXT(TAILQ_FIRST(p), next) != NULL) { + log_warnx("lldpctl", "unbalanced tags"); + /* memory will leak... */ + } else { + struct json_element *first = TAILQ_FIRST(p); + fprintf(stdout, "%s", json_object_to_json_string(first->el)); + json_object_put(first->el); + TAILQ_REMOVE(p, first, next); + free(first); + } + free(p); + free(w); +} + +struct writer* +jsonc_init(FILE *fh) +{ + struct writer *result; + struct json_writer_private *priv; + struct json_element *root; + + priv = malloc(sizeof(*priv)); + root = malloc(sizeof(*root)); + if (priv == NULL || root == NULL) fatal(NULL, NULL); + TAILQ_INIT(priv); + TAILQ_INSERT_TAIL(priv, root, next); + root->el = json_object_new_object(); + if (root->el == NULL) + fatalx("cannot create JSON root object"); + + result = malloc(sizeof(*result)); + if (result == NULL) fatal(NULL, NULL); + + result->priv = priv; + result->start = jsonc_start; + result->attr = jsonc_attr; + result->data = jsonc_data; + result->end = jsonc_end; + result->finish = jsonc_finish; + + return result; +} diff --git a/src/client/lldpcli.c b/src/client/lldpcli.c index 964c6bbe..689ef426 100644 --- a/src/client/lldpcli.c +++ b/src/client/lldpcli.c @@ -271,8 +271,11 @@ cmd_exec(lldpctl_conn_t *conn, const char *fmt, int argc, const char **argv) #ifdef USE_XML else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout); #endif -#ifdef USE_JSON - else if (strcmp(fmt, "json") == 0) w = json_init(stdout); +#ifdef USE_JANSSON + else if (strcmp(fmt, "json") == 0) w = jansson_init(stdout); +#endif +#ifdef USE_JSONC + else if (strcmp(fmt, "json") == 0) w = jsonc_init(stdout); #endif else w = txt_init(stdout); diff --git a/src/client/writer.h b/src/client/writer.h index f4a47592..ea2d4cff 100644 --- a/src/client/writer.h +++ b/src/client/writer.h @@ -41,8 +41,11 @@ extern struct writer * kv_init( FILE * ); #ifdef USE_XML extern struct writer * xml_init( FILE * ); #endif -#ifdef USE_JSON -extern struct writer * json_init( FILE * ); +#ifdef USE_JANSSON +extern struct writer * jansson_init( FILE * ); +#endif +#ifdef USE_JSONC +extern struct writer * jsonc_init( FILE * ); #endif #endif /* _WRITER_H */ -- 2.39.5