mirror of
https://github.com/eclipse-mosquitto/mosquitto.git
synced 2026-04-30 13:02:40 +02:00
Add $CONTROL/broker/v1 control & the getPluginInfo command.
This commit is contained in:
parent
3413001d47
commit
a25fc166a8
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 $@
|
||||
|
||||
|
|
|
|||
213
apps/mosquitto_ctrl/broker.c
Normal file
213
apps/mosquitto_ctrl/broker.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
284
src/broker_control.c
Normal 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
106
src/json_help.c
Normal 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
34
src/json_help.h
Normal 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
|
||||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue