Add $CONTROL/broker/v1 control & the getPluginInfo command.

This commit is contained in:
Roger A. Light 2021-09-07 23:17:31 +01:00
parent 3413001d47
commit a25fc166a8
15 changed files with 710 additions and 3 deletions

View file

@ -51,6 +51,9 @@ Broker:
having to connect to a broker.
- Add `mosquitto_plugin_set_info()` to allow plugins to tell the broker their
name and version.
- Add builtin $CONTROL/broker/v1 control topic with the `getPluginInfo`
command.
- Add support for `getPluginInfo` to mosquitto_ctrl.
Client library:
- Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair

View file

@ -5,6 +5,7 @@ if(WITH_TLS AND CJSON_FOUND)
add_executable(mosquitto_ctrl
mosquitto_ctrl.c mosquitto_ctrl.h
broker.c
client.c
dynsec.c
../../plugins/dynamic-security/hash.c

View file

@ -15,6 +15,7 @@ endif
LOCAL_CPPFLAGS:=-I../mosquitto_passwd -I../../plugins/dynamic-security -DWITH_CJSON
OBJS= mosquitto_ctrl.o \
broker.o \
client.o \
dynsec.o \
dynsec_client.o \
@ -51,6 +52,9 @@ mosquitto_ctrl_example.so : ${EXAMPLE_OBJS}
mosquitto_ctrl.o : mosquitto_ctrl.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
broker.o : broker.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
client.o : client.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@

View file

@ -0,0 +1,213 @@
/*
Copyright (c) 2021 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <cjson/cJSON.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mosquitto_ctrl.h"
#include "mosquitto.h"
void broker__print_usage(void)
{
printf("\nBroker Control module\n");
printf("=======================\n");
printf("Get plugin information: getPluginInfo\n");
}
/* ################################################################
* #
* # Payload callback
* #
* ################################################################ */
static void print_plugin_info(cJSON *j_response)
{
cJSON *j_data, *j_plugins, *j_plugin, *jtmp, *j_eps;
bool first;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL || !cJSON_IsObject(j_data)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_plugins = cJSON_GetObjectItem(j_data, "plugins");
if(j_plugins == NULL || !cJSON_IsArray(j_plugins)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
cJSON_ArrayForEach(j_plugin, j_plugins){
jtmp = cJSON_GetObjectItem(j_plugin, "name");
if(jtmp == NULL || !cJSON_IsString(jtmp)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
printf("Plugin: %-20s\n", jtmp->valuestring);
jtmp = cJSON_GetObjectItem(j_plugin, "version");
if(jtmp && cJSON_IsString(jtmp)){
printf("Version: %-20s\n", jtmp->valuestring);
}
j_eps = cJSON_GetObjectItem(j_plugin, "control-endpoints");
if(j_eps && cJSON_IsArray(j_eps)){
first = true;
cJSON_ArrayForEach(jtmp, j_eps){
if(jtmp && cJSON_IsString(jtmp)){
if(first){
first = false;
printf("Control endpoints: %-20s\n", jtmp->valuestring);
}else{
printf(" %-20s\n", jtmp->valuestring);
}
}
}
}
}
}
static void broker__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, const void *payload)
{
cJSON *tree, *j_responses, *j_response, *j_command, *j_error;
UNUSED(ctrl);
#if CJSON_VERSION_FULL < 1007013
UNUSED(payloadlen);
tree = cJSON_Parse(payload);
#else
tree = cJSON_ParseWithLength(payload, (size_t)payloadlen);
#endif
if(tree == NULL){
fprintf(stderr, "Error: Payload not JSON.\n");
return;
}
j_responses = cJSON_GetObjectItem(tree, "responses");
if(j_responses == NULL || !cJSON_IsArray(j_responses)){
fprintf(stderr, "Error: Payload missing data.\n");
cJSON_Delete(tree);
return;
}
j_response = cJSON_GetArrayItem(j_responses, 0);
if(j_response == NULL){
fprintf(stderr, "Error: Payload missing data.\n");
cJSON_Delete(tree);
return;
}
j_command = cJSON_GetObjectItem(j_response, "command");
if(j_command == NULL){
fprintf(stderr, "Error: Payload missing data.\n");
cJSON_Delete(tree);
return;
}
j_error = cJSON_GetObjectItem(j_response, "error");
if(j_error){
fprintf(stderr, "%s: Error: %s\n", j_command->valuestring, j_error->valuestring);
}else{
if(!strcasecmp(j_command->valuestring, "getPluginInfo")){
print_plugin_info(j_response);
}else{
/* fprintf(stderr, "%s: Success\n", j_command->valuestring); */
}
}
cJSON_Delete(tree);
}
static int broker__get_plugin_info(int argc, char *argv[], cJSON *j_command)
{
UNUSED(argc);
UNUSED(argv);
if(cJSON_AddStringToObject(j_command, "command", "getPluginInfo") == NULL
){
return MOSQ_ERR_NOMEM;
}
return MOSQ_ERR_SUCCESS;
}
/* ################################################################
* #
* # Main
* #
* ################################################################ */
int broker__main(int argc, char *argv[], struct mosq_ctrl *ctrl)
{
int rc = -1;
cJSON *j_tree;
cJSON *j_commands, *j_command;
if(!strcasecmp(argv[0], "help")){
broker__print_usage();
return -1;
}
/* The remaining commands need a network connection and JSON command. */
ctrl->payload_callback = broker__payload_callback;
ctrl->request_topic = strdup("$CONTROL/broker/v1");
ctrl->response_topic = strdup("$CONTROL/broker/v1/response");
if(ctrl->request_topic == NULL || ctrl->response_topic == NULL){
return MOSQ_ERR_NOMEM;
}
j_tree = cJSON_CreateObject();
if(j_tree == NULL) return MOSQ_ERR_NOMEM;
j_commands = cJSON_AddArrayToObject(j_tree, "commands");
if(j_commands == NULL){
cJSON_Delete(j_tree);
j_tree = NULL;
return MOSQ_ERR_NOMEM;
}
j_command = cJSON_CreateObject();
if(j_command == NULL){
cJSON_Delete(j_tree);
j_tree = NULL;
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToArray(j_commands, j_command);
if(!strcasecmp(argv[0], "getPluginInfo")){
rc = broker__get_plugin_info(argc-1, &argv[1], j_command);
}else{
fprintf(stderr, "Command '%s' not recognised.\n", argv[0]);
return MOSQ_ERR_UNKNOWN;
}
if(rc == MOSQ_ERR_SUCCESS){
ctrl->payload = cJSON_PrintUnformatted(j_tree);
cJSON_Delete(j_tree);
if(ctrl->payload == NULL){
fprintf(stderr, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
}
return rc;
}

View file

@ -42,7 +42,7 @@ static void print_usage(void)
print_version();
printf("\nGeneral usage: mosquitto_ctrl <module> <module-command> <command-options>\n");
printf("For module specific help use: mosquitto_ctrl <module> help\n");
printf("\nModules available: dynsec\n");
printf("\nModules available: broker dynsec\n");
printf("\nFor more information see:\n");
printf(" https://mosquitto.org/man/mosquitto_ctrl-1.html\n\n");
}
@ -76,7 +76,9 @@ int main(int argc, char *argv[])
}
/* In built modules */
if(!strcasecmp(argv[0], "dynsec")){
if(!strcasecmp(argv[0], "broker")){
l_ctrl_main = broker__main;
}else if(!strcasecmp(argv[0], "dynsec")){
l_ctrl_main = dynsec__main;
}else{
/* Attempt external module */

View file

@ -89,6 +89,9 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg);
cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number);
void broker__print_usage(void);
int broker__main(int argc, char *argv[], struct mosq_ctrl *ctrl);
void dynsec__print_usage(void);
int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl);

View file

@ -108,6 +108,7 @@ WITH_COVERAGE:=no
WITH_UNIX_SOCKETS:=yes
# Build mosquitto_sub with cJSON support
# Build mosquitto with broker control support
WITH_CJSON:=yes
# Build mosquitto with support for the $CONTROL topics.
@ -378,6 +379,8 @@ ifeq ($(WITH_CJSON),yes)
CLIENT_LDADD:=$(CLIENT_LDADD) -lcjson
CLIENT_STATIC_LDADD:=$(CLIENT_STATIC_LDADD) -lcjson
CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS)
BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_CJSON
BROKER_LDADD:=$(BROKER_LDADD) -lcjson
endif
ifeq ($(WITH_XTREPORT),yes)

View file

@ -160,7 +160,8 @@ static int client_add_admin(FILE *pwfile, cJSON *j_clients)
free(password_hash);
free(salt);
if(client_role_add(j_roles, "dynsec-admin")
if(client_role_add(j_roles, "broker-admin")
|| client_role_add(j_roles, "dynsec-admin")
|| client_role_add(j_roles, "sys-observe")
|| client_role_add(j_roles, "topic-observe")
){
@ -319,6 +320,35 @@ static int role_add_client(cJSON *j_roles)
return MOSQ_ERR_SUCCESS;
}
static int role_add_broker_admin(cJSON *j_roles)
{
cJSON *j_role, *j_acls;
j_role = cJSON_CreateObject();
if(j_role == NULL){
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToArray(j_roles, j_role);
if(cJSON_AddStringToObject(j_role, "rolename", "broker-admin") == NULL
|| cJSON_AddStringToObject(j_role, "textdescription",
"Grants access to administer general broker configuration.") == NULL
|| (j_acls = cJSON_AddArrayToObject(j_role, "acls")) == NULL
){
return MOSQ_ERR_NOMEM;
}
if(acl_add(j_acls, "publishClientSend", "$CONTROL/broker/#", 0, true)
|| acl_add(j_acls, "publishClientReceive", "$CONTROL/broker/#", 0, true)
|| acl_add(j_acls, "subscribePattern", "$CONTROL/broker/#", 0, true)
){
return MOSQ_ERR_NOMEM;
}
return MOSQ_ERR_SUCCESS;
}
static int role_add_dynsec_admin(cJSON *j_roles)
{
cJSON *j_role, *j_acls;
@ -443,6 +473,7 @@ static int add_roles(cJSON *j_tree)
}
if(role_add_client(j_roles)
|| role_add_broker_admin(j_roles)
|| role_add_dynsec_admin(j_roles)
|| role_add_sys_notify(j_roles)
|| role_add_sys_observe(j_roles)

View file

@ -1,6 +1,7 @@
set (MOSQ_SRCS
../lib/alias_mosq.c ../lib/alias_mosq.h
bridge.c bridge_topic.c
broker_control.c
conf.c
conf_includedir.c
context.c
@ -19,6 +20,7 @@ set (MOSQ_SRCS
handle_subscribe.c
../lib/handle_unsuback.c
handle_unsubscribe.c
json_help.c json_help.h
keepalive.c
lib_load.h
listeners.c

View file

@ -8,6 +8,7 @@ OBJS= mosquitto.o \
alias_mosq.o \
bridge.o \
bridge_topic.o \
broker_control.o \
conf.o \
conf_includedir.o \
context.o \
@ -26,6 +27,7 @@ OBJS= mosquitto.o \
handle_subscribe.o \
handle_unsuback.o \
handle_unsubscribe.o \
json_help.o \
keepalive.o \
listeners.o \
logging.o \
@ -98,6 +100,9 @@ bridge.o : bridge.c mosquitto_broker_internal.h
bridge_topic.o : bridge_topic.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
broker_control.o : broker_control.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
conf.o : conf.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
@ -152,6 +157,9 @@ handle_unsuback.o : ../lib/handle_unsuback.c ../lib/read_handle.h
handle_unsubscribe.o : handle_unsubscribe.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
json_help.o : json_help.c json_help.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
keepalive.o : keepalive.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@

284
src/broker_control.c Normal file
View file

@ -0,0 +1,284 @@
/*
Copyright (c) 2021 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#ifdef WITH_CJSON
#include <cjson/cJSON.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <utlist.h>
#include "json_help.h"
#include "mosquitto.h"
#include "mosquitto_broker_internal.h"
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
#include "mqtt_protocol.h"
static mosquitto_plugin_id_t plg_id;
static int broker__handle_control(cJSON *j_responses, struct mosquitto *context, cJSON *commands);
void broker__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data)
{
cJSON *j_response;
UNUSED(context);
j_response = cJSON_CreateObject();
if(j_response == NULL) return;
if(cJSON_AddStringToObject(j_response, "command", command) == NULL
|| (error && cJSON_AddStringToObject(j_response, "error", error) == NULL)
|| (correlation_data && cJSON_AddStringToObject(j_response, "correlationData", correlation_data) == NULL)
){
cJSON_Delete(j_response);
return;
}
cJSON_AddItemToArray(j_responses, j_response);
}
static void send_response(cJSON *tree)
{
char *payload;
size_t payload_len;
payload = cJSON_PrintUnformatted(tree);
cJSON_Delete(tree);
if(payload == NULL) return;
payload_len = strlen(payload);
if(payload_len > MQTT_MAX_PAYLOAD){
free(payload);
return;
}
mosquitto_broker_publish(NULL, "$CONTROL/broker/v1/response",
(int)payload_len, payload, 0, 0, NULL);
}
static int add_plugin_info(cJSON *j_plugins, mosquitto_plugin_id_t *pid)
{
cJSON *j_plugin, *j_eps, *j_ep;
struct control_endpoint *ep;
if(pid->plugin_name == NULL){
return MOSQ_ERR_SUCCESS;
}
j_plugin = cJSON_CreateObject();
if(j_plugin == NULL){
return MOSQ_ERR_NOMEM;
}
if(cJSON_AddStringToObject(j_plugin, "name", pid->plugin_name) == NULL
|| (pid->plugin_version && cJSON_AddStringToObject(j_plugin, "version", pid->plugin_version) == NULL)
|| (pid->listener && cJSON_AddNumberToObject(j_plugin, "port", pid->listener->port) == NULL)
|| (j_eps = cJSON_AddArrayToObject(j_plugin, "control-endpoints")) == NULL
){
cJSON_Delete(j_plugin);
return MOSQ_ERR_NOMEM;
}
DL_FOREACH(pid->control_endpoints, ep){
j_ep = cJSON_CreateString(ep->topic);
if(j_ep == NULL){
cJSON_Delete(j_plugin);
}
cJSON_AddItemToArray(j_eps, j_ep);
}
cJSON_AddItemToArray(j_plugins, j_plugin);
return MOSQ_ERR_SUCCESS;
}
static int broker__process_get_plugin_info(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
{
cJSON *tree, *jtmp, *j_data, *j_plugins;
const char *admin_clientid, *admin_username;
int i;
UNUSED(command);
tree = cJSON_CreateObject();
if(tree == NULL){
broker__command_reply(j_responses, context, "getPluginInfo", "Internal error", correlation_data);
return MOSQ_ERR_NOMEM;
}
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
mosquitto_log_printf(MOSQ_LOG_INFO, "Broker: %s/%s | getPluginInfo",
admin_clientid, admin_username);
if(cJSON_AddStringToObject(tree, "command", "getPluginInfo") == NULL
|| ((j_data = cJSON_AddObjectToObject(tree, "data")) == NULL)
){
goto internal_error;
}
j_plugins = cJSON_AddArrayToObject(j_data, "plugins");
if(j_plugins == NULL){
goto internal_error;
}
for(i=0; i<db.config->security_options.auth_plugin_config_count; i++){
if(add_plugin_info(j_plugins, db.config->security_options.auth_plugin_configs[i].plugin.identifier)){
goto internal_error;
}
}
cJSON_AddItemToArray(j_responses, tree);
if(correlation_data){
jtmp = cJSON_AddStringToObject(tree, "correlationData", correlation_data);
if(jtmp == NULL){
goto internal_error;
}
}
return MOSQ_ERR_SUCCESS;
internal_error:
cJSON_Delete(tree);
broker__command_reply(j_responses, context, "getPluginInfo", "Internal error", correlation_data);
return MOSQ_ERR_NOMEM;
}
static int broker_control_callback(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_control *ed = event_data;
cJSON *tree, *commands;
cJSON *j_response_tree, *j_responses;
UNUSED(event);
UNUSED(userdata);
/* Create object for responses */
j_response_tree = cJSON_CreateObject();
if(j_response_tree == NULL){
return MOSQ_ERR_NOMEM;
}
j_responses = cJSON_CreateArray();
if(j_responses == NULL){
cJSON_Delete(j_response_tree);
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToObject(j_response_tree, "responses", j_responses);
/* Parse cJSON tree.
* Using cJSON_ParseWithLength() is the best choice here, but Mosquitto
* always adds an extra 0 to the end of the payload memory, so using
* cJSON_Parse() on its own will still not overrun. */
#if CJSON_VERSION_FULL < 1007013
tree = cJSON_Parse(ed->payload);
#else
tree = cJSON_ParseWithLength(ed->payload, ed->payloadlen);
#endif
if(tree == NULL){
broker__command_reply(j_responses, ed->client, "Unknown command", "Payload not valid JSON", NULL);
send_response(j_response_tree);
return MOSQ_ERR_SUCCESS;
}
commands = cJSON_GetObjectItem(tree, "commands");
if(commands == NULL || !cJSON_IsArray(commands)){
cJSON_Delete(tree);
broker__command_reply(j_responses, ed->client, "Unknown command", "Invalid/missing commands", NULL);
send_response(j_response_tree);
return MOSQ_ERR_SUCCESS;
}
/* Handle commands */
broker__handle_control(j_responses, ed->client, commands);
cJSON_Delete(tree);
send_response(j_response_tree);
return MOSQ_ERR_SUCCESS;
}
int broker_control__init(void)
{
memset(&plg_id, 0, sizeof(plg_id));
mosquitto_callback_register(&plg_id, MOSQ_EVT_CONTROL, broker_control_callback, "$CONTROL/broker/v1", NULL);
return MOSQ_ERR_SUCCESS;
}
int broker_control__cleanup(void)
{
mosquitto_callback_unregister(&plg_id, MOSQ_EVT_CONTROL, broker_control_callback, "$CONTROL/broker/v1");
return MOSQ_ERR_SUCCESS;
}
/* ################################################################
* #
* # $CONTROL/broker/v1 handler
* #
* ################################################################ */
static int broker__handle_control(cJSON *j_responses, struct mosquitto *context, cJSON *commands)
{
int rc = MOSQ_ERR_SUCCESS;
cJSON *aiter;
char *command;
char *correlation_data = NULL;
cJSON_ArrayForEach(aiter, commands){
if(cJSON_IsObject(aiter)){
if(json_get_string(aiter, "command", &command, false) == MOSQ_ERR_SUCCESS){
if(json_get_string(aiter, "correlationData", &correlation_data, true) != MOSQ_ERR_SUCCESS){
broker__command_reply(j_responses, context, command, "Invalid correlationData data type.", NULL);
return MOSQ_ERR_INVAL;
}
if(!strcasecmp(command, "getPluginInfo")){
rc = broker__process_get_plugin_info(j_responses, context, aiter, correlation_data);
/* Unknown */
}else{
broker__command_reply(j_responses, context, command, "Unknown command", correlation_data);
rc = MOSQ_ERR_INVAL;
}
}else{
broker__command_reply(j_responses, context, "Unknown command", "Missing command", correlation_data);
rc = MOSQ_ERR_INVAL;
}
}else{
broker__command_reply(j_responses, context, "Unknown command", "Command not an object", correlation_data);
rc = MOSQ_ERR_INVAL;
}
}
return rc;
}
#endif

106
src/json_help.c Normal file
View file

@ -0,0 +1,106 @@
/*
Copyright (c) 2020 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#ifdef WITH_CJSON
#include <cjson/cJSON.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "json_help.h"
#include "mosquitto.h"
int json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value)
{
cJSON *jtmp;
if(optional == true){
*value = default_value;
}
jtmp = cJSON_GetObjectItem(json, name);
if(jtmp){
if(cJSON_IsBool(jtmp) == false){
return MOSQ_ERR_INVAL;
}
*value = cJSON_IsTrue(jtmp);
}else{
if(optional == false){
return MOSQ_ERR_INVAL;
}
}
return MOSQ_ERR_SUCCESS;
}
int json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value)
{
cJSON *jtmp;
if(optional == true){
*value = default_value;
}
jtmp = cJSON_GetObjectItem(json, name);
if(jtmp){
if(cJSON_IsNumber(jtmp) == false){
return MOSQ_ERR_INVAL;
}
*value = jtmp->valueint;
}else{
if(optional == false){
return MOSQ_ERR_INVAL;
}
}
return MOSQ_ERR_SUCCESS;
}
int json_get_string(cJSON *json, const char *name, char **value, bool optional)
{
cJSON *jtmp;
*value = NULL;
jtmp = cJSON_GetObjectItem(json, name);
if(jtmp){
if(cJSON_IsString(jtmp) == false){
return MOSQ_ERR_INVAL;
}
*value = jtmp->valuestring;
}else{
if(optional == false){
return MOSQ_ERR_INVAL;
}
}
return MOSQ_ERR_SUCCESS;
}
cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number)
{
char buf[30];
snprintf(buf, sizeof(buf), "%d", number);
return cJSON_AddRawToObject(object, name, buf);
}
#endif

34
src/json_help.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef JSON_HELP_H
#define JSON_HELP_H
/*
Copyright (c) 2020 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifdef WITH_CJSON
#include <cjson/cJSON.h>
#include <stdbool.h>
/* "optional==false" can also be taken to mean "only return success if the key exists and is valid" */
int json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value);
int json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value);
int json_get_string(cJSON *json, const char *name, char **value, bool optional);
cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number);
cJSON *cJSON_CreateInt(int num);
#endif
#endif

View file

@ -355,6 +355,10 @@ int main(int argc, char *argv[])
bridge__start_all();
#endif
#ifdef WITH_CJSON
broker_control__init();
#endif
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s running", VERSION);
#ifdef WITH_SYSTEMD
sd_notify(0, "READY=1");
@ -365,6 +369,10 @@ int main(int argc, char *argv[])
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s terminating", VERSION);
#ifdef WITH_CJSON
broker_control__cleanup();
#endif
/* FIXME - this isn't quite right, all wills with will delay zero should be
* sent now, but those with positive will delay should be persisted and
* restored, pending the client reconnecting in time. */

View file

@ -910,4 +910,9 @@ void will_delay__remove(struct mosquitto *mosq);
void xtreport(void);
#endif
#ifdef WITH_CJSON
int broker_control__init(void);
int broker_control__cleanup(void);
#endif
#endif