#include #include #include #include #include #include "triton.h" #include "cli.h" #include "cli_p.h" #include "log.h" #include "events.h" #include "memdebug.h" #define MAX_CMD_ITEMS 100 #define MSG_SYNTAX_ERROR "syntax error\r\n" #define MSG_INVAL_ERROR "invalid argument\r\n" #define MSG_UNKNOWN_CMD "command unknown\r\n" static const char helpcmd[] = "help"; static const size_t helpcmd_len = sizeof(helpcmd) -1; char *conf_cli_passwd; static const char *def_cli_prompt = "accel-ppp"; char *conf_cli_prompt; static LIST_HEAD(simple_cmd_list); static LIST_HEAD(regexp_cmd_list); void __export cli_register_simple_cmd(struct cli_simple_cmd_t *cmd) { list_add_tail(&cmd->entry, &simple_cmd_list); } void __export cli_register_simple_cmd2( int (*exec)(const char *cmd, char * const *fields, int fields_cnt, void *client), void (*help)(char * const *fields, int fields_cnt, void *client), int hdr_len, ... ) { struct cli_simple_cmd_t *c; int i; va_list ap; va_start(ap, hdr_len); c = malloc(sizeof(*c)); memset(c, 0, sizeof(*c)); c->exec = exec; c->help = help; c->hdr_len = hdr_len; c->hdr = malloc(hdr_len * sizeof(void*)); for (i = 0; i < hdr_len; i++) c->hdr[i] = va_arg(ap, char *); list_add_tail(&c->entry, &simple_cmd_list); va_end(ap); } void __export cli_register_regexp_cmd(struct cli_regexp_cmd_t *cmd) { int err; int erroffset; const char *errptr; if (cmd->exec == NULL) { log_emerg("cli: impossible to register regexp command" " without an execution callback function\n"); _exit(EXIT_FAILURE); } if (cmd->pattern == NULL) { log_emerg("cli: impossible to register regexp command" " without pattern\n"); _exit(EXIT_FAILURE); } cmd->re = pcre_compile2(cmd->pattern, cmd->options, &err, &errptr, &erroffset, NULL); if (!cmd->re) { log_emerg("cli: failed to compile regexp \"%s\": %s (error %i)" " at positon %i (unprocessed characters: \"%s\")\n", cmd->pattern, errptr, err, erroffset, cmd->pattern + erroffset); _exit(EXIT_FAILURE); } if (cmd->h_pattern) { cmd->h_re = pcre_compile2(cmd->h_pattern, cmd->h_options, &err, &errptr, &erroffset, NULL); if (!cmd->h_re) { log_emerg("cli: failed to compile help regexp \"%s\":" " %s (error %i) at position %i (unprocessed" " characters: \"%s\")\n", cmd->h_pattern, errptr, err, erroffset, cmd->h_pattern + erroffset); _exit(EXIT_FAILURE); } } else { cmd->h_re = NULL; cmd->h_pattern = NULL; } list_add_tail(&cmd->entry, ®exp_cmd_list); } int __export cli_send(void *client, const char *data) { struct cli_client_t *cln = (struct cli_client_t *)client; return cln->send(cln, data, strlen(data)); } int __export cli_sendv(void *client, const char *fmt, ...) { struct cli_client_t *cln = (struct cli_client_t *)client; int r; va_list ap; va_start(ap, fmt); r = cln->sendv(cln, fmt, ap); va_end(ap); return r; } static char *skip_word(char *ptr) { for(; *ptr; ptr++) if (!isgraph(*ptr)) break; return ptr; } static char *skip_space(char *ptr) { for(; *ptr; ptr++) if (*ptr != ' ' && *ptr != '\t') break; return ptr; } static int split(char *buf, char **ptr) { int i; buf = skip_space(buf); if (!*buf) return 0; for (i = 0; i < MAX_CMD_ITEMS; i++) { ptr[i] = buf; buf = skip_word(buf); if (!*buf) return i + 1; *buf = 0; buf = skip_space(buf + 1); if (!*buf) return i + 1; } return i; } static int cli_process_help_cmd(struct cli_client_t *cln) { struct cli_regexp_cmd_t *recmd = NULL; struct cli_simple_cmd_t *sicmd = NULL; char *cmd = (char *)cln->cmdline; char *items[MAX_CMD_ITEMS] = { 0 }; int cmd_found = 0; int nb_items; cmd = skip_space(cmd); if (strncmp(helpcmd, cmd, helpcmd_len) != 0) return 0; if (!isblank(cmd[helpcmd_len]) && cmd[helpcmd_len] != '\0') return 0; cmd = skip_space(cmd + helpcmd_len); if (cmd[0] == '\0') /* "help" with no argument always succeeds */ cmd_found = 1; list_for_each_entry(recmd, ®exp_cmd_list, entry) { if (cmd[0] == '\0' || pcre_exec(recmd->h_re, NULL, cmd, strlen(cmd), 0, 0, NULL, 0) >= 0) { cmd_found = 1; if (recmd->help) recmd->help(cmd, cln); } } nb_items = split(cmd, items); list_for_each_entry(sicmd, &simple_cmd_list, entry) { int indx = 0; int found = 1; while (indx < sicmd->hdr_len && indx < nb_items) { if (strcmp(sicmd->hdr[indx], items[indx]) != 0) { found = 0; break; } ++indx; } if (found) { cmd_found = 1; if (sicmd->help) sicmd->help(items, nb_items, cln); } } if (!cmd_found) cli_send(cln, MSG_INVAL_ERROR); return 1; } static int cli_process_regexp_cmd(struct cli_client_t *cln, int *err) { struct cli_regexp_cmd_t *recmd = NULL; char *cmd = (char *)cln->cmdline; int found = 0; int res; cmd = skip_space(cmd); list_for_each_entry(recmd, ®exp_cmd_list, entry) if (pcre_exec(recmd->re, NULL, cmd, strlen(cmd), 0, 0, NULL, 0) >= 0) { found = 1; res = recmd->exec(cmd, cln); if (res != CLI_CMD_OK) break; } if (found) *err = res; return found; } static int cli_process_simple_cmd(struct cli_client_t *cln, int *err) { struct cli_simple_cmd_t *sicmd = NULL; char *cmd = (char *)cln->cmdline; char *items[MAX_CMD_ITEMS] = { 0 }; int found = 0; int nb_items; int indx; int res; nb_items = split(cmd, items); list_for_each_entry(sicmd, &simple_cmd_list, entry) { if (sicmd->hdr_len <= 0 || nb_items < sicmd->hdr_len) continue; for (indx = 0; indx < sicmd->hdr_len; ++indx) { if (strcmp(sicmd->hdr[indx], items[indx]) != 0) break; } if (indx == sicmd->hdr_len) { found = 1; res = sicmd->exec(cmd, items, nb_items, cln); if (res != CLI_CMD_OK) break; } } if (found) *err = res; return found; } int __export cli_process_cmd(struct cli_client_t *cln) { int found; int err; if (cli_process_help_cmd(cln)) return 0; found = cli_process_regexp_cmd(cln, &err); if (found && err != CLI_CMD_OK) goto out_found; found |= cli_process_simple_cmd(cln, &err); if (found) goto out_found; if (cli_send(cln, MSG_UNKNOWN_CMD)) return -1; else return 0; out_found: switch (err) { case CLI_CMD_EXIT: cln->disconnect(cln); case CLI_CMD_FAILED: return -1; case CLI_CMD_SYNTAX: cli_send(cln, MSG_SYNTAX_ERROR); return 0; case CLI_CMD_INVAL: cli_send(cln, MSG_INVAL_ERROR); return 0; case CLI_CMD_OK: return 0; } return -1; } static void load_config(void) { const char *opt; if (conf_cli_passwd) _free(conf_cli_passwd); opt = conf_get_opt("cli", "password"); if (opt) conf_cli_passwd = _strdup(opt); else conf_cli_passwd = NULL; if (conf_cli_prompt && conf_cli_prompt != def_cli_prompt) _free(conf_cli_prompt); opt = conf_get_opt("cli", "prompt"); if (opt) conf_cli_prompt = _strdup(opt); else conf_cli_prompt = (char *)def_cli_prompt; } static void init(void) { load_config(); triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); } DEFINE_INIT(10, init);