26RCSID(
"$Id: 66b9b26cf82fd2b61cb2d4d7da715937d96243e2 $")
28#include <freeradius-devel/server/command.h>
29#include <freeradius-devel/server/log.h>
30#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/util/misc.h>
76static int split(
char **input,
char **output,
bool syntax_string);
84#define FR_TYPE_FIXED FR_TYPE_TIME_DELTA
86#define FR_TYPE_VARARGS FR_TYPE_TLV
87#define FR_TYPE_OPTIONAL FR_TYPE_STRUCT
88#define FR_TYPE_ALTERNATE FR_TYPE_VSA
89#define FR_TYPE_ALTERNATE_CHOICE FR_TYPE_GROUP
105 if (insert) *insert =
head;
110 if (insert) *insert =
head;
117 if (insert) *insert = NULL;
119 for (cmd = *
head; cmd != NULL; cmd = cmd->
next) {
128 where = &(cmd->
next);
135 if (status > 0)
break;
143 if (insert) *insert = where;
186 for (p = (
uint8_t const *)
name; *p !=
'\0'; p++) {
192 if (((*p >=
' ') && (*p <=
',')) ||
193 ((*p >=
':') && (*p <=
'@')) ||
194 ((*p >=
'[') && (*p <=
'^')) ||
195 ((*p >
'z') && (*p <= 0xf7)) ||
215 bool lowercase =
false;
216 bool uppercase =
false;
224 for (p = argv->
name; *p !=
'\0'; p++) {
225 if (isupper((
uint8_t) *p)) uppercase =
true;
226 if (islower((
uint8_t) *p)) lowercase =
true;
233 if (!uppercase && !lowercase) {
241 if (uppercase && lowercase) {
290 while ((*str ==
' ') ||
306 if ((*str ==
'[') || (*str ==
'(')) {
323 while ((*str != end) || (
count > 0)) {
329 if (*str == quote)
count++;
330 if (*str == end)
count--;
343 if ((*str !=
'\0') &&
361 }
else if (*str ==
')') {
363 }
else if (
count == 0) {
364 if (*str ==
'|')
break;
381 while ((*str ==
' ') ||
393static int split(
char **input,
char **output,
bool syntax_string)
407 while ((*str ==
' ') ||
436 if (!syntax_string && ((*str ==
'"') || (*str ==
'\''))) {
439 while (*str != quote) {
469 if ((*str !=
'\0') &&
479 }
else if (syntax_string && ((*str ==
'[') || (*str ==
'('))) {
496 while ((*str != end) || (
count > 0)) {
502 if (*str == quote)
count++;
503 if (*str == end)
count--;
516 if ((*str !=
'\0') &&
549 while ((*str ==
' ') ||
576 if (ret < 0)
return ret;
578 if (ret == 0)
return i;
586 if (strcmp(
name,
"...") == 0) {
587 if (!allow_varargs) {
608 allow_varargs =
false;
610 }
else if (
name[0] ==
'[') {
614 char *option = talloc_strdup(
ctx,
name + 1);
618 q = option + strlen(option) - 1;
631 if (ret < 0)
return ret;
638 }
else if (
name[0] ==
'(') {
642 char *option = talloc_strdup(
ctx,
name + 1);
646 q = option + strlen(option) - 1;
667 if (ret < 0)
return ret;
676 if (ret < 0)
return ret;
683 *last_child = choice;
684 last_child = &(choice->
next);
706 last = &(argv->
next);
737 int argc = 0,
depth = 0;
775 p = talloc_strdup(talloc_ctx, table->
parent);
778 ret =
split(&p, &parents[i],
true);
779 if (ret < 0)
return -1;
803 start = &(cmd->
child);
838 if (table->
help) cmd->
help = talloc_strdup(cmd, table->
help);
851 start = &(cmd->
child);
878 char *
syntax = talloc_strdup(talloc_ctx, table->
syntax);
881 if (argc < 0)
return -1;
921 if (cmd->
help == table->
help)
return 0;
923 if (cmd->
help != NULL) {
997 for (i = 0; table[i].
help != NULL; i++) {
1034 if (!walk_ctx || !callback) {
1091 ret = callback(ctx, &info);
1124 if (
stack->depth == 0) {
1125 if (!cmd)
goto done;
1151 len = strlen(partial);
1156 for (i = 0, cmd =
head; (i < max_expansions) && cmd != NULL; cmd = cmd->
next) {
1157 if (strncmp(partial, cmd->
name, len) != 0)
continue;
1159 expansions[i++] = cmd->
name;
1167 int max_expansions,
char const **expansions)
1177 expansions[0] = argv->
name;
1181 return cmd->
tab_expand(ctx, cmd->
ctx, info, max_expansions, expansions);
1201 for (child = argv->
child; child != NULL; child = child->
next) {
1221 (*p !=
'\0') && (*q !=
'\0');
1226 if (*p != *q)
return 0;
1233 if (p[1] && !q[1])
return 0;
1247 if (!p[1] && q[1]) {
1248 expansions[0] = argv->
name;
1271 int max_expansions,
char const **expansions)
1277 if (ret < 0)
return -1;
1303 if (!
head)
return 0;
1311 for (i = 0; i < info->
argc; i++) {
1330 if ((i + 1) == info->
argc)
return 0;
1335 if (!cmd->
live)
return 0;
1357 for (i = 0, cmd = cmd->
child; (i < max_expansions) && (cmd != NULL); i++, cmd = cmd->
next) {
1358 expansions[i] = cmd->
name;
1368#define SKIP_NAME(name) do { p = word; q = name; while (*p && *q && (*p == *q)) { \
1372#define MATCHED_NAME ((!*p || isspace((uint8_t) *p)) && !*q)
1373#define TOO_FAR (*p && (*q > *p))
1374#define MATCHED_START ((text + start) >= word) && ((text + start) <= p)
1395 for (i = offset + 1; i < info->
argc; i++) {
1396 char const *p, *q, *word;
1402 for (cmd = start; cmd != NULL; cmd = cmd->
next) {
1403 if (!cmd->
live)
continue;
1405 word = info->
argv[i];
1438 if (!cmd->
live)
return 0;
1441 fprintf(fp_err,
"No permissions to run command '%s'\n", cmd->
name);
1460 my_info.
box = &info->
box[i + 1];
1461 ret = cmd->
func(fp, fp_err, cmd->
ctx, &my_info);
1492 if (info->
argc == 0)
return 0;
1494 for (i = 0; i < info->
argc; i++) {
1501 for (; cmd != NULL; cmd = cmd->
next) {
1502 if (!cmd->
live)
continue;
1504 fprintf(fp,
"%s %s\n", info->
argv[i - 1], cmd->
name);
1507 if (ret < 0)
return ret;
1523 if (!cmd->
live)
return 0;
1526 fprintf(fp_err,
"No permissions to run command '%s' help %s\n", cmd->
name, cmd->
help);
1545 my_info.
box = &info->
box[i + 1];
1546 ret = cmd->
func(fp, fp_err, cmd->
ctx, &my_info);
1568 for (i = 0; i < argc; i++) {
1570 if (!cmd)
return NULL;
1584 if (start)
return start->
help;
1589static const char *
tabs =
"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
1602 for (cmd =
head; cmd != NULL; cmd = cmd->
next) {
1620 for (i = 0; i <
depth; i++) {
1621 fprintf(fp,
"%s ", argv[i]);
1629 fprintf(fp,
"%s\n", cmd->
name);
1631 fprintf(fp,
"%s %s\n", cmd->
name, cmd->
syntax);
1635 fprintf(fp,
"\t%s\n", cmd->
help);
1643 for (cmd =
head; cmd != NULL; cmd = cmd->
next) {
1648 if (cmd->
child && ((
depth + 1) < max_depth)) {
1661 if ((max_depth <= 0) || !
head)
return;
1689 TALLOC_CTX *ctx = NULL;
1697 if ((start +
used) >= argc) {
1725 if (strcmp(argv->
name, info->
argv[start +
used]) != 0) {
1732 if (optional)
return 0;
1745 child = argv->
child;
1748 if (ret < 0)
return ret;
1783 for (child = argv->
child; child != NULL; child = child->
next) {
1791 if (ret <= 0)
continue;
1817 if ((start +
used) < verify) {
1824 if ((
name[0] ==
'"') ||
1825 (
name[0] ==
'\'')) {
1835 if (!info->
box[start +
used]) {
1839 box = info->
box[start +
used];
1870 if ((start +
used) < argc) {
1883 char const *word = text;
1885 if ((*word !=
'"') && (*word !=
'\'')) {
1892 while (*word && (*word != quote)) {
1893 if (*word !=
'\\') {
1899 if (!*word)
return NULL;
1913 char const **text,
bool *runnable)
1915 int argc = start_argc;
1918 char const *word, *my_word, *p, *q;
1928 if (!*word)
goto done;
1947 if (argc < info->argc) {
1958 ((*word ==
'"') || (*word ==
'\''))) {
1964 if ((*word ==
'"') || (*word ==
'\'')) {
1978 if (!info->
box[argc]) {
1984 word + offset, len - (offset << 1),
1986 if (ret < 0)
return -1;
1994 info->
argv[argc] = str = talloc_memdup(info->
argv, word + offset, len + 1);
2031 info->
cmd[argc] = NULL;
2046 for (child = argv->
child; child != NULL; child = child->
next) {
2060 if (ret <= 0)
continue;
2078 child = argv->
child;
2082 if (ret < 0)
return ret;
2087 if (ret == 0)
goto next;
2130 if (!argv) *runnable =
true;
2132 return (argc - start_argc);
2152 char const *word, *p, *q;
2180 for (argc = 0; argc < info->
argc; argc++) {
2181 cmd = info->
cmd[argc];
2186 if ((word[0] ==
'*') && isspace(word[1]) && cmd->
added_name) {
2243 if (argc < info->argc) {
2244 cmd = info->
cmd[argc];
2255 if ((word[0] ==
'*') && isspace(word[1]) && cmd->
added_name) {
2259 info->
argv[argc] =
"*";
2260 info->
cmd[argc] = cmd;
2274 if (argc < info->argc) {
2300 info->
cmd[argc] = cmd;
2312 info->
cmd[argc] = cmd;
2344 if (*word > 0)
goto too_many;
2355 if (ret < 0)
return ret;
2365 if (*word)
goto too_many;
2382 (new_argc > info->
argc)) {
2387 if (new_argc == info->
argc)
return 0;
2389 for (i = new_argc; i < info->
argc; i++) {
2390 if (info->
box && info->
box[i]) {
2394 if (info->
cmd && info->
cmd[i]) info->
cmd[i] = NULL;
2395 info->
argv[i] = NULL;
2398 info->
argc = new_argc;
2407 memset(info, 0,
sizeof(*info));
2426 for (child = argv->
child; child != NULL; child = child->
next) {
2440 for (child = argv->
child; child != NULL; child = child->
next) {
2469 if (ret < 0)
return ret;
2479 char const **word_p,
int count,
int max_expansions,
char const **expansions)
2482 char const *word = *word_p;
2487 for ( ; argv != NULL; argv = argv->
next) {
2491 return expand_all(cmd, info, argv,
count, max_expansions, expansions);
2502 char const *my_word;
2508 if (word != my_word) *word_p = word;
2515 for (child = argv->
child; child != NULL; child = child->
next) {
2517 char const *my_word = word;
2528 if (my_word != word) {
2543 char quote, *my_word;
2548 if (!p)
return count;
2557 if (!*p || (isspace((
uint8_t) *p))) {
2579 if (ret < 0)
return ret;
2585 info->
argv[info->
argc] = my_word = talloc_zero_array(info->
argv,
char, len + 1);
2586 memcpy(my_word, word, len);
2587 my_word[len] =
'\0';
2593 if ((*word ==
'"') || (*word ==
'\'')) {
2603 word + offset, len - (offset << 1),
2605 if (ret < 0)
return -1;
2635 if (!*p || isspace((
uint8_t) *p)) {
2668 int max_expansions,
char const **expansions)
2670 char const *word, *p, *q;
2701 while (cmd && (
count < max_expansions)) {
2702 if (!cmd->
live)
goto next;
2710 if (!*p || isspace((
uint8_t) *p)) {
2796 fprintf(fp,
"%s\n", cmd->
name);
2798 fprintf(fp,
"%-30s%s\n", cmd->
name, cmd->
help);
2812 char const *word, *p, *q;
2899 fprintf(fp,
"%-30s%s\n", cmd->
name, cmd->
syntax);
2918 if (!*word)
return true;
2925 return (*p ==
'\0');
static int split_alternation(char **input, char **output)
static char const * skip_word(char const *text)
int fr_command_clear(int new_argc, fr_cmd_info_t *info)
Clear out any value boxes etc.
static int expand_all(fr_cmd_t *cmd, fr_cmd_info_t *info, fr_cmd_argv_t *argv, int count, int max_expansions, char const **expansions)
void fr_command_list(FILE *fp, int max_depth, fr_cmd_t *head, int options)
int fr_command_walk(fr_cmd_t *head, void **walk_ctx, void *ctx, fr_cmd_walk_t callback)
Walk over a command hierarchy.
bool added_name
was this name added?
int fr_command_str_to_argv(fr_cmd_t *head, fr_cmd_info_t *info, char const *text)
Split a string in-place, updating argv[].
int fr_command_run(FILE *fp, FILE *fp_err, fr_cmd_info_t *info, bool read_only)
Run a particular command.
static fr_cmd_t * fr_command_alloc(TALLOC_CTX *ctx, fr_cmd_t **head, char const *name)
Allocate an fr_cmd_t structure.
static int syntax_str_to_argv(int start_argc, fr_cmd_argv_t *start, fr_cmd_info_t *info, char const **text, bool *runnable)
Check the syntax of a command, starting at *text
static int fr_command_add_syntax(TALLOC_CTX *ctx, char *syntax, fr_cmd_argv_t **head, bool allow_varargs)
static void fr_command_list_node(FILE *fp, fr_cmd_t *cmd, int depth, char const **argv, int options)
struct fr_cmd_s * child
if there are subcommands
void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info)
Initialize an fr_cmd_info_t structure.
fr_cmd_argv_t * syntax_argv
arguments and types
static void fr_command_debug_node(FILE *fp, fr_cmd_t *cmd, int depth)
int fr_command_print_help(FILE *fp, fr_cmd_t *head, char const *text)
Do readline-style help completions.
fr_command_register_hook_t fr_command_register_hook
void fr_command_debug(FILE *fp, fr_cmd_t *head)
char const * fr_command_help(fr_cmd_t *head, int argc, char *argv[])
Get help text for a particular command.
static void fr_command_list_internal(FILE *fp, fr_cmd_t *head, int depth, int max_depth, char const **argv, int options)
int fr_command_complete(fr_cmd_t *head, char const *text, int start, int max_expansions, char const **expansions)
Do readline-style command completions.
static void fr_command_debug_internal(FILE *fp, fr_cmd_t *head, int depth)
static int fr_command_register(UNUSED TALLOC_CTX *talloc_ctx, UNUSED char const *name, UNUSED void *ctx, UNUSED fr_cmd_table_t *table)
static int fr_command_tab_expand_partial(fr_cmd_t *head, char const *partial, int max_expansions, char const **expansions)
static int fr_command_verify_argv(fr_cmd_info_t *info, int start, int verify, int argc, fr_cmd_argv_t **argv_p, bool optional)
static int fr_command_run_partial(FILE *fp, FILE *fp_err, fr_cmd_info_t *info, bool read_only, int offset, fr_cmd_t *head)
bool fr_command_strncmp(const char *word, const char *name)
#define FR_TYPE_ALTERNATE
int fr_command_add(TALLOC_CTX *talloc_ctx, fr_cmd_t **head, char const *name, void *ctx, fr_cmd_table_t const *table)
Add one command to the global command tree.
int fr_command_add_multi(TALLOC_CTX *talloc_ctx, fr_cmd_t **head, char const *name, void *ctx, fr_cmd_table_t const *table)
Add multiple commands to the global command tree.
bool intermediate
intermediate commands can't have callbacks
#define FR_TYPE_ALTERNATE_CHOICE
static bool fr_command_valid_syntax(fr_cmd_argv_t *argv)
static int expand_syntax(fr_cmd_t *cmd, fr_cmd_info_t *info, fr_cmd_argv_t *argv, char const *text, int start, char const **word_p, int count, int max_expansions, char const **expansions)
char const * syntax
only for terminal nodes
bool live
is this entry live?
static int split(char **input, char **output, bool syntax_string)
int fr_command_tab_expand(TALLOC_CTX *ctx, fr_cmd_t *head, fr_cmd_info_t *info, int max_expansions, char const **expansions)
Get the commands && help at a particular level.
static fr_cmd_t * fr_command_find(fr_cmd_t **head, char const *name, fr_cmd_t ***insert)
Find a command.
static bool fr_command_valid_name(char const *name)
static int fr_command_tab_expand_syntax(TALLOC_CTX *ctx, fr_cmd_t *cmd, int syntax_offset, fr_cmd_info_t *info, int max_expansions, char const **expansions)
static void print_help(FILE *fp, fr_cmd_t *cmd)
static int fr_command_tab_expand_argv(TALLOC_CTX *ctx, fr_cmd_t *cmd, fr_cmd_info_t *info, char const *name, fr_cmd_argv_t *argv, int max_expansions, char const **expansions)
A stack for walking commands.
int(* fr_cmd_walk_t)(void *ctx, fr_cmd_walk_info_t *)
bool add_name
do we add a name here?
char const * help
help text
int argc
current argument count
int(* fr_cmd_func_t)(FILE *fp, FILE *fp_err, void *ctx, fr_cmd_info_t const *info)
fr_cmd_func_t func
function to process this command
char const * syntax
e.g. "STRING"
fr_value_box_t ** box
value_box version of commands.
char const * parent
e.g. "show module"
#define FR_COMMAND_OPTION_NAME
#define FR_COMMAND_OPTION_LIST_CHILD
fr_cmd_tab_t tab_expand
tab expand things in the syntax string
bool runnable
is the command runnable?
int(* fr_command_register_hook_t)(TALLOC_CTX *talloc_ctx, char const *name, void *ctx, fr_cmd_table_t *table)
#define FR_COMMAND_OPTION_HELP
int max_argc
maximum number of arguments
char const * name
e.g. "stats"
char const ** argv
text version of commands
int(* fr_cmd_tab_t)(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
fr_cmd_t ** cmd
cached commands at each offset
static char * stack[MAX_STACK]
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VALUE_BOX
A boxed value.
static uint8_t depth(fr_minmax_heap_index_t i)
#define fr_skip_not_whitespace(_p)
Skip everything that's not whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
size_t fr_utf8_char(uint8_t const *str, ssize_t inlen)
Checks for utf-8, taken from http://www.w3.org/International/questions/qa-forms-utf-8.
fr_aka_sim_id_type_t type
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
static int talloc_const_free(void const *ptr)
Free const'd memory.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
#define fr_strerror_const(_msg)
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
#define FR_TYPE_STRUCTURAL
fr_sbuff_unescape_rules_t * fr_value_unescape_by_char[UINT8_MAX+1]
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.