From 830ed213750e924094e98290801be554c5684604 Mon Sep 17 00:00:00 2001 From: Alex 'AdUser' Z Date: Thu, 14 Jan 2021 00:43:29 +1000 Subject: [PATCH] * refactor f2b_cmd_t --- src/commands.c | 235 +++++++++++++++++++++-------------------------- src/commands.h | 71 ++++++-------- t/CMakeLists.txt | 2 +- t/t_cmd.c | 61 ++++++------ 4 files changed, 166 insertions(+), 203 deletions(-) diff --git a/src/commands.c b/src/commands.c index 62eb6f5..765b70f 100644 --- a/src/commands.c +++ b/src/commands.c @@ -5,211 +5,182 @@ * published by the Free Software Foundation. */ #include "common.h" +#include "buf.h" #include "commands.h" -struct f2b_cmd_t { +struct cmd_desc { + enum f2b_command_type type; const short int argc; const short int tokenc; const char *help; - const char *tokens[CMD_TOKENS_MAX]; -} commands[CMD_MAX_NUMBER] = { - [CMD_NONE] = { - .argc = 0, .tokenc = 0, - .tokens = { NULL }, - .help = "Unspecified command" - }, - [CMD_RESP] = { - .argc = 1, .tokenc = 0, - .tokens = { NULL }, - .help = "Command response, used internally", - }, - [CMD_HELP] = { + const char *tokens[CMD_TOKENS_MAXCOUNT]; +} commands[] = { + { + .type = CMD_HELP, .argc = 0, .tokenc = 1, .tokens = { "help", NULL }, .help = "Show available commands", - }, - [CMD_PING] = { - .argc = 0, .tokenc = 1, - .tokens = { "ping", NULL }, - .help = "Check the connection", - }, - [CMD_STATUS] = { + }, { + .type = CMD_STATUS, .argc = 0, .tokenc = 1, .tokens = { "status", NULL }, .help = "Show general stats and jails list", - }, - [CMD_LOG_ROTATE] = { - .argc = 0, .tokenc = 2, - .tokens = { "log", "rotate", NULL }, - .help = "Reopen daemon's own log file", - }, - [CMD_RELOAD] = { + }, { + .type = CMD_RELOAD, .argc = 0, .tokenc = 1, .tokens = { "reload", NULL }, .help = "Reload own config, all jails will be reset.", - }, - [CMD_SHUTDOWN] = { + }, { + .type = CMD_SHUTDOWN, .argc = 0, .tokenc = 1, .tokens = { "shutdown", NULL }, .help = "Gracefully terminate f2b daemon", - }, - [CMD_LOG_LEVEL] = { + }, { + .type = CMD_LOG_ROTATE, + .argc = 0, .tokenc = 2, + .tokens = { "log", "rotate", NULL }, + .help = "Reopen daemon's own log file", + }, { + .type = CMD_LOG_LEVEL, .argc = 1, .tokenc = 3, .tokens = { "log", "level", "", NULL }, .help = "Change maximum level of logged messages", - }, - [CMD_JAIL_STATUS] = { + }, { + .type = CMD_JAIL_STATUS, .argc = 1, .tokenc = 3, .tokens = { "jail", "", "status", NULL }, .help = "Show status and stats of given jail", - }, - [CMD_JAIL_SET] = { + }, { + .type = CMD_JAIL_SET, .argc = 3, .tokenc = 5, .tokens = { "jail", "", "set", "", "", NULL }, .help = "Set parameter of given jail", - }, - [CMD_JAIL_IP_STATUS] = { + }, { + .type = CMD_JAIL_IP_STATUS, .argc = 2, .tokenc = 5, .tokens = { "jail", "", "ip", "status", "", NULL }, .help = "Show ip status in given jail", - }, - [CMD_JAIL_IP_BAN] = { + }, { + .type = CMD_JAIL_IP_BAN, .argc = 2, .tokenc = 5, .tokens = { "jail", "", "ip", "ban", "", NULL }, .help = "Forcefully ban some ip in given jail", - }, - [CMD_JAIL_IP_RELEASE] = { + }, { + .type = CMD_JAIL_IP_RELEASE, .argc = 2, .tokenc = 5, .tokens = { "jail", "", "ip", "release", "", NULL }, .help = "Forcefully release some ip in given jail", - }, - [CMD_JAIL_FILTER_STATS] = { + }, { + .type = CMD_JAIL_FILTER_STATS, .argc = 1, .tokenc = 4, .tokens = { "jail", "", "filter", "stats", NULL }, .help = "Show matches stats for jail regexps", - }, - [CMD_JAIL_FILTER_RELOAD] = { + }, { + .type = CMD_JAIL_FILTER_RELOAD, .argc = 1, .tokenc = 4, .tokens = { "jail", "", "filter", "reload", NULL }, .help = "Reload regexps for given jail", - }, + }, { + .type = CMD_UNKNOWN, + .argc = 0, .tokenc = 0, + .tokens = { NULL }, + .help = "", + } }; -void +static char *help = NULL; + +const char * f2b_cmd_help() { - struct f2b_cmd_t *cmd = NULL; + f2b_buf_t buf; const char **p = NULL; - fputs("Available commands:\n\n", stdout); - for (size_t i = CMD_PING; i < CMD_MAX_NUMBER; i++) { - cmd = &commands[i]; - if (cmd->tokens[0] == NULL) - continue; - for (p = cmd->tokens; *p != NULL; p++) - fprintf(stdout, "%s ", *p); - fprintf(stdout, "\n\t%s\n\n", cmd->help); + if (help) + return help; + + if (!f2b_buf_alloc(&buf, 8192)) + return "internal error: can't allocate memory\n"; + f2b_buf_append(&buf, "Available commands:\n\n", 0); + for (struct cmd_desc *cmd = commands; cmd->type != CMD_UNKNOWN; cmd++) { + for (p = cmd->tokens; *p != NULL; p++) { + f2b_buf_append(&buf, *p, 0); + f2b_buf_append(&buf, " ", 1); + } + f2b_buf_append(&buf, "\n\t", 2); + f2b_buf_append(&buf, cmd->help, 0); + f2b_buf_append(&buf, "\n\n", 2); } - return; + help = strndup(buf.data, buf.used); + f2b_buf_free(&buf); + return help; } -void -f2b_cmd_append_arg(char *buf, size_t bufsize, const char *arg) { - assert(buf != NULL); - assert(arg != NULL); - strlcat(buf, arg, bufsize); - strlcat(buf, "\n", bufsize); -} +bool +f2b_cmd_parse(f2b_cmd_t *cmd, const char *src) { + char *p = NULL; -/** - * @brief Parse command from line - * @param buf Buffer for command parameters - * @param bufsize SSize of buffer above - * @param src Line taken from user input - * @return Type of parsed command or CMD_NONE if no matches - */ -enum f2b_cmd_type -f2b_cmd_parse(char *buf, size_t bufsize, const char *src) { - size_t tokenc = 0; /* tokens count */ - char *tokens[CMD_TOKENS_MAX] = { NULL }; - char line[INPUT_LINE_MAX]; - char *p; + assert(cmd != NULL); + assert(src != NULL); - assert(line != NULL); /* strip leading spaces */ - while (isblank(*line)) + while (isblank(*src)) src++; - /* strip trailing spaces, newlines, etc */ - strlcpy(line, src, sizeof(line)); - for (size_t l = strlen(line); l >= 1 && isspace(line[l - 1]); l--) - line[l - 1] = '\0'; - if (line[0] == '\0') - return CMD_NONE; /* empty string */ + f2b_buf_alloc(&cmd->data, strlen(src) + 1); + f2b_buf_append(&cmd->data, src, 0); /* simple commands without args */ - if (strcmp(line, "ping") == 0) { return CMD_PING; } - else if (strcmp(line, "help") == 0) { return CMD_HELP; } - else if (strcmp(line, "status") == 0) { return CMD_STATUS; } - else if (strcmp(line, "reload") == 0) { return CMD_RELOAD; } - else if (strcmp(line, "shutdown") == 0) { return CMD_SHUTDOWN; } + cmd->argc = 1; /* we has at least one arg */ + cmd->args[0] = cmd->data.data; + if (strcmp(src, "help") == 0) { cmd->type = CMD_HELP; return true; } + else if (strcmp(src, "status") == 0) { cmd->type = CMD_STATUS; return true; } + else if (strcmp(src, "reload") == 0) { cmd->type = CMD_RELOAD; return true; } + else if (strcmp(src, "shutdown") == 0) { cmd->type = CMD_SHUTDOWN; return true; } /* split string to tokens */ - tokenc = 1; /* we has at least one token */ - tokens[0] = strtok_r(line, " \t", &p); - for (size_t i = 1; i < CMD_TOKENS_MAX; i++) { - tokens[i] = strtok_r(NULL, " \t", &p); - if (tokens[i] == NULL) + p = cmd->data.data; + cmd->args[0] = strtok_r(cmd->data.data, " \t", &p); + for (size_t i = 1; i < CMD_TOKENS_MAXCOUNT; i++) { + cmd->args[i] = strtok_r(NULL, " \t", &p); + if (cmd->args[i] == NULL) break; - tokenc++; + cmd->argc++; } - buf[0] = '\0'; - if (strcmp(line, "jail") == 0 && tokenc > 1) { + if (strcmp(cmd->args[0], "jail") == 0 && cmd->argc > 1) { /* commands for jail */ - f2b_cmd_append_arg(buf, bufsize, tokens[1]); - if (tokenc == 3 && strcmp(tokens[2], "status") == 0) { - return CMD_JAIL_STATUS; + if (cmd->argc == 3 && strcmp(cmd->args[2], "status") == 0) { + cmd->type = CMD_JAIL_STATUS; return true; } - if (tokenc == 5 && strcmp(tokens[2], "set") == 0) { - f2b_cmd_append_arg(buf, bufsize, tokens[3]); - f2b_cmd_append_arg(buf, bufsize, tokens[4]); - return CMD_JAIL_SET; + if (cmd->argc == 5 && strcmp(cmd->args[2], "set") == 0) { + cmd->type = CMD_JAIL_SET; return true; } - if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "status") == 0) { - f2b_cmd_append_arg(buf, bufsize, tokens[4]); - return CMD_JAIL_IP_STATUS; + if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "status") == 0) { + cmd->type = CMD_JAIL_IP_STATUS; return true; } - if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "ban") == 0) { - f2b_cmd_append_arg(buf, bufsize, tokens[4]); - return CMD_JAIL_IP_BAN; + if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "ban") == 0) { + cmd->type = CMD_JAIL_IP_BAN; return true; } - if (tokenc == 5 && strcmp(tokens[2], "ip") == 0 && strcmp(tokens[3], "release") == 0) { - f2b_cmd_append_arg(buf, bufsize, tokens[4]); - return CMD_JAIL_IP_RELEASE; + if (cmd->argc == 5 && strcmp(cmd->args[2], "ip") == 0 && strcmp(cmd->args[3], "release") == 0) { + cmd->type = CMD_JAIL_IP_RELEASE; return true; } - if (tokenc == 4 && strcmp(tokens[2], "filter") == 0 && strcmp(tokens[3], "stats") == 0) { - return CMD_JAIL_FILTER_STATS; + if (cmd->argc == 4 && strcmp(cmd->args[2], "filter") == 0 && strcmp(cmd->args[3], "stats") == 0) { + cmd->type = CMD_JAIL_FILTER_STATS; return true; } - if (tokenc == 4 && strcmp(tokens[2], "filter") == 0 && strcmp(tokens[3], "reload") == 0) { - return CMD_JAIL_FILTER_RELOAD; + if (cmd->argc == 4 && strcmp(cmd->args[2], "filter") == 0 && strcmp(cmd->args[3], "reload") == 0) { + cmd->type = CMD_JAIL_FILTER_RELOAD; return true; } - } else if (strcmp(line, "log") == 0 && tokenc > 1) { - if (tokenc == 2 && strcmp(tokens[1], "rotate") == 0) { - return CMD_LOG_ROTATE; + } else if (strcmp(cmd->args[0], "log") == 0 && cmd->argc > 1) { + if (cmd->argc == 2 && strcmp(cmd->args[1], "rotate") == 0) { + cmd->type = CMD_LOG_ROTATE; return true; } - if (tokenc == 3 && strcmp(tokens[1], "level") == 0) { - f2b_cmd_append_arg(buf, bufsize, tokens[2]); - return CMD_LOG_LEVEL; + if (cmd->argc == 3 && strcmp(cmd->args[1], "level") == 0) { + cmd->type = CMD_LOG_LEVEL; return true; } } + cmd->type = CMD_UNKNOWN; + f2b_buf_free(&cmd->data); - return CMD_NONE; -} - -bool -f2b_cmd_check_argc(enum f2b_cmd_type type, int argc) { - if (commands[type].argc == argc) - return true; return false; } diff --git a/src/commands.h b/src/commands.h index cc00bd7..194ea39 100644 --- a/src/commands.h +++ b/src/commands.h @@ -10,30 +10,23 @@ /** * @file * This header contains definition of control commands and routines - * for work with data buffer of control message + * for parsing user input */ -/** - * Maximum length of input line in client - * @note yes, i know about LINE_MAX - */ -#define INPUT_LINE_MAX 256 -/** Maximum count of data pieces in control message data buf */ -#define CMD_TOKENS_MAX 6 +#define CMD_TOKENS_MAXCOUNT 6 /**< Maximum count of data pieces in control message data buf */ +#define CMD_TOKENS_MAXSIZE 260 /**< parsed tokens */ -/** control command type */ -enum f2b_cmd_type { - CMD_NONE = 0, /**< unset */ - CMD_RESP, /**< response of command */ - CMD_HELP, /**< show help for commands (used internally by client) */ - CMD_PING = 8, /**< check connection */ - CMD_STATUS, /**< show general status of f2b daemon */ - CMD_LOG_ROTATE,/**< reopen logfile. works only if set `logdest = file` */ - CMD_RELOAD, /**< reload all jails */ - CMD_SHUTDOWN, /**< gracefull shutdown */ - CMD_LOG_LEVEL, /**< change maximum level of logged messages */ +enum f2b_command_type { + CMD_UNKNOWN = 0, /**< unset */ + CMD_HELP, /**< show help for commands */ + CMD_STATUS, /**< show general status of f2b daemon */ + CMD_RELOAD, /**< reload all jails */ + CMD_SHUTDOWN, /**< gracefull shutdown daemon */ + /* logging */ + CMD_LOG_ROTATE, /**< reopen logfile. (only for `logdest = file`) */ + CMD_LOG_LEVEL, /**< change maximum level of logged messages */ /* jail commands */ - CMD_JAIL_STATUS = 16, /**< show status of given jail */ + CMD_JAIL_STATUS, /**< show status of given jail */ CMD_JAIL_SET, /**< set parameter of given jail */ CMD_JAIL_IP_STATUS, /**< show status of given ip */ CMD_JAIL_IP_BAN, /**< force ban given ip */ @@ -43,36 +36,28 @@ enum f2b_cmd_type { CMD_MAX_NUMBER, /**< placeholder */ }; +/** control command type */ +typedef struct f2b_cmd_t { + enum f2b_command_type type; + int argc; + char *args[CMD_TOKENS_MAXCOUNT+1]; + f2b_buf_t data; +} f2b_cmd_t; + /** - * @brief Print to stdout help for defined commands + * @brief Returns multiline string with commands list and it's brief descriptions + * @returns constant multiline string */ -void f2b_cmd_help(); +const char * +f2b_cmd_help(); /** * @brief Try to parse user input - * @param buf Buffer of control message for storing parsed args - * @param bufsize Size of buffer size above + * @param cmd pointer to preallocated f2b_cmd_t struct * @param src Line with user input - * @returns @a CMD_NONE if parsing fails, or cmd type less than @a CMD_MAX_NUMBER on success - */ -enum f2b_cmd_type -f2b_cmd_parse(char *buf, size_t bufsize, const char *src); - -/** - * @brief Append data piece to data buffer of control message - * @param buf Buffer of control message for storing parsed args - * @param bufsize Size of buffer size above - * @param arg Piece to append - */ -void -f2b_cmd_append_arg(char *buf, size_t bufsize, const char *arg); -/** - * @brief Checks is args count match given command type - * @param type Command type - * @param argc Args count - * @returns true if matches, false if not + * @returns true on success, false otherwise */ bool -f2b_cmd_check_argc(enum f2b_cmd_type type, int argc); +f2b_cmd_parse(f2b_cmd_t *cmd, const char *src); #endif /* F2B_COMMANDS_H_ */ diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index ad6c2a7..e7c1093 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -3,7 +3,7 @@ enable_testing() set(SRC_DIR "../src") add_executable("t_buf" "t_buf.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c") -add_executable("t_cmd" "t_cmd.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/commands.c") +add_executable("t_cmd" "t_cmd.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/buf.c" "${SRC_DIR}/commands.c") add_executable("t_cmsg" "t_cmsg.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/cmsg.c") add_executable("t_matches" "t_matches.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c") add_executable("t_ipaddr" "t_ipaddr.c" "${SRC_DIR}/strlcpy.c" "${SRC_DIR}/matches.c" "${SRC_DIR}/ipaddr.c") diff --git a/t/t_cmd.c b/t/t_cmd.c index 708ff53..7194a89 100644 --- a/t/t_cmd.c +++ b/t/t_cmd.c @@ -1,34 +1,41 @@ #include "../src/common.h" +#include "../src/buf.h" #include "../src/commands.h" int main() { - char buf[1024]; - const char *line; - - UNUSED(line); - - buf[0] = '\0'; - f2b_cmd_append_arg(buf, sizeof(buf), "42"); - assert(strcmp(buf, "42\n") == 0); - - line = "status"; - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_STATUS); - - line = "statu"; /* no such command */ - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); - - line = "jail test"; /* incomplete command */ - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); - - buf[0] = '\0'; - line = "jail test status"; - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_STATUS); - assert(strcmp(buf, "test\n") == 0); - - buf[0] = '\0'; - line = "jail test set bantime 7200"; - assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_SET); - assert(strcmp(buf, "test\nbantime\n7200\n") == 0); + f2b_cmd_t cmd; + + assert(f2b_cmd_parse(&cmd, "status") == true); + assert(cmd.type = CMD_STATUS); + assert(cmd.argc == 1); + assert(strcmp(cmd.args[0], "status") == 0); + assert(cmd.data.used == 6); /* "status" */ + f2b_buf_free(&cmd.data); + + assert(f2b_cmd_parse(&cmd, "stat") == false); /* no such command */ + assert(cmd.type == CMD_UNKNOWN); + + assert(f2b_cmd_parse(&cmd, "jail test") == false); /* incomplete command */ + assert(cmd.type == CMD_UNKNOWN); + + assert(f2b_cmd_parse(&cmd, "jail test status") == true); + assert(cmd.type == CMD_JAIL_STATUS); + assert(cmd.argc == 3); + assert(strcmp(cmd.args[0], "jail") == 0); + assert(strcmp(cmd.args[1], "test") == 0); + assert(strcmp(cmd.args[2], "status") == 0); + assert(cmd.data.used == 16); /* "jail\0test\0status" */ + f2b_buf_free(&cmd.data); + + assert(f2b_cmd_parse(&cmd, "jail test set bantime 7200") == true); + assert(cmd.type == CMD_JAIL_SET); + assert(cmd.argc == 5); + assert(strcmp(cmd.args[0], "jail") == 0); + assert(strcmp(cmd.args[1], "test") == 0); + assert(strcmp(cmd.args[2], "set") == 0); + assert(strcmp(cmd.args[3], "bantime") == 0); + assert(strcmp(cmd.args[4], "7200") == 0); + f2b_buf_free(&cmd.data); return EXIT_SUCCESS; }