Use cJSON for producing JSON output in clients.

Closes #1222. Thanks to Ben Barbour.
pull/1480/head
Roger A. Light 6 years ago
parent b2a9daf1db
commit ad5c2e11d9

@ -13,6 +13,8 @@ cmake_minimum_required(VERSION 2.8)
set (VERSION 1.6.7) set (VERSION 1.6.7)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
add_definitions (-DCMAKE -DVERSION=\"${VERSION}\") add_definitions (-DCMAKE -DVERSION=\"${VERSION}\")
if (WIN32) if (WIN32)

@ -15,6 +15,7 @@ Clients:
`mosquitto_rr -W <secs>`. Closes #275. `mosquitto_rr -W <secs>`. Closes #275.
- Add support for connecting to brokers through Unix domain sockets with the - Add support for connecting to brokers through Unix domain sockets with the
`--unix` argument. `--unix` argument.
- Use cJSON library for producing JSON output, where available. Closes #1222.
1.6.7 - 20190925 1.6.7 - 20190925

@ -1,7 +1,4 @@
include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib FIND_PACKAGE(cJSON)
${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR})
link_directories(${mosquitto_BINARY_DIR}/lib)
set(shared_src client_shared.c client_shared.h client_props.c) set(shared_src client_shared.c client_shared.h client_props.c)
@ -9,10 +6,32 @@ if (WITH_SRV)
add_definitions("-DWITH_SRV") add_definitions("-DWITH_SRV")
endif (WITH_SRV) endif (WITH_SRV)
set( CLIENT_INC ${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib
${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR})
set( CLIENT_DIR ${mosquitto_BINARY_DIR}/lib)
if (CJSON_FOUND)
message(STATUS ${CJSON_FOUND})
set( CLIENT_DIR "${CLIENT_DIR} ${CJSON_DIR}" )
set( CLIENT_INC "${CLIENT_INC};${CJSON_INCLUDE_DIRS}" )
add_definitions("-DWITH_CJSON")
endif()
include_directories(${CLIENT_INC})
link_directories(${CLIENT_DIR})
add_executable(mosquitto_pub pub_client.c pub_shared.c ${shared_src}) add_executable(mosquitto_pub pub_client.c pub_shared.c ${shared_src})
add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src}) add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src})
add_executable(mosquitto_rr rr_client.c pub_shared.c sub_client_output.c ${shared_src}) add_executable(mosquitto_rr rr_client.c pub_shared.c sub_client_output.c ${shared_src})
if (CJSON_FOUND)
target_link_libraries(mosquitto_pub ${CJSON_LIBRARIES})
target_link_libraries(mosquitto_sub ${CJSON_LIBRARIES})
target_link_libraries(mosquitto_rr ${CJSON_LIBRARIES})
endif()
if (WITH_STATIC_LIBRARIES) if (WITH_STATIC_LIBRARIES)
target_link_libraries(mosquitto_pub libmosquitto_static) target_link_libraries(mosquitto_pub libmosquitto_static)
target_link_libraries(mosquitto_sub libmosquitto_static) target_link_libraries(mosquitto_sub libmosquitto_static)

