Introduce dynamically registered shell command sets

Commands are part of a single array, which means that their definition
is static. However, different apps in the same source tree may
reasonably want to add to the command set (perhaps even shadow existing
commands), which would make for awkward code.

Instead, allow dynamic registration/deregistration of command sets at
runtime. This keeps the data overhead low (two pointers per enabled
command set).
This commit is contained in:
Angelos Oikonomopoulos 2018-06-08 10:16:49 +00:00
parent 1a95aad7a7
commit 45b0241f94
3 changed files with 66 additions and 23 deletions

View File

@ -45,6 +45,7 @@
#include "contiki.h"
#include "shell.h"
#include "shell-commands.h"
#include "lib/list.h"
#include "sys/log.h"
#include "dev/watchdog.h"
#include "net/ipv6/uip.h"
@ -76,7 +77,8 @@ static uint16_t curr_ping_datalen;
#if TSCH_WITH_SIXTOP
static shell_command_6top_sub_cmd_t sixtop_sub_cmd = NULL;
#endif /* TSCH_WITH_SIXTOP */
static struct shell_command_set_t builtin_shell_command_set;
LIST(shell_command_sets);
/*---------------------------------------------------------------------------*/
static const char *
ds6_nbr_state_to_str(uint8_t state)
@ -345,15 +347,16 @@ PT_THREAD(cmd_log(struct pt *pt, shell_output_func output, char *args))
static
PT_THREAD(cmd_help(struct pt *pt, shell_output_func output, char *args))
{
const struct shell_command_t *cmd_ptr;
struct shell_command_set_t *set;
const struct shell_command_t *cmd;
PT_BEGIN(pt);
SHELL_OUTPUT(output, "Available commands:\n");
cmd_ptr = shell_commands;
while(cmd_ptr->name != NULL) {
SHELL_OUTPUT(output, "%s\n", cmd_ptr->help);
cmd_ptr++;
/* Note: we explicitly don't expend any code space to deal with shadowing */
for(set = list_head(shell_command_sets); set != NULL; set = list_item_next(set)) {
for(cmd = set->commands; cmd->name != NULL; ++cmd) {
SHELL_OUTPUT(output, "%s\n", cmd->help);
}
}
PT_END(pt);
@ -729,12 +732,48 @@ PT_THREAD(cmd_6top(struct pt *pt, shell_output_func output, char *args))
void
shell_commands_init(void)
{
list_init(shell_command_sets);
list_add(shell_command_sets, &builtin_shell_command_set);
/* Set up Ping Reply callback */
uip_icmp6_echo_reply_callback_add(&echo_reply_notification,
echo_reply_handler);
}
/*---------------------------------------------------------------------------*/
const struct shell_command_t shell_commands[] = {
void
shell_command_set_register(struct shell_command_set_t *set)
{
list_push(shell_command_sets, set);
}
/*---------------------------------------------------------------------------*/
int
shell_command_set_deregister(struct shell_command_set_t *set)
{
if(!list_contains(shell_command_sets, set)) {
return !0;
}
list_remove(shell_command_sets, set);
return 0;
}
/*---------------------------------------------------------------------------*/
const struct shell_command_t *
shell_command_lookup(const char *name)
{
struct shell_command_set_t *set;
const struct shell_command_t *cmd;
for(set = list_head(shell_command_sets);
set != NULL;
set = list_item_next(set)) {
for(cmd = set->commands; cmd->name != NULL; ++cmd) {
if(!strcmp(cmd->name, name)) {
return cmd;
}
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
const struct shell_command_t builtin_shell_commands[] = {
{ "help", cmd_help, "'> help': Shows this help" },
{ "reboot", cmd_reboot, "'> reboot': Reboot the board by watchdog_reboot()" },
{ "ip-addr", cmd_ipaddr, "'> ip-addr': Shows all IPv6 addresses" },
@ -765,4 +804,8 @@ const struct shell_command_t shell_commands[] = {
{ NULL, NULL, NULL },
};
static struct shell_command_set_t builtin_shell_command_set = {
.next = NULL,
.commands = builtin_shell_commands,
};
/** @} */

View File

@ -53,8 +53,14 @@ struct shell_command_t {
const char *help;
};
/* The set of supported commands */
extern const struct shell_command_t shell_commands[];
struct shell_command_set_t {
struct shell_command_set_t *next;
const struct shell_command_t *const commands;
};
void shell_command_set_register(struct shell_command_set_t *);
int shell_command_set_deregister(struct shell_command_set_t *);
const struct shell_command_t *shell_command_lookup(const char *);
/**
* Initializes Shell-commands module

View File

@ -89,7 +89,7 @@ output_prompt(shell_output_func output)
PT_THREAD(shell_input(struct pt *pt, shell_output_func output, const char *cmd))
{
static char *args;
static const struct shell_command_t *cmd_ptr;
static const struct shell_command_t *cmd_descr = NULL;
PT_BEGIN(pt);
@ -105,20 +105,14 @@ PT_THREAD(shell_input(struct pt *pt, shell_output_func output, const char *cmd))
args++;
}
/* Lookup for command */
cmd_ptr = shell_commands;
while(cmd_ptr->name != NULL) {
if(strcmp(cmd, cmd_ptr->name) == 0) {
static struct pt cmd_pt;
PT_SPAWN(pt, &cmd_pt, cmd_ptr->func(&cmd_pt, output, args));
goto done;
}
cmd_ptr++;
cmd_descr = shell_command_lookup(cmd);
if(cmd_descr != NULL) {
static struct pt cmd_pt;
PT_SPAWN(pt, &cmd_pt, cmd_descr->func(&cmd_pt, output, args));
} else {
SHELL_OUTPUT(output, "Command not found. Type 'help' for a list of commands\n");
}
SHELL_OUTPUT(output, "Command not found. Type 'help' for a list of commands\n");
done:
output_prompt(output);
PT_END(pt);
}