mosquitto/apps/mosquitto_ctrl/ctrl_shell_post_connect.c

159 lines
4.2 KiB
C

/*
Copyright (c) 2023 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 <stdlib.h>
#include <string.h>
#include "ctrl_shell_internal.h"
#ifdef WITH_CTRL_SHELL
#define UNUSED(A) (void)(A)
static struct completion_tree_root *commands_post_connect = NULL;
static void command_tree_create(void)
{
struct completion_tree_cmd *cmd;
struct completion_tree_arg_list *help_arg_list;
if(commands_post_connect){
return;
}
commands_post_connect = calloc(1, sizeof(struct completion_tree_root));
cmd = completion_tree_cmd_add(commands_post_connect, NULL, "help");
help_arg_list = completion_tree_cmd_add_arg_list(cmd);
completion_tree_cmd_add(commands_post_connect, help_arg_list, "broker");
completion_tree_cmd_add(commands_post_connect, help_arg_list, "disconnect");
completion_tree_cmd_add(commands_post_connect, help_arg_list, "dynsec");
completion_tree_cmd_add(commands_post_connect, help_arg_list, "exit");
}
static void print_help(char **saveptr)
{
char *command = strtok_r(NULL, " ", saveptr);
if(command){
if(!strcasecmp(command, "dynsec")){
ctrl_shell_print_help_command("dynsec");
ctrl_shell_printf("\nStart the dynamic-security control mode.\n");
}else if(!strcasecmp(command, "broker")){
ctrl_shell_print_help_command("broker");
ctrl_shell_printf("\nStart the broker control mode.\n");
}else{
ctrl_shell_print_help_final(command, NULL);
}
}else{
ctrl_shell_printf("This is the mosquitto_ctrl interactive shell, for controlling aspects of a mosquitto broker.\n");
ctrl_shell_printf("Find help on a command using '%shelp <command>%s'\n", ANSI_INPUT, ANSI_RESET);
ctrl_shell_printf("Press tab multiple times to find currently available commands.\n\n");
ctrl_shell_printf("Example workflow:\n\n");
ctrl_shell_printf("> auth\n");
ctrl_shell_printf("username: admin\n");
ctrl_shell_printf("password:\n");
ctrl_shell_printf("> connect mqtt://localhost\n");
ctrl_shell_printf("mqtt://localhost:1883> dynsec\n");
ctrl_shell_printf("mqtt://localhost:1883|dynsec> createGroup newgroup\n");
ctrl_shell_printf("OK\n\n");
}
}
struct module_data {
const char *name;
void (*mod_init)(struct ctrl_shell__module *mod);
};
const struct module_data mod_data[] = {
{ "broker", ctrl_shell__broker_init },
{ "dynsec", ctrl_shell__dynsec_init },
};
static void line_callback(char *line)
{
if(!line){
ctrl_shell_callback_final(NULL);
return;
}
ctrl_shell_rtrim(line);
if(strlen(line) > 0){
add_history(line);
}else{
free(line);
return;
}
char *saveptr = NULL;
bool found = false;
char *command = strtok_r(line, " ", &saveptr);
if(!command){
free(line);
return;
}
for(size_t i=0; i<sizeof(mod_data)/sizeof(struct module_data); i++){
if(!strcasecmp(command, mod_data[i].name)){
snprintf(prompt, sizeof(prompt), "%s%s://%s:%d%s|%s%s%s>%s ",
ANSI_URL, data.url_scheme, data.hostname, data.port, ANSI_RESET,
ANSI_MODULE, mod_data[i].name, ANSI_INPUT, ANSI_RESET);
ctrl_shell__load_module(mod_data[i].mod_init);
found = true;
break;
}
}
if(!strcasecmp(command, "help")){
found = true;
print_help(&saveptr);
}
if(found == false){
if(!ctrl_shell_callback_final(line)){
ctrl_shell_printf("Unknown command '%s'\n", command);
}
}
free(line);
}
void ctrl_shell__post_connect_cleanup(void)
{
completion_tree_free(commands_post_connect);
commands_post_connect = NULL;
}
void ctrl_shell__post_connect_init(void)
{
command_tree_create();
ctrl_shell_completion_commands_set(commands_post_connect);
ctrl_shell_line_callback_set(line_callback);
snprintf(prompt, sizeof(prompt), "%s%s://%s:%d%s>%s ", ANSI_URL, data.url_scheme, data.hostname, data.port, ANSI_INPUT, ANSI_RESET);
}
#endif