@ -30,6 +30,10 @@ Contributors:
#define snprintf sprintf_s #define snprintf sprintf_s
#endif #endif
#ifdef WITH_CJSON
# include <cJSON.h>
#endif
#ifdef __APPLE__ #ifdef __APPLE__
# include <sys/time.h> # include <sys/time.h>
#endif #endif
@ -96,6 +100,7 @@ static void write_payload(const unsigned char *payload, int payloadlen, int hex)
} }
#ifndef WITH_CJSON
static void write_json_payload(const char *payload, int payloadlen) static void write_json_payload(const char *payload, int payloadlen)
{ {
int i; int i;
@ -108,10 +113,91 @@ static void write_json_payload(const char *payload, int payloadlen)
} }
} }
} }
#endif
static void json_print(const struct mosquitto_message *message, const struct tm *ti, bool escaped) static int json_print(const struct mosquitto_message *message, const struct tm *ti, bool escaped)
{ {
#ifdef WITH_CJSON
cJSON *root;
cJSON *tmp;
char *json_str;
const char *return_parse_end;
root = cJSON_CreateObject();
if(root == NULL){
return MOSQ_ERR_NOMEM;
}
tmp = cJSON_CreateNumber(time(NULL));
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "tst", tmp);
tmp = cJSON_CreateStringReference(message->topic);
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "topic", tmp);
tmp = cJSON_CreateNumber(message->qos);
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "qos", tmp);
tmp = cJSON_CreateNumber(message->retain);
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "retain", tmp);
tmp = cJSON_CreateNumber(message->payloadlen);
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "payloadlen", tmp);
if(message->qos > 0){
tmp = cJSON_CreateNumber(message->mid);
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "mid", tmp);
}
if(escaped){
tmp = cJSON_CreateStringReference(message->payload);
if(tmp == NULL){
cJSON_Delete(root);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(root, "payload", tmp);
}else{
return_parse_end = NULL;
tmp = cJSON_ParseWithOpts(message->payload, &return_parse_end, true);
if(tmp == NULL || return_parse_end != message->payload + message->payloadlen){
cJSON_Delete(root);
return MOSQ_ERR_INVAL;
}
cJSON_AddItemToObject(root, "payload", tmp);
}
json_str = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
fputs(json_str, stdout);
free(json_str);
return MOSQ_ERR_SUCCESS;
#else
char buf[100]; char buf[100];
strftime(buf, 100, "%s", ti); strftime(buf, 100, "%s", ti);
@ -128,6 +214,9 @@ static void json_print(const struct mosquitto_message *message, const struct tm
write_payload(message->payload, message->payloadlen, 0); write_payload(message->payload, message->payloadlen, 0);
fputs("}", stdout); fputs("}", stdout);
} }
return MOSQ_ERR_SUCCESS;
#endif
} }
@ -139,6 +228,7 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
long ns; long ns;
char strf[3]; char strf[3];
char buf[100]; char buf[100];
int rc;
len = strlen(lcfg->format); len = strlen(lcfg->format);
@ -170,7 +260,10 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
return; return;
} }
} }
json_print(message, ti, true); if(json_print(message, ti, true) != MOSQ_ERR_SUCCESS){
err_printf(lcfg, "Error: Out of memory.\n");
return;
}
break; break;
case 'J': case 'J':
@ -180,7 +273,14 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
return; return;
} }
} }
json_print(message, ti, false); rc = json_print(message, ti, false);
if(rc == MOSQ_ERR_NOMEM){
err_printf(lcfg, "Error: Out of memory.\n");
return;
}else if(rc == MOSQ_ERR_INVAL){
err_printf(lcfg, "Error: Message payload is not valid JSON on topic %s.\n", message->topic);
return;
}
break; break;
case 'l': case 'l':

@ -0,0 +1,39 @@
INCLUDE( FindPackageHandleStandardArgs )
# Checks an environment variable; note that the first check
# does not require the usual CMake $-sign.
IF( DEFINED ENV{CJSON_DIR} )
SET( CJSON_DIR "$ENV{CJSON_DIR}" )
ENDIF()
FIND_PATH(
CJSON_INCLUDE_DIR
cJSON.h
HINTS
CJSON_DIR
/usr/include/cjson
)
FIND_LIBRARY( CJSON_LIBRARY
NAMES cjson
HINTS ${CJSON_DIR}
)
FIND_PACKAGE_HANDLE_STANDARD_ARGS( CJSON DEFAULT_MSG
CJSON_INCLUDE_DIR CJSON_LIBRARY
)
IF( CJSON_FOUND )
SET( CJSON_INCLUDE_DIRS ${CJSON_INCLUDE_DIR} )
SET( CJSON_LIBRARIES ${CJSON_LIBRARY} )
MARK_AS_ADVANCED(
CJSON_LIBRARY
CJSON_INCLUDE_DIR
CJSON_DIR
)
ELSE()
SET( CJSON_DIR "" CACHE STRING
"An optional hint to a directory for finding `cJSON`"
)
ENDIF()

@ -4,7 +4,8 @@ are optional.
* openssl * openssl
* c-ares (for DNS-SRV support, disabled by default) * c-ares (for DNS-SRV support, disabled by default)
* tcp-wrappers (optional, package name libwrap0-dev) * tcp-wrappers (optional, package name libwrap0-dev)
* libwebsockets (optional, disabled by default, version 1.3 and above) * libwebsockets (optional, disabled by default, version 2.4 and above)
* cJSON (optional, for JSON output from mosquitto_sub/mosquitto_rr)
* On Windows, a pthreads library is required if threading support is to be * On Windows, a pthreads library is required if threading support is to be
included. included.

@ -100,6 +100,9 @@ WITH_COVERAGE:=no
# Build with unix domain socket support # Build with unix domain socket support
WITH_UNIX_SOCKETS:=yes WITH_UNIX_SOCKETS:=yes
# Build mosquitto_sub with cJSON support
WITH_CJSON:=yes
# ============================================================================= # =============================================================================
# End of user configuration # End of user configuration
# ============================================================================= # =============================================================================
@ -321,3 +324,8 @@ ifeq ($(WITH_COVERAGE),yes)
CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -coverage CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -coverage
CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS) -coverage CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS) -coverage
endif endif
ifeq ($(WITH_CJSON),yes)
CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -DWITH_CJSON -I/usr/include/cjson
CLIENT_LDADD:=$(CLIENT_LDADD) -lcjson
endif

@ -680,7 +680,9 @@
parameters and timestamp, with a non-quoted and parameters and timestamp, with a non-quoted and
non-escaped payload - this means the payload must non-escaped payload - this means the payload must
itself be valid JSON. For example: itself be valid JSON. For example:
<code>{"tst":1470825369,"topic":"foo","qos":0,"retain":0,"payload":{"temperature":27.0,"humidity":57}}</code>.</para></listitem> <code>{"tst":1470825369,"topic":"foo","qos":0,"retain":0,"payload":{"temperature":27.0,"humidity":57}}</code>.</para>
<para>If the payload is not valid JSON, then the error message "Error: Message payload is not valid JSON on topic
&lt;topic&gt;" will be printed to stderr.</para></listitem>
<listitem><para><option>%I</option> ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100</para></listitem> <listitem><para><option>%I</option> ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100</para></listitem>
<listitem><para><option>%U</option> Unix timestamp with nanoseconds, e.g. 1470818943.786368637</para></listitem> <listitem><para><option>%U</option> Unix timestamp with nanoseconds, e.g. 1470818943.786368637</para></listitem>
</itemizedlist> </itemizedlist>

@ -797,7 +797,10 @@ mosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained</programlisting>
parameters and timestamp, with a non-quoted and parameters and timestamp, with a non-quoted and
non-escaped payload - this means the payload must non-escaped payload - this means the payload must
itself be valid JSON. For example: itself be valid JSON. For example:
<code>{"tst":1470825369,"topic":"foo","qos":0,"retain":0,"payload":{"temperature":27.0,"humidity":57}}</code>.</para></listitem> <code>{"tst":1470825369,"topic":"foo","qos":0,"retain":0,"payload":{"temperature":27.0,"humidity":57}}</code>.</para>
<para>If the payload is not valid JSON, then the error message "Error: Message payload is not valid JSON on topic
&lt;topic&gt;" will be printed to stderr.</para></listitem>
<listitem><para><option>%I</option> ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100</para></listitem> <listitem><para><option>%I</option> ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100</para></listitem>
<listitem><para><option>%U</option> Unix timestamp with nanoseconds, e.g. 1470818943.786368637</para></listitem> <listitem><para><option>%U</option> Unix timestamp with nanoseconds, e.g. 1470818943.786368637</para></listitem>
</itemizedlist> </itemizedlist>

@ -71,6 +71,7 @@ already be built. Use `make binary` to skip building the man pages, or install
* openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no` * openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no`
* xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed when building from git sources - disable with `make WITH_DOCS=no` * xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed when building from git sources - disable with `make WITH_DOCS=no`
* uthash / utlist - bundled versions of these headers are provided, disable their use with `make WITH_BUNDLED_DEPS=no` * uthash / utlist - bundled versions of these headers are provided, disable their use with `make WITH_BUNDLED_DEPS=no`
* cJSON - for client JSON output support. Disable with `make WITH_CJSON=no` Auto detected with CMake.
Equivalent options for enabling/disabling features are available when using the CMake build. Equivalent options for enabling/disabling features are available when using the CMake build.

Loading…
Cancel
Save